generated from knso/webklar-preview-template
Der Nutzer konnte in einer Praesentation nicht kontern, und die Ursache war real: Alles, was BehoerdenKlar von ChatGPT unterscheidet (Erinnerungen, Kalender, Antwort-Entwurf), steckte in der App — und die ist noch nicht veroeffentlicht. Das Web-Produkt, das Kunden heute kaufen, war im Kern nur 'Brief erklaeren'. Kalender (.ics, rein im Browser, nichts wird hochgeladen): - Frist und Termin mit Erinnerung 3 Tage und 1 Tag vorher - Zeilen nach BYTES gefaltet (RFC 5545), damit Arabisch/Tigrinya nicht reissen - Termin 23:30 endet korrekt am naechsten Tag - Wanduhrzeit ohne Zeitzone = Ortszeit des Geraets - getestet: Frist, Termin mit Uhrzeit, Mitternacht, langer arabischer Text, Brief ohne Datum (-> keine Datei) Anruf-Hilfe (Worker-Schema um kontakt + anruf erweitert): - Telefonnummer als tel:-Knopf, Aktenzeichen hervorgehoben - Spickzettel: Satz auf Deutsch + Bedeutung in der Nutzersprache - Prompt-Regel: Kontaktdaten nur, wenn woertlich im Brief. Getestet mit Brief ohne Nummer -> telefon: null, nichts erfunden. Nebenbei zwei bestehende Fehler behoben: - Optionale Ergebniskarten wurden nie ausgeblendet: Beim zweiten Brief stand die Checkliste des ERSTEN Briefs noch da. Jetzt Reset zu Beginn. - dir=auto auf den Ergebnistexten: Arabisch/Farsi/Urdu wurden im Web linksbuendig dargestellt (in der App war das schon behoben). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
1083 lines
50 KiB
HTML
1083 lines
50 KiB
HTML
<!DOCTYPE html>
|
||
<html lang="de">
|
||
<head>
|
||
<meta charset="UTF-8">
|
||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||
<title>Gratis-Demo: Behördenbrief online erklären lassen | BehördenKlar</title>
|
||
<meta name="description" content="Testen Sie BehördenKlar direkt im Browser: Brief fotografieren, hochladen, in einfacher Sprache erklärt bekommen. 2 Analysen gratis, ohne Konto.">
|
||
<meta name="keywords" content="Amtsbrief, Behördenbrief, verständlich, einfache Sprache, Jobcenter, Ausländerbehörde, Warteliste">
|
||
<meta name="author" content="BehördenKlar">
|
||
<meta name="robots" content="index, follow, max-image-preview:large, max-snippet:-1, max-video-preview:-1">
|
||
<meta name="theme-color" content="#1d4e89">
|
||
<meta name="apple-mobile-web-app-title" content="BehördenKlar">
|
||
<meta http-equiv="X-UA-Compatible" content="ie=edge">
|
||
<meta name="apple-mobile-web-app-capable" content="yes">
|
||
<meta name="apple-mobile-web-app-status-bar-style" content="black-translucent">
|
||
<meta property="og:locale:alternate" content="tr_TR">
|
||
<meta property="og:locale:alternate" content="ar_AR">
|
||
<meta property="og:locale:alternate" content="en_US">
|
||
<meta property="og:locale:alternate" content="uk_UA">
|
||
<meta property="og:locale:alternate" content="ru_RU">
|
||
<meta property="og:locale:alternate" content="pl_PL">
|
||
<link rel="manifest" href="/manifest.json">
|
||
<meta name="google-site-verification" content="6o1sIKmK7WSloQrdTs2q4WtQsCUBB-bRa24gNXzVeFc">
|
||
<meta name="msvalidate.01" content="...">
|
||
<meta name="apple-app-site-association" content="...">
|
||
<meta name="app-links" content="...">
|
||
<meta name="target-audience" content="Alle, die schwierige Post bekommen — Privatpersonen, Selbständige, Menschen mit Deutsch als Fremdsprache">
|
||
<meta name="accessibility" content="WCAG 2.1 AA, Vorlesen, einfache Sprache, mehrsprachig">
|
||
<meta name="topics" content="civic-tech, legal-tech, accessibility, education, language-simplification, vulnerable-populations">
|
||
<meta name="target-organizations" content="Beratungsstellen, Jobcenter, Caritas, Diakonie, VHS, Migrationsberatung">
|
||
<meta name="problem-statement" content="Amtsdeutsch und Vertragssprache sind unnötig schwer. BehördenKlar macht daraus Klartext mit Frist und To-do-Liste.">
|
||
<link rel="canonical" href="https://behoerdenklar.de/demo">
|
||
<link rel="icon" href="/favicon.svg" type="image/svg+xml">
|
||
<meta property="og:site_name" content="BehördenKlar">
|
||
<meta property="og:type" content="website">
|
||
<meta property="og:locale" content="de_DE">
|
||
<meta property="og:url" content="https://behoerdenklar.de/demo">
|
||
<meta property="og:title" content="Probieren Sie es aus: Ihr Behördenbrief, einfach erklärt">
|
||
<meta property="og:description" content="Direkt im Browser testen — 2 Gratis-Analysen, ohne Konto. Brief fotografieren, Klartext lesen.">
|
||
<meta property="og:image" content="https://behoerdenklar.de/vorschau.png">
|
||
<meta property="og:image:width" content="1200">
|
||
<meta property="og:image:height" content="630">
|
||
<meta name="twitter:card" content="summary_large_image">
|
||
<style>
|
||
:root {
|
||
--papier: #faf7f0;
|
||
--papier-dunkel: #f1ecdf;
|
||
--tinte: #21211d;
|
||
--grau: #6b675e;
|
||
--amtsblau: #1d4e89;
|
||
--stempelrot: #b3372b;
|
||
--marker: #ffe66b;
|
||
--gruen: #2e7d4f;
|
||
--rot: #b3372b;
|
||
--gelb: #a06b00;
|
||
color-scheme: light;
|
||
}
|
||
* { margin: 0; padding: 0; box-sizing: border-box; }
|
||
html { scroll-behavior: smooth; }
|
||
body {
|
||
font-family: Georgia, 'Times New Roman', serif;
|
||
background:
|
||
repeating-linear-gradient(0deg, transparent 0 34px, rgba(33, 33, 29, 0.025) 34px 35px),
|
||
var(--papier);
|
||
color: var(--tinte);
|
||
line-height: 1.7;
|
||
font-size: 17px;
|
||
}
|
||
.mono { font-family: 'Courier New', ui-monospace, monospace; letter-spacing: 0.02em; }
|
||
.wrap { max-width: 720px; margin: 0 auto; padding: 0 20px 72px; }
|
||
|
||
.briefkopf {
|
||
display: flex; justify-content: space-between; align-items: baseline;
|
||
padding: 20px 0 12px; border-bottom: 2px solid var(--tinte);
|
||
font-size: 14px;
|
||
}
|
||
.briefkopf .absender { font-weight: 700; font-size: 16px; }
|
||
.briefkopf .absender a { color: inherit; text-decoration: none; }
|
||
.briefkopf .az { color: var(--grau); font-size: 13px; }
|
||
|
||
header { padding: 40px 0 8px; }
|
||
.betreff-label {
|
||
font-size: 13px; color: var(--grau); text-transform: uppercase;
|
||
letter-spacing: 0.14em; margin-bottom: 10px;
|
||
}
|
||
h1 {
|
||
font-size: clamp(30px, 6vw, 44px);
|
||
line-height: 1.15; font-weight: 700; letter-spacing: -0.01em;
|
||
}
|
||
.markiert {
|
||
background: linear-gradient(104deg, transparent 2%, var(--marker) 4%, var(--marker) 96%, transparent 98%);
|
||
padding: 0 6px; box-decoration-break: clone; -webkit-box-decoration-break: clone;
|
||
}
|
||
.anrede { margin-top: 20px; font-size: 18px; max-width: 580px; }
|
||
|
||
section { margin-top: 44px; }
|
||
h2 {
|
||
font-size: 15px; text-transform: uppercase; letter-spacing: 0.14em;
|
||
color: var(--grau); font-weight: 400; margin-bottom: 20px;
|
||
display: flex; align-items: center; gap: 12px;
|
||
}
|
||
h2::after { content: ''; flex: 1; border-top: 1px solid rgba(33,33,29,0.25); }
|
||
h2 .nr { font-family: 'Courier New', ui-monospace, monospace; }
|
||
|
||
.antrag {
|
||
background: #fff; border: 2px solid var(--tinte);
|
||
box-shadow: 7px 8px 0 rgba(33, 33, 29, 0.9);
|
||
padding: 28px 26px; position: relative;
|
||
}
|
||
.formular-kopf {
|
||
font-family: 'Courier New', ui-monospace, monospace;
|
||
font-size: 13px; text-transform: uppercase; letter-spacing: 0.1em;
|
||
color: var(--grau); margin-bottom: 10px;
|
||
}
|
||
.cta {
|
||
display: inline-block; width: 100%; padding: 17px 30px;
|
||
background: var(--amtsblau); color: #fff; text-decoration: none;
|
||
font-family: inherit; font-size: 18px; font-weight: 700; text-align: center;
|
||
border: 2px solid var(--tinte);
|
||
box-shadow: 5px 5px 0 var(--tinte);
|
||
transition: transform 0.12s ease, box-shadow 0.12s ease;
|
||
cursor: pointer;
|
||
}
|
||
.cta:hover:not(:disabled) { transform: translate(2px, 2px); box-shadow: 3px 3px 0 var(--tinte); }
|
||
.cta:disabled { opacity: 0.45; cursor: not-allowed; }
|
||
|
||
.konsens {
|
||
display: flex; gap: 12px; align-items: flex-start;
|
||
font-size: 15px; color: var(--grau); margin: 14px 0 18px;
|
||
}
|
||
.konsens input { width: 22px; height: 22px; margin-top: 4px; flex-shrink: 0; accent-color: var(--amtsblau); }
|
||
.konsens a { color: var(--amtsblau); }
|
||
|
||
.klein { font-size: 13.5px; color: var(--grau); margin-top: 12px; }
|
||
.klein a { color: var(--grau); }
|
||
|
||
.status { text-align: center; padding: 26px 10px; display: none; }
|
||
.status .gross { font-size: 20px; font-weight: 700; }
|
||
.status .sub { color: var(--grau); margin-top: 6px; font-size: 15px; }
|
||
.sanduhr {
|
||
display: inline-block; font-size: 34px;
|
||
animation: drehen 2.4s linear infinite;
|
||
}
|
||
@keyframes drehen { from { transform: rotate(0deg); } to { transform: rotate(360deg); } }
|
||
|
||
.fehlerbox {
|
||
display: none; margin-top: 16px;
|
||
border: 2px solid var(--stempelrot); background: #fdf1ef;
|
||
color: #7c2018; padding: 14px 16px; font-size: 16px;
|
||
}
|
||
|
||
.email-teil { display: none; margin-top: 16px; }
|
||
.wl-input {
|
||
width: 100%; padding: 15px 16px; font-size: 17px; font-family: inherit;
|
||
border: 2px solid var(--tinte); background: #fff; color: var(--tinte);
|
||
margin-bottom: 12px;
|
||
}
|
||
.wl-input::placeholder { color: #9a958a; }
|
||
|
||
/* Ergebnis */
|
||
#ergebnis { display: none; }
|
||
.anruf-satz { border-inline-start: 3px solid var(--amtsblau); background: #fff; padding: 6px 12px; margin: 10px 0; }
|
||
.anruf-de { font-weight: 700; margin: 0; }
|
||
.anruf-bed { color: var(--grau); font-size: 15px; margin: 4px 0 0; }
|
||
.kontakt-zeile { margin: 6px 0; font-size: 16px; }
|
||
.az-wert { font-family: 'Courier New', ui-monospace, monospace; font-weight: 700; background: var(--marker); padding: 1px 6px; }
|
||
#ergAnrufKontakt .cta { display: inline-block; width: auto; margin: 4px 0 8px; }
|
||
.brief-karte {
|
||
background: #fff; border: 1px solid rgba(33, 33, 29, 0.28);
|
||
padding: 22px 24px; position: relative; margin-top: 22px;
|
||
box-shadow: 4px 5px 0 rgba(33, 33, 29, 0.08);
|
||
}
|
||
.karten-label {
|
||
position: absolute; top: -12px; left: 16px;
|
||
font-family: 'Courier New', ui-monospace, monospace; font-size: 12px;
|
||
text-transform: uppercase; letter-spacing: 0.1em;
|
||
background: var(--gruen); color: var(--papier); padding: 3px 10px;
|
||
}
|
||
.karten-label.neutral { background: var(--tinte); }
|
||
.ampelzeile {
|
||
margin-top: 14px; padding-top: 12px;
|
||
border-top: 1px dashed rgba(33, 33, 29, 0.3);
|
||
font-size: 16px; font-weight: 700;
|
||
}
|
||
.todo { padding: 8px 0 8px 2px; border-bottom: 1px dotted rgba(33,33,29,0.3); font-size: 16.5px; }
|
||
.todo::before { content: '☐'; margin-right: 10px; color: var(--amtsblau); font-size: 18px; }
|
||
details { border-bottom: 1px solid rgba(33, 33, 29, 0.2); padding: 12px 4px; }
|
||
summary { font-weight: 700; cursor: pointer; font-size: 16.5px; list-style: none; }
|
||
summary::-webkit-details-marker { display: none; }
|
||
details p { margin-top: 8px; color: var(--grau); font-size: 15.5px; }
|
||
|
||
.stempel {
|
||
display: inline-block; padding: 7px 14px;
|
||
border: 2.5px solid var(--stempelrot); color: var(--stempelrot);
|
||
font-family: 'Courier New', ui-monospace, monospace; font-weight: 700;
|
||
font-size: 14px; letter-spacing: 0.1em; text-transform: uppercase;
|
||
transform: rotate(-4deg); opacity: 0.9;
|
||
}
|
||
.hinweis-recht {
|
||
margin-top: 18px; font-size: 14px; color: var(--grau);
|
||
border-top: 1px dashed rgba(33,33,29,0.3); padding-top: 12px;
|
||
}
|
||
|
||
/* Warteliste-Formular (wie Startseite) */
|
||
.wl-form { display: flex; flex-direction: column; gap: 12px; margin-top: 4px; }
|
||
.bonus-zeile {
|
||
margin: 14px 0; padding: 12px 16px;
|
||
background: linear-gradient(104deg, transparent 1%, var(--marker) 3%, var(--marker) 97%, transparent 99%);
|
||
font-size: 16px;
|
||
}
|
||
footer { margin-top: 48px; font-size: 14.5px; color: var(--grau); }
|
||
footer a { color: var(--grau); }
|
||
</style>
|
||
<script type="application/ld+json">
|
||
{
|
||
"@context": "https://schema.org",
|
||
"@type": "SoftwareApplication",
|
||
"name": "BehördenKlar",
|
||
"description": "KI erklärt schwierige Briefe von Ämtern, Versicherungen, Vermietern und Banken in einfacher Sprache — mit Fristen und Handlungsanleitung",
|
||
"applicationCategory": "AccessibilityApplication",
|
||
"offers": {
|
||
"@type": "Offer",
|
||
"price": "0",
|
||
"priceCurrency": "EUR"
|
||
},
|
||
"inLanguage": ["de", "tr", "ar", "en", "uk", "ru", "pl", "fa", "ro", "fr"],
|
||
"featureList": [
|
||
"Amtsbriefe fotografieren und erklären lassen",
|
||
"Klartext in einfacher Sprache",
|
||
"Übersetzung in über 25 Sprachen",
|
||
"Automatische Erinnerungen für Fristen",
|
||
"Fertige Antworten generieren",
|
||
"Vorlesen der Inhalte",
|
||
"Keine Anmeldung erforderlich"
|
||
],
|
||
"targetGroup": "Alle, die schwierige offizielle Post bekommen — Privatpersonen und Selbständige",
|
||
"author": {
|
||
"@type": "Organization",
|
||
"name": "BehördenKlar"
|
||
}
|
||
}
|
||
</script>
|
||
</head>
|
||
<body>
|
||
<div class="wrap">
|
||
|
||
<div class="briefkopf">
|
||
<span class="absender"><a href="index.html">📬 BehördenKlar</a> <span style="font-weight:400;font-size:.62em;opacity:.6;letter-spacing:.04em;">by Webklar</span></span>
|
||
<span class="az mono">Formular D-01 · Gratis-Demo</span>
|
||
</div>
|
||
|
||
<header>
|
||
<p class="betreff-label">Betreff</p>
|
||
<h1>Probieren Sie es aus —<br><span class="markiert">mit Ihrem eigenen Brief.</span></h1>
|
||
<p class="anrede">
|
||
Kein Konto. Keine App-Installation. Laden Sie ein Foto Ihres
|
||
Behördenbriefs hoch — in etwa einer Minute lesen Sie ihn in einfacher
|
||
Sprache. <strong>2 Analysen gratis.</strong>
|
||
</p>
|
||
</header>
|
||
|
||
<section id="demo">
|
||
<h2><span class="nr">§ 1</span> Brief hochladen</h2>
|
||
<div class="antrag">
|
||
<p class="formular-kopf">Antrag auf Klartext · 2 Gratis-Analysen</p>
|
||
|
||
<div id="eingabe">
|
||
<p id="kaufOk" style="display: none; background: #e7f4ea; border: 1px solid #b7dfc1; color: #1a7f37; padding: 12px 14px; border-radius: 8px; margin-bottom: 14px; font-weight: 600;">
|
||
✅ Kauf erfolgreich! Ihr Guthaben wurde aktiviert.
|
||
</p>
|
||
<p id="guthabenBanner" class="bonus-zeile" style="display: none;">
|
||
💳 Ihr Guthaben: <strong id="guthabenZahl">0</strong> Analysen — laden Sie den nächsten Brief hoch.
|
||
</p>
|
||
<p style="margin-bottom: 14px; font-size: 15.5px;">
|
||
🌐 <label for="sprachWahl">Erklärung in dieser Sprache:</label>
|
||
<select id="sprachWahl" style="font-family: inherit; font-size: 15.5px; padding: 5px 8px; border: 1.5px solid var(--tinte); background: #fff; margin-inline-start: 6px;"></select>
|
||
</p>
|
||
|
||
<label class="konsens">
|
||
<input type="checkbox" id="konsens">
|
||
<span>
|
||
Ich bin einverstanden, dass mein Brief-Foto zur Analyse an den
|
||
Server von BehördenKlar und an den KI-Dienst Anthropic (USA)
|
||
geschickt wird. Es wird dort nicht dauerhaft gespeichert.
|
||
Behördenbriefe enthalten persönliche Daten — laden Sie nur hoch,
|
||
was Sie analysieren lassen möchten.
|
||
<a href="datenschutz.html" target="_blank">Datenschutzerklärung</a>
|
||
</span>
|
||
</label>
|
||
|
||
<div id="emailTeil" class="email-teil">
|
||
<p style="margin-bottom: 10px;">
|
||
🎉 <strong>Ihre erste Analyse ist verbraucht.</strong> Für die
|
||
zweite geben Sie bitte Ihre E-Mail-Adresse ein — so stellen wir
|
||
sicher, dass jeder genau 2 Gratis-Analysen bekommt.
|
||
</p>
|
||
<input class="wl-input" type="email" id="email" placeholder="Ihre E-Mail-Adresse" autocomplete="email">
|
||
<p class="klein" style="margin: 0 0 12px;">
|
||
Die Adresse wird nur verschlüsselt (als Prüfsumme) gespeichert,
|
||
um mitzuzählen — Sie bekommen dadurch keine E-Mails von uns.
|
||
</p>
|
||
</div>
|
||
|
||
<input type="file" id="datei" accept="image/jpeg,image/png,application/pdf" style="display: none;">
|
||
<button class="cta" id="startKnopf" disabled>📷 Brief auswählen & erklären lassen</button>
|
||
|
||
<div class="cf-turnstile" data-sitekey="0x4AAAAAAESi2p41B915R4hW" data-action="turnstile-spin-v1" style="margin-top: 14px;"></div>
|
||
|
||
<p class="klein">
|
||
Geht mit Foto (JPG/PNG) oder PDF, max. 5 MB. Tipp: gutes Licht,
|
||
Brief von oben, ganzer Text im Bild.
|
||
</p>
|
||
<p class="klein">
|
||
📱 <strong>Am Handy:</strong> Wählen Sie „Galerie" oder „Dateien",
|
||
wenn Sie den Brief schon fotografiert haben. Tippen Sie auf
|
||
„Kamera", öffnet sich die Kamera-App Ihres Handys — und die fragt
|
||
nach ihren eigenen Berechtigungen. <strong>Diese Abfrage kommt von
|
||
Ihrem Handy, nicht von uns:</strong> Diese Seite greift weder auf
|
||
Kamera noch auf Mikrofon zu.
|
||
</p>
|
||
</div>
|
||
|
||
<div class="status" id="status">
|
||
<span class="sanduhr">⏳</span>
|
||
<p class="gross">Ihr Brief wird analysiert…</p>
|
||
<p class="sub">Das dauert bis zu einer Minute. Bitte lassen Sie die Seite geöffnet.</p>
|
||
</div>
|
||
|
||
<div class="fehlerbox" id="fehler"></div>
|
||
</div>
|
||
</section>
|
||
|
||
<section id="kaufBlock" style="display: none;">
|
||
<h2><span class="nr">§ 2</span> Guthaben kaufen & sofort nutzen</h2>
|
||
<div class="antrag">
|
||
<p class="formular-kopf">Im Browser nutzen · ohne App Store</p>
|
||
<h3 style="font-size: 24px; line-height: 1.3;">Guthaben kaufen — sofort loslegen</h3>
|
||
<p style="margin-top: 10px;">
|
||
Sie müssen nicht erst testen: Kaufen Sie direkt Guthaben und nutzen Sie
|
||
BehördenKlar hier im Browser — auf jedem Gerät, ohne App. Ihr Guthaben
|
||
bleibt gespeichert und verfällt nicht.
|
||
</p>
|
||
|
||
<div id="paketListe" style="display: flex; flex-direction: column; gap: 12px; margin-top: 18px;"></div>
|
||
<p style="margin-top: 16px; background: #eef4fb; border-inline-start: 4px solid var(--amtsblau); padding: 12px 14px; font-size: 15.5px;">
|
||
📧 <strong>Bitte geben Sie beim Bezahlen Ihre E-Mail-Adresse an.</strong>
|
||
Sie bekommen danach einen persönlichen Zugangs-Link — damit nutzen Sie
|
||
Ihr Guthaben auf <strong>jedem Gerät</strong>: Handy, Tablet, Computer.
|
||
Ohne diesen Link ist Ihr Guthaben nur in diesem einen Browser
|
||
gespeichert und wäre weg, wenn Sie den Verlauf löschen.
|
||
</p>
|
||
|
||
<label class="konsens" style="margin-top: 18px;">
|
||
<input type="checkbox" id="kaufKonsens">
|
||
<span>
|
||
Ich verlange ausdrücklich, dass die Analysen sofort nach dem Kauf zur
|
||
Verfügung stehen, und bestätige, dass mein Widerrufsrecht damit
|
||
erlischt. Ich habe die <a href="agb.html" target="_blank">AGB</a> und
|
||
die <a href="widerruf.html" target="_blank">Widerrufsbelehrung</a>
|
||
gelesen.
|
||
</span>
|
||
</label>
|
||
|
||
<div class="fehlerbox" id="kaufFehler" style="display: none; margin-top: 12px;"></div>
|
||
|
||
<p class="klein" style="margin-top: 12px;">
|
||
🔒 Sichere Bezahlung über Stripe — wir sehen Ihre Kartendaten nie.
|
||
Keine Umsatzsteuer (§ 19 UStG). Guthaben verfällt nicht.
|
||
</p>
|
||
<p class="klein" style="margin-top: 10px;">
|
||
Anbieter: <strong>Webklar GbR</strong> — BehördenKlar ist ein Angebot von
|
||
Webklar. Auf Ihrer Kontoabrechnung erscheint deshalb
|
||
<strong>„Webklar"</strong>.
|
||
</p>
|
||
</div>
|
||
</section>
|
||
|
||
<section id="ergebnis">
|
||
<h2><span class="nr">§ 3</span> Ihr Brief in Klartext</h2>
|
||
|
||
<div class="brief-karte">
|
||
<span class="karten-label neutral" id="ergBrieftyp">Brief</span>
|
||
<p style="margin-top: 6px;"><strong id="ergKern" dir="auto"></strong></p>
|
||
<p class="ampelzeile" id="ergAmpel" style="display: none;"></p>
|
||
</div>
|
||
|
||
<div class="brief-karte" id="ergKalenderKarte" style="display: none; border: 2px solid var(--amtsblau);">
|
||
<span class="karten-label">Nicht vergessen</span>
|
||
<div id="ergKalenderText" style="margin-top: 6px;"></div>
|
||
<button class="cta" id="kalenderKnopf" type="button" style="margin-top: 12px;">📅 In meinen Kalender eintragen</button>
|
||
<p class="klein">
|
||
Ihr Handy erinnert Sie <strong>3 Tage</strong> und <strong>1 Tag</strong> vorher.
|
||
Der Eintrag landet nur in Ihrem eigenen Kalender — wir speichern nichts.
|
||
</p>
|
||
</div>
|
||
|
||
<div class="brief-karte">
|
||
<span class="karten-label">Einfach erklärt</span>
|
||
<p id="ergErklaerung" dir="auto" style="white-space: pre-line;"></p>
|
||
</div>
|
||
|
||
<div class="brief-karte" id="ergChecklisteKarte" style="display: none;">
|
||
<span class="karten-label neutral">Das müssen Sie tun</span>
|
||
<div id="ergCheckliste"></div>
|
||
</div>
|
||
|
||
<div class="brief-karte" id="ergAnrufKarte" style="display: none;">
|
||
<span class="karten-label neutral">Anrufen</span>
|
||
<div id="ergAnrufKontakt" style="margin-top: 6px;"></div>
|
||
<div id="ergAnrufSaetze"></div>
|
||
</div>
|
||
|
||
<div class="brief-karte" id="ergBegriffeKarte" style="display: none;">
|
||
<span class="karten-label neutral">Schwere Wörter erklärt</span>
|
||
<div id="ergBegriffe"></div>
|
||
</div>
|
||
|
||
<p class="hinweis-recht">
|
||
⚖️ BehördenKlar erklärt Briefe, ersetzt aber keine Rechtsberatung. Bei
|
||
wichtigen Entscheidungen hilft eine Beratungsstelle oder ein Anwalt.
|
||
</p>
|
||
</section>
|
||
|
||
<section id="werbung" style="display: none;">
|
||
<h2><span class="nr">§ 4</span> Wie geht es weiter?</h2>
|
||
<div class="antrag">
|
||
<p class="formular-kopf" id="restZeile">Warteliste</p>
|
||
<h3 id="wlTitel" style="font-size: 24px; line-height: 1.3;">Das hat Ihnen geholfen?</h3>
|
||
<p style="margin-top: 10px;">
|
||
Die App kann noch mehr: Briefe <strong>übersetzen in über 25 Sprachen</strong>,
|
||
Fristen mit <strong>Erinnerung</strong>, fertige
|
||
<strong>Antwort-Entwürfe</strong> — und Ihre Briefe bleiben
|
||
verschlüsselt auf Ihrem Handy gespeichert.
|
||
</p>
|
||
<p class="bonus-zeile">
|
||
🎁 Warteliste = <strong>5 Gratis-Analysen</strong> in der App statt 3.
|
||
</p>
|
||
<p id="nochmalZeile" style="display: none; margin-bottom: 14px;">
|
||
<a href="#demo" style="color: var(--amtsblau); font-weight: 700;">↑ Noch einen
|
||
Brief testen? Ihre zweite Gratis-Analyse wartet oben.</a>
|
||
</p>
|
||
<form class="wl-form" action="https://86550585.sibforms.com/serve/MUIFAAvOQCpFg148k-xD2Ze2PDVKKaFEIaLECCCMe9fGKd_uIurPljyn_IhTrkR0EkM_pQp_8VBzcHZ6N9sKDX5GBZi9elYKN6isXGPUUxcAgO37OKhTFZyMlJiC9ikRmPI8Q2tU2GvqP6esJ5ReGfv1htZV_OfXRIgW59P6pkeUsYKxqdXA7nWOaWxNTYz4MzzoGg7czJMJ2fxuEg==" method="POST">
|
||
<input class="wl-input" style="margin-bottom: 0;" type="email" name="EMAIL" id="wlEmail" placeholder="Ihre E-Mail-Adresse" required autocomplete="email">
|
||
<input type="text" name="email_address_check" value="" style="position:absolute; left:-5000px;" tabindex="-1" autocomplete="off" aria-hidden="true">
|
||
<input type="hidden" name="locale" value="de">
|
||
<input type="hidden" name="html_type" value="simple">
|
||
<button class="cta" type="submit">Auf die Warteliste eintragen →</button>
|
||
</form>
|
||
<p class="klein">
|
||
Eine E-Mail zum Start, keine Werbung, jederzeit abmeldbar —
|
||
<a href="datenschutz.html">Datenschutzerklärung</a>.
|
||
</p>
|
||
<div style="margin-top: 16px;"><span class="stempel">Bald im App Store</span></div>
|
||
</div>
|
||
</section>
|
||
|
||
<footer>
|
||
<a href="index.html">← Zur Startseite</a> ·
|
||
<a href="agb.html">AGB</a> ·
|
||
<a href="widerruf.html">Widerruf</a> ·
|
||
<a href="impressum.html">Impressum</a> ·
|
||
<a href="datenschutz.html">Datenschutzerklärung</a>
|
||
</footer>
|
||
|
||
</div>
|
||
|
||
<script src="https://challenges.cloudflare.com/turnstile/v0/api.js" async defer></script>
|
||
<script>
|
||
(function () {
|
||
'use strict';
|
||
var BASIS = 'https://behoerdenklar-proxy.behoerdenbrief.workers.dev';
|
||
var ENDPUNKT = BASIS + '/demo';
|
||
var KAUFEN_ENDPUNKT = BASIS + '/kaufen';
|
||
var STATUS_ENDPUNKT = BASIS + '/kauf-status';
|
||
var MAX_KANTE = 2000; // wie in der App: reicht für A4, spart Datenvolumen
|
||
|
||
// Guthaben-Pakete (Stripe TEST-Preise). Für Live später die Live-price_…-IDs.
|
||
var PAKETE = [
|
||
{ priceId: 'price_1U6BNnBsRA1AOdnedGW5eefh', titel: '5 Analysen', preis: '2,99 €' },
|
||
{ priceId: 'price_1U6BPRBsRA1AOdnemDUalzb8', titel: '15 Analysen', preis: '6,99 €', best: true },
|
||
];
|
||
|
||
// Zielsprache: kommt als ?sprache=tr von den Sprachseiten, wird gemerkt.
|
||
var SPRACH_SCHLUESSEL = 'bk_sprache';
|
||
var SPRACH_NAMEN = {
|
||
de:'Deutsch', tr:'Türkçe', ar:'العربية', ru:'Русский', uk:'Українська', pl:'Polski',
|
||
ro:'Română', en:'English', fa:'فارسی', ku:'Kurdî', bg:'Български', hr:'Hrvatski',
|
||
sr:'Српски', bs:'Bosanski', sq:'Shqip', it:'Italiano', el:'Ελληνικά', es:'Español',
|
||
pt:'Português', fr:'Français', hu:'Magyar', vi:'Tiếng Việt', hi:'हिन्दी', ur:'اردو',
|
||
ps:'پښتو', ti:'ትግርኛ', so:'Soomaali', zh:'中文'
|
||
};
|
||
function zielsprache() {
|
||
var p = new URLSearchParams(window.location.search).get('sprache');
|
||
if (p && SPRACH_NAMEN[p]) { try { localStorage.setItem(SPRACH_SCHLUESSEL, p); } catch (e) {} return p; }
|
||
try { var g = localStorage.getItem(SPRACH_SCHLUESSEL); if (g && SPRACH_NAMEN[g]) return g; } catch (e) {}
|
||
return 'de';
|
||
}
|
||
|
||
var konsens = document.getElementById('konsens');
|
||
var startKnopf = document.getElementById('startKnopf');
|
||
var dateiFeld = document.getElementById('datei');
|
||
var emailTeil = document.getElementById('emailTeil');
|
||
var emailFeld = document.getElementById('email');
|
||
var status = document.getElementById('status');
|
||
var eingabe = document.getElementById('eingabe');
|
||
var fehlerBox = document.getElementById('fehler');
|
||
var kaufBlock = document.getElementById('kaufBlock');
|
||
var werbung = document.getElementById('werbung');
|
||
var guthabenBanner = document.getElementById('guthabenBanner');
|
||
var guthabenZahl = document.getElementById('guthabenZahl');
|
||
var kaufOk = document.getElementById('kaufOk');
|
||
var paketListe = document.getElementById('paketListe');
|
||
var kaufKonsens = document.getElementById('kaufKonsens');
|
||
var kaufFehler = document.getElementById('kaufFehler');
|
||
|
||
// Wallet (passwortloses Guthaben) — Token + zuletzt bekannter Stand lokal.
|
||
var WALLET_SCHLUESSEL = 'bk_wallet';
|
||
var GUTHABEN_SCHLUESSEL = 'bk_guthaben';
|
||
function walletToken() { return localStorage.getItem(WALLET_SCHLUESSEL) || ''; }
|
||
function setzeWallet(t) { localStorage.setItem(WALLET_SCHLUESSEL, t); }
|
||
function guthaben() { return parseInt(localStorage.getItem(GUTHABEN_SCHLUESSEL) || '0', 10); }
|
||
function setzeGuthaben(n) { localStorage.setItem(GUTHABEN_SCHLUESSEL, String(Math.max(0, n | 0))); }
|
||
function hatGuthaben() { return !!walletToken() && guthaben() > 0; }
|
||
|
||
// Schlüsselname bewusst 'v2': setzt evtl. falsch gezählte Stände (aus der
|
||
// Testphase) bei bestehenden Besuchern automatisch einmalig zurück.
|
||
var SPEICHER_SCHLUESSEL = 'bk_demo_v2';
|
||
function benutzt() { return parseInt(localStorage.getItem(SPEICHER_SCHLUESSEL) || '0', 10); }
|
||
function setzeBenutzt(n) { localStorage.setItem(SPEICHER_SCHLUESSEL, String(n)); }
|
||
|
||
function zeigeStand() {
|
||
// Bezahlter Modus: Eingabe offen, Guthaben-Stand zeigen, Gratis-Sperre egal
|
||
if (hatGuthaben()) {
|
||
eingabe.style.display = 'block';
|
||
guthabenBanner.style.display = 'block';
|
||
guthabenZahl.textContent = guthaben();
|
||
emailTeil.style.display = 'none';
|
||
fehlerBox.style.display = 'none';
|
||
kaufBlock.style.display = 'none';
|
||
return;
|
||
}
|
||
guthabenBanner.style.display = 'none';
|
||
// Kauf ist IMMER möglich — auch sofort, ohne die Gratis-Analysen zu nutzen.
|
||
kaufBlock.style.display = 'block';
|
||
// 2. Versuch: E-Mail-Feld einblenden; alles verbraucht: Gratis-Weg sperren
|
||
if (benutzt() >= 2) {
|
||
eingabe.style.display = 'none';
|
||
fehlerBox.style.display = 'block';
|
||
fehlerBox.innerHTML = 'Ihre 2 Gratis-Analysen sind verbraucht. Kaufen Sie unten <strong>Guthaben</strong>, um hier direkt weiterzumachen — oder tragen Sie sich für die App ein.';
|
||
werbung.style.display = 'block';
|
||
} else if (benutzt() === 1) {
|
||
emailTeil.style.display = 'block';
|
||
}
|
||
}
|
||
|
||
konsens.addEventListener('change', function () { startKnopf.disabled = !konsens.checked; });
|
||
startKnopf.addEventListener('click', function () {
|
||
fehlerBox.style.display = 'none';
|
||
if (!hatGuthaben() && benutzt() >= 1 && !emailFeld.value.trim()) {
|
||
emailTeil.style.display = 'block';
|
||
emailFeld.focus();
|
||
zeigeFehler('Bitte geben Sie zuerst Ihre E-Mail-Adresse ein — dann wählen Sie den Brief aus.');
|
||
return;
|
||
}
|
||
dateiFeld.click();
|
||
});
|
||
dateiFeld.addEventListener('change', function () {
|
||
if (dateiFeld.files && dateiFeld.files[0]) starteAnalyse(dateiFeld.files[0]);
|
||
});
|
||
|
||
function zeigeFehler(text) {
|
||
fehlerBox.style.display = 'block';
|
||
fehlerBox.textContent = text;
|
||
}
|
||
|
||
function turnstileToken() {
|
||
var feld = document.querySelector('[name="cf-turnstile-response"]');
|
||
return feld ? feld.value : '';
|
||
}
|
||
|
||
/** Foto verkleinern (max 2000px, JPEG 60%) — wie in der App. */
|
||
function bildVerkleinern(datei) {
|
||
return new Promise(function (resolve, reject) {
|
||
var img = new Image();
|
||
var url = URL.createObjectURL(datei);
|
||
img.onload = function () {
|
||
URL.revokeObjectURL(url);
|
||
var f = Math.min(1, MAX_KANTE / Math.max(img.width, img.height));
|
||
var c = document.createElement('canvas');
|
||
c.width = Math.round(img.width * f);
|
||
c.height = Math.round(img.height * f);
|
||
c.getContext('2d').drawImage(img, 0, 0, c.width, c.height);
|
||
resolve(c.toDataURL('image/jpeg', 0.6).split(',')[1]);
|
||
};
|
||
img.onerror = function () { URL.revokeObjectURL(url); reject(new Error('Bild unlesbar')); };
|
||
img.src = url;
|
||
});
|
||
}
|
||
|
||
function pdfAlsBase64(datei) {
|
||
return new Promise(function (resolve, reject) {
|
||
var leser = new FileReader();
|
||
leser.onload = function () { resolve(String(leser.result).split(',')[1]); };
|
||
leser.onerror = function () { reject(new Error('Datei unlesbar')); };
|
||
leser.readAsDataURL(datei);
|
||
});
|
||
}
|
||
|
||
async function starteAnalyse(datei) {
|
||
fehlerBox.style.display = 'none';
|
||
if (datei.size > 5 * 1024 * 1024 && datei.type === 'application/pdf') {
|
||
zeigeFehler('Die PDF-Datei ist zu groß (max. 5 MB). Tipp: Fotografieren Sie den Brief stattdessen.');
|
||
return;
|
||
}
|
||
var token = turnstileToken();
|
||
if (!token) {
|
||
zeigeFehler('Die Sicherheitsprüfung lädt noch — bitte warten Sie 2 Sekunden und versuchen Sie es erneut.');
|
||
return;
|
||
}
|
||
|
||
eingabe.style.display = 'none';
|
||
status.querySelector('.gross').textContent = 'Ihr Brief wird analysiert…';
|
||
status.style.display = 'block';
|
||
|
||
try {
|
||
var istPdf = datei.type === 'application/pdf';
|
||
var base64 = istPdf ? await pdfAlsBase64(datei) : await bildVerkleinern(datei);
|
||
var bezahlt = hatGuthaben();
|
||
var body = {
|
||
bild: base64,
|
||
mimeType: istPdf ? 'application/pdf' : 'image/jpeg',
|
||
turnstileToken: token,
|
||
};
|
||
var email = emailFeld.value.trim();
|
||
body.zielsprache = zielsprache();
|
||
if (bezahlt) body.walletToken = walletToken();
|
||
else if (benutzt() >= 1 && email) body.email = email;
|
||
|
||
var antwort = await fetch(ENDPUNKT, {
|
||
method: 'POST',
|
||
headers: { 'content-type': 'application/json' },
|
||
body: JSON.stringify(body),
|
||
});
|
||
var daten = await antwort.json();
|
||
|
||
if (!antwort.ok) {
|
||
status.style.display = 'none';
|
||
eingabe.style.display = 'block';
|
||
if (window.turnstile) window.turnstile.reset();
|
||
if (daten.code === 'email_noetig') {
|
||
// Nur das E-Mail-Feld zeigen — NICHT als verbraucht markieren.
|
||
// (Sonst würde ein serverseitiges "E-Mail nötig", z. B. bei
|
||
// geteiltem Anschluss, den Browser dauerhaft blockieren, obwohl
|
||
// gar keine Analyse gelungen ist.)
|
||
emailTeil.style.display = 'block';
|
||
emailFeld.focus();
|
||
}
|
||
if (daten.code === 'aufgebraucht') {
|
||
setzeBenutzt(2);
|
||
zeigeStand();
|
||
return;
|
||
}
|
||
if (daten.code === 'kein_guthaben') {
|
||
setzeGuthaben(0);
|
||
guthabenBanner.style.display = 'none';
|
||
kaufBlock.style.display = 'block';
|
||
kaufBlock.scrollIntoView({ behavior: 'smooth' });
|
||
return;
|
||
}
|
||
zeigeFehler(daten.meldung || 'Etwas ist schiefgelaufen. Bitte erneut versuchen.');
|
||
return;
|
||
}
|
||
|
||
// Token erneuern — sonst würde die nächste Analyse ein "Seite neu laden" verlangen
|
||
if (window.turnstile) window.turnstile.reset();
|
||
|
||
if (daten.versuch === 'bezahlt' || typeof daten.guthaben === 'number') {
|
||
setzeGuthaben(daten.guthaben);
|
||
zeigeErgebnis(daten.analyse, null, daten.guthaben);
|
||
return;
|
||
}
|
||
|
||
setzeBenutzt(Math.max(benutzt(), daten.versuch));
|
||
if (email) document.getElementById('wlEmail').value = email;
|
||
zeigeErgebnis(daten.analyse, daten.versuch);
|
||
} catch (e) {
|
||
status.style.display = 'none';
|
||
eingabe.style.display = 'block';
|
||
if (window.turnstile) window.turnstile.reset();
|
||
zeigeFehler('Verbindung fehlgeschlagen. Bitte prüfen Sie Ihr Internet und versuchen Sie es erneut.');
|
||
}
|
||
}
|
||
|
||
/** Ampel wie in der App: rot ≤ 7 Tage, gelb ≤ 21, sonst grün. */
|
||
function ampel(analyse) {
|
||
var daten = [];
|
||
if (analyse.frist && analyse.frist.datum) daten.push(analyse.frist.datum);
|
||
if (analyse.termin && analyse.termin.datum) daten.push(analyse.termin.datum);
|
||
if (!daten.length) return null;
|
||
daten.sort();
|
||
var tage = Math.ceil((new Date(daten[0] + 'T12:00:00') - new Date()) / 86400000);
|
||
if (isNaN(tage)) return null;
|
||
var farbe = tage <= 7 ? 'var(--rot)' : tage <= 21 ? 'var(--gelb)' : 'var(--gruen)';
|
||
var punkt = tage <= 7 ? '🔴' : tage <= 21 ? '🟡' : '🟢';
|
||
var text = tage < 0 ? 'Die Frist ist womöglich schon vorbei!' :
|
||
tage === 0 ? 'HEUTE ist der wichtige Tag!' :
|
||
'Noch ' + tage + ' Tage Zeit (' + daten[0].split('-').reverse().join('.') + ')';
|
||
return { farbe: farbe, text: punkt + ' ' + text };
|
||
}
|
||
|
||
function zeigeErgebnis(analyse, versuch, guthabenRest) {
|
||
status.style.display = 'none';
|
||
// Optionale Karten zuruecksetzen. Sonst bleibt beim zweiten Brief z. B.
|
||
// die Checkliste des ERSTEN Briefs stehen — zwei Briefe vermischt.
|
||
['ergAmpel', 'ergKalenderKarte', 'ergChecklisteKarte', 'ergAnrufKarte', 'ergBegriffeKarte']
|
||
.forEach(function (id) { document.getElementById(id).style.display = 'none'; });
|
||
document.getElementById('ergBrieftyp').textContent = analyse.brieftyp || 'Ihr Brief';
|
||
document.getElementById('ergKern').textContent = analyse.kernaussage || '';
|
||
document.getElementById('ergErklaerung').textContent = analyse.erklaerung_einfach || '';
|
||
|
||
var a = ampel(analyse);
|
||
var ampelEl = document.getElementById('ergAmpel');
|
||
if (a) {
|
||
ampelEl.style.display = 'block';
|
||
ampelEl.style.color = a.farbe;
|
||
var teile = [a.text];
|
||
if (analyse.frist && analyse.frist.aktion) teile.push(analyse.frist.aktion);
|
||
ampelEl.textContent = teile.join(' · ');
|
||
}
|
||
|
||
if (analyse.checkliste && analyse.checkliste.length) {
|
||
document.getElementById('ergChecklisteKarte').style.display = 'block';
|
||
document.getElementById('ergCheckliste').innerHTML = analyse.checkliste
|
||
.map(function (t) { var d = document.createElement('div'); d.className = 'todo'; d.setAttribute('dir', 'auto'); d.textContent = t; return d.outerHTML; })
|
||
.join('');
|
||
}
|
||
if (analyse.fachbegriffe && analyse.fachbegriffe.length) {
|
||
document.getElementById('ergBegriffeKarte').style.display = 'block';
|
||
document.getElementById('ergBegriffe').innerHTML = analyse.fachbegriffe
|
||
.map(function (b) {
|
||
var det = document.createElement('details');
|
||
var sum = document.createElement('summary');
|
||
sum.textContent = b.begriff;
|
||
var p = document.createElement('p');
|
||
p.textContent = b.erklaerung;
|
||
det.appendChild(sum); det.appendChild(p);
|
||
return det.outerHTML;
|
||
})
|
||
.join('');
|
||
}
|
||
|
||
zeigeKalender(analyse);
|
||
zeigeAnruf(analyse);
|
||
|
||
document.getElementById('ergebnis').style.display = 'block';
|
||
|
||
// Bezahlter Modus: Guthaben-Stand zeigen, Eingabe für den nächsten Brief öffnen
|
||
if (typeof guthabenRest === 'number') {
|
||
werbung.style.display = 'none';
|
||
kaufOk.style.display = 'none';
|
||
if (guthabenRest > 0) {
|
||
eingabe.style.display = 'block';
|
||
guthabenBanner.style.display = 'block';
|
||
guthabenZahl.textContent = guthabenRest;
|
||
emailTeil.style.display = 'none';
|
||
kaufBlock.style.display = 'none';
|
||
} else {
|
||
eingabe.style.display = 'none';
|
||
guthabenBanner.style.display = 'none';
|
||
kaufBlock.style.display = 'block';
|
||
}
|
||
document.getElementById('ergebnis').scrollIntoView({ behavior: 'smooth' });
|
||
return;
|
||
}
|
||
|
||
var rest = 2 - benutzt();
|
||
document.getElementById('restZeile').textContent =
|
||
rest > 0 ? 'Noch ' + rest + ' Gratis-Analyse übrig' : 'Ihre 2 Gratis-Analysen sind verbraucht';
|
||
document.getElementById('wlTitel').textContent =
|
||
rest > 0 ? 'Das hat Ihnen geholfen?' : 'Weiter geht es in der App';
|
||
document.getElementById('werbung').style.display = 'block';
|
||
if (rest > 0) {
|
||
// Upload-Bereich für die 2. Analyse wieder anbieten (mit E-Mail-Feld)
|
||
eingabe.style.display = 'block';
|
||
emailTeil.style.display = 'block';
|
||
document.getElementById('nochmalZeile').style.display = 'block';
|
||
}
|
||
document.getElementById('ergebnis').scrollIntoView({ behavior: 'smooth' });
|
||
}
|
||
|
||
// ---- Kalender (.ics) --------------------------------------------------
|
||
// Die Datei entsteht im Browser und wird nirgends hochgeladen. Die
|
||
// Erinnerungen stellt danach der Kalender des Handys selbst.
|
||
var letzteAnalyse = null;
|
||
|
||
function icsText(t) {
|
||
return String(t || '').replace(/\\/g, '\\\\').replace(/;/g, '\;')
|
||
.replace(/,/g, '\\,').replace(/\r?\n/g, '\\n');
|
||
}
|
||
// RFC 5545: max. 75 BYTE pro Zeile. Nach Bytes falten, nicht nach Zeichen —
|
||
// arabische oder tigrinische Texte brauchen 2–4 Byte pro Zeichen.
|
||
function icsFalte(zeile) {
|
||
var enc = new TextEncoder(), teile = [], akt = '', bytes = 0;
|
||
for (var zeichen of zeile) {
|
||
var b = enc.encode(zeichen).length;
|
||
if (bytes + b > 75) { teile.push(akt); akt = ' ' + zeichen; bytes = 1 + b; }
|
||
else { akt += zeichen; bytes += b; }
|
||
}
|
||
teile.push(akt);
|
||
return teile.join('\r\n');
|
||
}
|
||
function isoGueltig(d) { return typeof d === 'string' && /^\d{4}-\d{2}-\d{2}$/.test(d); }
|
||
function deDatum(iso) { return iso.split('-').reverse().join('.'); }
|
||
function zweistellig(x) { return ('0' + x).slice(-2); }
|
||
// "Wanduhrzeit" ohne Zeitzone: der Kalender nimmt die Ortszeit des Geraets.
|
||
// Richtig fuer einen Termin in Deutschland, egal wo der Server steht.
|
||
function wanduhr(dt) {
|
||
return dt.getUTCFullYear() + zweistellig(dt.getUTCMonth() + 1) + zweistellig(dt.getUTCDate()) +
|
||
'T' + zweistellig(dt.getUTCHours()) + zweistellig(dt.getUTCMinutes()) + '00';
|
||
}
|
||
function nurDatum(dt) { return wanduhr(dt).slice(0, 8); }
|
||
function ausIso(iso, h, m) { var t = iso.split('-'); return new Date(Date.UTC(+t[0], +t[1] - 1, +t[2], h || 0, m || 0)); }
|
||
function eindeutig() {
|
||
return (window.crypto && crypto.randomUUID ? crypto.randomUUID() : Date.now() + '-' + Math.random().toString(16).slice(2)) + '@behoerdenklar.de';
|
||
}
|
||
function alarm(ausloeser, text) {
|
||
return ['BEGIN:VALARM', 'ACTION:DISPLAY', 'TRIGGER:' + ausloeser, 'DESCRIPTION:' + icsText(text), 'END:VALARM'];
|
||
}
|
||
|
||
function baueIcs(a) {
|
||
var z = ['BEGIN:VCALENDAR', 'VERSION:2.0', 'PRODID:-//Webklar GbR//BehoerdenKlar//DE',
|
||
'CALSCALE:GREGORIAN', 'METHOD:PUBLISH'];
|
||
var titel = a.brieftyp || 'Brief', stempel = wanduhr(new Date()) + 'Z', n = 0;
|
||
|
||
if (a.frist && isoGueltig(a.frist.datum)) {
|
||
var fTag = ausIso(a.frist.datum);
|
||
var fDanach = new Date(fTag.getTime() + 86400000);
|
||
// Titel und Hinweis ohne deutsche Worte: Datum und Symbol versteht jeder,
|
||
// "aktion" kommt bereits in der Sprache des Nutzers von der KI.
|
||
z.push('BEGIN:VEVENT', 'UID:' + eindeutig(), 'DTSTAMP:' + stempel,
|
||
'DTSTART;VALUE=DATE:' + nurDatum(fTag), 'DTEND;VALUE=DATE:' + nurDatum(fDanach),
|
||
'SUMMARY:' + icsText('⚠️ ' + titel),
|
||
'DESCRIPTION:' + icsText((a.frist.aktion || '') + '\n\nbehoerdenklar.de'));
|
||
// Ganztaegig beginnt um 00:00 — so fallen beide Erinnerungen auf 9 Uhr morgens.
|
||
z.push.apply(z, alarm('-P2DT15H', '⏰ ' + titel + ' — ' + deDatum(a.frist.datum)));
|
||
z.push.apply(z, alarm('-PT15H', '⏰ ' + titel + ' — ' + deDatum(a.frist.datum)));
|
||
z.push('END:VEVENT'); n++;
|
||
}
|
||
|
||
if (a.termin && isoGueltig(a.termin.datum)) {
|
||
var zeit = a.termin.uhrzeit && String(a.termin.uhrzeit).match(/(\d{1,2})[:.](\d{2})/);
|
||
var h = zeit ? +zeit[1] : -1, m = zeit ? +zeit[2] : -1;
|
||
z.push('BEGIN:VEVENT', 'UID:' + eindeutig(), 'DTSTAMP:' + stempel);
|
||
if (zeit && h <= 23 && m <= 59) {
|
||
var beginn = ausIso(a.termin.datum, h, m);
|
||
var ende = new Date(beginn.getTime() + 3600000); // eine Stunde, auch ueber Mitternacht korrekt
|
||
z.push('DTSTART:' + wanduhr(beginn), 'DTEND:' + wanduhr(ende));
|
||
z.push.apply(z, alarm('-P3D', '📅 ' + titel + ' — ' + deDatum(a.termin.datum) + ', ' + zweistellig(h) + ':' + zweistellig(m)));
|
||
z.push.apply(z, alarm('-P1D', '📅 ' + titel + ' — ' + deDatum(a.termin.datum) + ', ' + zweistellig(h) + ':' + zweistellig(m)));
|
||
} else {
|
||
var tTag = ausIso(a.termin.datum);
|
||
z.push('DTSTART;VALUE=DATE:' + nurDatum(tTag), 'DTEND;VALUE=DATE:' + nurDatum(new Date(tTag.getTime() + 86400000)));
|
||
z.push.apply(z, alarm('-P2DT15H', '📅 ' + titel + ' — ' + deDatum(a.termin.datum)));
|
||
z.push.apply(z, alarm('-PT15H', '📅 ' + titel + ' — ' + deDatum(a.termin.datum)));
|
||
}
|
||
z.push('SUMMARY:' + icsText('📅 ' + titel));
|
||
if (a.termin.ort) z.push('LOCATION:' + icsText(a.termin.ort));
|
||
z.push('DESCRIPTION:' + icsText('behoerdenklar.de'), 'END:VEVENT'); n++;
|
||
}
|
||
|
||
z.push('END:VCALENDAR');
|
||
return n ? z.map(icsFalte).join('\r\n') + '\r\n' : null;
|
||
}
|
||
|
||
function zeigeKalender(a) {
|
||
var karte = document.getElementById('ergKalenderKarte');
|
||
var frist = a.frist && isoGueltig(a.frist.datum) ? a.frist : null;
|
||
var termin = a.termin && isoGueltig(a.termin.datum) ? a.termin : null;
|
||
if (!frist && !termin) return;
|
||
letzteAnalyse = a;
|
||
var box = document.getElementById('ergKalenderText');
|
||
box.innerHTML = '';
|
||
function zeile(text) { var p = document.createElement('p'); p.setAttribute('dir', 'auto'); p.textContent = text; box.appendChild(p); }
|
||
if (frist) zeile('⚠️ Frist: ' + deDatum(frist.datum) + (frist.aktion ? ' — ' + frist.aktion : ''));
|
||
if (termin) zeile('📅 Termin: ' + deDatum(termin.datum) + (termin.uhrzeit ? ', ' + termin.uhrzeit + ' Uhr' : '') + (termin.ort ? ' — ' + termin.ort : ''));
|
||
karte.style.display = 'block';
|
||
}
|
||
|
||
document.getElementById('kalenderKnopf').addEventListener('click', function () {
|
||
if (!letzteAnalyse) return;
|
||
var ics = baueIcs(letzteAnalyse);
|
||
if (!ics) return;
|
||
var url = URL.createObjectURL(new Blob([ics], { type: 'text/calendar;charset=utf-8' }));
|
||
var link = document.createElement('a');
|
||
link.href = url; link.download = 'BehoerdenKlar-Termin.ics';
|
||
document.body.appendChild(link); link.click(); link.remove();
|
||
setTimeout(function () { URL.revokeObjectURL(url); }, 10000);
|
||
});
|
||
|
||
// ---- Anruf-Hilfe ------------------------------------------------------
|
||
function zeigeAnruf(a) {
|
||
var karte = document.getElementById('ergAnrufKarte');
|
||
var k = a.kontakt || {}, saetze = Array.isArray(a.anruf) ? a.anruf : [];
|
||
if (!k.telefon && !saetze.length) return;
|
||
|
||
var box = document.getElementById('ergAnrufKontakt');
|
||
box.innerHTML = '';
|
||
if (k.telefon) {
|
||
var knopf = document.createElement('a');
|
||
knopf.className = 'cta';
|
||
knopf.href = 'tel:' + String(k.telefon).replace(/[^\d+]/g, '');
|
||
knopf.textContent = '📞 ' + k.telefon;
|
||
box.appendChild(knopf);
|
||
}
|
||
function angabe(label, wert, hervorheben) {
|
||
if (!wert) return;
|
||
var p = document.createElement('p'); p.className = 'kontakt-zeile';
|
||
var b = document.createElement('strong'); b.textContent = label + ': ';
|
||
var w = document.createElement('span'); w.textContent = wert;
|
||
if (hervorheben) w.className = 'az-wert';
|
||
p.appendChild(b); p.appendChild(w); box.appendChild(p);
|
||
}
|
||
angabe('Ansprechpartner', k.ansprechpartner);
|
||
angabe('Erreichbar', k.sprechzeiten);
|
||
angabe('Danach werden Sie gefragt', k.aktenzeichen, true);
|
||
|
||
var liste = document.getElementById('ergAnrufSaetze');
|
||
liste.innerHTML = '';
|
||
if (saetze.length) {
|
||
var kopf = document.createElement('p');
|
||
kopf.style.marginTop = '12px';
|
||
kopf.innerHTML = '<strong>Das können Sie am Telefon sagen:</strong>';
|
||
liste.appendChild(kopf);
|
||
var mitBedeutung = zielsprache() !== 'de';
|
||
saetze.forEach(function (s) {
|
||
var d = document.createElement('div'); d.className = 'anruf-satz';
|
||
var de = document.createElement('p'); de.className = 'anruf-de';
|
||
de.textContent = '„' + s.deutsch + '“';
|
||
d.appendChild(de);
|
||
if (mitBedeutung && s.bedeutung && s.bedeutung !== s.deutsch) {
|
||
var be = document.createElement('p'); be.className = 'anruf-bed';
|
||
be.setAttribute('dir', 'auto');
|
||
be.textContent = s.bedeutung;
|
||
d.appendChild(be);
|
||
}
|
||
liste.appendChild(d);
|
||
});
|
||
}
|
||
karte.style.display = 'block';
|
||
}
|
||
|
||
// ---- Guthaben kaufen ------------------------------------------------
|
||
function rendrePakete() {
|
||
paketListe.innerHTML = '';
|
||
PAKETE.forEach(function (p) {
|
||
var b = document.createElement('button');
|
||
b.className = 'cta';
|
||
b.type = 'button';
|
||
b.style.width = '100%';
|
||
b.style.display = 'flex';
|
||
b.style.justifyContent = 'space-between';
|
||
b.style.alignItems = 'center';
|
||
var links = document.createElement('span');
|
||
links.textContent = (p.best ? '⭐ ' : '') + p.titel;
|
||
var rechts = document.createElement('strong');
|
||
rechts.textContent = p.preis;
|
||
b.appendChild(links);
|
||
b.appendChild(rechts);
|
||
b.addEventListener('click', function () { starteKauf(p.priceId, b); });
|
||
paketListe.appendChild(b);
|
||
});
|
||
}
|
||
|
||
function starteKauf(priceId, knopf) {
|
||
if (!kaufKonsens.checked) {
|
||
kaufFehler.style.display = 'block';
|
||
kaufFehler.textContent = 'Bitte bestätigen Sie zuerst das Häkchen, um fortzufahren.';
|
||
kaufKonsens.focus();
|
||
return;
|
||
}
|
||
kaufFehler.style.display = 'none';
|
||
var alt = knopf.innerHTML;
|
||
knopf.disabled = true;
|
||
knopf.textContent = 'Weiter zu Stripe …';
|
||
fetch(KAUFEN_ENDPUNKT, {
|
||
method: 'POST',
|
||
headers: { 'content-type': 'application/json' },
|
||
body: JSON.stringify({ priceId: priceId, sprache: zielsprache() }),
|
||
})
|
||
.then(function (r) { return r.json(); })
|
||
.then(function (d) {
|
||
if (d && d.url) { window.location.href = d.url; return; }
|
||
throw new Error('keine url');
|
||
})
|
||
.catch(function () {
|
||
knopf.disabled = false;
|
||
knopf.innerHTML = alt;
|
||
kaufFehler.style.display = 'block';
|
||
kaufFehler.textContent = 'Die Bezahlseite konnte nicht geöffnet werden. Bitte erneut versuchen.';
|
||
});
|
||
}
|
||
|
||
// ---- Rückkehr von Stripe / Wallet-Wiederherstellung -----------------
|
||
function reinigeUrl() {
|
||
if (window.history.replaceState) {
|
||
window.history.replaceState({}, document.title, window.location.pathname);
|
||
}
|
||
}
|
||
|
||
function holeKaufStatus(sid, n) {
|
||
fetch(STATUS_ENDPUNKT + '?session_id=' + encodeURIComponent(sid))
|
||
.then(function (r) { return r.json().then(function (d) { return { ok: r.ok, d: d }; }); })
|
||
.then(function (res) {
|
||
if (res.ok && res.d && res.d.token) {
|
||
setzeWallet(res.d.token);
|
||
setzeGuthaben(res.d.guthaben);
|
||
status.style.display = 'none';
|
||
kaufOk.style.display = 'block';
|
||
zeigeStand();
|
||
document.getElementById('demo').scrollIntoView({ behavior: 'smooth' });
|
||
return;
|
||
}
|
||
if (n < 4) { setTimeout(function () { holeKaufStatus(sid, n + 1); }, 1500); return; }
|
||
status.style.display = 'none';
|
||
zeigeFehler('Ihre Zahlung wird noch bestätigt. Bitte laden Sie die Seite in einer Minute neu.');
|
||
})
|
||
.catch(function () {
|
||
status.style.display = 'none';
|
||
zeigeFehler('Die Zahlungsbestätigung konnte nicht geladen werden. Bitte Seite neu laden.');
|
||
});
|
||
}
|
||
|
||
function pruefeRueckkehr() {
|
||
var p = new URLSearchParams(window.location.search);
|
||
var w = p.get('wallet');
|
||
if (w) { setzeWallet(w); setzeGuthaben(Math.max(guthaben(), 1)); reinigeUrl(); return; }
|
||
var kauf = p.get('kauf');
|
||
if (kauf === 'abbruch') {
|
||
reinigeUrl();
|
||
fehlerBox.style.display = 'block';
|
||
fehlerBox.textContent = 'Kauf abgebrochen — es wurde nichts berechnet.';
|
||
return;
|
||
}
|
||
if (kauf === 'ok') {
|
||
var sid = p.get('session_id');
|
||
reinigeUrl();
|
||
if (!sid) return;
|
||
status.querySelector('.gross').textContent = 'Ihr Kauf wird bestätigt …';
|
||
eingabe.style.display = 'none';
|
||
status.style.display = 'block';
|
||
holeKaufStatus(sid, 0);
|
||
}
|
||
}
|
||
|
||
kaufKonsens.addEventListener('change', function () {
|
||
if (kaufKonsens.checked) kaufFehler.style.display = 'none';
|
||
});
|
||
|
||
// Sprachauswahl aufbauen
|
||
(function () {
|
||
var feld = document.getElementById('sprachWahl');
|
||
var aktiv = zielsprache();
|
||
Object.keys(SPRACH_NAMEN).forEach(function (c) {
|
||
var o = document.createElement('option');
|
||
o.value = c; o.textContent = SPRACH_NAMEN[c];
|
||
if (c === aktiv) o.selected = true;
|
||
feld.appendChild(o);
|
||
});
|
||
feld.addEventListener('change', function () {
|
||
try { localStorage.setItem(SPRACH_SCHLUESSEL, feld.value); } catch (e) {}
|
||
});
|
||
})();
|
||
|
||
rendrePakete();
|
||
pruefeRueckkehr();
|
||
zeigeStand();
|
||
})();
|
||
</script>
|
||
<!-- Cloudflare Web Analytics: cookielose Reichweitenmessung, keine Nutzer-Profile -->
|
||
<script type='module' src='https://static.cloudflareinsights.com/beacon.min.js' data-cf-beacon='{"token": "11f017e9a707466b9151921db6125dde"}'></script>
|
||
</body>
|
||
</html>
|