Release v1.5.0

This commit is contained in:
rene 2026-05-06 19:31:35 +02:00
commit ed9c482ae5
17 changed files with 837 additions and 267 deletions

View file

@ -87,7 +87,7 @@ def get_current_user(
user_id = int(payload["sub"])
with db() as conn:
row = conn.execute(
"SELECT id, email, name, rolle, is_premium, is_moderator, is_banned, ban_reason, is_social_media, notes_ki_enabled, gassi_stunde_push, breeder_status, is_founder, is_partner, founder_number, email_verified, luna_trial_until FROM users WHERE id=?",
"SELECT id, email, name, rolle, is_premium, is_moderator, is_banned, ban_reason, is_social_media, notes_ki_enabled, gassi_stunde_push, breeder_status, is_founder, is_partner, founder_number, email_verified, luna_trial_until, subscription_tier FROM users WHERE id=?",
(user_id,)
).fetchone()
@ -130,6 +130,32 @@ def require_admin(user=Depends(get_current_user)):
return user
def has_pro_access(user: dict) -> bool:
"""True wenn User Pro-Features nutzen darf."""
if not user:
return False
role = user.get("rolle", "user")
tier = user.get("subscription_tier", "standard")
if role in ("admin", "moderator"):
return True
if user.get("is_moderator") or user.get("is_social_media"):
return True
return tier in ("pro", "breeder", "pro_test", "breeder_test")
def has_breeder_access(user: dict) -> bool:
"""True wenn User Züchter-Features nutzen darf."""
if not user:
return False
role = user.get("rolle", "user")
tier = user.get("subscription_tier", "standard")
if role in ("admin", "moderator"):
return True
if user.get("is_moderator") or user.get("is_social_media"):
return True
return tier in ("breeder", "breeder_test") or role == "breeder"
def require_social_media(user=Depends(get_current_user)):
"""Dependency: Social-Media-Manager, Luna-Probezugang oder Admin."""
from datetime import datetime as _dt

View file

@ -2075,6 +2075,14 @@ def _migrate(conn_factory):
_seed_help_articles(conn)
logger.info("Migration: Hilfe/FAQ-Tabelle bereit.")
# ---- Feature: Subscription-Tier ----
try:
conn.execute("ALTER TABLE users ADD COLUMN subscription_tier TEXT DEFAULT 'standard'")
conn.execute("CREATE INDEX IF NOT EXISTS idx_users_tier ON users(subscription_tier)")
logger.info("Migration: subscription_tier Spalte hinzugefügt.")
except Exception:
pass # Spalte existiert bereits
def _seed_help_articles(conn):
"""Befüllt help_articles mit Starter-FAQs — nur wenn die Tabelle noch leer ist."""

View file

@ -327,7 +327,7 @@ MEDIA_DIR = os.getenv("MEDIA_DIR", "/data/media")
os.makedirs(MEDIA_DIR, exist_ok=True)
app.mount("/media", StaticFiles(directory=MEDIA_DIR), name="media")
APP_VER = "727" # muss mit APP_VER in app.js übereinstimmen
APP_VER = "741" # muss mit APP_VER in app.js übereinstimmen
@app.get("/api/version")
async def app_version():
@ -378,6 +378,7 @@ async def sitemap():
urls = [
("https://banyaro.app/", "weekly", "1.0"),
("https://banyaro.app/info", "monthly", "0.9"),
("https://banyaro.app/presse", "monthly", "0.8"),
("https://banyaro.app/wiki/rassen", "weekly", "0.8"),
("https://banyaro.app/knigge", "monthly", "0.8"),
("https://banyaro.app/wurfboerse", "daily", "0.8"),

View file

@ -81,12 +81,15 @@ def require_admin(user=Depends(get_current_user)):
# ------------------------------------------------------------------
# Schemas
# ------------------------------------------------------------------
_VALID_TIERS = {"standard", "pro", "breeder", "standard_test", "pro_test", "breeder_test"}
class UserPatch(BaseModel):
rolle: Optional[str] = None # user | moderator | admin
is_moderator: Optional[int] = None
is_banned: Optional[int] = None
ban_reason: Optional[str] = None
is_social_media: Optional[int] = None
rolle: Optional[str] = None # user | moderator | admin
is_moderator: Optional[int] = None
is_banned: Optional[int] = None
ban_reason: Optional[str] = None
is_social_media: Optional[int] = None
subscription_tier: Optional[str] = None
class WikiEnrichBody(BaseModel):
limit: int = 10
@ -331,7 +334,7 @@ async def list_users(
SELECT u.id, u.name, {_email_col}, u.rolle, u.is_premium,
u.is_moderator, u.is_banned, u.ban_reason,
u.is_founder, u.is_partner, u.founder_number,
u.created_at, u.last_login,
u.created_at, u.last_login, u.subscription_tier,
(SELECT COUNT(*) FROM dogs d WHERE d.user_id=u.id) AS dog_count,
(SELECT COUNT(*) FROM forum_threads t WHERE t.user_id=u.id AND t.is_deleted=0) AS thread_count,
ROUND(COALESCE((SELECT SUM(r.distanz_km) FROM routes r WHERE r.user_id=u.id), 0), 1) AS total_km,
@ -365,6 +368,10 @@ async def patch_user(uid: int, data: UserPatch, user=Depends(require_mod)):
raise HTTPException(403, "is_moderator darf nur von Admins geändert werden.")
if data.is_social_media is not None and user["rolle"] != "admin":
raise HTTPException(403, "is_social_media darf nur von Admins geändert werden.")
if data.subscription_tier is not None and user["rolle"] != "admin":
raise HTTPException(403, "subscription_tier darf nur von Admins geändert werden.")
if data.subscription_tier is not None and data.subscription_tier not in _VALID_TIERS:
raise HTTPException(400, f"Ungültiger Tier. Erlaubt: {', '.join(sorted(_VALID_TIERS))}")
with db() as conn:
target = conn.execute("SELECT id, rolle, name FROM users WHERE id=?", (uid,)).fetchone()
@ -385,7 +392,7 @@ async def patch_user(uid: int, data: UserPatch, user=Depends(require_mod)):
cols = ", ".join(f"{k}=?" for k in updates)
conn.execute(f"UPDATE users SET {cols} WHERE id=?", [*updates.values(), uid])
row = conn.execute(
"SELECT id, name, email, rolle, is_moderator, is_banned, ban_reason FROM users WHERE id=?",
"SELECT id, name, email, rolle, is_moderator, is_banned, ban_reason, subscription_tier FROM users WHERE id=?",
(uid,)
).fetchone()
@ -395,6 +402,8 @@ async def patch_user(uid: int, data: UserPatch, user=Depends(require_mod)):
detail_parts.append("gesperrt" if updates["is_banned"] else "entsperrt")
if "rolle" in updates:
detail_parts.append(f"Rolle→{updates['rolle']}")
if "subscription_tier" in updates:
detail_parts.append(f"Tier→{updates['subscription_tier']}")
_audit(conn, user, "user_patch", f"user:{uid} ({target['name']})", ", ".join(detail_parts) or None)
return dict(row)

View file

@ -7792,7 +7792,7 @@ svg.empty-state-icon {
#worlds-overlay {
position: fixed;
inset: 0;
z-index: 50;
z-index: 450;
overflow: hidden;
background: var(--c-bg);
display: none;

Binary file not shown.

View file

@ -95,7 +95,7 @@
<!-- CSS: Reihenfolge ist wichtig — ?v= zwingt Browser zur Neuladung -->
<link rel="stylesheet" href="/css/design-system.css?v=709">
<link rel="stylesheet" href="/css/layout.css?v=709">
<link rel="stylesheet" href="/css/components.css?v=709">
<link rel="stylesheet" href="/css/components.css?v=738">
</head>
<body>
@ -578,7 +578,7 @@
<script src="/js/api.js?v=94"></script>
<script src="/js/ui.js?v=94"></script>
<script src="/js/app.js?v=94"></script>
<script src="/js/worlds.js?v=727"></script>
<script src="/js/worlds.js?v=741"></script>
<!-- Feature-Seiten werden lazy geladen -->

View file

@ -3,8 +3,8 @@
Router, State-Management, Navigation, Initialisierung.
============================================================ */
const APP_VER = '727'; // ← bei jedem Deploy mit Frontend-Änderungen erhöhen
const APP_VERSION = '1.4.0'; // ← semantische Version, wird bei make release gesetzt
const APP_VER = '741'; // ← bei jedem Deploy mit Frontend-Änderungen erhöhen
const APP_VERSION = '1.5.0'; // ← semantische Version, wird bei make release gesetzt
const IS_STAGING = location.hostname === 'staging.banyaro.app';
const App = (() => {
@ -43,7 +43,7 @@ const App = (() => {
routes: { title: 'Routen', module: null },
events: { title: 'Events', module: null },
poison: { title: 'Giftköder-Alarm', module: null },
walks: { title: 'Gassi-Treffen', module: null, requiresAuth: true },
walks: { title: 'Gassi-Treffen', module: null, requiresAuth: true, requiresPro: true },
sitting: { title: 'Sitting', module: null, requiresAuth: true },
forum: { title: 'Forum', module: null },
wiki: { title: 'Wiki', module: null },
@ -51,12 +51,12 @@ const App = (() => {
movies: { title: 'Filme', module: null },
trainingsplaene: { title: 'Trainingspläne', module: null },
uebungen: { title: 'Übungsbibliothek', module: null },
notes: { title: 'Notizblock', module: null, requiresAuth: true },
notes: { title: 'Notizblock', module: null, requiresAuth: true, requiresPro: true },
'erste-hilfe': { title: 'Erste Hilfe', module: null },
settings: { title: 'Einstellungen', module: null },
lost: { title: 'Verlorener Hund', module: null },
friends: { title: 'Freunde', module: null, requiresAuth: true },
chat: { title: 'Nachrichten', module: null, requiresAuth: true },
friends: { title: 'Freunde', module: null, requiresAuth: true, requiresPro: true },
chat: { title: 'Nachrichten', module: null, requiresAuth: true, requiresPro: true },
social: { title: 'Social Media', module: null, requiresAuth: true },
admin: { title: 'Admin', module: null, requiresAuth: true },
moderation: { title: 'Moderation', module: null, requiresAuth: true },
@ -74,14 +74,28 @@ const App = (() => {
expenses: { title: 'Ausgaben', module: null, requiresAuth: true },
recalls: { title: 'Rückrufe', module: null },
adoption: { title: 'Adoption', module: null },
playdate: { title: 'Playdate', module: null, requiresAuth: true },
playdate: { title: 'Playdate', module: null, requiresAuth: true, requiresPro: true },
wetter: { title: 'Wetter', module: null },
ernaehrung: { title: 'Ernährung', module: null, requiresAuth: true },
ernaehrung: { title: 'Ernährung', module: null, requiresAuth: true, requiresPro: true },
personality: { title: 'Persönlichkeitstest', module: null },
reise: { title: 'Reise mit Hund', module: null },
reise: { title: 'Reise mit Hund', module: null, requiresPro: true },
hilfe: { title: 'Hilfe & FAQ', module: null },
};
// ----------------------------------------------------------
// TIER-CHECK — Frontend-Pendant zu has_pro_access() in auth.py
// ----------------------------------------------------------
function _hasPro(user) {
if (!user) return false;
const t = user.subscription_tier || 'standard';
// _test-Tiers simulieren ihren Tier ohne Admin-Override — so sieht Admin was echte User sehen
if (t.endsWith('_test')) return ['pro_test','breeder_test'].includes(t);
// Normale Prüfung: Admin/Mod/Social bekommen immer Pro
if (user.rolle === 'admin' || user.rolle === 'moderator') return true;
if (user.is_moderator || user.is_social_media) return true;
return ['pro','breeder'].includes(t);
}
// ----------------------------------------------------------
// AUTH GUARD — Login-Gate Texte pro Seite
// ----------------------------------------------------------
@ -142,6 +156,34 @@ const App = (() => {
return;
}
// Pro-Guard — nur wenn User eingeloggt aber kein Pro-Zugang
if (page.requiresPro && state.user && !_hasPro(state.user)) {
const container = document.querySelector(`#page-${pageId} .page-body`);
if (container) {
container.innerHTML = `
<div style="max-width:420px;margin:40px auto;padding:var(--space-6) var(--space-4);text-align:center">
<div style="font-size:3rem;margin-bottom:var(--space-4)"></div>
<h2 style="font-size:var(--text-xl);font-weight:800;margin:0 0 var(--space-3)">Ban Yaro Pro</h2>
<p style="color:var(--c-text-secondary);margin:0 0 var(--space-6);line-height:1.6">
Dieses Feature ist Teil von Ban Yaro Pro verfügbar wenn wir die nächste Stufe zünden.<br>
Du wirst benachrichtigt wenn es soweit ist.
</p>
<div style="background:var(--c-bg-card);border:1px solid var(--c-border);border-radius:var(--radius-lg);
padding:var(--space-4);text-align:left;font-size:var(--text-sm);color:var(--c-text-secondary)">
<div style="font-weight:700;margin-bottom:var(--space-2);color:var(--c-text)">Ban Yaro Pro enthält:</div>
<ul style="margin:0;padding-left:var(--space-5);display:flex;flex-direction:column;gap:var(--space-1)">
<li>Mehrere Hunde verwalten</li>
<li>KI-Trainer für personalisiertes Training</li>
<li>Direktnachrichten &amp; Freunde</li>
<li>Gassi-Treffen, Playdate, Ernährung, Reise</li>
<li>Notizblock</li>
</ul>
</div>
</div>`;
}
return;
}
if (page.module) {
const hasParams = params && Object.keys(params).length > 0;
if (hasParams) {
@ -819,6 +861,7 @@ const App = (() => {
try { localStorage.removeItem('by_wissen_open'); } catch (_) {}
_initVersionCheck();
await _checkAuth();
// Einladungslink /teilen/{token} → direkt annehmen
@ -916,6 +959,124 @@ const App = (() => {
});
}
// ----------------------------------------------------------
// ----------------------------------------------------------
// VERSION-CHECK — persistentes Banner wenn neue Version verfügbar
// ----------------------------------------------------------
let _updateBannerShown = false;
async function _checkVersion() {
try {
const r = await fetch('/api/version', { cache: 'no-store' });
if (!r.ok) return;
const { version } = await r.json();
if (version && version !== APP_VER && !_updateBannerShown) {
_updateBannerShown = true;
_showUpdateBanner(version);
}
} catch { /* offline — ignorieren */ }
}
function _showUpdateBanner(newVersion) {
const isIos = /iphone|ipad|ipod/i.test(navigator.userAgent);
const existing = document.getElementById('app-update-banner');
if (existing) return;
const banner = document.createElement('div');
banner.id = 'app-update-banner';
banner.style.cssText = [
'position:fixed;bottom:calc(env(safe-area-inset-bottom,0px) + 72px);left:12px;right:12px',
'z-index:9000;background:var(--c-primary);color:#fff;border-radius:16px',
'padding:14px 16px;box-shadow:0 4px 20px rgba(0,0,0,0.3)',
'display:flex;flex-direction:column;gap:10px',
].join(';');
banner.innerHTML = `
<div style="display:flex;align-items:center;justify-content:space-between;gap:10px">
<div>
<div style="font-weight:700;font-size:var(--text-sm)">
Neue Version verfügbar (v${newVersion})
</div>
<div style="font-size:var(--text-xs);opacity:0.85;margin-top:2px">
Tippe auf Aktualisieren um die neueste Version zu laden.
</div>
</div>
<div style="display:flex;gap:8px;flex-shrink:0">
<button id="upd-btn-reload"
style="background:rgba(255,255,255,0.2);border:1px solid rgba(255,255,255,0.4);
color:#fff;border-radius:10px;padding:8px 14px;cursor:pointer;
font-size:var(--text-sm);font-weight:700;white-space:nowrap">
Aktualisieren
</button>
<button id="upd-btn-close"
style="background:none;border:none;color:rgba(255,255,255,0.7);
cursor:pointer;font-size:1.1rem;padding:4px 6px;line-height:1"></button>
</div>
</div>
<div id="upd-ios-hint" style="display:none;font-size:var(--text-xs);
background:rgba(0,0,0,0.2);border-radius:10px;padding:10px 12px;line-height:1.6">
${isIos
? `Falls die App nach dem Aktualisieren noch die alte Version zeigt:<br>
<strong>1.</strong> Drücke lange auf das App-Icon am Homescreen<br>
<strong>2.</strong> Wähle App entfernen" (nur das Symbol, keine Daten)<br>
<strong>3.</strong> Öffne banyaro.app in Safari und füge die App erneut hinzu`
: `Falls die Seite noch die alte Version zeigt:<br>
Drücke <strong>Cmd+Shift+R</strong> (Mac) bzw. <strong>Ctrl+Shift+R</strong> (Windows/Android Chrome) für einen harten Reload.`}
</div>
`;
document.body.appendChild(banner);
banner.querySelector('#upd-btn-close').addEventListener('click', () => banner.remove());
banner.querySelector('#upd-btn-reload').addEventListener('click', async () => {
const btn = banner.querySelector('#upd-btn-reload');
btn.textContent = 'Lädt…';
btn.disabled = true;
sessionStorage.setItem('by_update_reload', APP_VER);
try {
// SW aktivieren + alle Caches leeren für sauberen Reload
const reg = await navigator.serviceWorker?.getRegistration();
if (reg?.waiting) reg.waiting.postMessage({ type: 'SKIP_WAITING' });
await reg?.update();
const keys = await caches.keys();
await Promise.all(keys.map(k => caches.delete(k)));
} catch { /* ignorieren */ }
setTimeout(() => location.reload(), 600);
});
}
function _initVersionCheck() {
// Beim Start nach 10 Sekunden prüfen (nicht sofort — Prio für Auth)
setTimeout(_checkVersion, 10_000);
// Dann alle 30 Minuten
setInterval(_checkVersion, 30 * 60_000);
// Beim Wiedereinstieg in die App
document.addEventListener('visibilitychange', () => {
if (document.visibilityState === 'visible') _checkVersion();
});
// Nach Reload: war das ein Update-Reload? Falls Version immer noch alt → iOS-Hinweis
const reloadVer = sessionStorage.getItem('by_update_reload');
if (reloadVer && reloadVer === APP_VER) {
// Version hat sich nicht geändert nach Reload → iOS-Cache-Problem
sessionStorage.removeItem('by_update_reload');
setTimeout(() => {
fetch('/api/version', { cache: 'no-store' })
.then(r => r.json())
.then(({ version }) => {
if (version && version !== APP_VER) {
_updateBannerShown = true;
_showUpdateBanner(version);
// iOS-Hinweis sofort aufklappen
setTimeout(() => {
document.getElementById('upd-ios-hint')?.style.setProperty('display', 'block');
}, 300);
}
}).catch(() => {});
}, 2000);
}
}
// ----------------------------------------------------------
// ÖFFENTLICHE API
// (andere Module können App.state, App.navigate etc. nutzen)

View file

@ -795,6 +795,9 @@ window.Page_admin = (() => {
<span style="color:${u.rolle === 'admin' ? 'var(--c-danger)' : u.rolle === 'moderator' ? '#f59e0b' : 'var(--c-text-muted)'}">
${_esc(u.rolle)}
</span>
· <span style="color:${u.subscription_tier && u.subscription_tier !== 'standard' ? 'var(--c-primary)' : 'var(--c-text-muted)'}">
${_esc(u.subscription_tier || 'standard')}
</span>
· ${u.dog_count} Hund${u.dog_count !== 1 ? 'e' : ''}
· ${u.thread_count} Threads
</div>
@ -823,6 +826,11 @@ window.Page_admin = (() => {
title="Rolle ändern">
<svg class="ph-icon"><use href="/icons/phosphor.svg#shield"></use></svg>
</button>
<button class="btn btn-sm btn-ghost adm-tier" data-uid="${u.id}"
data-name="${_esc(u.name)}" data-tier="${_esc(u.subscription_tier || 'standard')}"
title="Abo-Stufe ändern">
<svg class="ph-icon"><use href="/icons/phosphor.svg#star"></use></svg>
</button>
<button class="btn btn-sm btn-ghost adm-delete" data-uid="${u.id}"
data-name="${_esc(u.name)}" title="Löschen"
style="color:var(--c-danger)">
@ -847,6 +855,9 @@ window.Page_admin = (() => {
el.querySelectorAll('.adm-rolle').forEach(btn => {
btn.addEventListener('click', () => _changeRolle(btn.dataset.uid, btn.dataset.name, btn.dataset.rolle));
});
el.querySelectorAll('.adm-tier').forEach(btn => {
btn.addEventListener('click', () => _changeTier(btn.dataset.uid, btn.dataset.name, btn.dataset.tier));
});
el.querySelectorAll('.adm-delete').forEach(btn => {
btn.addEventListener('click', () => _deleteUser(btn.dataset.uid, btn.dataset.name));
});
@ -903,6 +914,54 @@ window.Page_admin = (() => {
});
}
async function _changeTier(uid, name, currentTier) {
const tiers = ['standard', 'pro', 'breeder', 'standard_test', 'pro_test', 'breeder_test'];
const tierLabels = {
standard: 'Standard (kostenlos)',
pro: 'Pro (bezahlt)',
breeder: 'Breeder (Züchter)',
standard_test: 'Standard Test (intern)',
pro_test: 'Pro Test (intern)',
breeder_test: 'Breeder Test (intern)',
};
UI.modal.open({
title: `Abo-Stufe ändern: ${name}`,
body: `
<p style="font-size:var(--text-sm);color:var(--c-text-secondary);margin:0 0 var(--space-4)">
Aktuelle Stufe: <strong>${currentTier}</strong>
</p>
<div style="display:flex;flex-direction:column;gap:var(--space-2)">
${tiers.map(t => `
<button class="btn ${t === currentTier ? 'btn-primary' : 'btn-secondary'} adm-tier-choice"
data-tier="${t}" form="" ${t === currentTier ? 'disabled' : ''}>
${tierLabels[t]}${t === currentTier ? ' ✓' : ''}
</button>
`).join('')}
</div>
`,
});
document.querySelectorAll('.adm-tier-choice:not([disabled])').forEach(btn => {
btn.addEventListener('click', async () => {
UI.modal.close();
try {
await API.patch(`/admin/users/${uid}`, { subscription_tier: btn.dataset.tier });
// Eigenes Tier geändert → User-State neu laden + Welten neu rendern
if (String(uid) === String(_appState?.user?.id)) {
_appState.user.subscription_tier = btn.dataset.tier;
if (window.Worlds) {
window.Worlds.init(_appState);
}
UI.toast.success(`Dein Tier ist jetzt ${btn.dataset.tier} — Ansicht aktualisiert.`);
} else {
UI.toast.success(`${name}: Abo-Stufe ist jetzt ${btn.dataset.tier}.`);
}
_renderTab();
} catch (e) { UI.toast.error(e.message); }
});
});
}
async function _deleteUser(uid, name) {
const ok = await UI.modal.confirm({
title: `${name} löschen?`,

View file

@ -82,6 +82,16 @@ window.Page_datenschutz = (() => {
Plattformsicherheit).
</p>`)}
${sec('Direktnachrichten', `
<p style="${S.p}">
Nachrichten zwischen Nutzern (z. B. zwischen Hundesitter und Hundeeigentümer oder
zwischen Interessenten und Züchtern) werden auf unserem Server gespeichert, bis du
das Gespräch oder deinen Account löschst. Admins können gemeldete Nachrichten zur
Missbrauchsprüfung einsehen (Art. 6 Abs. 1 lit. f DSGVO berechtigtes Interesse
an Plattformsicherheit). Nachrichten werden nicht an Dritte weitergegeben.
Du kannst Gespräche jederzeit selbst löschen.
</p>`)}
${sec('KI-Funktionen', `
<p style="${S.p}">
Ban Yaro bietet KI-gestützte Funktionen (Trainingsempfehlungen, Terminvorschläge,
@ -99,12 +109,34 @@ window.Page_datenschutz = (() => {
<a href="https://www.anthropic.com/privacy" target="_blank" rel="noopener"
style="${S.a}">anthropic.com/privacy</a>.
</p>
<p style="${S.p};margin-top:var(--space-3)">
Der <strong>KI-Trainer</strong> analysiert deinen bisherigen Trainingsfortschritt
(Übungshistorie, Erfolgsquoten, Streaks) und gibt personalisierte Empfehlungen.
Diese Analyse läuft auf unserem lokalen Server in Deutschland deine Trainingsdaten
verlassen dabei nicht unsere Infrastruktur. Es findet kein Training oder Fine-Tuning
von KI-Modellen auf Basis deiner Nutzerdaten statt.
</p>
<p style="${S.p};margin-top:var(--space-3)">
KI-Empfehlungen sind Vorschläge und ersetzen keine tierärztliche Beratung.
Eine automatisierte Entscheidungsfindung mit rechtlicher Wirkung (Art. 22 DSGVO)
findet nicht statt.
</p>`)}
${sec('Wetterdaten (Open-Meteo)', `
<p style="${S.p}">
Die Wetter-Funktion übermittelt auf Wunsch deine GPS-Koordinaten einmalig an
<strong>Open-Meteo</strong> (Österreich, DSGVO-konform), um die lokale
Wettervorhersage abzurufen. Es werden ausschließlich anonyme Koordinaten übertragen
keine Account- oder Profildaten. Open-Meteo protokolliert keine personenbezogenen
Daten. Die Funktion wird nur aktiv, wenn du deinen Standort im Browser freigibst.
Rechtsgrundlage: Einwilligung gem. Art. 6 Abs. 1 lit. a DSGVO.
</p>
<p style="${S.p};margin-top:var(--space-3)">
Datenschutzerklärung von Open-Meteo:
<a href="https://open-meteo.com/en/terms" target="_blank" rel="noopener"
style="${S.a}">open-meteo.com/en/terms</a>
</p>`)}
${sec('Routenvorschläge (OpenRouteService)', `
<p style="${S.p}">
Die Funktion <strong>Routenvorschläge"</strong> berechnet auf Wunsch einen Rundweg

View file

@ -276,6 +276,13 @@ window.Page_settings = (() => {
<span>Hilfe &amp; FAQ</span>
<span style="margin-left:auto;color:var(--c-text-secondary)"></span>
</div>
${!_appState.user?.subscription_tier || _appState.user.subscription_tier === 'standard' || _appState.user.subscription_tier === 'standard_test' ? `
<div style="margin:var(--space-3) 0;padding:var(--space-3) var(--space-4);
background:rgba(196,132,58,0.1);border-radius:var(--radius-md);
font-size:var(--text-xs);color:var(--c-text-secondary)">
<strong>Ban Yaro Pro</strong> kommt bald mehr Features, mehrere Hunde.
</div>
` : ''}
<div class="sidebar-item" id="settings-logout-btn"
style="padding:var(--space-4);border-radius:0;cursor:pointer;
color:var(--c-danger)">

View file

@ -87,6 +87,14 @@ window.Page_welcome = (() => {
// LANDING PAGE — nicht eingeloggte Besucher
// ----------------------------------------------------------
function _renderLanding(isInstalled) {
// Browser-Besucher (kein PWA) ohne Login → auf /info weiterleiten
const isPWA = window.matchMedia('(display-mode: standalone)').matches
|| window.navigator.standalone === true;
if (!isPWA && !sessionStorage.getItem('by_stay_in_app')) {
window.location.replace('/info');
return;
}
const hasPrompt = !!App.getInstallPrompt();
_container.innerHTML = `
@ -108,7 +116,7 @@ window.Page_welcome = (() => {
${hasPrompt ? `
<button class="btn wc-btn-hero" id="welcome-install-hero-btn">
<svg class="ph-icon" aria-hidden="true"><use href="/icons/phosphor.svg#download-simple"></use></svg>
App installieren
Zum Home-Bildschirm hinzufügen
</button>
` : `
<button class="btn wc-btn-hero" id="welcome-register-btn">
@ -218,7 +226,7 @@ window.Page_welcome = (() => {
${hasPrompt ? `
<button class="btn wc-btn-hero" id="welcome-install-hero-btn2">
<svg class="ph-icon" aria-hidden="true"><use href="/icons/phosphor.svg#download-simple"></use></svg>
App installieren kostenlos
Zum Home-Bildschirm hinzufügen
</button>
` : `
<button class="btn wc-btn-hero" id="welcome-register-btn2">
@ -231,7 +239,7 @@ window.Page_welcome = (() => {
${!isInstalled ? `
<button class="wc-install-link" id="welcome-install-link">
<svg class="ph-icon" aria-hidden="true"><use href="/icons/phosphor.svg#download-simple"></use></svg>
Installationsanleitung
Zum Home-Bildschirm hinzufügen
</button>
` : ''}
</div>
@ -242,7 +250,7 @@ window.Page_welcome = (() => {
<div class="card wc-install-card">
<div class="wc-install-header">
<svg class="ph-icon" aria-hidden="true"><use href="/icons/phosphor.svg#download-simple"></use></svg>
App installieren
Immer griffbereit kein App Store
</div>
<div style="padding:var(--space-4)">${_installHTML()}</div>
</div>
@ -481,7 +489,7 @@ window.Page_welcome = (() => {
<div class="card wc-install-card">
<div class="wc-install-header">
<svg class="ph-icon" aria-hidden="true"><use href="/icons/phosphor.svg#download-simple"></use></svg>
App installieren
Immer griffbereit kein App Store
</div>
<div style="padding:var(--space-4)">${_installHTML()}</div>
</div>
@ -1069,11 +1077,11 @@ window.Page_welcome = (() => {
if (hasPrompt) {
return `
<p style="font-size:var(--text-sm);color:var(--c-text-secondary);margin:0 0 var(--space-3);line-height:1.5">
Kein App Store nötig direkt auf den Home-Bildschirm.
Ban Yaro immer griffbereit einmal hinzufügen, dann direkt vom Home-Bildschirm öffnen.
</p>
<button class="btn btn-primary" id="install-android-btn" style="width:100%;margin-bottom:var(--space-2)">
<svg class="ph-icon" aria-hidden="true"><use href="/icons/phosphor.svg#download-simple"></use></svg>
Ban Yaro installieren
Zum Home-Bildschirm hinzufügen
</button>`;
}
@ -1095,7 +1103,7 @@ window.Page_welcome = (() => {
if (isIOS && !isSafari) {
return `
<p style="font-size:var(--text-sm);color:var(--c-text-secondary);margin:0 0 var(--space-4);line-height:1.5">
Auf dem iPhone geht die Installation nur über <strong>Safari</strong>.
Auf dem iPhone funktioniert das Hinzufügen nur über <strong>Safari</strong>.
</p>
${_steps([
['safari-logo', 'Öffne <strong>Safari</strong> auf deinem iPhone'],

View file

@ -457,14 +457,14 @@ window.Worlds = (() => {
// Alle verfügbaren Chips mit Metadaten
const _ALL_CHIPS = [
{ icon:'note-pencil', label:'Notizblock', page:'notes',
{ icon:'note-pencil', label:'Notizblock', page:'notes', pro: true,
fab:[{ icon:'note-pencil', color:'#10B981', label:'Neue Notiz', sub:'Schnellnotiz erstellen', page:'notes', action:'openNew' }] },
{ icon:'currency-eur', label:'Ausgaben', page:'expenses',
fab:[{ icon:'currency-eur', color:'#3B82F6', label:'Ausgabe eintragen', sub:'Einmalig oder Dauerauftrag', page:'expenses', action:'openNew' }] },
{ icon:'first-aid', label:'Erste Hilfe', page:'erste-hilfe' },
{ icon:'handshake', label:'Playdate', page:'playdate',
{ icon:'handshake', label:'Playdate', page:'playdate', pro: true,
fab:[{ icon:'handshake', color:'#F59E0B', label:'Playdate anfragen', sub:'Treffen mit anderen Hunden', page:'playdate', action:'openNew' }] },
{ icon:'chat-circle-dots', label:'Nachrichten', page:'chat' },
{ icon:'chat-circle-dots', label:'Nachrichten', page:'chat', pro: true },
{ icon:'sun', label:'Wetter', page:'wetter' },
{ icon:'book-open', label:'Tagebuch', page:'diary',
@ -486,9 +486,9 @@ window.Worlds = (() => {
fab:[{ icon:'map-pin', color:'#10B981', label:'Ort vorschlagen', sub:'Neuen POI auf der Karte', page:'map' }] },
{ icon:'push-pin', label:'Forum', page:'forum',
fab:[{ icon:'push-pin', color:'#8B5CF6', label:'Forum-Beitrag', sub:'Thema oder Frage erstellen', page:'forum', action:'openNew' }] },
{ icon:'users', label:'Freunde', page:'friends',
{ icon:'users', label:'Freunde', page:'friends', pro: true,
fab:[{ icon:'users', color:'#3B82F6', label:'Freund einladen', sub:'Per Link einladen', page:'friends', action:'openNew' }] },
{ icon:'paw-print', label:'Gassi', page:'walks',
{ icon:'paw-print', label:'Gassi', page:'walks', pro: true,
fab:[{ icon:'paw-print', color:'#F59E0B', label:'Gassirunde', sub:'Neue Runde starten', page:'walks', action:'openNew' },
{ icon:'paw-print', color:'#10B981', label:'Schnell-Gassi', sub:'Kurze Runde ohne GPS eintragen', page:'walks', action:'quickGassi' }] },
{ icon:'skull', label:'Giftköder', page:'poison',
@ -513,9 +513,9 @@ window.Worlds = (() => {
{ icon:'shield-check', label:'Moderation', page:'moderation', role:'mod' },
{ icon:'gear', label:'Admin', page:'admin', role:'admin' },
// ── NEUE FEATURES ────────────────────────────────────────────
{ icon:'fork-knife', label:'Ernährung', page:'ernaehrung',
{ icon:'fork-knife', label:'Ernährung', page:'ernaehrung', pro: true,
fab:[{ icon:'fork-knife', color:'#F97316', label:'Futter-Tagebuch', sub:'Mahlzeit oder Futtercheck', page:'ernaehrung' }] },
{ icon:'airplane', label:'Reise', page:'reise' },
{ icon:'airplane', label:'Reise', page:'reise', pro: true },
{ icon:'smiley', label:'Persönlichkeit', page:'personality' },
];
@ -567,13 +567,31 @@ window.Worlds = (() => {
}
function _chipAllowed(chip) {
const u = _state?.user;
const tier = u?.subscription_tier || 'standard';
const isTest = tier.endsWith('_test');
// Role-Checks (hart — komplett ausblenden)
if (!chip?.role) return true;
if (chip.role === 'breeder') return u?.rolle === 'breeder' || u?.rolle === 'admin';
if (chip.role === 'breeder') {
if (isTest) return tier === 'breeder_test';
return u?.rolle === 'breeder' || u?.rolle === 'admin';
}
if (chip.role === 'social') return u?.is_social_media || u?.rolle === 'admin';
if (chip.role === 'mod') return u?.rolle === 'admin' || u?.rolle === 'moderator' || u?.is_moderator;
if (chip.role === 'admin') return u?.rolle === 'admin';
return true;
}
// Gibt true zurück wenn User vollen Pro-Zugriff hat
function _hasProAccess() {
const u = _state?.user;
if (!u) return false;
const tier = u.subscription_tier || 'standard';
if (tier.endsWith('_test')) return ['pro_test','breeder_test'].includes(tier);
if (u.rolle === 'admin' || u.rolle === 'moderator') return true;
if (u.is_moderator || u.is_social_media) return true;
return ['pro','breeder'].includes(tier);
}
function _chipsForWorld(world) {
const pages = _getConfig()[world] || _DEFAULT_CONFIG[world];
return pages.map(_chipMeta).filter(c => c && _chipAllowed(c));
@ -869,9 +887,10 @@ window.Worlds = (() => {
// ── CHIP-HELPER ──────────────────────────────────────────────
function _chip(icon, label, page) {
function _chip(icon, label, page, locked = false) {
const style = locked ? 'opacity:0.25;cursor:default;' : '';
return `
<div class="world-chip" data-wnav="${page}">
<div class="world-chip" ${locked ? '' : `data-wnav="${page}"`} style="${style}">
<svg class="ph-icon" style="width:1.4rem;height:1.4rem">
<use href="/icons/phosphor.svg#${icon}"></use>
</svg>
@ -1240,7 +1259,7 @@ window.Worlds = (() => {
` : ''}
<div class="world-section-label">Alles über ${_esc(dog.name)}</div>
<div class="world-chips-grid">
${chips.map(c => _chip(c.icon, c.label, c.page)).join('')}
${chips.map(c => _chip(c.icon, c.label, c.page, !!(c.pro && !_hasProAccess()))).join('')}
</div>
<div class="world-footer-links">
<span data-wnav="gruender">Die 100 Gründer</span>
@ -1334,7 +1353,7 @@ window.Worlds = (() => {
<div class="world-bottom">
<div class="world-section-label">Die Welt da draußen</div>
<div class="world-chips-grid">
${chips.map(c => _chip(c.icon, c.label, c.page)).join('')}
${chips.map(c => _chip(c.icon, c.label, c.page, !!(c.pro && !_hasProAccess()))).join('')}
</div>
<div class="world-footer-links">
<span data-wnav="datenschutz">Datenschutz</span>

View file

@ -92,7 +92,12 @@
"Notizblock mit KI-Analyse-Funktion",
"Erste-Hilfe-Ratgeber für häufige Notfälle",
"Hunde-Knigge (Begegnungen, ÖPNV, Leinenpflicht, Haftpflicht)",
"Admin-Panel mit Moderation, Outreach-Mailing und Statistiken"
"Admin-Panel mit Moderation, Outreach-Mailing und Statistiken",
"Gassi-Score mit 7-Tage-Wetter und persönlichen Wetter-Rekorden",
"Ernährungsrechner, BARF-Guide und Giftliste",
"Hunde-Persönlichkeitstest mit Trainingstipps",
"Reise-Checkliste und EU-Länder-Einreiseregeln",
"Integrierte Hilfe und FAQ ohne App Store"
],
"screenshot": "https://banyaro.app/icons/icon-512.png",
"softwareVersion": "1.2.1",
@ -372,6 +377,89 @@
.usp-item h3 { font-size: 0.95rem; font-weight: 700; margin-bottom: 0.2rem; }
.usp-item p { font-size: 0.85rem; color: var(--text-secondary); }
/* Outcome Cards */
.outcome-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(240px, 1fr));
gap: 24px;
margin-top: 2rem;
}
.outcome-card {
background: white;
border-radius: 12px;
padding: 24px;
box-shadow: 0 2px 12px rgba(0,0,0,.07);
}
.outcome-card .oc-icon {
font-size: 0; /* Emoji-Fallback ausblenden */
width: 3rem;
height: 3rem;
background: var(--primary-light);
border-radius: 14px;
display: flex;
align-items: center;
justify-content: center;
margin-bottom: 1rem;
flex-shrink: 0;
}
.outcome-card .oc-icon svg {
width: 1.6rem;
height: 1.6rem;
color: var(--primary);
}
.feature-card .feature-icon svg {
width: 1.4rem;
height: 1.4rem;
color: var(--primary);
}
.feature-card .feature-icon {
font-size: 0;
display: flex;
align-items: center;
justify-content: center;
}
.outcome-card h3 {
font-size: 1.05rem;
font-weight: 700;
color: var(--text);
margin-bottom: 0.5rem;
}
.outcome-card p {
font-size: 0.9rem;
color: var(--text-secondary);
line-height: 1.6;
margin: 0;
}
.section-cta-link {
display: inline-block;
margin-top: 1.75rem;
font-size: 0.9rem;
color: var(--text-muted);
cursor: pointer;
user-select: none;
}
.section-cta-link:hover { color: var(--primary); text-decoration: none; }
.collapsible-content { display: none; margin-top: 1.5rem; }
.collapsible-content.open { display: block; }
.section-cta-btn {
display: inline-block;
background: var(--primary);
color: white;
font-weight: 700;
font-size: 1rem;
padding: 0.75rem 2rem;
border-radius: 999px;
margin-top: 2rem;
box-shadow: 0 4px 16px rgba(196,132,58,.35);
transition: transform 0.15s, box-shadow 0.15s;
}
.section-cta-btn:hover {
transform: translateY(-2px);
box-shadow: 0 6px 24px rgba(196,132,58,.45);
text-decoration: none;
color: white;
}
/* Footer */
footer {
background: #1a1a1a;
@ -393,10 +481,10 @@
<span class="logo-name">Ban Yaro</span>
</div>
<h1>Die deutschsprachige Hunde-Plattform</h1>
<p>Alles rund um deinen Hund — von Welpe bis Opa. Kostenlos, DSGVO-konform, ohne App Store.</p>
<p>Alles rund um deinen Hund — von Welpe bis Opa. Kostenlos, ohne App Store, Daten in Deutschland.</p>
<div class="header-badges">
<span class="badge">Kostenlos nutzbar</span>
<span class="badge">DSGVO-konform</span>
<span class="badge">Daten in Deutschland</span>
<span class="badge">Kein App Store nötig</span>
<span class="badge">Made in Germany</span>
<span class="badge">Offline-fähig</span>
@ -408,214 +496,266 @@
<nav>
<div class="container">
<span class="nav-brand">Ban Yaro</span>
<a href="#funktionen">Funktionen</a>
<a href="#demo">Demo</a>
<a href="#funktionen">Hundebesitzer</a>
<a href="#zuechter">Züchter</a>
<a href="#welpen">Welpen</a>
<a href="#vergleich">Vergleich</a>
<a href="#preise">Preise</a>
<a href="#warum">Warum Ban Yaro?</a>
<a href="/wiki/rassen">Rassen-Wiki</a>
<a href="/knigge">Knigge</a>
<a href="/">App öffnen</a>
</div>
</nav>
<section id="funktionen">
<div class="container">
<h2>Alles für Hundebesitzer in einer App</h2>
<p class="section-intro">Ban Yaro vereint alle wichtigen Hunde-Tools — ohne Werbung, ohne Datenweitergabe an US-Konzerne, ohne monatliche Pflichtkosten.</p>
<div class="feature-group">
<div class="feature-group-label">Mein Hund</div>
<div class="feature-grid">
<div class="feature-card">
<span class="feature-icon">🏠</span>
<div><h3>Personalisiertes Dashboard</h3><p>Die Startseite begrüßt dich mit einem täglich wechselnden Foto deines Hundes aus dem Tagebuch, zeigt aktuelle Stats (letzter Eintrag, nächster Termin, Gewicht, Übung des Tages) und navigiert direkt zu allen Bereichen.</p><span class="feature-tag">Kostenlos</span></div>
</div>
<div class="feature-card">
<span class="feature-icon">📓</span>
<div><h3>Tagebuch</h3><p>Fotos, Videos, Texte und GPS-Orte — alle Momente mit deinem Hund. Kategorien wie Spaziergänge, Meilensteine, Lustiges.</p><span class="feature-tag">Kostenlos</span></div>
</div>
<div class="feature-card">
<span class="feature-icon">💉</span>
<div><h3>Gesundheit & Impfpass</h3><p>Impfungen, Tierarztbesuche, Medikamente digital verwalten. Automatische Erinnerungen per Push-Notification.</p><span class="feature-tag">Kostenlos</span></div>
</div>
<div class="feature-card">
<span class="feature-icon">🎯</span>
<div><h3>Training & Übungen</h3><p>Einheiten loggen (Wiederholungen, Erfolgsquote, Stimmung), Fortschritt in 5 Stufen verfolgen. Virtueller Trainer empfiehlt täglich welche Übungen anstehen — inkl. Trendanalyse und Prognose bis zur Meisterschaft. Streaks, Abzeichen und Trainingskalender motivieren dranzubleiben.</p><span class="feature-tag">Kostenlos</span></div>
</div>
<div class="feature-card">
<span class="feature-icon">🏅</span>
<div><h3>Kommandos & Fähigkeiten</h3><p>Alle beherrschten Kommandos sichtbar im Hunde-Profil — praktisch für Hundesitter, die genau wissen müssen worauf dein Hund reagiert.</p><span class="feature-tag">Kostenlos</span></div>
</div>
<div class="feature-card">
<span class="feature-icon">🏥</span>
<div><h3>Symptom-Checker</h3><p>KI-gestützte Ersteinschätzung: beobachten, Tierarzt oder Notfall? Orientierung wann es wirklich dringend ist.</p><span class="feature-tag">Kostenlos</span></div>
</div>
<div class="feature-card">
<span class="feature-icon">🛁</span>
<div><h3>Pflege-System</h3><p>43 rassenspezifische Pflegetipps: Fell (mit Schneiden/Trimmen-Unterscheidung), Krallen, Zähne, Ohren, Augen, Pfoten und mehr — Tipp des Tages automatisch ausgewählt.</p><span class="feature-tag">Kostenlos</span></div>
</div>
<div class="feature-card">
<span class="feature-icon">🎉</span>
<div><h3>Wöchentlicher Lober</h3><p>Jeden Montag schreibt die KI 2-3 Sätze Lob für eure Vorwoche — nur Lob, kein Rat, kein Druck. Motivation die gut tut.</p><span class="feature-tag">Kostenlos</span></div>
</div>
<div class="feature-card">
<span class="feature-icon">🌤️</span>
<div><h3>Wetter & Zecken-Alarm</h3><p>Aktuelles Wetter direkt in der App (ohne API-Key). Automatische Zecken-Warnung wenn Saison und Temperatur passen.</p><span class="feature-tag">Kostenlos</span></div>
</div>
<div class="feature-card">
<span class="feature-icon">📄</span>
<div><h3>Digitaler Heimtierausweis</h3><p>Alle Gesundheitsdaten als druckbares Dokument — für Tierarzt, Tierpension oder Auslandsreise.</p><span class="feature-tag">Kostenlos</span></div>
</div>
<div class="feature-card">
<span class="feature-icon">🆔</span>
<div><h3>NFC-Halsband-Tags</h3><p>Öffentliche Profilseite für jeden Hund. Finder kontaktiert dich anonym — ohne deine Nummer preiszugeben.</p><span class="feature-tag">Kostenlos + Shop</span></div>
</div>
</div>
<!-- Demo-Video -->
<section id="demo" style="background:#1a1a1a;padding:64px 20px;text-align:center">
<div style="max-width:480px;margin:0 auto">
<h2 style="color:white;font-size:clamp(1.3rem,3.5vw,1.8rem);font-weight:800;margin:0 0 12px">
Sieh selbst wie es sich anfühlt
</h2>
<p style="color:rgba(255,255,255,0.65);font-size:1rem;margin:0 0 32px;line-height:1.6">
2:48 Minuten echte App — kein Marketing-Video, keine Dummy-Daten.
</p>
<div style="position:relative;display:inline-block;
border-radius:36px;overflow:hidden;
box-shadow:0 20px 60px rgba(0,0,0,0.5);
border:3px solid rgba(255,255,255,0.12);
max-width:280px;width:100%">
<video
src="/img/demo/app-demo.mp4"
controls
preload="metadata"
playsinline
style="display:block;width:100%;border-radius:33px"
poster="/icons/icon-512.png">
</video>
</div>
<div class="feature-group">
<div class="feature-group-label">Entdecken</div>
<div class="feature-grid">
<div class="feature-card">
<span class="feature-icon">🗺️</span>
<div><h3>Karte & Umgebung</h3><p>Interaktive Karte mit Giftköder-Meldungen, Gassi-Treffen, Routen und hundefreundlichen Orten in der Nähe.</p><span class="feature-tag">Kostenlos</span></div>
</div>
<div class="feature-card">
<span class="feature-icon">🐾</span>
<div><h3>GPS-Routen</h3><p>Routen aufzeichnen, teilen und bewerten — Untergrund, Schatten, Leinenpflicht, Sicherheit.</p><span class="feature-tag">Kostenlos</span></div>
</div>
<div class="feature-card">
<span class="feature-icon">🧭</span>
<div><h3>Tages-Gassirunde</h3><p>Täglich eine neue Rundroute vorgeschlagen — 2, 4 oder 6 km ab deinem Standort. Berechnet via OpenRouteService, direkt navigierbar. 3 verschiedene Varianten pro Tag.</p><span class="feature-tag">Kostenlos</span></div>
</div>
<div class="feature-card">
<span class="feature-icon">📅</span>
<div><h3>Events & Turniere</h3><p>Agility-Turniere, Hundeausstellungen und lokale Veranstaltungen in deiner Region.</p><span class="feature-tag">Kostenlos</span></div>
</div>
<div class="feature-card">
<span class="feature-icon">📍</span>
<div><h3>Hundefreundliche Orte</h3><p>Crowd-sourced Datenbank: Restaurants, Cafés, Parks, Geschäfte — mit echten Bewertungen von Hundebesitzern.</p><span class="feature-tag">Kostenlos</span></div>
</div>
</div>
</div>
<div class="feature-group">
<div class="feature-group-label">Community</div>
<div class="feature-grid">
<div class="feature-card">
<span class="feature-icon">⚠️</span>
<div><h3>Giftköder-Alarm</h3><p>Meldungen mit GPS und Foto. Alle Nutzer im Umkreis bekommen sofort eine Push-Benachrichtigung.</p><span class="feature-tag">Kostenlos</span></div>
</div>
<div class="feature-card">
<span class="feature-icon">🚨</span>
<div><h3>Verlorener Hund</h3><p>Sofortalarm für alle Nutzer in der Nähe — mit Foto, letzter GPS-Position und direktem Kontakt.</p><span class="feature-tag">Kostenlos</span></div>
</div>
<div class="feature-card">
<span class="feature-icon">🐕</span>
<div><h3>Gassi-Treffen</h3><p>Spontane oder geplante Gassi-Treffen erstellen und finden. Mit Hunde-Profilen der Teilnehmer.</p><span class="feature-tag">Kostenlos</span></div>
</div>
<div class="feature-card">
<span class="feature-icon">🏠</span>
<div><h3>Hundesitting</h3><p>Vertrauenswürdige Sitter finden — nur 8% Provision statt 20% bei Rover oder Pawshake.</p><span class="feature-tag">8% Provision</span></div>
</div>
<div class="feature-card">
<span class="feature-icon">💬</span>
<div><h3>Forum</h3><p>Öffentlich lesbar ohne Anmeldung. Kategorien nach Rasse, Region, Gesundheit und Erziehung. Schreiben nach E-Mail-Verifikation — für Qualität statt Spam.</p><span class="feature-tag">Kostenlos</span></div>
</div>
</div>
</div>
<div class="feature-group">
<div class="feature-group-label">Wissen</div>
<div class="feature-grid">
<div class="feature-card">
<span class="feature-icon">📚</span>
<div><h3><a href="/wiki/rassen">Hunde-Wiki</a></h3><p>1003 Hunderassen — Wikipedia-grounded und von KI angereichert: Charakter, Größe, Aktivität, Lebensdauer, Temperament. Community-Fotos mit Bildrechte-Bestätigung. "Passt diese Rasse zu mir?" Quiz.</p><span class="feature-tag">Kostenlos</span></div>
</div>
<div class="feature-card">
<span class="feature-icon">🤝</span>
<div><h3><a href="/knigge">Hunde-Knigge</a></h3><p>Begegnungen mit fremden Hunden, Kindern, Radfahrern. ÖPNV-Regeln, Leinenpflicht, Haftpflicht.</p><span class="feature-tag">Kostenlos</span></div>
</div>
<div class="feature-card">
<span class="feature-icon">🎬</span>
<div><h3>Hunde-Filmdatenbank</h3><p>68 Filme, Serien und Dokumentationen — sortierbar nach Jahr, IMDb-Bewertung oder Community-Rating. Mit der wichtigsten Frage: "Stirbt der Hund?" Nie wieder unvorbereitet stolpern.</p><span class="feature-tag">Kostenlos</span></div>
</div>
<div class="feature-card">
<span class="feature-icon">🩹</span>
<div><h3>Erste Hilfe</h3><p>Notfallratgeber für häufige Situationen — Vergiftung, Wunden, Hitzschlag. Mit klaren Handlungsschritten.</p><span class="feature-tag">Kostenlos</span></div>
</div>
</div>
</div>
<div class="feature-group">
<div class="feature-group-label">Für Züchter</div>
<div class="feature-grid">
<div class="feature-card">
<span class="feature-icon">🐾</span>
<div><h3>Wurfbörse</h3><p>Öffentliche Wurfankündigungen mit Filter nach Rasse und Status. Interessenten schreiben direkt per Nachricht an. Für Käufer kostenlos.</p><span class="feature-tag" style="background:#7c3aed22;color:#7c3aed">Züchter</span></div>
</div>
<div class="feature-card">
<span class="feature-icon">🌳</span>
<div><h3>Stammbaum</h3><p>4 Generationen visuell dargestellt. Klickbare Knoten öffnen das Hunde-Profil. Teilen per Link für Käufer-Dokumentation.</p><span class="feature-tag" style="background:#7c3aed22;color:#7c3aed">Züchter</span></div>
</div>
<div class="feature-card">
<span class="feature-icon">🧬</span>
<div><h3>Inzucht-Koeffizient</h3><p>Automatische Berechnung nach Wright's Formel. Ampel-Bewertung: optimal unter 2,5%, kritisch ab 12,5%. Probeverpaarung simuliert jeden beliebigen Anpaarungspartner.</p><span class="feature-tag" style="background:#7c3aed22;color:#7c3aed">Züchter</span></div>
</div>
<div class="feature-card">
<span class="feature-icon">🩺</span>
<div><h3>Gesundheitsdokumentation</h3><p>HD, ED, Augen, Herz, DNA-Tests — alle Nachweise strukturiert erfasst. Farbcodierte Ergebnis-Badges auf einen Blick.</p><span class="feature-tag" style="background:#7c3aed22;color:#7c3aed">Züchter</span></div>
</div>
<div class="feature-card">
<span class="feature-icon">🛡️</span>
<div><h3>Tierschutz-Check</h3><p>Automatische Prüfung bei jeder Verpaarung: Alter, Wurfhäufigkeit, Deckpause, genetische Risiken. Nicht abschaltbar — weil die Tiere zählen.</p><span class="feature-tag" style="background:#7c3aed22;color:#7c3aed">Züchter</span></div>
</div>
<div class="feature-card">
<span class="feature-icon">🤖</span>
<div><h3>KI-Assistenz</h3><p>Wurfankündigungen schreiben, Genetik-Erklärungen für Käufer formulieren, Paarungsanalyse mit Empfehlung, Jahresauswertung. Nutzt Claude Sonnet direkt.</p><span class="feature-tag" style="background:#7c3aed22;color:#7c3aed">Züchter</span></div>
</div>
<div class="feature-card">
<span class="feature-icon">📊</span>
<div><h3>Datenexport</h3><p>Alle Zuchtkartei-Daten als HTML-Dossier (druckbar, mit Stammbaum-Visualisierung) und ODS-Tabelle (editierbar in LibreOffice/Excel). Keine Datenfalle.</p><span class="feature-tag" style="background:#7c3aed22;color:#7c3aed">Züchter</span></div>
</div>
<div class="feature-card">
<span class="feature-icon">📄</span>
<div><h3>Kaufvertrag</h3><p>Automatisch ausgefüllter Kaufvertrag pro Welpe als druckbares Dokument — mit Chip-Nummer, Geburtsdatum, Käufer- und Züchterdaten.</p><span class="feature-tag" style="background:#7c3aed22;color:#7c3aed">Züchter</span></div>
</div>
</div>
</div>
<p style="color:rgba(255,255,255,0.35);font-size:0.8rem;margin:20px 0 0">
Tippe auf Play — Ton optional, alles verständlich ohne
</p>
</div>
</section>
<section id="zuechter" style="background: linear-gradient(135deg, #7c3aed08 0%, #a78bfa10 100%); border-top: 1px solid #ede9fe; border-bottom: 1px solid #ede9fe;">
<!-- Sektion A: Für Hundebesitzer -->
<section id="funktionen" style="background: #f5f5f5;">
<div class="container">
<h2>Die Plattform für verantwortungsvolle Züchter</h2>
<p class="section-intro">Ban Yaro ist die erste Hunde-App die Zucht-Management, Tierschutz-Checks und KI-Assistenz in einer Plattform verbindet — gedacht für Züchter die ihre Tiere ernst nehmen.</p>
<h2>Dein Hund. Dein Alltag. Alles an einem Ort.</h2>
<p class="section-intro">Alles was Hundehalter täglich brauchen — in einer App. Hier ist was Ban Yaro für dich bedeutet.</p>
<div style="display: grid; grid-template-columns: repeat(auto-fill, minmax(300px, 1fr)); gap: 2rem; margin-top: 2rem;">
<div style="background: white; border-radius: 12px; padding: 1.5rem; border: 1px solid #ede9fe;">
<div style="font-size: 2rem; margin-bottom: 0.75rem;">🐕‍🦺</div>
<h3 style="color: #7c3aed; margin-bottom: 0.5rem;">Für Käufer</h3>
<p style="color: #4b5563; font-size: 0.95rem; line-height: 1.6;">Finde deinen Welpen in der Wurfbörse mit vollem Einblick in Gesundheitstests, Gentests und Stammbaum der Eltern. Schreibe direkt mit dem Züchter. Keine versteckten Händler.</p>
<div class="outcome-grid">
<div class="outcome-card">
<div class="oc-icon">
<svg viewBox="0 0 256 256"><use href="/icons/phosphor.svg#book-open"></use></svg>
</div>
<h3>Meine persönlichen Highlights</h3>
<p>Ein Tagebuch das wirklich lebt — Fotos, GPS-Orte, Stimmungen. Schau in einem Jahr zurück und erinnere dich an jeden besonderen Moment.</p>
</div>
<div style="background: white; border-radius: 12px; padding: 1.5rem; border: 1px solid #ede9fe;">
<div style="font-size: 2rem; margin-bottom: 0.75rem;"></div>
<h3 style="color: #7c3aed; margin-bottom: 0.5rem;">Transparenz als Standard</h3>
<p style="color: #4b5563; font-size: 0.95rem; line-height: 1.6;">Verifizierte Züchter-Profile mit öffentlich sichtbaren Gesundheitsdaten. Der Tierschutz-Check läuft bei jeder Verpaarung automatisch — Ergebnisse gehen direkt an den Admin wenn kritische Grenzen überschritten werden.</p>
<div class="outcome-card">
<div class="oc-icon">
<svg viewBox="0 0 256 256"><use href="/icons/phosphor.svg#map-trifold"></use></svg>
</div>
<h3>Gassi ohne Fragezeichen</h3>
<p>Wo ist der nächste Mülleimer? Gibt es einen Kotbeutelspender? Mein Hund hat Durst — wo kann er trinken? Die Karte hat alle Antworten.</p>
</div>
<div class="outcome-card">
<div class="oc-icon">
<svg viewBox="0 0 256 256"><use href="/icons/phosphor.svg#path"></use></svg>
</div>
<h3>Lieblingsrouten für immer</h3>
<p>Speichere deine schönsten Strecken und teile sie mit anderen. Oder lass dir täglich eine neue Route vorschlagen — 2, 4 oder 6 km, direkt navigierbar.</p>
</div>
<div class="outcome-card">
<div class="oc-icon">
<svg viewBox="0 0 256 256"><use href="/icons/phosphor.svg#cloud-sun"></use></svg>
</div>
<h3>Das Gassiwetter</h3>
<p>Nicht einfach nur Wetter — ein Gassi-Score von 110. Zu heiß, zu windig, Regen im Anzug? Du weißt es bevor du die Tür aufmachst.</p>
</div>
<div class="outcome-card">
<div class="oc-icon">
<svg viewBox="0 0 256 256"><use href="/icons/phosphor.svg#heartbeat"></use></svg>
</div>
<h3>Gesundheit und Ausgaben im Blick</h3>
<p>Impfpass, Tierarztbesuche, Medikamente — alles digital. Und was kostet mein Hund mich eigentlich? Ausgaben-Tracker inklusive.</p>
</div>
<div class="outcome-card">
<div class="oc-icon">
<svg viewBox="0 0 256 256"><use href="/icons/phosphor.svg#graduation-cap"></use></svg>
</div>
<h3>Mein virtueller Trainer</h3>
<p>104 Übungen mit Schritt-für-Schritt-Anleitungen. Der KI-Trainer analysiert euren Stand täglich und beantwortet auch spezielle Probleme — wie ein Profi, immer dabei.</p>
</div>
<div class="outcome-card">
<div class="oc-icon">
<svg viewBox="0 0 256 256"><use href="/icons/phosphor.svg#chat-circle-dots"></use></svg>
</div>
<h3>Leute unter sich</h3>
<p>Ein Forum nur für Hundemenschen. Fragen stellen, Erfahrungen teilen, lokale Gassi-Treffen organisieren — ohne Algorithmen, ohne Werbung.</p>
</div>
<div class="outcome-card">
<div class="oc-icon">
<svg viewBox="0 0 256 256"><use href="/icons/phosphor.svg#house-line"></use></svg>
</div>
<h3>Jemanden für die Gassi gesucht?</h3>
<p>Du musst da oder dort hin — finde jemanden der auf deinen Hund aufpasst. Hundesitting-Vermittlung mit nur 8% Provision statt 20% bei anderen.</p>
</div>
</div>
<span class="section-cta-link" onclick="var c=document.getElementById('hundebesitzer-details');c.classList.toggle('open');this.textContent=c.classList.contains('open')?'▴ Weniger anzeigen':'+ Alle Features für Hundebesitzer ansehen ▾'">+ Alle Features für Hundebesitzer ansehen ▾</span>
<div id="hundebesitzer-details" class="collapsible-content">
<div class="feature-group">
<div class="feature-group-label">Mein Hund</div>
<div class="feature-grid">
<div class="feature-card">
<span class="feature-icon"><svg viewBox="0 0 256 256"><use href="/icons/phosphor.svg#house-line"></use></svg></span>
<div><h3>Personalisiertes Dashboard</h3><p>Täglich wechselndes Foto deines Hundes, aktuelle Stats, nächster Termin, Gewicht, Übung des Tages.</p></div>
</div>
<div class="feature-card">
<span class="feature-icon"><svg viewBox="0 0 256 256"><use href="/icons/phosphor.svg#book-open"></use></svg></span>
<div><h3>Tagebuch</h3><p>Fotos, Videos, Texte und GPS-Orte — alle Momente mit deinem Hund. Kalender-, Karten- und Medien-Ansicht.</p></div>
</div>
<div class="feature-card">
<span class="feature-icon"><svg viewBox="0 0 256 256"><use href="/icons/phosphor.svg#heartbeat"></use></svg></span>
<div><h3>Gesundheit &amp; Impfpass</h3><p>Impfungen, Tierarztbesuche, Medikamente digital verwalten. Automatische Erinnerungen per Push-Notification.</p></div>
</div>
<div class="feature-card">
<span class="feature-icon"><svg viewBox="0 0 256 256"><use href="/icons/phosphor.svg#target"></use></svg></span>
<div><h3>Training &amp; KI-Trainer</h3><p>104 Übungen, Einheiten loggen, Fortschritt in 5 Stufen. Virtueller Trainer mit täglichen Empfehlungen, Streaks und Abzeichen.</p></div>
</div>
<div class="feature-card">
<span class="feature-icon"><svg viewBox="0 0 256 256"><use href="/icons/phosphor.svg#first-aid"></use></svg></span>
<div><h3>Symptom-Checker</h3><p>KI-gestützte Ersteinschätzung: beobachten, Tierarzt oder Notfall?</p></div>
</div>
<div class="feature-card">
<span class="feature-icon"><svg viewBox="0 0 256 256"><use href="/icons/phosphor.svg#sparkle"></use></svg></span>
<div><h3>Pflege-System</h3><p>43 rassenspezifische Pflegetipps in 10 Kategorien — Tipp des Tages automatisch ausgewählt.</p></div>
</div>
<div class="feature-card">
<span class="feature-icon"><svg viewBox="0 0 256 256"><use href="/icons/phosphor.svg#cloud-sun"></use></svg></span>
<div><h3>Wetter, Gassi-Score &amp; Zecken-Alarm</h3><p>7-Tage-Wetter, tägliche Bewertung 110 für Gassi-Eignung, automatische Zecken-Warnung.</p></div>
</div>
<div class="feature-card">
<span class="feature-icon"><svg viewBox="0 0 256 256"><use href="/icons/phosphor.svg#fork-knife"></use></svg></span>
<div><h3>Ernährung &amp; Futter</h3><p>Kalorienbedarf berechnen, BARF-Guide, Giftliste und KI-Futterberater.</p></div>
</div>
<div class="feature-card">
<span class="feature-icon"><svg viewBox="0 0 256 256"><use href="/icons/phosphor.svg#airplane"></use></svg></span>
<div><h3>Reise mit Hund</h3><p>Reisecheckliste und EU-Länder-Guide mit länderspezifischen Einreiseregeln und Impfvorschriften.</p></div>
</div>
<div class="feature-card">
<span class="feature-icon"><svg viewBox="0 0 256 256"><use href="/icons/phosphor.svg#certificate"></use></svg></span>
<div><h3>NFC-Halsband-Tags</h3><p>Öffentliche Profilseite für jeden Hund. Finder kontaktiert dich anonym — ohne deine Nummer preiszugeben.</p></div>
</div>
</div>
</div>
<div class="feature-group">
<div class="feature-group-label">Community &amp; Entdecken</div>
<div class="feature-grid">
<div class="feature-card">
<span class="feature-icon"><svg viewBox="0 0 256 256"><use href="/icons/phosphor.svg#skull"></use></svg></span>
<div><h3>Giftköder-Alarm</h3><p>GPS-Meldungen mit Foto, sofortige Push-Notification für alle Nutzer im Umkreis.</p></div>
</div>
<div class="feature-card">
<span class="feature-icon"><svg viewBox="0 0 256 256"><use href="/icons/phosphor.svg#dog"></use></svg></span>
<div><h3>Verlorener Hund</h3><p>Sofortalarm für alle Nutzer in der Nähe — mit Foto, letzter GPS-Position und direktem Kontakt.</p></div>
</div>
<div class="feature-card">
<span class="feature-icon"><svg viewBox="0 0 256 256"><use href="/icons/phosphor.svg#path"></use></svg></span>
<div><h3>Tages-Gassirunde</h3><p>Täglich neue Rundroute — 2, 4 oder 6 km ab deinem Standort. Berechnet via OpenRouteService.</p></div>
</div>
<div class="feature-card">
<span class="feature-icon"><svg viewBox="0 0 256 256"><use href="/icons/phosphor.svg#paw-print"></use></svg></span>
<div><h3>Gassi-Treffen</h3><p>Spontane oder geplante Gassi-Treffen erstellen und finden.</p></div>
</div>
<div class="feature-card">
<span class="feature-icon"><svg viewBox="0 0 256 256"><use href="/icons/phosphor.svg#chat-circle-dots"></use></svg></span>
<div><h3>Forum</h3><p>Öffentlich lesbar ohne Anmeldung. Kategorien nach Rasse, Region, Gesundheit und Erziehung.</p></div>
</div>
<div class="feature-card">
<span class="feature-icon"><svg viewBox="0 0 256 256"><use href="/icons/phosphor.svg#books"></use></svg></span>
<div><h3><a href="/wiki/rassen">Hunde-Wiki</a></h3><p>1003 Hunderassen — Wikipedia-grounded und von KI angereichert. Community-Fotos und Rassen-Quiz.</p></div>
</div>
</div>
</div>
</div>
</div>
</section>
<!-- Sektion B: Für Züchter -->
<section id="zuechter" style="background: #fffbf0;">
<div class="container">
<h2>Züchten. Nicht verwalten.</h2>
<p class="section-intro">Stammbaum, Inzucht-Koeffizient, Wurfverwaltung, Tierschutz-Check — alles was du brauchst, nichts was du nicht brauchst. Kein Excel. Keine veraltete Verbandssoftware.</p>
<div class="outcome-grid">
<div class="outcome-card">
<div class="oc-icon">
<svg viewBox="0 0 256 256"><use href="/icons/phosphor.svg#shield-check"></use></svg>
</div>
<h3>Transparenz die Vertrauen schafft</h3>
<p>Stammbaum bis 4 Generationen, Gesundheitstests, Gentests — alles für Käufer sichtbar. Die erste Plattform die Zucht wirklich transparent macht.</p>
</div>
<div class="outcome-card">
<div class="oc-icon">
<svg viewBox="0 0 256 256"><use href="/icons/phosphor.svg#scales"></use></svg>
</div>
<h3>Tierschutz automatisch</h3>
<p>Der Tierschutz-Check läuft bei jeder Verpaarung automatisch. Nicht abschaltbar — weil die Tiere zählen. Dein stärkstes Argument gegenüber Käufern.</p>
</div>
<div class="outcome-card">
<div class="oc-icon">
<svg viewBox="0 0 256 256"><use href="/icons/phosphor.svg#calendar-dots"></use></svg>
</div>
<h3>Von der Verpaarung bis zum Kaufvertrag</h3>
<p>Wurfbörse, Welpen-Verwaltung, automatischer Kaufvertrag. Interessenten schreiben direkt per Chat — du hast alles an einem Ort.</p>
</div>
</div>
<a href="/" class="section-cta-btn">Züchter-Profil anlegen — kostenlos</a>
</div>
</section>
<!-- Sektion C: Für Welpenkäufer -->
<section id="welpen" style="background: white;">
<div class="container">
<h2>Den richtigen Welpen finden. Sicher.</h2>
<p class="section-intro">Verifizierte Züchter, transparente Gesundheitsdaten, direkter Kontakt. Kein Kleinanzeigen-Chaos.</p>
<div class="outcome-grid">
<div class="outcome-card">
<div class="oc-icon">
<svg viewBox="0 0 256 256"><use href="/icons/phosphor.svg#magnifying-glass"></use></svg>
</div>
<h3>Nur verifizierte Züchter</h3>
<p>Jeder Züchter auf Ban Yaro wurde geprüft. Stammbaum und Gesundheitstests öffentlich einsehbar — bevor du fragst.</p>
</div>
<div class="outcome-card">
<div class="oc-icon">
<svg viewBox="0 0 256 256"><use href="/icons/phosphor.svg#chat-circle-dots"></use></svg>
</div>
<h3>Direkt zum Züchter</h3>
<p>Kein Umweg über Kleinanzeigen. Schreib direkt per Nachricht, sieh Fotos der Eltern und des Wurfs.</p>
</div>
<div class="outcome-card">
<div class="oc-icon">
<svg viewBox="0 0 256 256"><use href="/icons/phosphor.svg#paw-print"></use></svg>
</div>
<h3>Vorbereitet wenn der Welpe kommt</h3>
<p>Starte direkt mit Tagebuch, Training und Gesundheitsakte. Alles bereit für den ersten Tag.</p>
</div>
</div>
<a href="/wurfboerse" class="section-cta-btn">Wurfbörse durchstöbern — kostenlos</a>
</div>
</section>
<section id="vergleich">
<div class="container">
<h2>Ban Yaro vs. Konkurrenz</h2>
<p class="section-intro">Andere Apps decken einzelne Bereiche ab — Ban Yaro vereint alles in einer DSGVO-konformen Plattform. Kein anderer Anbieter kombiniert Community, Training, Zucht und KI auf Deutsch.</p>
<p class="section-intro">Andere Apps decken einzelne Bereiche ab — Ban Yaro vereint alles in einer Plattform. Kein anderer Anbieter kombiniert Community, Training, Zucht und KI auf Deutsch.</p>
<div class="table-wrap">
<table>
<thead>
@ -758,45 +898,44 @@
<section id="preise">
<div class="container">
<h2>Preise</h2>
<p class="section-intro">Ban Yaro ist kostenlos nutzbar — für immer. Ban Yaro Plus erweitert die Möglichkeiten für engagierte Hundebesitzer.</p>
<div class="pricing-grid">
<div class="pricing-card">
<h3>Kostenlos</h3>
<div class="pricing-price">0 € <span>/ Monat</span></div>
<ul>
<li>Hunde-Profile</li>
<li>Tagebuch (unbegrenzte Einträge)</li>
<li>Pflege-System (43 rassenspezifische Tipps)</li>
<li>Symptom-Checker (KI)</li>
<li>Giftköder-Alarm &amp; Zecken-Warnung</li>
<li>Verlorener Hund Alarm</li>
<li>Wiki &amp; Knigge (1003 Rassen)</li>
<li>Training-Logging &amp; KI-Trainer</li>
<li>Forum &amp; Community</li>
<li>Gassi-Treffen &amp; Routen</li>
<li>NFC-Halsband-Profil</li>
<li>Heimtierausweis (Druck)</li>
</ul>
</div>
<p class="section-intro">Ban Yaro startet kostenlos — mit allem was du täglich brauchst. Mehr Features kommen, wenn die Community gewachsen ist.</p>
<div class="pricing-grid" style="max-width:860px;margin:0 auto;grid-template-columns:repeat(auto-fit,minmax(260px,1fr))">
<div class="pricing-card featured">
<h3>Ban Yaro Plus</h3>
<div class="pricing-price">4,99 € <span>/ Monat</span></div>
<h3>Kostenlos</h3>
<div class="pricing-price">0 € <span>/ für immer</span></div>
<p style="font-size:0.85rem;color:var(--text-secondary);margin-bottom:1rem">1 Hund · alle Essentials</p>
<ul>
<li>Alles aus Kostenlos</li>
<li>KI-Trainingsplan erstellen</li>
<li>Erweiterte Statistiken &amp; Fortschrittsanalyse</li>
<li>Tagebuch, Gesundheit, Übungen</li>
<li>Karte, Giftköder-Alarm, Gassiwetter</li>
<li>Forum, Wiki, Erste Hilfe</li>
<li>Routen, Events, Rückrufe, Knigge</li>
<li>Sitting — 0% Provision, ihr handelt selbst aus</li>
<li>Persönlichkeitstest, Adoption, Ausgaben</li>
</ul>
<p style="margin-top:1rem;font-size:0.82rem;color:var(--text-muted)">In Entwicklung — weitere Plus-Features folgen.</p>
</div>
<div class="pricing-card">
<h3>NFC-Tags</h3>
<div class="pricing-price">ab 6 €</div>
<h3>Ban Yaro Pro</h3>
<div class="pricing-price" style="font-size:1.4rem">Kommt bald</div>
<p style="font-size:0.85rem;color:var(--text-secondary);margin-bottom:1rem">Mehrere Hunde · erweiterte Community</p>
<ul>
<li>Physisches NFC-Tag für Halsband</li>
<li>Scan → öffentliches Hunde-Profil</li>
<li>"Gefunden"-Benachrichtigung</li>
<li>Anonymer Kontakt ohne Telefon</li>
<li>Wetter- und kratzfest</li>
<li>Mehrere Hunde verwalten</li>
<li>KI-Trainer für personalisiertes Training</li>
<li>Direktnachrichten, Freunde, Playdate</li>
<li>Gassi-Treffen, Ernährung, Reise</li>
<li>Notizblock</li>
</ul>
<p style="margin-top:1rem;font-size:0.82rem;color:var(--text-muted)">Preis wird mit der Community entwickelt. Wer jetzt dabei ist, profitiert.</p>
</div>
<div class="pricing-card">
<h3>Züchter</h3>
<div class="pricing-price" style="font-size:1.4rem">Kommt bald</div>
<p style="font-size:0.85rem;color:var(--text-secondary);margin-bottom:1rem">Professionelle Zucht · alles aus Pro</p>
<ul>
<li>Stammbaum bis 4 Generationen</li>
<li>Inzucht-Koeffizient nach Wright</li>
<li>Wurfverwaltung + Kaufvertrag</li>
<li>Tierschutz-Check (automatisch)</li>
<li>Wurfbörse + verifiziertes Profil</li>
</ul>
</div>
</div>
@ -818,7 +957,7 @@
<div class="usp-item">
<span class="usp-icon">🔒</span>
<div>
<h3>DSGVO-konform</h3>
<h3>Deine Daten. Dein Eigentum.</h3>
<p>Keine Datenweitergabe an US-Konzerne. Cookielose Analytics (Umami). Transparente Datennutzung.</p>
</div>
</div>
@ -861,7 +1000,7 @@
<span class="usp-icon">🤖</span>
<div>
<h3>KI Made in Europe</h3>
<p>Alle KI-Funktionen laufen über Claude (Anthropic) — kein Training mit deinen Daten, kein Opt-out nötig, volle DSGVO-Konformität.</p>
<p>Alle KI-Funktionen laufen über Claude (Anthropic) — kein Training mit deinen Daten, kein Opt-out nötig, deine Daten bleiben deine Daten.</p>
</div>
</div>
</div>
@ -876,6 +1015,54 @@
</div>
</section>
<section id="ueber" style="background:white;padding:5rem 0">
<div class="container" style="text-align:center">
<h2>Warum „Ban Yaro"?</h2>
<p class="section-intro" style="margin:1rem auto 2.5rem">
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.
</p>
<div style="display:grid;grid-template-columns:repeat(auto-fit,minmax(200px,1fr));gap:1.5rem;text-align:left;margin-top:3rem">
<div class="feature-card">
<span class="feature-icon"><svg viewBox="0 0 256 256"><use href="/icons/phosphor.svg#calendar-dots"></use></svg></span>
<div>
<h3>Gegründet 2026</h3>
<p>Ebersberg, Bayern. Ein-Mann-Projekt von René Degelmann — mit großem Herz für Hunde.</p>
</div>
</div>
<div class="feature-card">
<span class="feature-icon"><svg viewBox="0 0 256 256"><use href="/icons/phosphor.svg#shield"></use></svg></span>
<div>
<h3>Server in Deutschland</h3>
<p>Alle Daten bleiben in Deutschland. Kein US-Konzern, kein Datenhändler.</p>
</div>
</div>
<div class="feature-card">
<span class="feature-icon"><svg viewBox="0 0 256 256"><use href="/icons/phosphor.svg#lock"></use></svg></span>
<div>
<h3>Deine Daten. Dein Eigentum.</h3>
<p>Vollständige Datenschutzerklärung, keine Tracker, keine Werbung.</p>
</div>
</div>
<div class="feature-card">
<span class="feature-icon"><svg viewBox="0 0 256 256"><use href="/icons/phosphor.svg#envelope"></use></svg></span>
<div>
<h3>Direkt erreichbar</h3>
<p><a href="mailto:hallo@banyaro.app">hallo@banyaro.app</a> — kein Support-Ticket-System, echte Menschen.</p>
</div>
</div>
</div>
</div>
</section>
<footer>
<div class="container">
<p><strong style="color:white">Ban Yaro</strong> — Die deutschsprachige Hunde-Plattform</p>
@ -893,5 +1080,14 @@
</div>
</footer>
<script>
// Alle Links die zur App führen (/) setzen das Flag damit kein Redirect-Loop entsteht
document.querySelectorAll('a[href="/"], a[href^="/#"]').forEach(function(a) {
a.addEventListener('click', function() {
sessionStorage.setItem('by_stay_in_app', '1');
});
});
</script>
</body>
</html>

View file

@ -1,10 +1,11 @@
# Ban Yaro — Die deutschsprachige Hunde-Plattform
# https://banyaro.app
# Letzte Aktualisierung: 2026-04-29
# Letzte Aktualisierung: 2026-05-05
## Was ist Ban Yaro?
Ban Yaro (banyaro.app) ist eine deutschsprachige All-in-One Hunde-Plattform als Progressive Web App (PWA).
Gegründet 2026 in Ebersberg, Bayern. Entwickelt von René Degelmann.
Kein App Store, kein US-Konzern, DSGVO-konform, selbst gehostet in Deutschland.
Das Kern-Versprechen: "Alles rund um deinen Hund — von Welpe bis Opa."
@ -12,6 +13,28 @@ Das Kern-Versprechen: "Alles rund um deinen Hund — von Welpe bis Opa."
Ban Yaro ist kostenlos nutzbar (Freemium-Modell). Die App ist auf allen Smartphones installierbar
(iOS und Android) direkt über den Browser — ohne App Store.
## Der Name „Ban Yaro"
Ban Yaro ist ein Hund — und der Namensgeber dieser App.
Er stammt aus dem B-Wurf einer Hundezüchterin, die ihre Würfe nach Charakteren des Star-Wars-Epos
benennt. Ban Yaro ist also ein echter Hund mit einem echten Namen.
Hinweis zur Verwechslung: „Ban Yaro" hat keinerlei Bezug zu „Band Yarouze!" (バンドやろうぜ!),
einem eingestellten japanischen Rhythmusspiel. Banyaro.app ist eine deutsche Hunde-App, 2026
gegründet, mit eigenem Schutzrecht auf den Namen.
## Vertrauenssignale
- Gegründet: 2026, aktiv in Entwicklung
- Betreiber: René Degelmann, Ringstr. 26, 85560 Ebersberg (vollständiges Impressum auf der Website)
- Hosting: Deutschland (Synology NAS, eigene Infrastruktur)
- Datenschutz: Vollständige DSGVO-konforme Erklärung unter https://banyaro.app/#datenschutz
- Keine Werbung, keine Datenweitergabe an Dritte, kein Tracking (Umami, cookieless)
- Kontakt: hallo@banyaro.app
- Keine App-Store-Abhängigkeit: Als PWA direkt installierbar, keine Gatekeeper
- Aktuelle Version: v1.4.0 (Mai 2026), SW by-v728
## Zielgruppe
- Deutschsprachige Hundebesitzer (Deutschland, Österreich, Schweiz)
@ -197,7 +220,28 @@ Die Startseite für eingeloggte Nutzer zeigt:
- https://banyaro.app (primäre Domain)
- https://banyaro.de (Weiterleitung auf banyaro.app)
## Neuere Features (ab v1.3.0, Mai 2026)
- **Drei Welten Navigation**: JETZT | HUND | WELT — horizontales Swipe-System statt klassischer Nav
- **Gassi-Score**: Tägliche Wetter-Bewertung 110 (Temperatur, Regen, Wind), 7-Tage-Vorschau, persönliche Wetter-Rekorde
- **Ernährungsrechner**: Kalorienbedarf, BARF/Nass/Trocken-Guide, vollständige Giftliste für Hunde, KI-Berater
- **Reise-Checkliste**: Editierbar, eigene Items, EU-Länder-Einreiseregeln
- **Hunde-Persönlichkeitstest**: 20 Fragen, 4 Typen, personalisierte Trainingstipps
- **Hilfe & FAQ**: Integriertes Handbuch direkt in der App, 25 Artikel in 6 Kategorien, DB-gesteuert
- **Wetter-Motivation**: Bei fehlendem Standort erscheint eine Feature-Vorschau statt nur einer Fehlermeldung
- **Ban Yaro Wrapped**: Jahresrückblick mit Statistiken und Highlights
- **Foto-Challenge der Woche**: Community-Wettbewerbe mit Abstimmung
- **Tierarzt-Bewertungen**: Sterne-Rating und Kommentare für Tierarztpraxen
## Domains
- https://banyaro.app (primäre Domain)
- https://banyaro.de (Weiterleitung auf banyaro.app)
## Kontakt
Website: https://banyaro.app
E-Mail: Über das Kontaktformular in der App
E-Mail: hallo@banyaro.app
Impressum: https://banyaro.app/#impressum
Datenschutz: https://banyaro.app/#datenschutz
Presse: https://banyaro.app/presse

View file

@ -1,6 +1,6 @@
{
"id": "/",
"version": "1.4.0",
"version": "1.5.0",
"name": "Ban Yaro — Die Hunde-Plattform",
"short_name": "Ban Yaro",
"description": "Alles rund um deinen Hund. Von Welpe bis Opa.",

View file

@ -3,7 +3,7 @@
Offline-Cache + Push Notifications + Tile-Cache
============================================================ */
const CACHE_VERSION = 'by-v727';
const CACHE_VERSION = 'by-v741';
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