Kundenansicht neu P2: öffentliche Seiten und Auftragsbestätigung im neuen Stil, Text unverändert

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
ClaudeandClaude Sonnet 5.5 committed 2026-10-16 17:17:45 +00:00
1 parent ade7e872d9
commit 3c2450dd2e
7 files changed
+571

No files matched your search

+42
View File
@@ -0,0 +1,42 @@
<!DOCTYPE html>
<html lang="de">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, viewport-fit=cover">
<meta name="robots" content="noindex, nofollow">
<meta name="referrer" content="no-referrer">
<meta name="color-scheme" content="light">
<meta name="theme-color" content="#ffffff">
<title>Auftragsbestätigung – WEBklar</title>
<link rel="icon" href="/favicon.ico" sizes="32x32">
<link rel="icon" href="/favicon.svg" type="image/svg+xml">
<link rel="stylesheet" href="/neu/tokens.css">
<link rel="stylesheet" href="/neu/oeffentlich/oeffentlich.css">
<noscript><style>.ab-laden{display:none}</style></noscript>
</head>
<body class="ab-seite">
<!-- Bestätigungsseite für eine Auftragsbestätigung (Link aus der Mail K31: /auftrag.html#t=<Token>, Rückfall ?t=<Token>).
Der Token steht nur in der Adresse und im Speicher der Seite: keine Cookies, kein Browserspeicher, keine Fremdanfragen,
nichts im Protokoll (auftrag.js). Ohne Anmeldung erreichbar, nicht für Suchmaschinen. -->
<a class="ab-app-back" href="/dashboard.html">‹ Zurück zum Kundenbereich</a>
<header class="ab-kopf">
<div class="ab-innen"><img class="ab-logo" src="/webklar-logo.png" width="36" height="36" alt=""><span class="ab-marke">WEBklar</span></div>
</header>
<main class="ab-haupt">
<div class="ab-innen">
<div class="ab-wurzel" id="ab-wurzel" data-zustand="laedt" aria-busy="true">
<noscript><p>Bitte aktivieren Sie JavaScript, um Ihren Auftrag zu bestätigen.</p></noscript>
<p class="ab-laden">Ihre Auftragsbestätigung wird geladen …</p>
</div>
</div>
</main>
<div class="ab-vh" id="ab-ansage" aria-live="polite"></div>
<footer class="ab-fuss">
<div class="ab-innen">
<a href="/datenschutz.html" target="_blank" rel="noopener noreferrer">Datenschutz</a>
<a href="https://webklar.com/impressum" target="_blank" rel="noopener noreferrer">Impressum</a>
</div>
</footer>
<script type="module" src="/auftrag.js"></script>
</body>
</html>
+75
View File
@@ -0,0 +1,75 @@
<!DOCTYPE html>
<html lang="de">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, viewport-fit=cover">
<meta name="theme-color" content="#ffffff">
<title>Verträge hier kündigen – WEBklar</title>
<link rel="stylesheet" href="/neu/tokens.css">
<link rel="stylesheet" href="/neu/oeffentlich/oeffentlich.css">
</head>
<body class="kn kn-seite kn-recht">
<main>
<!-- Nur in der installierten App sichtbar: dort gibt es keinen Zurück-Knopf des Browsers -->
<a class="kn-zurueck no-print" href="/dashboard.html">‹ Zurück zum Kundenbereich</a>
<div class="kn-markenzeile">WEBKLAR</div>
<h1>Verträge hier kündigen</h1>
<form id="decl-form" class="kn-formular" novalidate>
<!-- § 312k Abs. 3 BGB: eindeutige Bezeichnung des Vertrags - das Abo (mit Zusatzspeicher) oder nur der Zusatzspeicher -->
<fieldset>
<legend data-label-for="contract" data-label="Vertrag">Vertrag</legend>
<label class="kn-auswahl"><input type="radio" name="contract" value="abo" data-text="Mein Abo (Hosting- oder Wartungs-Abo, mit Zusatzspeicher)" checked> Mein Abo (Hosting- oder Wartungs-Abo, mit Zusatzspeicher)</label>
<label class="kn-auswahl"><input type="radio" name="contract" value="zusatzspeicher" data-text="Nur den E-Mail-Zusatzspeicher"> Nur den E-Mail-Zusatzspeicher</label>
</fieldset>
<div id="packages-wrap" class="hidden">
<label for="packages" data-label-for="packages" data-label="Pakete">Zahl der Pakete, die Sie kündigen <small>(je 5 GB; leer = alle gebuchten Pakete)</small></label>
<input id="packages" name="packages" type="number" inputmode="numeric" min="1" max="10" step="1" placeholder="alle">
<p class="kn-hinweis">Die Kündigung des Zusatzspeichers wirkt zum Ende des laufenden Abrechnungsmonats. Das geht auch, wenn Ihr Abo schon gekündigt ist.</p>
</div>
<fieldset>
<legend data-label-for="kind" data-label="Art der Kündigung">Art der Kündigung</legend>
<label class="kn-auswahl"><input type="radio" name="kind" value="ordentlich" data-text="Ordentliche Kündigung" checked> Ordentliche Kündigung</label>
<label class="kn-auswahl"><input type="radio" name="kind" value="ausserordentlich" data-text="Außerordentliche Kündigung"> Außerordentliche Kündigung aus wichtigem Grund</label>
<label class="kn-auswahl"><input type="radio" name="kind" value="geschaeftsaufgabe" data-text="Sonderkündigung wegen Geschäftsaufgabe"> Sonderkündigung wegen Geschäftsaufgabe</label>
</fieldset>
<div id="reason-wrap" class="hidden">
<label for="reason" data-label-for="reason" data-label="Kündigungsgrund">Kündigungsgrund</label>
<textarea id="reason" name="reason" maxlength="2000"></textarea>
</div>
<label for="name" data-label-for="name" data-label="Name">Name</label>
<input id="name" name="name" autocomplete="name" required maxlength="200">
<label for="email" data-label-for="email" data-label="E-Mail für die Bestätigung">E-Mail-Adresse <small>(dorthin schicken wir die Bestätigung)</small></label>
<input id="email" name="email" type="email" autocomplete="email" required maxlength="255">
<label for="customerNumber" data-label-for="customerNumber" data-label="Kundennummer">Kundennummer <small>(optional)</small></label>
<input id="customerNumber" name="customerNumber" maxlength="50">
<fieldset id="when-wrap">
<legend>Zeitpunkt</legend>
<label class="kn-auswahl"><input type="radio" name="when" value="naechstmoeglich" checked> zum nächstmöglichen Zeitpunkt</label>
<label class="kn-auswahl"><input type="radio" name="when" value="datum"> zu einem bestimmten Datum</label>
<input type="date" id="effective-date" class="hidden">
</fieldset>
<p class="kn-fehlertext hidden" id="form-error"></p>
<button type="submit" class="kn-absenden">Jetzt kündigen</button>
</form>
<div id="done" class="kn-bestaetigung hidden">
<h2>Ihre Kündigung ist eingegangen</h2>
<p>Eingang: <strong id="done-at"></strong></p>
<dl id="done-list"></dl>
<p>Eine Eingangsbestätigung mit diesen Angaben haben wir an Ihre E-Mail-Adresse geschickt. Den Zeitpunkt, zu dem die Kündigung wirkt, bestätigen wir Ihnen per E-Mail.</p>
<button type="button" class="kn-drucken" id="print-btn">Diese Bestätigung drucken oder als PDF speichern</button>
</div>
</main>
<script src="/legal-form.js"></script>
<script>
initLegalForm({ endpoint: '/api/abo/public/cancel', fields: ['contract', 'packages', 'kind', 'reason', 'name', 'email', 'customerNumber', 'effective'] })
</script>
</body>
</html>
+90
View File
@@ -0,0 +1,90 @@
<!DOCTYPE html>
<html lang="de">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, viewport-fit=cover">
<title>Anmelden – Webklar Kundenbereich</title>
<!-- Anmeldung im weißen Stil (noch nicht ausgeliefert, P10 schaltet sie um). Als App auf den Homescreen, helle Statusleiste -->
<link rel="manifest" href="/neu/manifest.webmanifest">
<link rel="icon" href="/favicon.ico" sizes="32x32">
<link rel="icon" href="/favicon.svg" type="image/svg+xml">
<link rel="apple-touch-icon" href="/img/apple-touch-icon.png">
<meta name="theme-color" content="#ffffff">
<meta name="mobile-web-app-capable" content="yes">
<meta name="apple-mobile-web-app-capable" content="yes">
<meta name="apple-mobile-web-app-status-bar-style" content="default">
<meta name="apple-mobile-web-app-title" content="WEBklar">
<link rel="stylesheet" href="/neu/tokens.css">
<link rel="stylesheet" href="/neu/oeffentlich/oeffentlich.css">
</head>
<body class="kn kn-seite kn-login-seite">
<header class="kn-login-kopf">
<a class="kn-login-logo" href="/login.html">Webklar</a>
</header>
<main class="kn-login-haupt">
<div class="kn-login-spalte">
<!-- Handy: App-Icon wie beim Start einer App -->
<div class="kn-login-marke">
<img src="/img/app-icon-192.png" alt="" width="64" height="64">
<span>WEBklar Kundenbereich</span>
</div>
<div class="kn-login-abschnitt">
<p class="kn-login-titel">Anmelden</p>
<form id="login-form" class="kn-login-form" novalidate>
<input
class="kn-feld-eingabe"
type="email"
name="email"
id="email"
placeholder="E-Mail"
required
autocomplete="username"
autocapitalize="none"
spellcheck="false"
enterkeyhint="next"
>
<input
class="kn-feld-eingabe"
type="password"
name="password"
id="password"
placeholder="Passwort"
required
autocomplete="current-password"
enterkeyhint="go"
>
<p id="login-error" class="kn-fehlertext hidden"></p>
<!-- Mitarbeiter-Login ist gesperrt: Link ins Ticketsystem (Adresse aus der Antwort, ticketUrl) -->
<a id="login-staff-link" class="kn-login-staff hidden" href="https://ticket.webklar.com">Zum Ticketsystem</a>
<button type="submit" id="login-btn" class="kn-login-knopf">
<span class="button-label">Anmelden</span>
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 11 10" aria-hidden="true">
<path fill="currentColor" d="m4.56 8.352 1.824-1.84q.224-.225.144-.4-.064-.192-.384-.192H0V3.76h6.128q.32 0 .4-.176.08-.192-.144-.416l-1.84-1.84L5.84 0l4.848 4.848L5.856 9.68z"></path>
</svg>
<svg class="loading-spinner" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" aria-hidden="true">
<path stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M21 12a9 9 0 1 1-6.219-8.56"></path>
</svg>
</button>
</form>
</div>
<p class="kn-login-hinweis">
Zugangsdaten erhältst du von Webklar. Dein Passwort kannst du nach der Anmeldung selbst ändern (Profilbild oben rechts, „Passwort ändern“).
Passwort vergessen? Schreib uns an <a href="mailto:support@webklar.com">support@webklar.com</a>.
</p>
<p class="kn-login-hinweis">
<a class="kn-login-wichtig" href="/kuendigen.html">Verträge hier kündigen</a> ·
<a class="kn-login-wichtig" href="/widerrufen.html">Vertrag widerrufen</a><br>
<a href="/abo-bedingungen.html" target="_blank" rel="noopener">Vertragsbedingungen</a> ·
<a href="/datenschutz.html" target="_blank" rel="noopener">Datenschutz</a> ·
<a href="https://webklar.com/impressum" target="_blank" rel="noopener">Impressum</a>
</p>
</div>
</main>
<script src="/app.js"></script>
<script>
initLoginPage();
</script>
</body>
</html>
+218
View File
@@ -0,0 +1,218 @@
/* Kundenansicht neu: die öffentlichen Seiten im weißen Stil (Anmeldung, Kündigen, Widerrufen, Auftragsbestätigung).
Werte aus tokens.css (Klasse .kn auf dem body); die Auftragsseite trägt kein .kn und setzt ihre Werte unten selbst.
Ohne Karten, Verläufe, Glas, Lila und Dunkelmodus. Die Offline-Seite hat ihr Aussehen inline (der Service Worker liefert nur diese eine Datei). */
/* ------------------------------------------------------------------ Gemeinsam (Anmeldung, Kündigen, Widerrufen) --- */
.kn-seite { margin: 0; background: var(--kn-bg); color: var(--kn-text); font: 16px/1.55 var(--kn-font); -webkit-font-smoothing: antialiased; -webkit-text-size-adjust: 100%; }
.kn-seite *, .kn-seite *::before, .kn-seite *::after { box-sizing: border-box; }
.kn-seite .hidden { display: none !important; }
.kn-seite a { color: inherit; text-decoration: underline; text-decoration-color: var(--kn-line-strong); text-underline-offset: 3px; }
.kn-seite a:hover { text-decoration-color: currentColor; }
.kn-seite :focus-visible { outline: 2px solid #111; outline-offset: 2px; }
.kn-seite :disabled { opacity: .55; cursor: not-allowed; }
.kn-seite .kn-fehlertext { margin: 0; font-size: 14px; line-height: 20px; color: var(--kn-danger); font-weight: 600; }
/* ------------------------------------------------------------------ Anmeldung --- */
.kn-login-seite { min-height: 100svh; }
.kn-login-kopf { position: fixed; top: 0; left: 0; right: 0; z-index: 2; padding: calc(24px + env(safe-area-inset-top)) 24px 16px; pointer-events: none; }
.kn-login-logo { pointer-events: auto; font-size: 18px; line-height: 24px; font-weight: 700; letter-spacing: .06em; text-transform: uppercase; }
.kn-seite a.kn-login-logo { text-decoration: none; }
.kn-login-haupt { min-height: 100svh; display: flex; align-items: center; justify-content: center; padding: calc(88px + env(safe-area-inset-top)) 24px calc(40px + env(safe-area-inset-bottom)); }
.kn-login-spalte { width: 100%; max-width: 360px; }
.kn-login-marke { display: none; }
.kn-login-titel { margin: 0 0 24px; font-size: 22px; line-height: 28px; font-weight: 700; letter-spacing: -.02em; }
.kn-login-form { display: flex; flex-direction: column; gap: 12px; }
.kn-feld-eingabe {
width: 100%; height: 44px; min-width: 0; margin: 0; padding: 0 12px; border: 0; border-radius: var(--kn-radius);
background: var(--kn-bg); box-shadow: var(--kn-ring); color: inherit; font: inherit; font-size: 16px; outline: none; transition: box-shadow .12s;
}
.kn-feld-eingabe::placeholder { color: var(--kn-text-3); }
.kn-feld-eingabe:focus { box-shadow: 0 0 0 1px var(--kn-accent), 0 0 0 4px var(--kn-accent-ring); }
.kn-login-form .kn-fehlertext { text-align: center; }
.kn-login-staff { display: block; text-align: center; font-size: 14px; font-weight: 600; }
.kn-login-knopf {
position: relative; display: flex; align-items: center; justify-content: center; gap: 8px; width: 100%; min-height: 44px; margin-top: 4px;
border: 0; border-radius: var(--kn-radius); background: var(--kn-accent); color: #fff; font: inherit; font-weight: 600; cursor: pointer;
box-shadow: var(--kn-shadow-btn); transition: background-color .12s;
}
.kn-login-knopf:hover:not(:disabled) { background: var(--kn-accent-hover); }
.kn-login-knopf svg { width: 12px; height: 12px; flex-shrink: 0; }
.kn-login-knopf .loading-spinner { display: none; }
.kn-login-knopf.is-loading svg { display: none; }
.kn-login-knopf.is-loading .button-label::after { content: ' …'; }
.kn-login-hinweis { margin: 32px 0 0; text-align: center; font-size: 14px; line-height: 20px; color: var(--kn-text-2); }
.kn-login-hinweis + .kn-login-hinweis { margin-top: 16px; }
.kn-seite a.kn-login-wichtig { color: var(--kn-text); font-weight: 600; }
@media (max-width: 880px), (pointer: coarse) and (max-height: 500px) {
.kn-login-marke { display: flex; flex-direction: column; align-items: center; gap: 12px; margin: 0 0 32px; font-size: 15px; font-weight: 600; }
.kn-login-marke img { width: 64px; height: 64px; border-radius: var(--kn-radius-lg); box-shadow: var(--kn-ring); }
.kn-login-titel { text-align: center; }
.kn-login-hinweis a { display: inline-flex; align-items: center; min-height: 44px; }
}
/* ------------------------------------------------------------------ Kündigen und Widerrufen (schlicht) --- */
.kn-recht main { max-width: 620px; margin: 0 auto; padding: 48px 20px 80px; }
.kn-recht h1 { margin: 0 0 8px; font-size: 28px; line-height: 34px; font-weight: 700; letter-spacing: -.02em; }
.kn-recht h2 { margin: 0 0 12px; font-size: 18px; line-height: 26px; font-weight: 600; }
.kn-markenzeile { margin-bottom: 8px; font-size: 13px; font-weight: 700; letter-spacing: .12em; color: var(--kn-text-2); }
.kn-recht .kn-formular { margin: 24px 0; }
.kn-recht label { display: block; margin: 16px 0 6px; font-size: 14px; font-weight: 600; }
.kn-recht label small { font-weight: 400; color: var(--kn-text-2); }
.kn-recht input:not([type="radio"]):not([type="checkbox"]), .kn-recht textarea {
width: 100%; min-height: 44px; padding: 0 12px; border: 0; border-radius: var(--kn-radius); background: var(--kn-bg);
box-shadow: var(--kn-ring); color: inherit; font: inherit; font-size: 16px; outline: none; transition: box-shadow .12s;
}
.kn-recht textarea { min-height: 96px; padding: 8px 12px; resize: vertical; }
.kn-recht input:not([type="radio"]):not([type="checkbox"]):focus, .kn-recht textarea:focus { box-shadow: 0 0 0 1px var(--kn-accent), 0 0 0 4px var(--kn-accent-ring); }
.kn-recht input::placeholder { color: var(--kn-text-3); }
.kn-recht fieldset { margin: 0; padding: 0; border: 0; min-width: 0; }
.kn-recht legend { margin: 16px 0 6px; padding: 0; font-size: 14px; font-weight: 600; }
.kn-recht .kn-auswahl { display: flex; align-items: center; gap: 10px; min-height: 44px; margin: 0; font-weight: 400; font-size: 16px; cursor: pointer; }
.kn-recht .kn-auswahl input { flex: 0 0 auto; width: 20px; height: 20px; margin: 0; accent-color: var(--kn-accent); }
.kn-recht #effective-date { max-width: 220px; margin-top: 6px; }
.kn-hinweis { margin: 8px 0 0; font-size: 14px; line-height: 20px; color: var(--kn-text-2); }
.kn-recht .kn-fehlertext { margin: 16px 0 0; }
.kn-absenden {
display: inline-flex; align-items: center; justify-content: center; min-height: 44px; margin-top: 20px; padding: 0 24px; border: 0; border-radius: var(--kn-radius);
background: var(--kn-accent); color: #fff; font: inherit; font-weight: 600; cursor: pointer; box-shadow: var(--kn-shadow-btn);
}
.kn-absenden:hover:not(:disabled) { background: var(--kn-accent-hover); }
.kn-bestaetigung { margin: 24px 0; padding-top: 24px; border-top: 1px solid var(--kn-line); }
.kn-bestaetigung h2 { color: var(--kn-gruen-fg); }
.kn-recht dl { display: grid; grid-template-columns: max-content 1fr; gap: 4px 16px; margin: 12px 0; }
.kn-recht dt { color: var(--kn-text-2); }
.kn-recht dd { margin: 0; overflow-wrap: anywhere; }
.kn-drucken {
display: inline-flex; align-items: center; min-height: 44px; margin-top: 14px; padding: 0 16px; border: 0; border-radius: var(--kn-radius);
background: var(--kn-bg); box-shadow: var(--kn-ring); color: inherit; font: inherit; font-weight: 500; cursor: pointer;
}
.kn-drucken:hover { background: var(--kn-bg-hover); }
@media print { .kn-drucken, .no-print { display: none !important; } }
/* Installierte App (Homescreen): Platz für die Statusleiste und ein Weg zurück in den Kundenbereich */
.kn-zurueck { display: none; }
@media (display-mode: standalone) {
.kn-recht main { padding-top: max(48px, calc(env(safe-area-inset-top) + 20px)); }
/* Die Statusleiste ist hell: ein weißer Streifen dahinter hält Uhrzeit und Netz lesbar */
.kn-seite::before { content: ''; position: fixed; top: 0; left: 0; right: 0; z-index: 10; height: env(safe-area-inset-top); background: var(--kn-bg); }
.kn-seite a.kn-zurueck { display: inline-block; margin-bottom: 18px; font-size: 15px; font-weight: 600; text-decoration: none; }
}
@media (max-width: 640px) {
.kn-recht dl { grid-template-columns: 1fr; gap: 0; }
.kn-recht dd { margin-bottom: 8px; }
.kn-absenden { width: 100%; }
}
/* ------------------------------------------------------------------ Auftragsbestätigung (auftrag.html, Link aus der Mail) --- */
body.ab-seite {
--ab-grund: #ffffff; --ab-karte: #ffffff; --ab-text: #1a1f36; --ab-weich: #5e6678; --ab-linie: #ebeef1; --ab-box: #f8f9fb;
--ab-knopf: #111111; --ab-knopf-text: #ffffff; --ab-fokus: #111111;
--ab-warn-bg: #fcedb9; --ab-warn-text: #a82c00; --ab-ok-bg: #d7f7c2; --ab-ok-text: #006908;
--ab-rot-bg: #ffe7f2; --ab-rot-text: #b3093c; --ab-muted-bg: #ebeef1; --ab-muted-text: #414552; --ab-fehler: #c0123c;
color-scheme: light;
}
/* Grundlayout */
body.ab-seite {
margin: 0; min-height: 100dvh; display: flex; flex-direction: column;
background: var(--ab-grund); color: var(--ab-text);
font: 16px/1.55 -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif; -webkit-text-size-adjust: 100%; -webkit-font-smoothing: antialiased;
}
.ab-seite .hidden { display: none !important; }
.ab-seite .ab-innen { max-width: 40rem; margin: 0 auto; padding: 0 16px; box-sizing: border-box; width: 100%; }
.ab-seite .ab-kopf { padding: max(20px, env(safe-area-inset-top)) 0 12px; }
.ab-seite .ab-kopf .ab-innen { display: flex; align-items: center; gap: 10px; }
.ab-seite .ab-logo { width: 36px; height: 36px; border-radius: 6px; display: block; }
.ab-seite .ab-marke { font-weight: 700; letter-spacing: 0.08em; font-size: 15px; }
.ab-seite .ab-haupt { flex: 1 0 auto; padding-bottom: 24px; }
.ab-seite .ab-wurzel { border-top: 1px solid var(--ab-linie); padding: 24px 0; overflow-wrap: anywhere; word-break: normal; }
.ab-seite .ab-laden { margin: 0; color: var(--ab-weich); }
.ab-seite a { color: inherit; text-underline-offset: 3px; }
.ab-seite .ab-vh {
position: absolute; width: 1px; height: 1px; margin: -1px; padding: 0; overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; border: 0;
}
.ab-seite .ab-fuss { flex-shrink: 0; padding: 8px 0 max(24px, env(safe-area-inset-bottom)); font-size: 13px; color: var(--ab-weich); }
.ab-seite .ab-fuss .ab-innen { display: flex; gap: 16px; flex-wrap: wrap; }
.ab-seite .ab-fuss a { color: var(--ab-weich); display: inline-flex; align-items: center; min-height: 44px; }
/* Inhalt */
.ab-seite .ab-pille {
display: inline-block; margin: 0 0 12px; padding: 2px 8px; border-radius: 4px; font-size: 12px; font-weight: 500; line-height: 20px;
background: var(--ab-muted-bg); color: var(--ab-muted-text);
}
.ab-seite .ab-pille-warn { background: var(--ab-warn-bg); color: var(--ab-warn-text); }
.ab-seite .ab-pille-ok { background: var(--ab-ok-bg); color: var(--ab-ok-text); }
.ab-seite .ab-pille-rot { background: var(--ab-rot-bg); color: var(--ab-rot-text); }
.ab-seite .ab-h1 { font-size: 1.6rem; line-height: 1.25; font-weight: 700; letter-spacing: -.02em; margin: 0 0 12px; }
.ab-seite .ab-h1:focus { outline: none; }
.ab-seite .ab-intro, .ab-seite .ab-text { margin: 0 0 16px; }
.ab-seite .ab-text { white-space: pre-line; }
.ab-seite .ab-kontakt { margin: 0 0 16px; color: var(--ab-weich); }
.ab-seite .ab-h2 {
font-size: 0.8rem; text-transform: uppercase; letter-spacing: 0.06em; font-weight: 700; color: var(--ab-weich); margin: 24px 0 8px;
}
.ab-seite .ab-liste { margin: 0 0 8px; padding: 0; }
.ab-seite .ab-zeile {
display: grid; grid-template-columns: 9rem 1fr; gap: 4px 12px; padding: 8px 0; border-top: 1px solid var(--ab-linie);
}
.ab-seite .ab-zeile dt { color: var(--ab-weich); }
.ab-seite .ab-zeile dd { margin: 0; }
.ab-seite .ab-gesamt { display: flex; justify-content: space-between; gap: 12px; margin: 12px 0 8px; padding-top: 12px; border-top: 2px solid var(--ab-text); }
.ab-seite .ab-gesamt strong { font-weight: 700; font-variant-numeric: tabular-nums; }
.ab-seite .ab-hinweis { margin: 4px 0 0; font-size: 0.85rem; color: var(--ab-weich); }
.ab-seite .ab-links { list-style: none; margin: 0 0 16px; padding: 0; }
.ab-seite .ab-links a { display: flex; align-items: center; min-height: 44px; line-height: 1.35; padding: 4px 0; font-weight: 600; }
.ab-seite .ab-bestaetigt {
background: var(--ab-ok-bg); color: var(--ab-ok-text); border-radius: var(--kn-radius, 6px); padding: 12px 16px; margin: 0 0 16px;
}
.ab-seite .ab-bestaetigt p { margin: 2px 0; }
.ab-seite .ab-bestaetigt:focus-visible { outline: 2px solid var(--ab-fokus); outline-offset: 2px; }
/* Tabelle */
.ab-seite .ab-tabelle { width: 100%; border-collapse: collapse; font-size: 0.95rem; font-variant-numeric: tabular-nums; }
.ab-seite .ab-tabelle th { text-align: left; font-weight: 600; color: var(--ab-weich); padding: 6px 8px 6px 0; border-bottom: 1px solid var(--ab-linie); }
.ab-seite .ab-tabelle td { padding: 8px 8px 8px 0; border-bottom: 1px solid var(--ab-linie); vertical-align: top; }
.ab-seite .ab-tabelle th:nth-child(n+2), .ab-seite .ab-tabelle td:nth-child(n+2) { text-align: right; white-space: nowrap; }
@media (max-width: 640px) {
.ab-seite .ab-tabelle thead {
position: absolute; width: 1px; height: 1px; margin: -1px; overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap;
}
.ab-seite .ab-tabelle, .ab-seite .ab-tabelle tbody, .ab-seite .ab-tabelle tr, .ab-seite .ab-tabelle td { display: block; }
.ab-seite .ab-tabelle tr { padding: 8px 0; border-bottom: 1px solid var(--ab-linie); }
.ab-seite .ab-tabelle td {
display: flex; justify-content: space-between; gap: 12px; border: 0; padding: 2px 0; text-align: right; white-space: normal;
}
.ab-seite .ab-tabelle td:first-child { display: block; text-align: left; font-weight: 700; }
.ab-seite .ab-tabelle td:nth-child(n+2) { white-space: normal; }
.ab-seite .ab-tabelle td:nth-child(n+2)::before { content: attr(data-label); color: var(--ab-weich); text-align: left; }
.ab-seite .ab-zeile { grid-template-columns: 1fr; gap: 0; }
.ab-seite .ab-wurzel { padding: 20px 0; }
}
/* Formular */
.ab-seite .ab-bestaetigen { margin-top: 24px; padding-top: 8px; border-top: 1px solid var(--ab-linie); }
.ab-seite .ab-feldgruppe { border: 0; margin: 0; padding: 0; min-width: 0; }
.ab-seite .ab-feldgruppe legend { font-weight: 700; padding: 0; margin: 12px 0 4px; }
.ab-seite .ab-haken { display: flex; gap: 12px; align-items: flex-start; padding: 10px 0; min-height: 44px; font-size: 15px; cursor: pointer; }
.ab-seite .ab-haken input { flex: 0 0 22px; width: 22px; height: 22px; margin: 1px 0 0; accent-color: var(--ab-knopf); }
.ab-seite .ab-frei { color: var(--ab-weich); font-size: 0.85em; }
.ab-seite .ab-fehler { margin: 8px 0; color: var(--ab-fehler); font-weight: 600; }
.ab-seite .ab-fehler[hidden] { display: none; }
.ab-seite .ab-knopf {
display: inline-block; min-height: 44px; padding: 0 24px; border: 0; border-radius: var(--kn-radius, 6px); cursor: pointer;
background: var(--ab-knopf); color: var(--ab-knopf-text); font: inherit; font-weight: 600; box-shadow: 0 1px 1px rgba(0, 0, 0, .12), 0 2px 5px rgba(60, 66, 87, .08);
}
.ab-seite .ab-knopf:disabled { opacity: 0.45; cursor: default; }
.ab-seite .ab-knopf:focus-visible, .ab-seite .ab-link:focus-visible { outline: 2px solid var(--ab-fokus); outline-offset: 2px; }
.ab-seite .ab-notiz, .ab-seite .ab-gesperrt { margin: 12px 0 0; font-size: 0.85rem; color: var(--ab-weich); }
@media (max-width: 640px) {
.ab-seite .ab-knopf { width: 100%; }
}
/* Installierte App: Weg zurück in den Kundenbereich */
.ab-app-back { display: none; }
@media (display-mode: standalone) {
.ab-app-back {
display: inline-block; margin: calc(env(safe-area-inset-top) + 12px) 16px 0; font-size: 15px; font-weight: 600; color: var(--ab-text); text-decoration: none;
}
.ab-seite .ab-kopf { padding-top: 12px; }
}
+48
View File
@@ -0,0 +1,48 @@
<!DOCTYPE html>
<html lang="de">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, viewport-fit=cover">
<meta name="theme-color" content="#ffffff">
<title>Offline – WEBklar Kundenbereich</title>
<!-- Wird vom Service Worker (sw.js) ohne Internet angezeigt: alles inline, keine weiteren Dateien -->
<style>
html, body { height: 100%; margin: 0; }
body {
display: grid; place-items: center; padding: 24px; box-sizing: border-box;
background: #fff; color: #1a1f36;
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
text-align: center; line-height: 1.5; -webkit-font-smoothing: antialiased;
}
main { max-width: 340px; }
svg { width: 72px; height: 72px; border-radius: 12px; box-shadow: 0 0 0 1px rgba(60, 66, 87, 0.16), 0 1px 1px rgba(0, 0, 0, 0.06); }
h1 { margin: 22px 0 8px; font-size: 22px; font-weight: 700; letter-spacing: -0.02em; }
p { margin: 0 0 26px; color: #5e6678; font-size: 15px; }
button {
min-height: 44px; padding: 0 24px; border: none; border-radius: 6px; background: #111; color: #fff;
font: inherit; font-weight: 600; font-size: 15px; cursor: pointer;
box-shadow: 0 1px 1px rgba(0, 0, 0, 0.12), 0 2px 5px rgba(60, 66, 87, 0.08);
}
button:active { opacity: 0.85; }
button:focus-visible { outline: 2px solid #111; outline-offset: 2px; }
</style>
</head>
<body>
<main>
<svg viewBox="0 0 512 512" aria-hidden="true">
<rect width="512" height="512" fill="#fff"/>
<g transform="translate(256 256) scale(0.9) translate(-256 -256)">
<ellipse cx="256" cy="256" rx="210" ry="150" fill="#111" transform="rotate(-28 256 256)"/>
<circle cx="356" cy="172" r="18" fill="#fff"/>
<path transform="translate(256 256) rotate(-28)" d="M 0,-170 C 22,-105 60,-72 120,-56 C 60,-40 22,-8 0,64 C -22,-8 -60,-40 -120,-56 C -60,-72 -22,-105 0,-170 Z" fill="#fff"/>
</g>
</svg>
<h1>Keine Internetverbindung</h1>
<p>Der Kundenbereich braucht eine Verbindung. Sobald du wieder online bist, geht es hier weiter.</p>
<button type="button" onclick="location.reload()">Erneut versuchen</button>
</main>
<script>
window.addEventListener('online', () => location.reload())
</script>
</body>
</html>
+65
View File
@@ -0,0 +1,65 @@
<!DOCTYPE html>
<html lang="de">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, viewport-fit=cover">
<meta name="theme-color" content="#ffffff">
<title>Vertrag widerrufen – WEBklar</title>
<link rel="stylesheet" href="/neu/tokens.css">
<link rel="stylesheet" href="/neu/oeffentlich/oeffentlich.css">
</head>
<body class="kn kn-seite kn-recht">
<main>
<!-- Nur in der installierten App sichtbar: dort gibt es keinen Zurück-Knopf des Browsers -->
<a class="kn-zurueck no-print" href="/dashboard.html">‹ Zurück zum Kundenbereich</a>
<div class="kn-markenzeile">WEBKLAR</div>
<h1>Vertrag widerrufen</h1>
<form id="decl-form" class="kn-formular" novalidate>
<!-- § 356a BGB: welche Bestellung widerrufen wird (jede hat ihre eigene Widerrufsfrist) -->
<fieldset>
<legend data-label-for="order" data-label="Vertrag">Vertrag</legend>
<label class="kn-auswahl"><input type="radio" name="order" value="abschluss" data-text="Abschluss meines Abos" checked> Abschluss meines Abos</label>
<label class="kn-auswahl"><input type="radio" name="order" value="wechsel" data-text="Wechsel ins Wartungs-Abo"> Wechsel ins Wartungs-Abo</label>
<label class="kn-auswahl"><input type="radio" name="order" value="zusatzspeicher" data-text="E-Mail-Zusatzspeicher"> E-Mail-Zusatzspeicher</label>
<label class="kn-auswahl"><input type="radio" name="order" value="funktion" data-text="Funktion (z. B. News-Pinnwand)"> Funktion (z. B. News-Pinnwand)</label>
<label class="kn-auswahl"><input type="radio" name="order" value="termine" data-text="Terminplaner (Einrichtung)"> Terminplaner (Einrichtung)</label>
<label class="kn-auswahl"><input type="radio" name="order" value="shop" data-text="Webshop (Einrichtung)"> Webshop (Einrichtung)</label>
<label class="kn-auswahl"><input type="radio" name="order" value="zeiten" data-text="Öffnungszeiten live"> Öffnungszeiten live</label>
<label class="kn-auswahl"><input type="radio" name="order" value="projekt" data-text="Projektauftrag (Auftragsbestätigung)"> Projektauftrag (Auftragsbestätigung)</label>
</fieldset>
<!-- Nur bei „Projektauftrag“ und freiwillig (auftrag-widerruf.js blendet das Feld sonst aus und leert es); ohne das Skript bleibt es sichtbar -->
<div id="ab-nummer-wrap">
<label for="abNummer" data-label-for="abNummer" data-label="Nummer der Auftragsbestätigung">Nummer der Auftragsbestätigung <small>(falls zur Hand, zum Beispiel AB-2026-0001)</small></label>
<input id="abNummer" name="abNummer" maxlength="20" autocomplete="off" autocapitalize="characters" spellcheck="false" placeholder="AB-2026-0001">
</div>
<label for="name" data-label-for="name" data-label="Name">Name</label>
<input id="name" name="name" autocomplete="name" required maxlength="200">
<label for="customerNumber" data-label-for="customerNumber" data-label="Kundennummer">Kundennummer <small>(optional)</small></label>
<input id="customerNumber" name="customerNumber" maxlength="50">
<label for="email" data-label-for="email" data-label="E-Mail für die Eingangsbestätigung">E-Mail-Adresse <small>(dorthin schicken wir die Eingangsbestätigung)</small></label>
<input id="email" name="email" type="email" autocomplete="email" required maxlength="255">
<p class="kn-fehlertext hidden" id="form-error"></p>
<button type="submit" class="kn-absenden">Widerruf bestätigen</button>
</form>
<div id="done" class="kn-bestaetigung hidden">
<h2>Ihr Widerruf ist bei uns eingegangen</h2>
<p>Eingang: <strong id="done-at"></strong></p>
<dl id="done-list"></dl>
<p>Eine Eingangsbestätigung mit diesen Angaben haben wir an Ihre E-Mail-Adresse geschickt.</p>
<button type="button" class="kn-drucken" id="print-btn">Diese Bestätigung drucken oder als PDF speichern</button>
</div>
</main>
<script src="/legal-form.js"></script>
<script>
initLegalForm({ endpoint: '/api/abo/public/withdraw', fields: ['order', 'abNummer', 'name', 'customerNumber', 'email'] })
</script>
<script type="module" src="/auftrag-widerruf.js"></script>
</body>
</html>