generated from knso/webklar-preview-template
Der Nutzer testete mit einem echten Finanzamt-Brief (Zuteilung der Steuernummer). Der enthaelt eine Frist — 'bis zum 10. Tag nach Ablauf des Kalendervierteljahres' — aber kein einzelnes Datum, weil sie sich jedes Quartal wiederholt und nur unter Bedingungen gilt. Die KI hat daraus richtigerweise kein Datum gemacht. Die Meldung vom letzten Commit haette dazu gesagt: '✓ In diesem Brief steht keine Frist.' Bei genau diesem Brief falsch — und das Haekchen signalisiert 'alles erledigt'. Jetzt: 'ℹ️ Kein festes Datum zum Eintragen gefunden' plus Hinweis, dass manche Fristen kein festes Datum haben (z. B. jedes Vierteljahr) und in der Erklaerung oben stehen. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
1108 lines
51 KiB
HTML
1108 lines
51 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;">
|
||
<span class="karten-label" id="ergKalenderLabel">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" id="ergKalenderHinweis">
|
||
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 knopf = document.getElementById('kalenderKnopf');
|
||
var hinweis = document.getElementById('ergKalenderHinweis');
|
||
var label = document.getElementById('ergKalenderLabel');
|
||
var frist = a.frist && isoGueltig(a.frist.datum) ? a.frist : null;
|
||
var termin = a.termin && isoGueltig(a.termin.datum) ? a.termin : null;
|
||
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); }
|
||
|
||
// Kein Datum im Brief: trotzdem sichtbar sagen, dass gesucht wurde.
|
||
// Sonst weiss niemand, ob nichts da ist oder etwas kaputt ist — und
|
||
// "keine Frist" ist fuer einen aengstlichen Leser selbst eine Nachricht.
|
||
if (!frist && !termin) {
|
||
letzteAnalyse = null;
|
||
// NICHT "keine Frist" behaupten: Manche Briefe nennen wiederkehrende
|
||
// oder bedingte Fristen ("bis zum 10. Tag nach jedem Vierteljahr"),
|
||
// aus denen sich kein einzelnes Datum machen laesst. Ein Haekchen
|
||
// wuerde dann "alles erledigt" signalisieren — gefaehrlich falsch.
|
||
label.textContent = 'Kalender';
|
||
zeile('ℹ️ Kein festes Datum zum Eintragen gefunden.');
|
||
zeile('Wichtig: Manche Fristen haben kein festes Datum, zum Beispiel „jedes Vierteljahr" oder nur unter bestimmten Bedingungen. Lesen Sie dazu die Erklärung oben.');
|
||
knopf.style.display = 'none';
|
||
hinweis.style.display = 'none';
|
||
karte.style.border = '';
|
||
karte.style.display = 'block';
|
||
return;
|
||
}
|
||
letzteAnalyse = a;
|
||
label.textContent = 'Nicht vergessen';
|
||
knopf.style.display = '';
|
||
hinweis.style.display = '';
|
||
karte.style.border = '2px solid var(--amtsblau)';
|
||
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>
|