@cachly-dev/mcp-server 0.10.47 → 0.10.49
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.github/workflows/eas-build-android.yml +58 -0
- package/.github/workflows/eas-update.yml +55 -0
- package/STRATEGY.md +209 -0
- package/apps/travel-chaos-organizer/.env.example +49 -0
- package/apps/travel-chaos-organizer/DEPLOY.md +328 -0
- package/apps/travel-chaos-organizer/Makefile +57 -0
- package/apps/travel-chaos-organizer/README.md +159 -0
- package/apps/travel-chaos-organizer/backend/.env.example +10 -0
- package/apps/travel-chaos-organizer/backend/Dockerfile +25 -0
- package/apps/travel-chaos-organizer/backend/alembic/env.py +56 -0
- package/apps/travel-chaos-organizer/backend/alembic/script.py.mako +24 -0
- package/apps/travel-chaos-organizer/backend/alembic/versions/.gitkeep +0 -0
- package/apps/travel-chaos-organizer/backend/alembic/versions/0001_stripe_customer_id.py +24 -0
- package/apps/travel-chaos-organizer/backend/alembic/versions/0002_telegram_chat_id.py +24 -0
- package/apps/travel-chaos-organizer/backend/alembic.ini +39 -0
- package/apps/travel-chaos-organizer/backend/app/__init__.py +0 -0
- package/apps/travel-chaos-organizer/backend/app/auth/__init__.py +0 -0
- package/apps/travel-chaos-organizer/backend/app/auth/keycloak.py +81 -0
- package/apps/travel-chaos-organizer/backend/app/config.py +66 -0
- package/apps/travel-chaos-organizer/backend/app/db/__init__.py +0 -0
- package/apps/travel-chaos-organizer/backend/app/db/database.py +20 -0
- package/apps/travel-chaos-organizer/backend/app/db/migrations/001_init.sql +71 -0
- package/apps/travel-chaos-organizer/backend/app/db/migrations/002_telemetry_freemium.sql +31 -0
- package/apps/travel-chaos-organizer/backend/app/db/migrations/003_telegram_chat_id.sql +4 -0
- package/apps/travel-chaos-organizer/backend/app/db/migrations/004_stripe_and_email.sql +5 -0
- package/apps/travel-chaos-organizer/backend/app/limiter.py +4 -0
- package/apps/travel-chaos-organizer/backend/app/logging_config.py +25 -0
- package/apps/travel-chaos-organizer/backend/app/main.py +77 -0
- package/apps/travel-chaos-organizer/backend/app/models/__init__.py +0 -0
- package/apps/travel-chaos-organizer/backend/app/models/schemas.py +142 -0
- package/apps/travel-chaos-organizer/backend/app/routers/__init__.py +0 -0
- package/apps/travel-chaos-organizer/backend/app/routers/admin.py +237 -0
- package/apps/travel-chaos-organizer/backend/app/routers/cache.py +13 -0
- package/apps/travel-chaos-organizer/backend/app/routers/events.py +29 -0
- package/apps/travel-chaos-organizer/backend/app/routers/export.py +122 -0
- package/apps/travel-chaos-organizer/backend/app/routers/inbox.py +122 -0
- package/apps/travel-chaos-organizer/backend/app/routers/items.py +130 -0
- package/apps/travel-chaos-organizer/backend/app/routers/mail.py +108 -0
- package/apps/travel-chaos-organizer/backend/app/routers/parse.py +303 -0
- package/apps/travel-chaos-organizer/backend/app/routers/payments.py +179 -0
- package/apps/travel-chaos-organizer/backend/app/routers/trips.py +134 -0
- package/apps/travel-chaos-organizer/backend/app/routers/users.py +169 -0
- package/apps/travel-chaos-organizer/backend/app/routers/waitlist.py +37 -0
- package/apps/travel-chaos-organizer/backend/app/services/__init__.py +0 -0
- package/apps/travel-chaos-organizer/backend/app/services/cache.py +131 -0
- package/apps/travel-chaos-organizer/backend/app/services/drip.py +128 -0
- package/apps/travel-chaos-organizer/backend/app/services/email.py +61 -0
- package/apps/travel-chaos-organizer/backend/app/services/errors.py +44 -0
- package/apps/travel-chaos-organizer/backend/app/services/notifier.py +10 -0
- package/apps/travel-chaos-organizer/backend/app/services/ollama.py +101 -0
- package/apps/travel-chaos-organizer/backend/app/services/parser.py +22 -0
- package/apps/travel-chaos-organizer/backend/app/services/quota.py +85 -0
- package/apps/travel-chaos-organizer/backend/app/services/sentry.py +11 -0
- package/apps/travel-chaos-organizer/backend/app/services/telemetry.py +35 -0
- package/apps/travel-chaos-organizer/backend/pytest.ini +4 -0
- package/apps/travel-chaos-organizer/backend/requirements-dev.txt +6 -0
- package/apps/travel-chaos-organizer/backend/requirements.txt +25 -0
- package/apps/travel-chaos-organizer/backend/tests/__init__.py +0 -0
- package/apps/travel-chaos-organizer/backend/tests/conftest.py +223 -0
- package/apps/travel-chaos-organizer/backend/tests/test_admin.py +99 -0
- package/apps/travel-chaos-organizer/backend/tests/test_cache.py +142 -0
- package/apps/travel-chaos-organizer/backend/tests/test_drip.py +32 -0
- package/apps/travel-chaos-organizer/backend/tests/test_e2e_chain.py +121 -0
- package/apps/travel-chaos-organizer/backend/tests/test_export.py +48 -0
- package/apps/travel-chaos-organizer/backend/tests/test_inbox.py +107 -0
- package/apps/travel-chaos-organizer/backend/tests/test_items.py +138 -0
- package/apps/travel-chaos-organizer/backend/tests/test_mail.py +113 -0
- package/apps/travel-chaos-organizer/backend/tests/test_ollama.py +145 -0
- package/apps/travel-chaos-organizer/backend/tests/test_parse_url.py +98 -0
- package/apps/travel-chaos-organizer/backend/tests/test_parser.py +72 -0
- package/apps/travel-chaos-organizer/backend/tests/test_payments.py +163 -0
- package/apps/travel-chaos-organizer/backend/tests/test_quota.py +46 -0
- package/apps/travel-chaos-organizer/backend/tests/test_search.py +67 -0
- package/apps/travel-chaos-organizer/backend/tests/test_trips.py +123 -0
- package/apps/travel-chaos-organizer/backend/tests/test_users.py +52 -0
- package/apps/travel-chaos-organizer/backups/.gitkeep +0 -0
- package/apps/travel-chaos-organizer/caddy/Caddyfile +27 -0
- package/apps/travel-chaos-organizer/docker-compose.node1.yml +31 -0
- package/apps/travel-chaos-organizer/docker-compose.override.yml +14 -0
- package/apps/travel-chaos-organizer/docker-compose.yml +128 -0
- package/apps/travel-chaos-organizer/frontend/.env.example +13 -0
- package/apps/travel-chaos-organizer/frontend/app/(app)/_layout.tsx +80 -0
- package/apps/travel-chaos-organizer/frontend/app/(app)/inbox.tsx +166 -0
- package/apps/travel-chaos-organizer/frontend/app/(app)/search.tsx +130 -0
- package/apps/travel-chaos-organizer/frontend/app/(app)/settings.tsx +381 -0
- package/apps/travel-chaos-organizer/frontend/app/(app)/trips/[id].tsx +346 -0
- package/apps/travel-chaos-organizer/frontend/app/(app)/trips.tsx +363 -0
- package/apps/travel-chaos-organizer/frontend/app/(app)/upgrade/cancel.tsx +34 -0
- package/apps/travel-chaos-organizer/frontend/app/(app)/upgrade/success.tsx +35 -0
- package/apps/travel-chaos-organizer/frontend/app/(auth)/login.tsx +91 -0
- package/apps/travel-chaos-organizer/frontend/app/_layout.tsx +71 -0
- package/apps/travel-chaos-organizer/frontend/app/auth.tsx +29 -0
- package/apps/travel-chaos-organizer/frontend/app/index.tsx +16 -0
- package/apps/travel-chaos-organizer/frontend/app/onboarding.tsx +119 -0
- package/apps/travel-chaos-organizer/frontend/app.config.ts +54 -0
- package/apps/travel-chaos-organizer/frontend/assets/adaptive-icon.svg +66 -0
- package/apps/travel-chaos-organizer/frontend/assets/icon.svg +79 -0
- package/apps/travel-chaos-organizer/frontend/assets/splash.svg +15 -0
- package/apps/travel-chaos-organizer/frontend/components/BottomSheet.tsx +102 -0
- package/apps/travel-chaos-organizer/frontend/components/ConfettiCannon.tsx +107 -0
- package/apps/travel-chaos-organizer/frontend/components/ConfirmDialog.tsx +109 -0
- package/apps/travel-chaos-organizer/frontend/components/ErrorBoundary.tsx +45 -0
- package/apps/travel-chaos-organizer/frontend/components/FileUploadButton.tsx +19 -0
- package/apps/travel-chaos-organizer/frontend/components/ImportSheet.tsx +155 -0
- package/apps/travel-chaos-organizer/frontend/components/OfflineBadge.tsx +55 -0
- package/apps/travel-chaos-organizer/frontend/components/QRCodeViewer.tsx +80 -0
- package/apps/travel-chaos-organizer/frontend/components/ScoreRing.tsx +39 -0
- package/apps/travel-chaos-organizer/frontend/components/Screen.tsx +39 -0
- package/apps/travel-chaos-organizer/frontend/components/Skeleton.tsx +70 -0
- package/apps/travel-chaos-organizer/frontend/components/SwipeableInboxItem.tsx +121 -0
- package/apps/travel-chaos-organizer/frontend/components/SwipeableTimelineItem.tsx +70 -0
- package/apps/travel-chaos-organizer/frontend/components/TimelineItem.tsx +73 -0
- package/apps/travel-chaos-organizer/frontend/components/TimelineItemDetail.tsx +130 -0
- package/apps/travel-chaos-organizer/frontend/components/Toast.tsx +60 -0
- package/apps/travel-chaos-organizer/frontend/components/ToastContext.tsx +42 -0
- package/apps/travel-chaos-organizer/frontend/components/TripCard.tsx +146 -0
- package/apps/travel-chaos-organizer/frontend/components/TripQRModal.tsx +70 -0
- package/apps/travel-chaos-organizer/frontend/components/UpgradeBanner.tsx +44 -0
- package/apps/travel-chaos-organizer/frontend/eas.json +22 -0
- package/apps/travel-chaos-organizer/frontend/hooks/useInbox.ts +24 -0
- package/apps/travel-chaos-organizer/frontend/hooks/useNetworkSync.ts +60 -0
- package/apps/travel-chaos-organizer/frontend/hooks/useTrips.ts +112 -0
- package/apps/travel-chaos-organizer/frontend/lib/api.ts +223 -0
- package/apps/travel-chaos-organizer/frontend/lib/auth.ts +93 -0
- package/apps/travel-chaos-organizer/frontend/lib/db.ts +86 -0
- package/apps/travel-chaos-organizer/frontend/lib/fileCache.ts +54 -0
- package/apps/travel-chaos-organizer/frontend/lib/haptics.ts +13 -0
- package/apps/travel-chaos-organizer/frontend/lib/notifications.ts +112 -0
- package/apps/travel-chaos-organizer/frontend/lib/offlineQueue.ts +145 -0
- package/apps/travel-chaos-organizer/frontend/lib/quota.tsx +43 -0
- package/apps/travel-chaos-organizer/frontend/lib/sentry.ts +28 -0
- package/apps/travel-chaos-organizer/frontend/lib/shareIntent.ts +44 -0
- package/apps/travel-chaos-organizer/frontend/package-lock.json +13037 -0
- package/apps/travel-chaos-organizer/frontend/package.json +44 -0
- package/apps/travel-chaos-organizer/frontend/tsconfig.json +23 -0
- package/apps/travel-chaos-organizer/keycloak/realm-export.json +59 -0
- package/apps/travel-chaos-organizer/landing/index.html +773 -0
- package/apps/travel-chaos-organizer/scripts/backup.sh +14 -0
- package/apps/travel-chaos-organizer/scripts/configure-env.sh +82 -0
- package/apps/travel-chaos-organizer/scripts/deploy.sh +54 -0
- package/apps/travel-chaos-organizer/scripts/health-check.sh +36 -0
- package/apps/travel-chaos-organizer/scripts/migrate.sh +7 -0
- package/apps/travel-chaos-organizer/scripts/server-setup.sh +85 -0
- package/apps/travel-chaos-organizer/scripts/smoke-test.sh +29 -0
- package/apps/travel-chaos-organizer/scripts/verify-tracking.sh +71 -0
- package/apps/travel-chaos-organizer/telegram-bot/.env.example +3 -0
- package/apps/travel-chaos-organizer/telegram-bot/Dockerfile +6 -0
- package/apps/travel-chaos-organizer/telegram-bot/bot.py +504 -0
- package/apps/travel-chaos-organizer/telegram-bot/requirements.txt +3 -0
- package/dist/packages/telegram-notify/client.d.ts +19 -0
- package/dist/packages/telegram-notify/client.js +75 -0
- package/dist/packages/telegram-notify/client.js.map +1 -0
- package/dist/src/__tests__/auth.test.js.map +1 -0
- package/dist/{__tests__ → src/__tests__}/brain-flow.test.js +143 -2
- package/dist/src/__tests__/brain-flow.test.js.map +1 -0
- package/dist/src/__tests__/confidence.test.js.map +1 -0
- package/dist/src/__tests__/e2e/api-auth.test.js.map +1 -0
- package/dist/src/__tests__/e2e/api-health.test.js.map +1 -0
- package/dist/src/__tests__/e2e/cli.test.js.map +1 -0
- package/dist/src/__tests__/e2e/config.js.map +1 -0
- package/dist/src/__tests__/e2e/funnel.test.js.map +1 -0
- package/dist/src/__tests__/e2e/mcp-tools.test.js.map +1 -0
- package/dist/src/__tests__/e2e/provisioning.test.js.map +1 -0
- package/dist/{__tests__ → src/__tests__}/handlers-unit.test.js +1 -1
- package/dist/src/__tests__/handlers-unit.test.js.map +1 -0
- package/dist/src/__tests__/namespace.test.js.map +1 -0
- package/dist/src/auth.js.map +1 -0
- package/dist/src/ckg.js.map +1 -0
- package/dist/src/confidence.js.map +1 -0
- package/dist/src/embeddings.js.map +1 -0
- package/dist/src/handlers/advanced.js.map +1 -0
- package/dist/{handlers → src/handlers}/brain.js +135 -31
- package/dist/src/handlers/brain.js.map +1 -0
- package/dist/src/handlers/cache.js.map +1 -0
- package/dist/{handlers → src/handlers}/context.js +9 -4
- package/dist/src/handlers/context.js.map +1 -0
- package/dist/{handlers → src/handlers}/fedbrain.d.ts +6 -0
- package/dist/{handlers → src/handlers}/fedbrain.js +15 -2
- package/dist/src/handlers/fedbrain.js.map +1 -0
- package/dist/src/handlers/instances.js.map +1 -0
- package/dist/{handlers → src/handlers}/roadmap.js +8 -4
- package/dist/src/handlers/roadmap.js.map +1 -0
- package/dist/src/handlers/syndicate.js.map +1 -0
- package/dist/src/handlers/tco.d.ts +16 -0
- package/dist/src/handlers/tco.js +303 -0
- package/dist/src/handlers/tco.js.map +1 -0
- package/dist/{handlers → src/handlers}/team.js +1 -1
- package/dist/src/handlers/team.js.map +1 -0
- package/dist/{index.js → src/index.js} +145 -97
- package/dist/src/index.js.map +1 -0
- package/dist/src/namespace.js.map +1 -0
- package/dist/src/notifier.d.ts +5 -0
- package/dist/src/notifier.js +6 -0
- package/dist/src/notifier.js.map +1 -0
- package/dist/{search.js → src/search.js} +3 -3
- package/dist/src/search.js.map +1 -0
- package/dist/src/search.test.js.map +1 -0
- package/dist/{tools.d.ts → src/tools.d.ts} +196 -0
- package/dist/{tools.js → src/tools.js} +178 -0
- package/dist/src/tools.js.map +1 -0
- package/dist/src/utils.js.map +1 -0
- package/package.json +3 -2
- package/packages/telegram-notify/SPLIT_PLAN.md +106 -0
- package/packages/telegram-notify/client.ts +88 -0
- package/packages/telegram-notify/emojis.json +22 -0
- package/packages/telegram-notify/pyproject.toml +17 -0
- package/packages/telegram-notify/telegram_notify/__init__.py +3 -0
- package/packages/telegram-notify/telegram_notify/client.py +87 -0
- package/packages/telegram-notify/telegram_notify/emojis.json +22 -0
- package/scripts/generate-icons.js +28 -0
- package/smithery.yaml +21 -10
- package/src/__tests__/brain-flow.test.ts +176 -2
- package/src/__tests__/handlers-unit.test.ts +1 -1
- package/src/handlers/brain.ts +139 -32
- package/src/handlers/context.ts +9 -4
- package/src/handlers/fedbrain.ts +17 -2
- package/src/handlers/roadmap.ts +8 -4
- package/src/handlers/tco.ts +347 -0
- package/src/handlers/team.ts +1 -1
- package/src/index.ts +137 -147
- package/src/notifier.ts +5 -0
- package/src/search.ts +3 -3
- package/src/tools.ts +191 -0
- package/tsconfig.json +2 -3
- package/dist/__tests__/auth.test.js.map +0 -1
- package/dist/__tests__/brain-flow.test.js.map +0 -1
- package/dist/__tests__/brain-predict-failures.test.d.ts +0 -17
- package/dist/__tests__/brain-predict-failures.test.js +0 -339
- package/dist/__tests__/brain-predict-failures.test.js.map +0 -1
- package/dist/__tests__/confidence.test.js.map +0 -1
- package/dist/__tests__/e2e/api-auth.test.js.map +0 -1
- package/dist/__tests__/e2e/api-health.test.js.map +0 -1
- package/dist/__tests__/e2e/cli.test.js.map +0 -1
- package/dist/__tests__/e2e/config.js.map +0 -1
- package/dist/__tests__/e2e/funnel.test.js.map +0 -1
- package/dist/__tests__/e2e/mcp-tools.test.js.map +0 -1
- package/dist/__tests__/e2e/provisioning.test.js.map +0 -1
- package/dist/__tests__/handlers-unit.test.js.map +0 -1
- package/dist/__tests__/namespace.test.js.map +0 -1
- package/dist/auth.js.map +0 -1
- package/dist/ckg.js.map +0 -1
- package/dist/confidence.js.map +0 -1
- package/dist/embeddings.js.map +0 -1
- package/dist/handlers/advanced.js.map +0 -1
- package/dist/handlers/brain.js.map +0 -1
- package/dist/handlers/cache.js.map +0 -1
- package/dist/handlers/context.js.map +0 -1
- package/dist/handlers/fedbrain.js.map +0 -1
- package/dist/handlers/instances.js.map +0 -1
- package/dist/handlers/roadmap.js.map +0 -1
- package/dist/handlers/syndicate.js.map +0 -1
- package/dist/handlers/team.js.map +0 -1
- package/dist/index.js.map +0 -1
- package/dist/namespace.js.map +0 -1
- package/dist/search.js.map +0 -1
- package/dist/search.test.js.map +0 -1
- package/dist/tools.js.map +0 -1
- package/dist/utils.js.map +0 -1
- package/src/__tests__/brain-predict-failures.test.ts +0 -430
- /package/dist/{__tests__ → src/__tests__}/auth.test.d.ts +0 -0
- /package/dist/{__tests__ → src/__tests__}/auth.test.js +0 -0
- /package/dist/{__tests__ → src/__tests__}/brain-flow.test.d.ts +0 -0
- /package/dist/{__tests__ → src/__tests__}/confidence.test.d.ts +0 -0
- /package/dist/{__tests__ → src/__tests__}/confidence.test.js +0 -0
- /package/dist/{__tests__ → src/__tests__}/e2e/api-auth.test.d.ts +0 -0
- /package/dist/{__tests__ → src/__tests__}/e2e/api-auth.test.js +0 -0
- /package/dist/{__tests__ → src/__tests__}/e2e/api-health.test.d.ts +0 -0
- /package/dist/{__tests__ → src/__tests__}/e2e/api-health.test.js +0 -0
- /package/dist/{__tests__ → src/__tests__}/e2e/cli.test.d.ts +0 -0
- /package/dist/{__tests__ → src/__tests__}/e2e/cli.test.js +0 -0
- /package/dist/{__tests__ → src/__tests__}/e2e/config.d.ts +0 -0
- /package/dist/{__tests__ → src/__tests__}/e2e/config.js +0 -0
- /package/dist/{__tests__ → src/__tests__}/e2e/funnel.test.d.ts +0 -0
- /package/dist/{__tests__ → src/__tests__}/e2e/funnel.test.js +0 -0
- /package/dist/{__tests__ → src/__tests__}/e2e/mcp-tools.test.d.ts +0 -0
- /package/dist/{__tests__ → src/__tests__}/e2e/mcp-tools.test.js +0 -0
- /package/dist/{__tests__ → src/__tests__}/e2e/provisioning.test.d.ts +0 -0
- /package/dist/{__tests__ → src/__tests__}/e2e/provisioning.test.js +0 -0
- /package/dist/{__tests__ → src/__tests__}/handlers-unit.test.d.ts +0 -0
- /package/dist/{__tests__ → src/__tests__}/namespace.test.d.ts +0 -0
- /package/dist/{__tests__ → src/__tests__}/namespace.test.js +0 -0
- /package/dist/{auth.d.ts → src/auth.d.ts} +0 -0
- /package/dist/{auth.js → src/auth.js} +0 -0
- /package/dist/{ckg.d.ts → src/ckg.d.ts} +0 -0
- /package/dist/{ckg.js → src/ckg.js} +0 -0
- /package/dist/{confidence.d.ts → src/confidence.d.ts} +0 -0
- /package/dist/{confidence.js → src/confidence.js} +0 -0
- /package/dist/{embeddings.d.ts → src/embeddings.d.ts} +0 -0
- /package/dist/{embeddings.js → src/embeddings.js} +0 -0
- /package/dist/{handlers → src/handlers}/advanced.d.ts +0 -0
- /package/dist/{handlers → src/handlers}/advanced.js +0 -0
- /package/dist/{handlers → src/handlers}/brain.d.ts +0 -0
- /package/dist/{handlers → src/handlers}/cache.d.ts +0 -0
- /package/dist/{handlers → src/handlers}/cache.js +0 -0
- /package/dist/{handlers → src/handlers}/context.d.ts +0 -0
- /package/dist/{handlers → src/handlers}/instances.d.ts +0 -0
- /package/dist/{handlers → src/handlers}/instances.js +0 -0
- /package/dist/{handlers → src/handlers}/roadmap.d.ts +0 -0
- /package/dist/{handlers → src/handlers}/syndicate.d.ts +0 -0
- /package/dist/{handlers → src/handlers}/syndicate.js +0 -0
- /package/dist/{handlers → src/handlers}/team.d.ts +0 -0
- /package/dist/{index.d.ts → src/index.d.ts} +0 -0
- /package/dist/{namespace.d.ts → src/namespace.d.ts} +0 -0
- /package/dist/{namespace.js → src/namespace.js} +0 -0
- /package/dist/{search.d.ts → src/search.d.ts} +0 -0
- /package/dist/{search.test.d.ts → src/search.test.d.ts} +0 -0
- /package/dist/{search.test.js → src/search.test.js} +0 -0
- /package/dist/{utils.d.ts → src/utils.d.ts} +0 -0
- /package/dist/{utils.js → src/utils.js} +0 -0
|
@@ -0,0 +1,773 @@
|
|
|
1
|
+
<!DOCTYPE html>
|
|
2
|
+
<html lang="de">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="UTF-8" />
|
|
5
|
+
<!--
|
|
6
|
+
TCO Landing Page Config — add before this script tag on your server:
|
|
7
|
+
<script>
|
|
8
|
+
window.TCO_API_URL = 'https://your-tco-backend.example.com';
|
|
9
|
+
window.TCO_PLAUSIBLE_DOMAIN = 'your-tco-domain.example.com';
|
|
10
|
+
</script>
|
|
11
|
+
-->
|
|
12
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
13
|
+
<title>Travel Chaos Organizer — Nie wieder am Gate nach der Buchungsnummer suchen.</title>
|
|
14
|
+
<meta name="description" content="TCO verwandelt Screenshots, PDFs und Booking-E-Mails in eine saubere Reise-Timeline. KI-powered, selbst gehostet, offline-fähig." />
|
|
15
|
+
<meta property="og:title" content="Travel Chaos Organizer" />
|
|
16
|
+
<meta property="og:description" content="Nie wieder am Gate nach der Buchungsnummer suchen. TCO bringt Ordnung in dein Reisechaos." />
|
|
17
|
+
<meta property="og:type" content="website" />
|
|
18
|
+
<link rel="icon" href="../frontend/assets/icon.svg" type="image/svg+xml" />
|
|
19
|
+
<style>
|
|
20
|
+
*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }
|
|
21
|
+
|
|
22
|
+
:root {
|
|
23
|
+
--bg: #0f0f1a;
|
|
24
|
+
--bg2: #1a1a2e;
|
|
25
|
+
--border: #2a2a4e;
|
|
26
|
+
--accent: #4f46e5;
|
|
27
|
+
--glow: #4f46e522;
|
|
28
|
+
--text: #e2e8f0;
|
|
29
|
+
--muted: #6666aa;
|
|
30
|
+
--faint: #3a3a5e;
|
|
31
|
+
--purple: #a5b4fc;
|
|
32
|
+
--green: #34d399;
|
|
33
|
+
--red: #f87171;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
html { scroll-behavior: smooth; }
|
|
37
|
+
body {
|
|
38
|
+
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
|
|
39
|
+
background: var(--bg); color: var(--text); line-height: 1.6; overflow-x: hidden;
|
|
40
|
+
}
|
|
41
|
+
.container { max-width: 1060px; margin: 0 auto; padding: 0 24px; }
|
|
42
|
+
.fade-up { opacity: 0; transform: translateY(28px); transition: opacity .65s ease, transform .65s ease; }
|
|
43
|
+
.fade-up.visible { opacity: 1; transform: translateY(0); }
|
|
44
|
+
|
|
45
|
+
/* ─── NAV ─── */
|
|
46
|
+
nav {
|
|
47
|
+
position: sticky; top: 0; z-index: 100;
|
|
48
|
+
background: #0f0f1add; backdrop-filter: blur(18px);
|
|
49
|
+
border-bottom: 1px solid var(--border); padding: 14px 0;
|
|
50
|
+
}
|
|
51
|
+
.nav-inner { display: flex; align-items: center; justify-content: space-between; }
|
|
52
|
+
.nav-logo { font-size: 17px; font-weight: 800; color: var(--text); text-decoration: none; display: flex; align-items: center; gap: 8px; }
|
|
53
|
+
.nav-logo em { font-style: normal; color: var(--purple); }
|
|
54
|
+
.nav-right { display: flex; align-items: center; gap: 10px; }
|
|
55
|
+
.lang-btn { background: none; border: 1px solid var(--border); color: var(--muted); padding: 5px 11px; border-radius: 6px; cursor: pointer; font-size: 12px; font-weight: 600; transition: all .2s; }
|
|
56
|
+
.lang-btn.active, .lang-btn:hover { border-color: var(--accent); color: var(--purple); }
|
|
57
|
+
.nav-cta { background: var(--accent); color: #fff; text-decoration: none; padding: 9px 20px; border-radius: 8px; font-size: 14px; font-weight: 700; transition: opacity .2s; }
|
|
58
|
+
.nav-cta:hover { opacity: .85; }
|
|
59
|
+
|
|
60
|
+
/* ─── HERO (SB7: Character + Problem + Promise + CTA) ─── */
|
|
61
|
+
.hero {
|
|
62
|
+
padding: 96px 0 80px; text-align: center;
|
|
63
|
+
background: radial-gradient(ellipse 80% 55% at 50% -5%, #4f46e51a 0%, transparent 68%);
|
|
64
|
+
}
|
|
65
|
+
.hero-eyebrow {
|
|
66
|
+
display: inline-flex; align-items: center; gap: 8px;
|
|
67
|
+
background: var(--glow); border: 1px solid #4f46e540;
|
|
68
|
+
color: var(--purple); font-size: 12px; font-weight: 700; letter-spacing: .5px;
|
|
69
|
+
padding: 6px 16px; border-radius: 99px; margin-bottom: 36px;
|
|
70
|
+
}
|
|
71
|
+
.hero-eyebrow .dot { width: 7px; height: 7px; border-radius: 50%; background: var(--accent); flex-shrink: 0; }
|
|
72
|
+
h1 { font-size: clamp(34px, 5.5vw, 66px); font-weight: 900; letter-spacing: -2px; line-height: 1.08; margin-bottom: 24px; }
|
|
73
|
+
h1 em { font-style: normal; color: var(--purple); }
|
|
74
|
+
.hero-sub { font-size: clamp(16px, 2.2vw, 20px); color: var(--muted); max-width: 580px; margin: 0 auto 48px; line-height: 1.6; }
|
|
75
|
+
.btn-row { display: flex; gap: 14px; justify-content: center; flex-wrap: wrap; }
|
|
76
|
+
.btn-primary { background: var(--accent); color: #fff; text-decoration: none; padding: 16px 36px; border-radius: 12px; font-size: 16px; font-weight: 700; box-shadow: 0 4px 28px #4f46e550; transition: transform .15s, box-shadow .15s; }
|
|
77
|
+
.btn-primary:hover { transform: translateY(-2px); box-shadow: 0 8px 36px #4f46e565; }
|
|
78
|
+
.btn-ghost { background: none; border: 1px solid var(--border); color: var(--text); text-decoration: none; padding: 16px 32px; border-radius: 12px; font-size: 16px; font-weight: 600; transition: border-color .2s; }
|
|
79
|
+
.btn-ghost:hover { border-color: var(--accent); }
|
|
80
|
+
|
|
81
|
+
/* Phone */
|
|
82
|
+
.mockup-wrap { margin: 72px auto 0; max-width: 320px; position: relative; }
|
|
83
|
+
.glow-ring { position: absolute; inset: -3px; border-radius: 43px; background: conic-gradient(from 0deg, transparent 55%, #4f46e57a, transparent 78%); animation: spin 7s linear infinite; z-index: -1; }
|
|
84
|
+
@keyframes spin { to { transform: rotate(360deg); } }
|
|
85
|
+
.phone { background: var(--bg2); border: 2px solid var(--border); border-radius: 40px; padding: 10px; box-shadow: 0 40px 80px #00000090, 0 0 0 1px #ffffff07; overflow: hidden; }
|
|
86
|
+
.phone::before { content: ''; position: absolute; top: 10px; left: 50%; transform: translateX(-50%); width: 90px; height: 24px; background: var(--bg); border-radius: 0 0 16px 16px; z-index: 2; }
|
|
87
|
+
.pscreen { background: var(--bg); border-radius: 32px; overflow: hidden; }
|
|
88
|
+
.ph { background: var(--bg2); padding: 44px 14px 12px; border-bottom: 1px solid var(--border); display: flex; align-items: center; justify-content: space-between; }
|
|
89
|
+
.ph-title { font-size: 15px; font-weight: 700; }
|
|
90
|
+
.plist { padding: 12px 10px; display: flex; flex-direction: column; gap: 8px; }
|
|
91
|
+
.pi { background: var(--bg2); border: 1px solid var(--border); border-radius: 12px; padding: 10px 12px; display: flex; align-items: center; gap: 10px; }
|
|
92
|
+
.pi-ico { width: 34px; height: 34px; border-radius: 9px; display: flex; align-items: center; justify-content: center; font-size: 17px; flex-shrink: 0; }
|
|
93
|
+
.pi-ico.f { background: #4f46e520; } .pi-ico.h { background: #7c3aed20; } .pi-ico.t { background: #06b6d420; }
|
|
94
|
+
.pi-title { font-size: 12px; font-weight: 700; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
|
|
95
|
+
.pi-sub { font-size: 10px; color: var(--muted); margin-top: 1px; }
|
|
96
|
+
.pi-ref { font-size: 9px; color: var(--purple); font-family: monospace; margin-top: 2px; }
|
|
97
|
+
.pbar { background: var(--bg2); border-top: 1px solid var(--border); padding: 8px 10px; display: flex; gap: 6px; }
|
|
98
|
+
.pbtn { flex: 1; background: var(--bg); border: 1px solid var(--border); border-radius: 9px; padding: 7px 4px; text-align: center; font-size: 9px; color: var(--muted); }
|
|
99
|
+
.pbtn span { display: block; font-size: 14px; margin-bottom: 1px; }
|
|
100
|
+
|
|
101
|
+
/* ─── SECTION BASE ─── */
|
|
102
|
+
section { padding: 96px 0; }
|
|
103
|
+
.s-label { font-size: 11px; font-weight: 700; letter-spacing: 2px; text-transform: uppercase; color: var(--accent); margin-bottom: 10px; }
|
|
104
|
+
h2 { font-size: clamp(26px, 3.8vw, 46px); font-weight: 800; letter-spacing: -1px; line-height: 1.15; margin-bottom: 16px; }
|
|
105
|
+
.s-sub { font-size: 17px; color: var(--muted); line-height: 1.65; max-width: 580px; }
|
|
106
|
+
.divider { width: 48px; height: 3px; background: var(--accent); border-radius: 2px; margin: 28px 0; }
|
|
107
|
+
.alt-bg { background: var(--bg2); }
|
|
108
|
+
|
|
109
|
+
/* ─── PROBLEM (Villain + External + Internal + Philosophical) ─── */
|
|
110
|
+
.villain-banner {
|
|
111
|
+
background: #7c3aed18; border: 1px solid #7c3aed40;
|
|
112
|
+
border-radius: 16px; padding: 28px 32px; margin-top: 56px;
|
|
113
|
+
display: flex; align-items: flex-start; gap: 20px;
|
|
114
|
+
}
|
|
115
|
+
.villain-icon { font-size: 36px; flex-shrink: 0; }
|
|
116
|
+
.villain-title { font-size: 18px; font-weight: 800; margin-bottom: 6px; color: var(--text); }
|
|
117
|
+
.villain-text { font-size: 15px; color: var(--muted); line-height: 1.6; }
|
|
118
|
+
.problem-cols { display: grid; grid-template-columns: repeat(auto-fit, minmax(280px, 1fr)); gap: 20px; margin-top: 40px; }
|
|
119
|
+
.prob-card { background: var(--bg); border: 1px solid var(--border); border-radius: 16px; padding: 28px; }
|
|
120
|
+
.prob-card.ext { border-top: 3px solid #4f46e5; }
|
|
121
|
+
.prob-card.int { border-top: 3px solid #7c3aed; }
|
|
122
|
+
.prob-card.phi { border-top: 3px solid #06b6d4; }
|
|
123
|
+
.prob-type { font-size: 10px; font-weight: 700; letter-spacing: 1.5px; text-transform: uppercase; margin-bottom: 10px; }
|
|
124
|
+
.prob-card.ext .prob-type { color: #4f46e5; }
|
|
125
|
+
.prob-card.int .prob-type { color: #a78bfa; }
|
|
126
|
+
.prob-card.phi .prob-type { color: #22d3ee; }
|
|
127
|
+
.prob-title { font-size: 17px; font-weight: 700; margin-bottom: 8px; }
|
|
128
|
+
.prob-text { font-size: 14px; color: var(--muted); line-height: 1.6; }
|
|
129
|
+
|
|
130
|
+
/* ─── GUIDE (Empathy first, Authority second) ─── */
|
|
131
|
+
.empathy-box {
|
|
132
|
+
background: var(--glow); border: 1px solid #4f46e540;
|
|
133
|
+
border-radius: 20px; padding: 40px 44px; max-width: 680px;
|
|
134
|
+
margin: 56px auto 0; text-align: center;
|
|
135
|
+
}
|
|
136
|
+
.empathy-quote { font-size: clamp(18px, 2.5vw, 26px); font-weight: 700; line-height: 1.4; margin-bottom: 16px; color: var(--text); }
|
|
137
|
+
.empathy-quote em { font-style: normal; color: var(--purple); }
|
|
138
|
+
.empathy-sub { font-size: 15px; color: var(--muted); line-height: 1.6; }
|
|
139
|
+
.authority-strip { display: flex; flex-wrap: wrap; justify-content: center; gap: 12px; margin-top: 56px; }
|
|
140
|
+
.auth-pill {
|
|
141
|
+
background: var(--bg2); border: 1px solid var(--border);
|
|
142
|
+
border-radius: 10px; padding: 14px 24px; text-align: center;
|
|
143
|
+
display: flex; align-items: center; gap: 10px;
|
|
144
|
+
}
|
|
145
|
+
.auth-val { font-size: 22px; font-weight: 800; color: var(--purple); }
|
|
146
|
+
.auth-label { font-size: 12px; color: var(--muted); line-height: 1.4; }
|
|
147
|
+
|
|
148
|
+
/* ─── PLAN (3 simple steps, each removes a fear) ─── */
|
|
149
|
+
.plan-steps { display: grid; grid-template-columns: repeat(auto-fit, minmax(260px, 1fr)); gap: 0; margin-top: 64px; position: relative; }
|
|
150
|
+
.plan-step { padding: 36px 32px; border: 1px solid var(--border); border-radius: 0; position: relative; }
|
|
151
|
+
.plan-step:first-child { border-radius: 16px 0 0 16px; }
|
|
152
|
+
.plan-step:last-child { border-radius: 0 16px 16px 0; }
|
|
153
|
+
.plan-step + .plan-step { border-left: none; }
|
|
154
|
+
@media (max-width: 800px) {
|
|
155
|
+
.plan-steps { grid-template-columns: 1fr; }
|
|
156
|
+
.plan-step:first-child { border-radius: 16px 16px 0 0; }
|
|
157
|
+
.plan-step:last-child { border-radius: 0 0 16px 16px; }
|
|
158
|
+
.plan-step + .plan-step { border-left: 1px solid var(--border); border-top: none; }
|
|
159
|
+
}
|
|
160
|
+
.plan-num { width: 40px; height: 40px; border-radius: 50%; background: var(--glow); border: 1px solid #4f46e550; color: var(--purple); font-size: 16px; font-weight: 800; display: flex; align-items: center; justify-content: center; margin-bottom: 20px; }
|
|
161
|
+
.plan-icon { font-size: 30px; margin-bottom: 14px; }
|
|
162
|
+
.plan-title { font-size: 17px; font-weight: 700; margin-bottom: 8px; }
|
|
163
|
+
.plan-text { font-size: 14px; color: var(--muted); line-height: 1.6; }
|
|
164
|
+
.plan-fear { font-size: 12px; color: var(--green); margin-top: 12px; display: flex; align-items: center; gap: 6px; }
|
|
165
|
+
.plan-fear::before { content: '✓'; font-weight: 700; }
|
|
166
|
+
|
|
167
|
+
/* ─── CTA BAND (mid-page) ─── */
|
|
168
|
+
.cta-band {
|
|
169
|
+
background: linear-gradient(135deg, #4f46e518 0%, #7c3aed12 100%);
|
|
170
|
+
border-top: 1px solid #4f46e530; border-bottom: 1px solid #4f46e530;
|
|
171
|
+
}
|
|
172
|
+
.cta-band-inner { display: flex; align-items: center; justify-content: space-between; gap: 32px; flex-wrap: wrap; padding: 48px 0; }
|
|
173
|
+
.cta-band-text { flex: 1; min-width: 280px; }
|
|
174
|
+
.cta-band h3 { font-size: clamp(20px, 2.8vw, 32px); font-weight: 800; letter-spacing: -.5px; margin-bottom: 8px; }
|
|
175
|
+
.cta-band p { font-size: 15px; color: var(--muted); }
|
|
176
|
+
|
|
177
|
+
/* ─── FAILURE vs SUCCESS ─── */
|
|
178
|
+
.stakes-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 24px; margin-top: 56px; }
|
|
179
|
+
@media (max-width: 680px) { .stakes-grid { grid-template-columns: 1fr; } }
|
|
180
|
+
.stakes-col { border-radius: 20px; padding: 32px; }
|
|
181
|
+
.stakes-col.fail { background: #f8717110; border: 1px solid #f8717130; }
|
|
182
|
+
.stakes-col.win { background: #34d39910; border: 1px solid #34d39930; }
|
|
183
|
+
.stakes-header { display: flex; align-items: center; gap: 10px; margin-bottom: 20px; }
|
|
184
|
+
.stakes-icon { font-size: 24px; }
|
|
185
|
+
.stakes-label { font-size: 13px; font-weight: 700; letter-spacing: .5px; }
|
|
186
|
+
.stakes-col.fail .stakes-label { color: var(--red); }
|
|
187
|
+
.stakes-col.win .stakes-label { color: var(--green); }
|
|
188
|
+
.stakes-list { list-style: none; display: flex; flex-direction: column; gap: 10px; }
|
|
189
|
+
.stakes-list li { font-size: 14px; line-height: 1.5; display: flex; gap: 10px; }
|
|
190
|
+
.stakes-col.fail .stakes-list li::before { content: '✗'; color: var(--red); font-weight: 700; flex-shrink: 0; }
|
|
191
|
+
.stakes-col.win .stakes-list li::before { content: '✓'; color: var(--green); font-weight: 700; flex-shrink: 0; }
|
|
192
|
+
|
|
193
|
+
/* ─── CACHLY ─── */
|
|
194
|
+
.cachly-row { display: flex; align-items: center; gap: 56px; flex-wrap: wrap; margin-top: 64px; }
|
|
195
|
+
.cachly-text { flex: 1; min-width: 280px; }
|
|
196
|
+
.cachly-card { background: var(--bg); border: 1px solid #4f46e540; border-radius: 20px; padding: 28px; flex-shrink: 0; min-width: 220px; text-align: center; }
|
|
197
|
+
.cachly-pill { display: inline-flex; align-items: center; gap: 7px; background: var(--glow); border: 1px solid #4f46e540; color: var(--purple); font-size: 11px; font-weight: 700; letter-spacing: 1px; text-transform: uppercase; padding: 5px 13px; border-radius: 99px; margin-bottom: 16px; }
|
|
198
|
+
.cachly-pill .dot { width: 6px; height: 6px; border-radius: 50%; background: var(--accent); }
|
|
199
|
+
.cachly-stat-row { display: flex; gap: 28px; margin-top: 24px; flex-wrap: wrap; }
|
|
200
|
+
.cs-val { font-size: 24px; font-weight: 800; color: var(--purple); }
|
|
201
|
+
.cs-lbl { font-size: 12px; color: var(--muted); margin-top: 2px; }
|
|
202
|
+
|
|
203
|
+
/* ─── EMAIL FORM ─── */
|
|
204
|
+
.email-form { display: flex; gap: 10px; justify-content: center; flex-wrap: wrap; max-width: 480px; margin: 0 auto; }
|
|
205
|
+
.email-input {
|
|
206
|
+
flex: 1; min-width: 220px; background: var(--bg2); border: 1px solid var(--border);
|
|
207
|
+
color: var(--text); padding: 15px 18px; border-radius: 12px; font-size: 15px;
|
|
208
|
+
outline: none; transition: border-color .2s;
|
|
209
|
+
}
|
|
210
|
+
.email-input:focus { border-color: var(--accent); }
|
|
211
|
+
.email-input::placeholder { color: var(--muted); }
|
|
212
|
+
.email-submit {
|
|
213
|
+
background: var(--accent); color: #fff; border: none; cursor: pointer;
|
|
214
|
+
padding: 15px 28px; border-radius: 12px; font-size: 15px; font-weight: 700;
|
|
215
|
+
box-shadow: 0 4px 24px #4f46e548; transition: transform .15s, box-shadow .15s; white-space: nowrap;
|
|
216
|
+
}
|
|
217
|
+
.email-submit:hover:not(:disabled) { transform: translateY(-2px); box-shadow: 0 8px 32px #4f46e560; }
|
|
218
|
+
.email-submit:disabled { opacity: .6; cursor: default; }
|
|
219
|
+
.form-feedback { font-size: 14px; margin-top: 10px; text-align: center; min-height: 20px; }
|
|
220
|
+
.form-feedback.ok { color: var(--green); }
|
|
221
|
+
.form-feedback.err { color: var(--red); }
|
|
222
|
+
.form-caption { font-size: 12px; color: var(--faint); margin-top: 8px; text-align: center; }
|
|
223
|
+
|
|
224
|
+
/* ─── FINAL CTA (Success vision) ─── */
|
|
225
|
+
.final-cta {
|
|
226
|
+
padding: 112px 0; text-align: center;
|
|
227
|
+
background: radial-gradient(ellipse 70% 60% at 50% 100%, #4f46e514 0%, transparent 70%);
|
|
228
|
+
}
|
|
229
|
+
.final-cta h2 { font-size: clamp(30px, 5vw, 58px); letter-spacing: -1.5px; margin-bottom: 20px; }
|
|
230
|
+
.final-cta .s-sub { margin: 0 auto 40px; }
|
|
231
|
+
.cta-note { font-size: 13px; color: var(--faint); margin-top: 18px; }
|
|
232
|
+
.cta-note a { color: var(--muted); text-decoration: none; border-bottom: 1px solid var(--faint); }
|
|
233
|
+
|
|
234
|
+
/* ─── FOOTER ─── */
|
|
235
|
+
footer { border-top: 1px solid var(--border); padding: 28px 0; }
|
|
236
|
+
.foot-inner { display: flex; align-items: center; justify-content: space-between; flex-wrap: wrap; gap: 12px; }
|
|
237
|
+
.foot-copy { font-size: 12px; color: var(--faint); }
|
|
238
|
+
.foot-links { display: flex; gap: 20px; }
|
|
239
|
+
.foot-links a { font-size: 12px; color: var(--muted); text-decoration: none; }
|
|
240
|
+
.foot-links a:hover { color: var(--text); }
|
|
241
|
+
|
|
242
|
+
@media (max-width: 640px) {
|
|
243
|
+
section { padding: 64px 0; }
|
|
244
|
+
.hero { padding: 60px 0 48px; }
|
|
245
|
+
.empathy-box { padding: 28px 24px; }
|
|
246
|
+
.plan-step:first-child { border-radius: 16px 16px 0 0; }
|
|
247
|
+
.plan-step:last-child { border-radius: 0 0 16px 16px; }
|
|
248
|
+
.plan-step + .plan-step { border-left: 1px solid var(--border); border-top: none; }
|
|
249
|
+
}
|
|
250
|
+
</style>
|
|
251
|
+
<!-- Plausible Analytics — replace data-domain with your domain, or remove if self-hosting -->
|
|
252
|
+
<script>
|
|
253
|
+
// Set window.TCO_PLAUSIBLE_DOMAIN to your domain to enable Plausible
|
|
254
|
+
// e.g., <script>window.TCO_PLAUSIBLE_DOMAIN='tco.yourapp.com'</script> before this file
|
|
255
|
+
if (window.TCO_PLAUSIBLE_DOMAIN) {
|
|
256
|
+
var s = document.createElement('script');
|
|
257
|
+
s.defer = true;
|
|
258
|
+
s.setAttribute('data-domain', window.TCO_PLAUSIBLE_DOMAIN);
|
|
259
|
+
s.src = 'https://plausible.io/js/script.outbound-links.js';
|
|
260
|
+
document.head.appendChild(s);
|
|
261
|
+
window.plausible = window.plausible || function() {
|
|
262
|
+
(window.plausible.q = window.plausible.q || []).push(arguments);
|
|
263
|
+
};
|
|
264
|
+
}
|
|
265
|
+
</script>
|
|
266
|
+
</head>
|
|
267
|
+
<body>
|
|
268
|
+
|
|
269
|
+
<!-- NAV -->
|
|
270
|
+
<nav>
|
|
271
|
+
<div class="container nav-inner">
|
|
272
|
+
<a class="nav-logo" href="#">🗺️ Travel Chaos <em>Organizer</em></a>
|
|
273
|
+
<div class="nav-right">
|
|
274
|
+
<button class="lang-btn active" onclick="setLang('de')" id="btn-de">DE</button>
|
|
275
|
+
<button class="lang-btn" onclick="setLang('en')" id="btn-en">EN</button>
|
|
276
|
+
<a class="nav-cta" href="#waitlist">
|
|
277
|
+
<span data-de="Early Access →" data-en="Early Access →">Early Access →</span>
|
|
278
|
+
</a>
|
|
279
|
+
</div>
|
|
280
|
+
</div>
|
|
281
|
+
</nav>
|
|
282
|
+
|
|
283
|
+
<!-- ═══ 1 · HERO — Character + Problem + Guide Promise + CTA ═══ -->
|
|
284
|
+
<section class="hero">
|
|
285
|
+
<div class="container">
|
|
286
|
+
<div class="hero-eyebrow fade-up">
|
|
287
|
+
<span class="dot"></span>
|
|
288
|
+
<span data-de="Open Source · KI-powered · Selbst gehostet" data-en="Open Source · AI-powered · Self-hosted">Open Source · KI-powered · Selbst gehostet</span>
|
|
289
|
+
</div>
|
|
290
|
+
|
|
291
|
+
<h1 class="fade-up">
|
|
292
|
+
<span data-de="Nie wieder am Gate<br>nach der Buchungsnummer<br><em>suchen.</em>" data-en="Never search for your<br>booking number<br><em>at the gate again.</em>">
|
|
293
|
+
Nie wieder am Gate<br>nach der Buchungsnummer<br><em>suchen.</em>
|
|
294
|
+
</span>
|
|
295
|
+
</h1>
|
|
296
|
+
|
|
297
|
+
<p class="hero-sub fade-up"
|
|
298
|
+
data-de="Du bist Vielreisender — kein Dokumentenverwalter. Wirf deine Tickets, E-Mails und PDFs rein. TCO baut daraus eine saubere Timeline. Automatisch."
|
|
299
|
+
data-en="You're a frequent traveler — not a document manager. Drop in your tickets, emails and PDFs. TCO builds a clean timeline. Automatically.">
|
|
300
|
+
Du bist Vielreisender — kein Dokumentenverwalter. Wirf deine Tickets, E-Mails und PDFs rein. TCO baut daraus eine saubere Timeline. Automatisch.
|
|
301
|
+
</p>
|
|
302
|
+
|
|
303
|
+
<div class="fade-up" id="waitlist">
|
|
304
|
+
<form class="email-form" onsubmit="submitEmail(event,'hero')">
|
|
305
|
+
<input class="email-input" type="email" required id="email-hero"
|
|
306
|
+
data-placeholder-de="deine@email.de" data-placeholder-en="your@email.com"
|
|
307
|
+
placeholder="deine@email.de"
|
|
308
|
+
data-de="deine@email.de" data-en="your@email.com" />
|
|
309
|
+
<button class="email-submit" type="submit" id="btn-hero">
|
|
310
|
+
<span data-de="Chaos beenden →" data-en="End the chaos →">Chaos beenden →</span>
|
|
311
|
+
</button>
|
|
312
|
+
</form>
|
|
313
|
+
<div class="form-feedback" id="fb-hero"></div>
|
|
314
|
+
<div class="form-caption" data-de="Kein Spam. Kein Abo. Nur Updates wenn's wirklich wichtig ist." data-en="No spam. No subscription. Only updates when it really matters.">Kein Spam. Kein Abo. Nur Updates wenn's wirklich wichtig ist.</div>
|
|
315
|
+
</div>
|
|
316
|
+
<div class="fade-up" style="margin-top:16px">
|
|
317
|
+
<a class="btn-ghost" href="#plan" style="font-size:14px;padding:10px 22px">
|
|
318
|
+
<span data-de="Wie es funktioniert ↓" data-en="See how it works ↓">Wie es funktioniert ↓</span>
|
|
319
|
+
</a>
|
|
320
|
+
</div>
|
|
321
|
+
|
|
322
|
+
<!-- Phone mockup -->
|
|
323
|
+
<div class="mockup-wrap fade-up">
|
|
324
|
+
<div class="glow-ring"></div>
|
|
325
|
+
<div class="phone">
|
|
326
|
+
<div class="pscreen">
|
|
327
|
+
<div class="ph">
|
|
328
|
+
<span style="color:var(--accent);font-size:12px">← <span data-de="Zurück" data-en="Back">Zurück</span></span>
|
|
329
|
+
<span class="ph-title">Timeline</span>
|
|
330
|
+
<span style="font-size:17px">🗑️</span>
|
|
331
|
+
</div>
|
|
332
|
+
<div class="plist">
|
|
333
|
+
<div class="pi">
|
|
334
|
+
<div class="pi-ico f">✈️</div>
|
|
335
|
+
<div style="flex:1;min-width:0">
|
|
336
|
+
<div class="pi-title">LH 400 · FRA → JFK</div>
|
|
337
|
+
<div class="pi-sub">15. Aug · 10:30</div>
|
|
338
|
+
<div class="pi-ref">ABC123</div>
|
|
339
|
+
</div>
|
|
340
|
+
</div>
|
|
341
|
+
<div class="pi">
|
|
342
|
+
<div class="pi-ico h">🏨</div>
|
|
343
|
+
<div style="flex:1;min-width:0">
|
|
344
|
+
<div class="pi-title">Hilton Midtown New York</div>
|
|
345
|
+
<div class="pi-sub">15.–20. Aug · Check-in 15:00</div>
|
|
346
|
+
<div class="pi-ref">HTL-99281</div>
|
|
347
|
+
</div>
|
|
348
|
+
</div>
|
|
349
|
+
<div class="pi">
|
|
350
|
+
<div class="pi-ico t">🚆</div>
|
|
351
|
+
<div style="flex:1;min-width:0">
|
|
352
|
+
<div class="pi-title">Amtrak NYC → Boston</div>
|
|
353
|
+
<div class="pi-sub">20. Aug · 09:15</div>
|
|
354
|
+
<div class="pi-ref">TKT-44210</div>
|
|
355
|
+
</div>
|
|
356
|
+
</div>
|
|
357
|
+
</div>
|
|
358
|
+
<div class="pbar">
|
|
359
|
+
<div class="pbtn"><span>📄</span>PDF</div>
|
|
360
|
+
<div class="pbtn"><span>🖼️</span>Foto</div>
|
|
361
|
+
<div class="pbtn"><span>📝</span><span data-de="Text" data-en="Text">Text</span></div>
|
|
362
|
+
</div>
|
|
363
|
+
</div>
|
|
364
|
+
</div>
|
|
365
|
+
</div>
|
|
366
|
+
</div>
|
|
367
|
+
</section>
|
|
368
|
+
|
|
369
|
+
<!-- ═══ 2 · PROBLEM — Villain + External + Internal + Philosophical ═══ -->
|
|
370
|
+
<section>
|
|
371
|
+
<div class="container">
|
|
372
|
+
<div class="fade-up">
|
|
373
|
+
<p class="s-label" data-de="Das Problem" data-en="The Problem">Das Problem</p>
|
|
374
|
+
<h2 data-de="Dokumentenchaos ist der Feind jeder entspannten Reise." data-en="Document chaos is the enemy of every relaxed trip.">
|
|
375
|
+
Dokumentenchaos ist der Feind<br>jeder entspannten Reise.
|
|
376
|
+
</h2>
|
|
377
|
+
<p class="s-sub"
|
|
378
|
+
data-de="Nicht das Reisen ist stressig. Es ist das Suchen. Das Wühlen. Das Panisch-Tippen in eine Suchbox — während alle anderen schon längst durch die Security sind."
|
|
379
|
+
data-en="It's not the travel that's stressful. It's the searching. The digging. The panicked typing into a search box — while everyone else is already through security.">
|
|
380
|
+
Nicht das Reisen ist stressig. Es ist das Suchen. Das Wühlen. Das Panisch-Tippen in eine Suchbox — während alle anderen schon längst durch die Security sind.
|
|
381
|
+
</p>
|
|
382
|
+
</div>
|
|
383
|
+
|
|
384
|
+
<!-- The Villain -->
|
|
385
|
+
<div class="villain-banner fade-up">
|
|
386
|
+
<div class="villain-icon">😤</div>
|
|
387
|
+
<div>
|
|
388
|
+
<div class="villain-title" data-de="Der Schuldige: Verstreute Reisedokumente." data-en="The villain: scattered travel documents.">Der Schuldige: Verstreute Reisedokumente.</div>
|
|
389
|
+
<div class="villain-text"
|
|
390
|
+
data-de="Vier Buchungsportale, drei E-Mail-Adressen, zwei WhatsApp-Weiterleitungen, eine Galerie voller Screenshots — und die Boarding-Zeit in 8 Minuten."
|
|
391
|
+
data-en="Four booking portals, three email accounts, two WhatsApp forwards, a camera roll full of screenshots — and boarding in 8 minutes.">
|
|
392
|
+
Vier Buchungsportale, drei E-Mail-Adressen, zwei WhatsApp-Weiterleitungen, eine Galerie voller Screenshots — und die Boarding-Zeit in 8 Minuten.
|
|
393
|
+
</div>
|
|
394
|
+
</div>
|
|
395
|
+
</div>
|
|
396
|
+
|
|
397
|
+
<!-- 3-Level Problem -->
|
|
398
|
+
<div class="problem-cols">
|
|
399
|
+
<div class="prob-card ext fade-up">
|
|
400
|
+
<div class="prob-type" data-de="Äußeres Problem" data-en="External Problem">Äußeres Problem</div>
|
|
401
|
+
<div class="prob-title" data-de="Alles liegt überall." data-en="Everything is everywhere.">Alles liegt überall.</div>
|
|
402
|
+
<div class="prob-text"
|
|
403
|
+
data-de="Tickets in der Airline-App, Hotels im Postfach, Transfers im WhatsApp-Chat. Kein einziger Ort für alle Reisedaten."
|
|
404
|
+
data-en="Tickets in the airline app, hotels in your inbox, transfers in a WhatsApp chat. Not one single place for all your travel data.">
|
|
405
|
+
Tickets in der Airline-App, Hotels im Postfach, Transfers im WhatsApp-Chat. Kein einziger Ort für alle Reisedaten.
|
|
406
|
+
</div>
|
|
407
|
+
</div>
|
|
408
|
+
<div class="prob-card int fade-up">
|
|
409
|
+
<div class="prob-type" data-de="Inneres Problem" data-en="Internal Problem">Inneres Problem</div>
|
|
410
|
+
<div class="prob-title" data-de="Du fühlst dich unvorbereitet." data-en="You feel unprepared.">Du fühlst dich unvorbereitet.</div>
|
|
411
|
+
<div class="prob-text"
|
|
412
|
+
data-de="Ausgerechnet dann, wenn Ruhe gefragt ist — am Gate, beim Check-in, im Ausland — kommt das Gefühl: 'Warum habe ich das nicht im Griff?'"
|
|
413
|
+
data-en="At exactly the moment when you need calm — at the gate, at check-in, abroad — comes the feeling: 'Why don't I have this together?'">
|
|
414
|
+
Ausgerechnet dann, wenn Ruhe gefragt ist, kommt das Gefühl: "Warum habe ich das nicht im Griff?"
|
|
415
|
+
</div>
|
|
416
|
+
</div>
|
|
417
|
+
<div class="prob-card phi fade-up">
|
|
418
|
+
<div class="prob-type" data-de="Philosophisches Problem" data-en="Philosophical Problem">Philosophisches Problem</div>
|
|
419
|
+
<div class="prob-title" data-de="Du solltest Reisen genießen — nicht verwalten." data-en="You should enjoy travel — not manage it.">Du solltest Reisen genießen — nicht verwalten.</div>
|
|
420
|
+
<div class="prob-text"
|
|
421
|
+
data-de="Wer viel reist, verdient ein Tool, das die Bürokratie übernimmt. Reisen ist Erfahrung. Dokumente suchen ist Zeitverschwendung."
|
|
422
|
+
data-en="Frequent travelers deserve a tool that handles the admin. Travel is experience. Searching for documents is a waste of time.">
|
|
423
|
+
Wer viel reist, verdient ein Tool, das die Bürokratie übernimmt. Reisen ist Erfahrung. Dokumente suchen ist Zeitverschwendung.
|
|
424
|
+
</div>
|
|
425
|
+
</div>
|
|
426
|
+
</div>
|
|
427
|
+
</div>
|
|
428
|
+
</section>
|
|
429
|
+
|
|
430
|
+
<!-- ═══ 3 · GUIDE — Empathy first, then Authority ═══ -->
|
|
431
|
+
<section class="alt-bg">
|
|
432
|
+
<div class="container">
|
|
433
|
+
<div class="fade-up" style="text-align:center">
|
|
434
|
+
<p class="s-label" data-de="Der Guide" data-en="The Guide">Der Guide</p>
|
|
435
|
+
<h2 data-de="Wir kennen dieses Gefühl." data-en="We know that feeling.">Wir kennen dieses Gefühl.</h2>
|
|
436
|
+
</div>
|
|
437
|
+
|
|
438
|
+
<!-- Empathy first -->
|
|
439
|
+
<div class="empathy-box fade-up">
|
|
440
|
+
<div class="empathy-quote"
|
|
441
|
+
data-de="“Gate B42. Boarding in 3 Minuten. Die Buchungsnummer irgendwo in <em>200 ungelesenen E-Mails.</em>”"
|
|
442
|
+
data-en="“Gate B42. Boarding in 3 minutes. The booking number somewhere in <em>200 unread emails.</em>”">
|
|
443
|
+
“Gate B42. Boarding in 3 Minuten. Die Buchungsnummer irgendwo in <em>200 ungelesenen E-Mails.</em>”
|
|
444
|
+
</div>
|
|
445
|
+
<div class="empathy-sub"
|
|
446
|
+
data-de="TCO wurde genau für diesen Moment gebaut — und für alle Momente davor, in denen man hätte vorbereitet sein können. Du bist nicht das Problem. Das Chaos ist das Problem."
|
|
447
|
+
data-en="TCO was built for exactly this moment — and for all the moments before it where you could have been prepared. You're not the problem. The chaos is the problem.">
|
|
448
|
+
TCO wurde genau für diesen Moment gebaut — und für alle Momente davor. Du bist nicht das Problem. Das Chaos ist das Problem.
|
|
449
|
+
</div>
|
|
450
|
+
</div>
|
|
451
|
+
|
|
452
|
+
<!-- Authority strip -->
|
|
453
|
+
<div class="authority-strip fade-up">
|
|
454
|
+
<div class="auth-pill">
|
|
455
|
+
<div>
|
|
456
|
+
<div class="auth-val">71</div>
|
|
457
|
+
<div class="auth-label" data-de="Backend-Tests<br>alles grün" data-en="backend tests<br>all green">Backend-Tests<br>alles grün</div>
|
|
458
|
+
</div>
|
|
459
|
+
</div>
|
|
460
|
+
<div class="auth-pill">
|
|
461
|
+
<div>
|
|
462
|
+
<div class="auth-val">5</div>
|
|
463
|
+
<div class="auth-label" data-de="Eingabeformate:<br>PDF, Foto, Text, URL, E-Mail" data-en="input formats:<br>PDF, photo, text, URL, email">Eingabeformate:<br>PDF, Foto, Text, URL, E-Mail</div>
|
|
464
|
+
</div>
|
|
465
|
+
</div>
|
|
466
|
+
<div class="auth-pill">
|
|
467
|
+
<div>
|
|
468
|
+
<div class="auth-val">0</div>
|
|
469
|
+
<div class="auth-label" data-de="Cloud-Abhängigkeiten.<br>Deine Daten bleiben bei dir." data-en="cloud dependencies.<br>Your data stays with you.">Cloud-Abhängigkeiten.<br>Deine Daten bleiben bei dir.</div>
|
|
470
|
+
</div>
|
|
471
|
+
</div>
|
|
472
|
+
<div class="auth-pill">
|
|
473
|
+
<div>
|
|
474
|
+
<div class="auth-val">MIT</div>
|
|
475
|
+
<div class="auth-label" data-de="Lizenz.<br>Open Source, für immer." data-en="license.<br>Open source, forever.">Lizenz.<br>Open Source, für immer.</div>
|
|
476
|
+
</div>
|
|
477
|
+
</div>
|
|
478
|
+
</div>
|
|
479
|
+
</div>
|
|
480
|
+
</section>
|
|
481
|
+
|
|
482
|
+
<!-- ═══ 4 · PLAN — 3 simple steps, each removes a fear ═══ -->
|
|
483
|
+
<section id="plan">
|
|
484
|
+
<div class="container">
|
|
485
|
+
<div class="fade-up">
|
|
486
|
+
<p class="s-label" data-de="Der Plan" data-en="The Plan">Der Plan</p>
|
|
487
|
+
<h2 data-de="In drei Schritten zu einer sauberen Timeline." data-en="Three steps to a clean timeline.">In drei Schritten zu einer sauberen Timeline.</h2>
|
|
488
|
+
<p class="s-sub"
|
|
489
|
+
data-de="Kein Konfigurationsmarathon. Kein Abtippen. Kein Lernen neuer Tools. Einfach reinwerfen."
|
|
490
|
+
data-en="No configuration marathon. No manual typing. No learning new tools. Just drop it in.">
|
|
491
|
+
Kein Konfigurationsmarathon. Kein Abtippen. Kein Lernen neuer Tools. Einfach reinwerfen.
|
|
492
|
+
</p>
|
|
493
|
+
</div>
|
|
494
|
+
|
|
495
|
+
<div class="plan-steps fade-up">
|
|
496
|
+
<div class="plan-step">
|
|
497
|
+
<div class="plan-icon">📥</div>
|
|
498
|
+
<div class="plan-num">1</div>
|
|
499
|
+
<div class="plan-title" data-de="Dokument hineinwerfen" data-en="Drop your document in">Dokument hineinwerfen</div>
|
|
500
|
+
<div class="plan-text"
|
|
501
|
+
data-de="Screenshot aus der Galerie, PDF aus dem Postfach, Text aus der Buchungs-E-Mail, URL einer Buchungsseite — alles wird akzeptiert, nichts muss vorbereitet werden."
|
|
502
|
+
data-en="Screenshot from your gallery, PDF from your inbox, text from a booking email, URL of a booking page — everything is accepted, nothing needs preparation.">
|
|
503
|
+
Screenshot, PDF, Text, URL — alles wird akzeptiert. Einfach teilen oder einfügen.
|
|
504
|
+
</div>
|
|
505
|
+
<div class="plan-fear"
|
|
506
|
+
data-de="Kein manuelles Eintippen mehr."
|
|
507
|
+
data-en="No more manual entry.">
|
|
508
|
+
Kein manuelles Eintippen mehr.
|
|
509
|
+
</div>
|
|
510
|
+
</div>
|
|
511
|
+
|
|
512
|
+
<div class="plan-step">
|
|
513
|
+
<div class="plan-icon">🤖</div>
|
|
514
|
+
<div class="plan-num">2</div>
|
|
515
|
+
<div class="plan-title" data-de="KI liest und versteht" data-en="AI reads and understands">KI liest und versteht</div>
|
|
516
|
+
<div class="plan-text"
|
|
517
|
+
data-de="Ollama — lokal auf deinem Server — erkennt Datum, Uhrzeit, Buchungsnummer, Route, Passagiere und Preis. Keine Cloud. Keine Drittanbieter. Keine Datenweitergabe."
|
|
518
|
+
data-en="Ollama — running locally on your server — extracts date, time, booking number, route, passengers and price. No cloud. No third parties. No data sharing.">
|
|
519
|
+
Ollama — lokal auf deinem Server — erkennt alle relevanten Daten. Privat, deterministisch, gecacht.
|
|
520
|
+
</div>
|
|
521
|
+
<div class="plan-fear"
|
|
522
|
+
data-de="Deine Reisedaten verlassen nie deine Infrastruktur."
|
|
523
|
+
data-en="Your travel data never leaves your infrastructure.">
|
|
524
|
+
Deine Reisedaten verlassen nie deine Infrastruktur.
|
|
525
|
+
</div>
|
|
526
|
+
</div>
|
|
527
|
+
|
|
528
|
+
<div class="plan-step">
|
|
529
|
+
<div class="plan-icon">✅</div>
|
|
530
|
+
<div class="plan-num">3</div>
|
|
531
|
+
<div class="plan-title" data-de="Saubere Timeline. Sofort." data-en="Clean timeline. Instantly.">Saubere Timeline. Sofort.</div>
|
|
532
|
+
<div class="plan-text"
|
|
533
|
+
data-de="Alle Reiseelemente in chronologischer Reihenfolge. Buchungsnummer mit einem Tap. QR-Code für den Scanner. Offline verfügbar — auch ohne Internet."
|
|
534
|
+
data-en="All travel items in chronological order. Booking number in one tap. QR code for the scanner. Available offline — even without internet.">
|
|
535
|
+
Alle Events chronologisch. Buchungsnummer per Tap. QR-Code. Offline-fähig.
|
|
536
|
+
</div>
|
|
537
|
+
<div class="plan-fear"
|
|
538
|
+
data-de="Am Gate sofort griffbereit — mit oder ohne Internet."
|
|
539
|
+
data-en="Ready at the gate — with or without internet.">
|
|
540
|
+
Am Gate sofort griffbereit — mit oder ohne Internet.
|
|
541
|
+
</div>
|
|
542
|
+
</div>
|
|
543
|
+
</div>
|
|
544
|
+
</div>
|
|
545
|
+
</section>
|
|
546
|
+
|
|
547
|
+
<!-- ═══ 5 · MID-PAGE CTA ═══ -->
|
|
548
|
+
<section class="cta-band">
|
|
549
|
+
<div class="container">
|
|
550
|
+
<div class="cta-band-inner fade-up">
|
|
551
|
+
<div class="cta-band-text">
|
|
552
|
+
<h3 data-de="Bereit, das Chaos hinter dir zu lassen?" data-en="Ready to leave the chaos behind?">Bereit, das Chaos hinter dir zu lassen?</h3>
|
|
553
|
+
<p data-de="Trag dich ein — du bekommst Early Access sobald wir live gehen." data-en="Sign up — you'll get early access as soon as we go live.">Trag dich ein — du bekommst Early Access sobald wir live gehen.</p>
|
|
554
|
+
</div>
|
|
555
|
+
<div style="flex-shrink:0">
|
|
556
|
+
<form class="email-form" style="justify-content:flex-end" onsubmit="submitEmail(event,'band')">
|
|
557
|
+
<input class="email-input" type="email" required id="email-band"
|
|
558
|
+
placeholder="deine@email.de"
|
|
559
|
+
data-de="deine@email.de" data-en="your@email.com" />
|
|
560
|
+
<button class="email-submit" type="submit" id="btn-band">
|
|
561
|
+
<span data-de="Eintragen →" data-en="Sign up →">Eintragen →</span>
|
|
562
|
+
</button>
|
|
563
|
+
</form>
|
|
564
|
+
<div class="form-feedback" id="fb-band"></div>
|
|
565
|
+
</div>
|
|
566
|
+
</div>
|
|
567
|
+
</div>
|
|
568
|
+
</section>
|
|
569
|
+
|
|
570
|
+
<!-- ═══ 6 · STAKES — Failure vs. Success ═══ -->
|
|
571
|
+
<section>
|
|
572
|
+
<div class="container">
|
|
573
|
+
<div class="fade-up">
|
|
574
|
+
<p class="s-label" data-de="Was auf dem Spiel steht" data-en="What's at stake">Was auf dem Spiel steht</p>
|
|
575
|
+
<h2 data-de="Zwei mögliche Reiseszenarien." data-en="Two possible travel scenarios.">Zwei mögliche Reiseszenarien.</h2>
|
|
576
|
+
<p class="s-sub"
|
|
577
|
+
data-de="Die Entscheidung liegt bei dir — weitermachen wie bisher, oder das Chaos ein für alle Mal beenden."
|
|
578
|
+
data-en="The choice is yours — keep going as before, or end the chaos once and for all.">
|
|
579
|
+
Die Entscheidung liegt bei dir.
|
|
580
|
+
</p>
|
|
581
|
+
</div>
|
|
582
|
+
|
|
583
|
+
<div class="stakes-grid">
|
|
584
|
+
<div class="stakes-col fail fade-up">
|
|
585
|
+
<div class="stakes-header">
|
|
586
|
+
<div class="stakes-icon">❌</div>
|
|
587
|
+
<div class="stakes-label" data-de="Ohne TCO" data-en="Without TCO">Ohne TCO</div>
|
|
588
|
+
</div>
|
|
589
|
+
<ul class="stakes-list">
|
|
590
|
+
<li data-de="Panisch die Buchungsmail suchen, während andere boarden." data-en="Frantically searching for the booking email while others board.">Panisch die Buchungsmail suchen, während andere boarden.</li>
|
|
591
|
+
<li data-de="Buchungsnummer irgendwo in 200 E-Mails begraben." data-en="Booking number buried somewhere in 200 emails.">Buchungsnummer irgendwo in 200 E-Mails begraben.</li>
|
|
592
|
+
<li data-de="Kein Internet, keine Daten — und die PDF liegt auf dem Laptop zu Hause." data-en="No internet, no data — and the PDF is on the laptop at home.">Kein Internet, keine Daten — PDF liegt zu Hause auf dem Laptop.</li>
|
|
593
|
+
<li data-de="Sich unvorbereitet und unprofessionell fühlen — obwohl man ein Profi ist." data-en="Feeling unprepared and unprofessional — even though you're a pro.">Sich unprofessionell fühlen — obwohl man ein Profi ist.</li>
|
|
594
|
+
<li data-de="Dasselbe Ticket dreimal analysieren, weil man es nicht findet." data-en="Analyzing the same ticket three times because you can't find it.">Dasselbe Ticket dreimal analysieren, weil man es nicht findet.</li>
|
|
595
|
+
</ul>
|
|
596
|
+
</div>
|
|
597
|
+
|
|
598
|
+
<div class="stakes-col win fade-up">
|
|
599
|
+
<div class="stakes-header">
|
|
600
|
+
<div class="stakes-icon">✅</div>
|
|
601
|
+
<div class="stakes-label" data-de="Mit TCO" data-en="With TCO">Mit TCO</div>
|
|
602
|
+
</div>
|
|
603
|
+
<ul class="stakes-list">
|
|
604
|
+
<li data-de="Buchungsnummer in einem Tap — auch offline." data-en="Booking number in one tap — even offline.">Buchungsnummer in einem Tap — auch offline.</li>
|
|
605
|
+
<li data-de="Alle Trips in einer sauberen Timeline, chronologisch sortiert." data-en="All trips in a clean timeline, sorted chronologically.">Alle Trips in einer sauberen Timeline, chronologisch sortiert.</li>
|
|
606
|
+
<li data-de="QR-Code auf dem Sperrbildschirm, bevor der Scanner fragt." data-en="QR code on the lock screen before the scanner asks.">QR-Code auf dem Sperrbildschirm, bevor der Scanner fragt.</li>
|
|
607
|
+
<li data-de="Ruhig und vorbereitet — so wie es sich anfühlen sollte." data-en="Calm and prepared — the way it's supposed to feel.">Ruhig und vorbereitet — so wie es sich anfühlen sollte.</li>
|
|
608
|
+
<li data-de="Daten bleiben privat. Keine Cloud. Kein Abo. Keine Abhängigkeit." data-en="Data stays private. No cloud. No subscription. No dependency.">Daten bleiben privat. Keine Cloud. Kein Abo.</li>
|
|
609
|
+
</ul>
|
|
610
|
+
</div>
|
|
611
|
+
</div>
|
|
612
|
+
</div>
|
|
613
|
+
</section>
|
|
614
|
+
|
|
615
|
+
<!-- ═══ 7 · CACHLY — Technology credibility ═══ -->
|
|
616
|
+
<section class="alt-bg">
|
|
617
|
+
<div class="container">
|
|
618
|
+
<div class="fade-up">
|
|
619
|
+
<p class="s-label">Powered by Cachly</p>
|
|
620
|
+
<h2 data-de="Kein zweiter KI-Call für dasselbe Dokument." data-en="No second AI call for the same document.">Kein zweiter KI-Call für dasselbe Dokument.</h2>
|
|
621
|
+
</div>
|
|
622
|
+
|
|
623
|
+
<div class="cachly-row fade-up">
|
|
624
|
+
<div class="cachly-text">
|
|
625
|
+
<p class="s-sub"
|
|
626
|
+
data-de="TCO nutzt Cachly als Redis-Cache-Layer. Jedes Parsing-Ergebnis wird per SHA-256-Content-Hash gespeichert. Gleiches Dokument, zweiter Upload — sofortige Antwort aus dem Cache, kein Ollama-Call, keine Wartezeit."
|
|
627
|
+
data-en="TCO uses Cachly as its Redis caching layer. Every parse result is stored by SHA-256 content hash. Same document, second upload — instant response from cache, no Ollama call, zero wait time.">
|
|
628
|
+
TCO nutzt Cachly als Redis-Cache-Layer. Jedes Parsing-Ergebnis wird per SHA-256-Content-Hash gespeichert. Gleiches Dokument, zweiter Upload — sofortige Antwort aus dem Cache.
|
|
629
|
+
</p>
|
|
630
|
+
<div class="cachly-stat-row">
|
|
631
|
+
<div>
|
|
632
|
+
<div class="cs-val">7 Tage</div>
|
|
633
|
+
<div class="cs-lbl" data-de="Cache-TTL" data-en="Cache TTL">Cache-TTL</div>
|
|
634
|
+
</div>
|
|
635
|
+
<div>
|
|
636
|
+
<div class="cs-val">~0 ms</div>
|
|
637
|
+
<div class="cs-lbl" data-de="bei Cache-Hit" data-en="on cache hit">bei Cache-Hit</div>
|
|
638
|
+
</div>
|
|
639
|
+
<div>
|
|
640
|
+
<div class="cs-val">optional</div>
|
|
641
|
+
<div class="cs-lbl" data-de="läuft auch ohne Redis" data-en="works without Redis too">läuft auch ohne Redis</div>
|
|
642
|
+
</div>
|
|
643
|
+
</div>
|
|
644
|
+
</div>
|
|
645
|
+
<div class="cachly-card">
|
|
646
|
+
<div style="font-size:38px;margin-bottom:12px">⚡</div>
|
|
647
|
+
<div style="font-size:22px;font-weight:900;color:var(--purple);letter-spacing:-1px;margin-bottom:6px">Cachly</div>
|
|
648
|
+
<div style="font-size:12px;color:var(--muted);margin-bottom:18px" data-de="Redis-Cache für KI-Ergebnisse" data-en="Redis cache for AI results">Redis-Cache für KI-Ergebnisse</div>
|
|
649
|
+
<div style="border-top:1px solid var(--border);padding-top:14px;font-size:10px;color:var(--faint);font-family:monospace;line-height:1.7">
|
|
650
|
+
tco:parse:{sha256[:16]}<br>
|
|
651
|
+
TTL 604800s<br>
|
|
652
|
+
redis:7-alpine ✓
|
|
653
|
+
</div>
|
|
654
|
+
</div>
|
|
655
|
+
</div>
|
|
656
|
+
</div>
|
|
657
|
+
</section>
|
|
658
|
+
|
|
659
|
+
<!-- ═══ 8 · FINAL CTA — Success vision ═══ -->
|
|
660
|
+
<section class="final-cta">
|
|
661
|
+
<div class="container">
|
|
662
|
+
<h2 class="fade-up"
|
|
663
|
+
data-de="Stell dir vor, du bist am Gate.<br><em>Ruhig. Vorbereitet. In Kontrolle.</em>"
|
|
664
|
+
data-en="Imagine being at the gate.<br><em>Calm. Prepared. In control.</em>">
|
|
665
|
+
Stell dir vor, du bist am Gate.<br><em>Ruhig. Vorbereitet. In Kontrolle.</em>
|
|
666
|
+
</h2>
|
|
667
|
+
|
|
668
|
+
<p class="s-sub fade-up"
|
|
669
|
+
data-de="Das ist kein Wunschdenken. Das ist ein Tool, das du in 60 Sekunden aufsetzen kannst."
|
|
670
|
+
data-en="That's not wishful thinking. That's a tool you can set up in 60 seconds.">
|
|
671
|
+
Das ist kein Wunschdenken. Das ist ein Tool, das du in 60 Sekunden aufsetzen kannst.
|
|
672
|
+
</p>
|
|
673
|
+
|
|
674
|
+
<div class="fade-up">
|
|
675
|
+
<form class="email-form" onsubmit="submitEmail(event,'final')">
|
|
676
|
+
<input class="email-input" type="email" required id="email-final"
|
|
677
|
+
placeholder="deine@email.de"
|
|
678
|
+
data-de="deine@email.de" data-en="your@email.com" />
|
|
679
|
+
<button class="email-submit" type="submit" id="btn-final">
|
|
680
|
+
<span data-de="Early Access sichern →" data-en="Get early access →">Early Access sichern →</span>
|
|
681
|
+
</button>
|
|
682
|
+
</form>
|
|
683
|
+
<div class="form-feedback" id="fb-final"></div>
|
|
684
|
+
<div class="form-caption" data-de="Kein Spam. Kein Abo. Nur Updates wenn's wirklich wichtig ist." data-en="No spam. No subscription. Only updates when it really matters.">Kein Spam. Kein Abo. Nur Updates wenn's wirklich wichtig ist.</div>
|
|
685
|
+
</div>
|
|
686
|
+
|
|
687
|
+
<p class="cta-note fade-up" style="margin-top:32px">
|
|
688
|
+
<span data-de="Open Source · Self-hosted · " data-en="Open source · Self-hosted · ">Open Source · Self-hosted · </span>
|
|
689
|
+
<a href="https://github.com/cachly-dev/cachly-mcp" target="_blank" rel="noopener"
|
|
690
|
+
data-de="GitHub ansehen" data-en="View on GitHub">GitHub ansehen</a>
|
|
691
|
+
<span data-de=" · MIT Lizenz" data-en=" · MIT License"> · MIT Lizenz</span>
|
|
692
|
+
</p>
|
|
693
|
+
</div>
|
|
694
|
+
</section>
|
|
695
|
+
|
|
696
|
+
<!-- FOOTER -->
|
|
697
|
+
<footer>
|
|
698
|
+
<div class="container foot-inner">
|
|
699
|
+
<span class="foot-copy">© 2025 Travel Chaos Organizer</span>
|
|
700
|
+
<div class="foot-links">
|
|
701
|
+
<a href="https://github.com/cachly-dev/cachly-mcp" target="_blank" rel="noopener">GitHub</a>
|
|
702
|
+
<a href="https://cachly.dev" target="_blank" rel="noopener">Cachly</a>
|
|
703
|
+
</div>
|
|
704
|
+
</div>
|
|
705
|
+
</footer>
|
|
706
|
+
|
|
707
|
+
<script>
|
|
708
|
+
// ── Config — point to self-hosted backend in production ──
|
|
709
|
+
const API = (window.TCO_API_URL || '').replace(/\/$/, '');
|
|
710
|
+
|
|
711
|
+
// ── Language toggle ──
|
|
712
|
+
let _lang = 'de';
|
|
713
|
+
function setLang(lang) {
|
|
714
|
+
_lang = lang;
|
|
715
|
+
document.documentElement.lang = lang;
|
|
716
|
+
document.getElementById('btn-de').classList.toggle('active', lang === 'de');
|
|
717
|
+
document.getElementById('btn-en').classList.toggle('active', lang === 'en');
|
|
718
|
+
document.querySelectorAll('[data-de][data-en]').forEach(el => {
|
|
719
|
+
el.innerHTML = el.dataset[lang];
|
|
720
|
+
});
|
|
721
|
+
// Update placeholder-only elements (inputs with data-de/data-en as placeholder values)
|
|
722
|
+
document.querySelectorAll('input[data-de][data-en]').forEach(el => {
|
|
723
|
+
el.placeholder = el.dataset[lang];
|
|
724
|
+
});
|
|
725
|
+
}
|
|
726
|
+
|
|
727
|
+
// ── Email form submit ──
|
|
728
|
+
const MSG = {
|
|
729
|
+
de: { ok: '✓ Eingetragen! Wir melden uns.', already: '✓ Du bist bereits auf der Liste.', err: '⚠ Ups — bitte versuche es nochmal.' },
|
|
730
|
+
en: { ok: '✓ You\'re on the list!', already: '✓ Already signed up — you\'re in.', err: '⚠ Oops — please try again.' },
|
|
731
|
+
};
|
|
732
|
+
|
|
733
|
+
async function submitEmail(e, slot) {
|
|
734
|
+
e.preventDefault();
|
|
735
|
+
const input = document.getElementById('email-' + slot);
|
|
736
|
+
const btn = document.getElementById('btn-' + slot);
|
|
737
|
+
const fb = document.getElementById('fb-' + slot);
|
|
738
|
+
const email = input.value.trim();
|
|
739
|
+
|
|
740
|
+
btn.disabled = true;
|
|
741
|
+
fb.textContent = '…';
|
|
742
|
+
fb.className = 'form-feedback';
|
|
743
|
+
|
|
744
|
+
try {
|
|
745
|
+
let joined = true;
|
|
746
|
+
if (API) {
|
|
747
|
+
const res = await fetch(API + '/api/v1/waitlist', {
|
|
748
|
+
method: 'POST',
|
|
749
|
+
headers: { 'Content-Type': 'application/json' },
|
|
750
|
+
body: JSON.stringify({ email, source: 'landing-' + slot }),
|
|
751
|
+
});
|
|
752
|
+
const data = await res.json();
|
|
753
|
+
joined = !!data.joined;
|
|
754
|
+
}
|
|
755
|
+
fb.textContent = joined ? MSG[_lang].ok : MSG[_lang].already;
|
|
756
|
+
if (window.plausible) window.plausible('Waitlist Signup', { props: { slot, lang: _lang } });
|
|
757
|
+
fb.className = 'form-feedback ok';
|
|
758
|
+
input.value = '';
|
|
759
|
+
} catch {
|
|
760
|
+
fb.textContent = MSG[_lang].err;
|
|
761
|
+
fb.className = 'form-feedback err';
|
|
762
|
+
btn.disabled = false;
|
|
763
|
+
}
|
|
764
|
+
}
|
|
765
|
+
|
|
766
|
+
// ── Scroll fade-in ──
|
|
767
|
+
const io = new IntersectionObserver(entries => {
|
|
768
|
+
entries.forEach(e => { if (e.isIntersecting) { e.target.classList.add('visible'); io.unobserve(e.target); } });
|
|
769
|
+
}, { threshold: 0.1 });
|
|
770
|
+
document.querySelectorAll('.fade-up').forEach(el => io.observe(el));
|
|
771
|
+
</script>
|
|
772
|
+
</body>
|
|
773
|
+
</html>
|