diff --git a/backend/main.py b/backend/main.py index 1f8dd9c..f1025de 100644 --- a/backend/main.py +++ b/backend/main.py @@ -406,7 +406,7 @@ async def serve_media(path: str, request: _Request): raise _HE(404, "Nicht gefunden.") return _media_response(filepath) -APP_VER = "951" # muss mit APP_VER in app.js übereinstimmen +APP_VER = "961" # muss mit APP_VER in app.js übereinstimmen @app.get("/.well-known/assetlinks.json") async def assetlinks(): 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/img/landing/alvan-nee.webp b/backend/static/img/landing/alvan-nee.webp new file mode 100644 index 0000000..cf98a6d Binary files /dev/null and b/backend/static/img/landing/alvan-nee.webp differ diff --git a/backend/static/img/landing/ban-yaro-original.webp b/backend/static/img/landing/ban-yaro-original.webp new file mode 100644 index 0000000..c03ba5a Binary files /dev/null and b/backend/static/img/landing/ban-yaro-original.webp differ diff --git a/backend/static/img/landing/baptist-standaert.webp b/backend/static/img/landing/baptist-standaert.webp new file mode 100644 index 0000000..509adb7 Binary files /dev/null and b/backend/static/img/landing/baptist-standaert.webp differ diff --git a/backend/static/img/landing/chewy.webp b/backend/static/img/landing/chewy.webp new file mode 100644 index 0000000..7d439e6 Binary files /dev/null and b/backend/static/img/landing/chewy.webp differ diff --git a/backend/static/img/landing/eric-ward.webp b/backend/static/img/landing/eric-ward.webp new file mode 100644 index 0000000..c7aa36c Binary files /dev/null and b/backend/static/img/landing/eric-ward.webp differ diff --git a/backend/static/img/landing/nicholas-brownlow.webp b/backend/static/img/landing/nicholas-brownlow.webp new file mode 100644 index 0000000..9393209 Binary files /dev/null and b/backend/static/img/landing/nicholas-brownlow.webp differ diff --git a/backend/static/img/landing/tamas-pap.webp b/backend/static/img/landing/tamas-pap.webp new file mode 100644 index 0000000..21142c0 Binary files /dev/null and b/backend/static/img/landing/tamas-pap.webp differ diff --git a/backend/static/img/landing/wade-austin-ellis.webp b/backend/static/img/landing/wade-austin-ellis.webp new file mode 100644 index 0000000..ba13449 Binary files /dev/null and b/backend/static/img/landing/wade-austin-ellis.webp differ diff --git a/backend/static/js/app.js b/backend/static/js/app.js index 2282add..6594b45 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 = '961'; // ← 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..49a2a5d 100644 --- a/backend/static/landing.html +++ b/backend/static/landing.html @@ -3,6 +3,14 @@ + + Ban Yaro — Die Hunde-App für Deutschland, Österreich & Schweiz @@ -161,6 +169,7 @@ --surface: #fff; --border: #e8ddd0; --radius: 12px; + color-scheme: light dark; } body { @@ -516,6 +525,199 @@ } 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; } + + /* Foto-Sections */ + .photo-credit { + position: absolute; + bottom: 7px; right: 9px; + font-size: 0.62rem; + color: rgba(255,255,255,0.8); + background: rgba(0,0,0,0.32); + padding: 2px 7px; + border-radius: 4px; + letter-spacing: 0.03em; + backdrop-filter: blur(3px); + pointer-events: none; + } + .photo-strip { + display: grid; + grid-template-columns: repeat(4,1fr); + gap: 3px; + } + @media (max-width: 640px) { .photo-strip { grid-template-columns: repeat(2,1fr); } } + .photo-strip-item { + position: relative; + aspect-ratio: 1; + overflow: hidden; + } + .photo-strip-item img { + width: 100%; height: 100%; + object-fit: cover; + transition: transform 0.45s ease; + display: block; + } + .photo-strip-item:hover img { transform: scale(1.04); } + .moment-split { + display: grid; + grid-template-columns: 1fr 1fr; + min-height: 420px; + } + @media (max-width: 700px) { + .moment-split { grid-template-columns: 1fr; } + .moment-split .moment-photo { aspect-ratio: 4/3; min-height: unset; } + } + .moment-photo { position: relative; overflow: hidden; } + .moment-photo img { width: 100%; height: 100%; object-fit: cover; display: block; } + .moment-text { + background: linear-gradient(135deg, #C4843A 0%, #d4944a 100%); + color: white; + display: flex; + flex-direction: column; + justify-content: center; + padding: clamp(2rem,5vw,3.5rem); + gap: 1.5rem; + } + .moment-text blockquote { + font-size: clamp(1.3rem,2.5vw,1.9rem); + font-weight: 800; + line-height: 1.3; + margin: 0; + } + .moment-text p { font-size: 1rem; opacity: 0.88; margin: 0; line-height: 1.65; } + + /* 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; } + + /* Dark Mode */ + /* Dark-Mode via JS-Klasse (html.dark) — Fallback für macOS 26 / Brave */ + html.dark { + --bg: #141210; + --surface: #1e1a16; + --text: #ede8e2; + --text-secondary:#a89880; + --text-muted: #6b5e50; + --border: #2e2620; + --primary-light: #3a2510; + color-scheme: dark; + } + /* Dark-Mode via Media Query (Standard-Browser-Fallback) */ + @media (prefers-color-scheme: dark) { + :root { + --bg: #141210; + --surface: #1e1a16; + --text: #ede8e2; + --text-secondary:#a89880; + --text-muted: #6b5e50; + --border: #2e2620; + --primary-light: #3a2510; + color-scheme: dark; + } + } + html.dark body { background: #141210 !important; color: #ede8e2 !important; } + html.dark nav { background: #1a1612 !important; border-color: #2e2620 !important; } + html.dark nav a { color: #a89880 !important; } + html.dark nav .nav-brand { color: #C4843A !important; } + html.dark section { background: #141210 !important; } + html.dark section:nth-child(even) { background: #1a1612 !important; } + html.dark #fuer-beide { background: #1a1612 !important; } + html.dark #funktionen { background: #1a1612 !important; } + html.dark #welpen { background: #1a1612 !important; } + html.dark #stimmen { background: #141210 !important; } + html.dark #zuechter { background: #1a1208 !important; } + html.dark #warum { background: #141210 !important; } + html.dark #vergleich { background: #1a1612 !important; } + html.dark #preise { background: #141210 !important; } + html.dark #ueber { background: #1a1612 !important; } + html.dark .outcome-card { background: #1e1a16 !important; border-color: #2e2620 !important; } + html.dark .feature-card { background: #1e1a16 !important; border-color: #2e2620 !important; } + html.dark .testimonial-card{ background: #1e1a16 !important; border-color: #2e2620 !important; } + html.dark .pricing-card { background: #1e1a16 !important; border-color: #2e2620 !important; } + html.dark .pricing-card.featured { border-color: #C4843A !important; } + html.dark .audience-card > div { background: #1e1a16 !important; border-color: #2e2620 !important; } + html.dark .feature-group-label { background: #3a2510 !important; color: #C4843A !important; } + html.dark h2, html.dark h3 { color: #ede8e2 !important; } + html.dark p { color: #a89880; } + html.dark table { background: #1e1a16 !important; } + html.dark th { background: #C4843A !important; } + html.dark td { border-color: #2e2620 !important; color: #ede8e2 !important; } + html.dark tr:nth-child(even) td { background: #141210 !important; } + html.dark footer { background: #0d0b09 !important; } + html.dark .section-intro { color: #a89880 !important; } @@ -526,18 +728,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,8 +804,59 @@
+ +
+
+
+
+
+
Hundemenschen
+
+
+
+
Hunde registriert
+
+
+
+
km Gassi-Routen
+
+
+
+
Forum-Beiträge
+
+
+
+
+ + +
+
+ Hundebesitzer umarmt seinen Golden Retriever + © Eric Ward · Unsplash +
+
+

+ „Ich kann mir ein Leben ohne ihn nicht mehr vorstellen." +

+

+ Das sagen uns Tausende Hundemenschen täglich. Ban Yaro begleitet diese Liebe — mit Tagebuch, Gesundheit, Training und einer Gemeinschaft die genauso fühlt wie du. +

+ Kostenlos starten +
+
+ -
+
+ + © Baptist Standaert · Unsplash

Sieh selbst wie es sich anfühlt @@ -759,6 +1019,70 @@

+ +
+
+
+ Zwei Hunde laufen einen Waldweg entlang + © Alvan Nee · Unsplash +
+
+ Frau gibt ihrem Golden Retriever einen Kuss + © Nicholas Brownlow · Unsplash +
+
+ Frau mit Labrador auf der Wiese + © Wade Austin Ellis · Unsplash +
+
+ Frau lacht während ihr Hund sie abschleckt + © Tamas Pap · Unsplash +
+
+
+ + +
+
+

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
+
+
+ +
+
+
+
@@ -802,7 +1126,9 @@
-
🏡
+
+ +
Züchter
Legt Wurf an, dokumentiert Welpen, veröffentlicht in der Wurfbörse
@@ -810,7 +1136,9 @@
-
🔍
+
+ +
Käufer
Findet Wurf in der Wurfbörse, kontaktiert Züchter direkt per Chat
@@ -818,7 +1146,9 @@
-
🐶
+
+ +
Übergabe
Welpen-Profil mit allem landet direkt in der App des neuen Besitzers
@@ -831,8 +1161,14 @@
+
+ Pärchen mit Hund aus dem Tierheim +
+ © Chewy · Unsplash +
-

Den richtigen Welpen finden. Sicher.

+

Den richtigen Welpen finden. Sicher.

Verifizierte Züchter, transparente Gesundheitsdaten, direkter Kontakt. Kein Kleinanzeigen-Chaos.

@@ -1118,15 +1454,32 @@
-
-
-

Warum „Ban Yaro"?

-

- Ban Yaro ist ein Hund. Genauer gesagt: ein Hund aus dem B-Wurf einer Züchterin, - die Star Wars liebt und ihre Würfe nach Charakteren dieses Epos benennt. - Ban Yaro ist der Namensgeber dieser App — und ein gutes Zeichen, dass hier - Hundemenschen für Hundemenschen bauen. -

+
+ +
+
+ Ban Yaro — der echte Hund hinter der App +
+
+

Warum „Ban Yaro"?

+

+ Ban Yaro ist ein echter Hund — aus dem B-Wurf einer Züchterin die Star Wars liebt + und ihre Würfe nach Charakteren des Epos benennt. Er ist der Namensgeber dieser App + und lebt in Ebersberg, Bayern. +

+

+ Ban Yaro wurde von einem Hundemenschen für Hundemenschen gebaut — von René Degelmann, + der genau weiß wie es sich anfühlt seinen Hund täglich zu begleiten. +

+

+ Ebersberg, Bayern · gegründet 2026 · hallo@banyaro.app +

+
+
+
@@ -1164,12 +1517,13 @@
+

Ban Yaro — Die deutschsprachige Hunde-Plattform

-

banyaro.app · DSGVO-konform · Hosting in Deutschland · Made with 🐾

+

banyaro.app · DSGVO-konform · Hosting in Deutschland · Made with

diff --git a/backend/static/sw.js b/backend/static/sw.js index 5be5d42..ddcd02a 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-v961'; 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