From f9160307bc8756d06297ee7952fb48b0b2533769 Mon Sep 17 00:00:00 2001 From: rene Date: Thu, 14 May 2026 18:23:23 +0200 Subject: [PATCH] Landing: emotionaler Hero, Social-Proof-Stats, Testimonial-Slots, Scroll-Animationen (SW by-v952) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Hero-Headline: "Weil jeder Moment mit ihm zählt." (warm/emotional statt Feature-Liste) - CTA umbenannt: "Kostenlos starten" statt "Ich bin Hundebesitzer" - Hero-Stats-Zeile: live Nutzer/Hunde/km-Zähler (nur wenn >0) - Stats-Band: orangener Balken mit 4 Live-Kennzahlen nach der Zwei-Welten-Section - Testimonial-Section: 3 Platzhalter-Karten zwischen Features und Züchter-Bereich - Scroll-Animationen: IntersectionObserver auf alle Cards (fade-up) - API: /api/stats/public — öffentlicher Endpoint, 5-Min-Cache --- backend/routes/stats.py | 30 ++++++ backend/static/js/app.js | 2 +- backend/static/landing.html | 207 ++++++++++++++++++++++++++++++++++-- backend/static/sw.js | 2 +- 4 files changed, 232 insertions(+), 9 deletions(-) diff --git a/backend/routes/stats.py b/backend/routes/stats.py index e8cd68c..49fe778 100644 --- a/backend/routes/stats.py +++ b/backend/routes/stats.py @@ -1,3 +1,4 @@ +import time from fastapi import APIRouter, Depends from database import db from auth import get_current_user, get_current_user_optional @@ -19,6 +20,35 @@ _STATS_SQL = """ """ +_pub_cache: dict = {"data": None, "ts": 0.0} +_PUB_TTL = 300 # 5 Minuten + + +@router.get("/public") +async def public_stats(): + now = time.time() + if _pub_cache["data"] and now - _pub_cache["ts"] < _PUB_TTL: + return _pub_cache["data"] + with db() as conn: + users = conn.execute("SELECT COUNT(*) FROM users").fetchone()[0] + dogs = conn.execute("SELECT COUNT(*) FROM dogs").fetchone()[0] + km = conn.execute( + "SELECT ROUND(COALESCE(SUM(distanz_km),0),0) FROM routes" + ).fetchone()[0] + posts = conn.execute("SELECT COUNT(*) FROM forum_posts").fetchone()[0] + diary = conn.execute("SELECT COUNT(*) FROM diary").fetchone()[0] + data = { + "users": users, + "dogs": dogs, + "km": int(km or 0), + "forum_posts": posts, + "diary_entries": diary, + } + _pub_cache["data"] = data + _pub_cache["ts"] = now + return data + + @router.get("/leaderboard") async def leaderboard(_user=Depends(get_current_user_optional)): with db() as conn: diff --git a/backend/static/js/app.js b/backend/static/js/app.js index 2282add..0f3f6ce 100644 --- a/backend/static/js/app.js +++ b/backend/static/js/app.js @@ -3,7 +3,7 @@ Router, State-Management, Navigation, Initialisierung. ============================================================ */ -const APP_VER = '951'; // ← bei jedem Deploy mit Frontend-Änderungen erhöhen +const APP_VER = '952'; // ← bei jedem Deploy mit Frontend-Änderungen erhöhen const APP_VERSION = '1.5.1'; // ← semantische Version, wird bei make release gesetzt const IS_STAGING = location.hostname === 'staging.banyaro.app'; // Cache-Bust-Parameter nach Update-Reload sofort entfernen diff --git a/backend/static/landing.html b/backend/static/landing.html index df90d27..28b4d44 100644 --- a/backend/static/landing.html +++ b/backend/static/landing.html @@ -516,6 +516,84 @@ } footer a { color: var(--primary); } footer .footer-links { margin-top: 0.75rem; display: flex; gap: 1.5rem; justify-content: center; flex-wrap: wrap; } + + /* Hero Stats */ + .hero-stats { + margin-top: 1.5rem; + font-size: 0.88rem; + opacity: 0.88; + display: flex; + gap: 0.6rem; + justify-content: center; + flex-wrap: wrap; + align-items: center; + } + .hero-stats strong { font-weight: 800; } + .hero-stats .sep { opacity: 0.45; } + + /* Big Stats Band */ + .stats-band { + background: linear-gradient(135deg, #a86e2e 0%, #C4843A 50%, #d4944a 100%); + color: white; + padding: 2.5rem 0; + } + .stats-band-grid { + display: grid; + grid-template-columns: repeat(auto-fit, minmax(140px, 1fr)); + gap: 1.5rem; + text-align: center; + } + .stats-band-item { padding: 0.5rem; } + .stats-band-num { + font-size: clamp(2rem, 5vw, 2.8rem); + font-weight: 900; + line-height: 1; + letter-spacing: -0.02em; + margin-bottom: 0.4rem; + } + .stats-band-label { + font-size: 0.82rem; + opacity: 0.82; + font-weight: 500; + } + + /* Testimonials */ + .testimonials-grid { + display: grid; + grid-template-columns: repeat(auto-fit, minmax(280px, 1fr)); + gap: 1.5rem; + margin-top: 2rem; + } + .testimonial-card { + background: white; + border-radius: 16px; + padding: 1.75rem; + box-shadow: 0 2px 16px rgba(0,0,0,.07); + display: flex; + flex-direction: column; + gap: 0.85rem; + border: 1px solid var(--border); + } + .testimonial-stars { color: #f59e0b; letter-spacing: 2px; font-size: 0.95rem; } + .testimonial-quote { + font-size: 0.97rem; + line-height: 1.7; + color: var(--text); + flex: 1; + } + .testimonial-quote::before { content: "„"; font-size: 1.3em; color: var(--primary); line-height: 0; vertical-align: -0.2em; margin-right: 2px; } + .testimonial-quote::after { content: """; font-size: 1.3em; color: var(--primary); line-height: 0; vertical-align: -0.2em; margin-left: 2px; } + .testimonial-author { font-size: 0.875rem; font-weight: 700; color: var(--text); } + .testimonial-dog { font-size: 0.8rem; color: var(--primary); font-weight: 500; margin-top: 0.1rem; } + .testimonial-placeholder { opacity: 0.4; font-style: italic; } + + /* Scroll animations */ + .fade-up { + opacity: 0; + transform: translateY(22px); + transition: opacity 0.55s ease, transform 0.55s ease; + } + .fade-up.visible { opacity: 1; transform: none; } @@ -526,18 +604,25 @@ Ban Yaro App Icon Ban Yaro -

Für Hundebesitzer.
Für Züchter.
Eine App.

-

Tagebuch, Training und Gesundheit für Hundebesitzer. Stammbaum, Wurfverwaltung und Warteliste für Züchter. Nahtlos verbunden — kostenlos, ohne App Store, Daten in Deutschland.

-
+

Weil jeder Moment
mit ihm zählt.

+

Ban Yaro begleitet euch durch jeden gemeinsamen Tag — Tagebuch, Training und Gesundheit für Hundebesitzer, Stammbaum und Wurfverwaltung für Züchter. Eine App. Mit ganzem Herzen.

+
+ Kostenlos starten + Ich bin Züchter +
+
Kostenlos nutzbar Daten in Deutschland Kein App Store nötig Made in Germany Offline-fähig
-
- Ich bin Hundebesitzer - Ich bin Züchter +
@@ -595,6 +680,30 @@
+ +
+
+
+
+
+
Hundemenschen
+
+
+
+
Hunde registriert
+
+
+
+
km Gassi-Routen
+
+
+
+
Forum-Beiträge
+
+
+
+
+
@@ -759,6 +868,44 @@
+ +
+
+

Was Hundemenschen sagen

+

Echte Menschen. Echte Hunde. Echte Momente.

+
+ +
+
★★★★★
+

Hier könnte dein Zitat stehen — schreib uns an hallo@banyaro.app

+
+
Maria K.
+
🐾 Luna · Golden Retriever
+
+
+ +
+
★★★★★
+

Hier könnte dein Zitat stehen — schreib uns an hallo@banyaro.app

+
+
Thomas W.
+
🐾 Max · Labrador
+
+
+ +
+
★★★★★
+

Hier könnte dein Zitat stehen — schreib uns an hallo@banyaro.app

+
+
Sarah M.
+
🐾 Bella · Schäferhund
+
+
+ +
+
+
+
@@ -1179,12 +1326,58 @@ diff --git a/backend/static/sw.js b/backend/static/sw.js index 5be5d42..f5ff89b 100644 --- a/backend/static/sw.js +++ b/backend/static/sw.js @@ -3,7 +3,7 @@ Offline-Cache + Push Notifications + Tile-Cache ============================================================ */ -const CACHE_VERSION = 'by-v951'; +const CACHE_VERSION = 'by-v952'; const CACHE_STATIC = `${CACHE_VERSION}-static`; const CACHE_TILES = 'ban-yaro-tiles-v1'; // bleibt über SW-Updates erhalten const CACHE_API = 'ban-yaro-api-v1'; // API-Response-Cache