Rechtliches: elektronische Widerrufsfunktion (§ 356a BGB), Impressum, AGB,
Widerrufsbelehrung und Datenschutz
RECHERCHIERT, NICHT AUS DEM GEDAECHTNIS
Der Webdesign-Bereich hatte KEINE eigene Rechtsseite -- obwohl dort
Vertraege geschlossen und Zahlungen ausgeloest werden. Die Fusszeile
verwies auf die Kontaktseite des Universe, die ein Content-Projekt
beschreibt, kein Dienstleistungsgeschaeft.
Die Web-Recherche hat eine Pflicht zutage gefoerdert, die seit zwei
Monaten gilt und die die Seite nicht erfuellt hat:
§ 356a BGB -- elektronische Widerrufsfunktion, in Kraft seit
19.06.2026. Sie gilt fuer ALLE Fernabsatzvertraege, die ueber eine
Online-Benutzeroberflaeche geschlossen werden, nicht nur fuer
Finanzdienstleistungen.
WARUM DAS HIER GILT: Im Kundenportal nimmt der Kunde Zusatzangebote per
Klick VERBINDLICH an ("Damit nimmst du das Zusatzangebot verbindlich
an"). Das ist ein Vertragsschluss ueber eine Online-Benutzeroberflaeche.
Fuer die geplanten PayPal-Zahlungen gilt dasselbe.
Der Anbieter sitzt in Luxemburg. Fuer Verbraucher mit gewoehnlichem
Aufenthalt in Deutschland gilt nach Art. 6 Rom-I-VO trotzdem das
deutsche zwingende Verbraucherrecht, weil die Seite sich erkennbar an den
deutschsprachigen Markt richtet. Deshalb wurde der STRENGERE Standard
umgesetzt, nicht der bequemere.
WAS GEBAUT WURDE
webdesign/widerruf.html -- die Widerrufsfunktion, genau nach dem
Wortlaut der Vorschrift:
* Beschriftung "Vertrag widerrufen" (gesetzlich vorgegeben)
* ZWEITE Schaltflaeche "Widerruf bestaetigen" (ebenfalls vorgegeben --
ein einstufiges Formular wuerde die Vorschrift nicht erfuellen)
* unverzuegliche Eingangsbestaetigung mit Nummer, ZEITPUNKT (nicht nur
Datum), Empfaenger und dem Wortlaut der Erklaerung
* Link in der Fusszeile JEDER Seite -- "staendig verfuegbar,
hervorgehoben platziert, leicht zugaenglich" heisst nicht "in den AGB
versteckt"
Drei Entscheidungen, die unmittelbar aus dem Sinn der Vorschrift folgen:
KEINE ANMELDUNG. Die Seite ist von der Zugangswand ausgenommen. Wer
seinen Code verlegt hat, wuerde sonst seine Frist verlieren --
Fristverlust durch eine selbstgebaute Huerde ist der schlimmste
denkbare Fall.
KEINE MENGENBEGRENZUNG. Ueberall sonst richtig, hier ein
Rechtsverlust: Wer in der letzten Stunde seiner Frist wegen eines
hakenden Netzes dreimal klickt, darf nicht abgewiesen werden.
KEIN ZWISCHENSPEICHER. Beim Anfrageformular ein Segen, hier eine
Falle: Auf einem geteilten Rechner laege der halb ausgefuellte Widerruf
des einen im Browser des naechsten.
webdesign/rechtliches.html -- Impressum, AGB, Widerrufsbelehrung samt
Muster-Widerrufsformular, Datenschutzerklaerung. Alle Betreiberangaben
sind aus den bestehenden Seiten UEBERNOMMEN, nichts erfunden: Anschrift
in Mondercange, Kleinunternehmerregelung nach Art. 57 TVA-Gesetz LU und
Richtlinie (EU) 2020/285, netcup/Cloudflare, PayPal Luxemburg.
Migration 0014: wd_widerrufe (der dauerhafte Datentraeger und der
Nachweis, wird nie geleert) und wd_zustimmungen. Letztere speichert nicht
nur den Haken, sondern den WORTLAUT, den der Kunde gesehen hat -- die
Beweislast fuer die Zustimmung zum vorzeitigen Leistungsbeginn liegt beim
Unternehmer (§ 356 Abs. 4 BGB), und im Streit zaehlt, WAS bestaetigt
wurde. Bewusst OHNE Fremdschluessel: CASCADE wuerde den Nachweis mit dem
Kunden mitloeschen, RESTRICT wuerde eine DSGVO-Loeschung blockieren.
Druckansicht: Die Eingangsbestaetigung ist ein Rechtsnachweis. Auf Papier
schwarz auf weiss, ohne Navigation und Knoepfe -- ein Nachweis, den
niemand ausdruckt, weil er eine halbe Patrone kostet, erfuellt seinen
Zweck nicht.
ZWEI ECHTE FEHLER GEFUNDEN
1. Die Pflicht-Sternchen verschwanden nach der Uebersetzung. Ursache:
data-i18n stand auf dem <label> selbst und ersetzte dessen ganzen
Inhalt -- samt <span class="wd-pflicht">. Das Anfrageformular macht es
laengst richtig (data-i18n auf einem INNEREN span); die neue Seite
hatte das Muster nicht uebernommen. Aufgefallen ist es nur, weil der
Test die Pflichtfelder GEZAEHLT hat statt sie vorauszusetzen.
2. window.WD.sprache() gibt es nicht, die Funktion heisst getSprache().
Dadurch brach der Uebergang zur zweiten Stufe stumm ab.
GEPRUEFT: 58 Pruefungen, alle bestanden. Darunter die gesetzlich
vorgegebenen Beschriftungen in allen FUENF Sprachen, "kein Grund noetig",
genau drei Pflichtfelder, Datum UND Uhrzeit in der Bestaetigung, und die
Druckansicht.
WAS FILIPE NOCH PRUEFEN LASSEN MUSS: Diese Texte sind sorgfaeltig
recherchiert, aber ich bin keine Rechtsanwaeltin. Vor dem oeffentlichen
Start gehoert das Ganze einmal zu einer im luxemburgischen Recht
qualifizierten Fachperson -- besonders die grenzueberschreitende Lage
(Sitz LU, Kunden in DE/CH/FR/PT) und die Frage, ob die Seite in
Franzoesisch und Portugiesisch aktiv verkaufen soll. Dann muessen die
Rechtstexte auch dorthin uebersetzt werden, und zwar fachlich.
Versionsstempel und Cache-Name auf v7.
Co-Authored-By: Claude Opus 5 <[email protected]>
This commit is contained in:
@@ -1542,3 +1542,159 @@ button.po-aufg-haken:hover { border-color: var(--wd-gold); background: rgba(232,
|
|||||||
margin: .8rem 0 .2rem; padding-right: .3rem;
|
margin: .8rem 0 .2rem; padding-right: .3rem;
|
||||||
display: flex; flex-direction: column; gap: .5rem;
|
display: flex; flex-direction: column; gap: .5rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* =====================================================================
|
||||||
|
WIDERRUF (§ 356a BGB) — und die Druckansicht der Bestätigung
|
||||||
|
|
||||||
|
Gestalterisch bewusst ZURÜCKHALTEND. Alles andere im Bereich darf
|
||||||
|
Stimmung machen; diese Seite nicht. Wer hier landet, will eine Sache
|
||||||
|
erledigen und danach Gewissheit haben. Grosse Effekte würden hier wie
|
||||||
|
Ablenkung wirken — und der Vorwurf, man wolle den Widerruf erschweren,
|
||||||
|
ist genau der, den man am wenigsten gebrauchen kann.
|
||||||
|
===================================================================== */
|
||||||
|
.wr-h2 { font-size: 1.12rem; margin: 0 0 .5rem; }
|
||||||
|
|
||||||
|
.wr-info {
|
||||||
|
border-left: 3px solid var(--wd-blau);
|
||||||
|
margin-bottom: 1.4rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.wr-pruef {
|
||||||
|
display: grid; grid-template-columns: minmax(0,.42fr) minmax(0,1fr);
|
||||||
|
gap: .45rem .9rem; margin: .9rem 0 1.1rem;
|
||||||
|
padding: .9rem 1rem; border-radius: 12px;
|
||||||
|
background: rgba(127,208,232,.05); border: 1px solid var(--wd-rand);
|
||||||
|
}
|
||||||
|
@media (max-width: 520px) { .wr-pruef { grid-template-columns: 1fr; gap: .1rem; } }
|
||||||
|
.wr-pruef dt { color: var(--wd-text-soft); font-size: .88rem; }
|
||||||
|
.wr-pruef dd { margin: 0; color: var(--wd-text); overflow-wrap: anywhere; white-space: pre-wrap; }
|
||||||
|
@media (max-width: 520px) { .wr-pruef dd { margin-bottom: .55rem; } }
|
||||||
|
|
||||||
|
/* Der Bestätigungsschritt hebt sich ab — hier passiert das Verbindliche. */
|
||||||
|
.wr-bestaetigen { border-color: var(--wd-rand-stark); }
|
||||||
|
|
||||||
|
/* Grün statt Gold für den Abschluss: Der Widerruf ist erfolgreich
|
||||||
|
erklärt, das ist aus Sicht des Kunden eine gute Nachricht. Gold ist im
|
||||||
|
Bereich für "das betrifft dich, tu was" reserviert — hier ist nichts
|
||||||
|
mehr zu tun. */
|
||||||
|
.wr-fertig { border-color: rgba(111,214,163,.42); }
|
||||||
|
.wr-haken {
|
||||||
|
width: 46px; height: 46px; border-radius: 50%;
|
||||||
|
display: grid; place-items: center; line-height: 1;
|
||||||
|
font-size: 1.5rem; margin-bottom: .7rem;
|
||||||
|
background: rgba(111,214,163,.14); color: var(--wd-ok);
|
||||||
|
border: 1px solid rgba(111,214,163,.4);
|
||||||
|
}
|
||||||
|
|
||||||
|
.wr-beleg {
|
||||||
|
margin: 1rem 0; padding: 1.1rem 1.2rem;
|
||||||
|
border-radius: 14px;
|
||||||
|
background: rgba(8,14,24,.55);
|
||||||
|
border: 1px solid var(--wd-rand-stark);
|
||||||
|
}
|
||||||
|
.wr-beleg-kopf {
|
||||||
|
display: flex; flex-wrap: wrap; gap: .5rem; align-items: baseline;
|
||||||
|
justify-content: space-between;
|
||||||
|
padding-bottom: .6rem; margin-bottom: .7rem;
|
||||||
|
border-bottom: 1px solid var(--wd-rand);
|
||||||
|
}
|
||||||
|
.wr-beleg-nr { font-family: var(--font-head); font-weight: 800; color: var(--wd-blau); }
|
||||||
|
.wr-beleg dl {
|
||||||
|
display: grid; grid-template-columns: minmax(0,.42fr) minmax(0,1fr);
|
||||||
|
gap: .45rem .9rem; margin: 0;
|
||||||
|
}
|
||||||
|
@media (max-width: 520px) { .wr-beleg dl { grid-template-columns: 1fr; gap: .1rem; } }
|
||||||
|
.wr-beleg dt { color: var(--wd-text-soft); font-size: .86rem; }
|
||||||
|
.wr-beleg dd { margin: 0; overflow-wrap: anywhere; }
|
||||||
|
@media (max-width: 520px) { .wr-beleg dd { margin-bottom: .55rem; } }
|
||||||
|
|
||||||
|
/* ---------------------------------------------------------------------
|
||||||
|
DRUCKANSICHT
|
||||||
|
|
||||||
|
Die Eingangsbestätigung ist ein Rechtsnachweis. Ein Ausdruck mit
|
||||||
|
dunklem Hintergrund verbraucht eine halbe Patrone und ist auf Papier
|
||||||
|
schlecht lesbar — und ein Nachweis, den niemand ausdruckt, weil er
|
||||||
|
hässlich wird, erfüllt seinen Zweck nicht.
|
||||||
|
|
||||||
|
Deshalb: auf Papier schwarz auf weiss, ohne Navigation, ohne Knöpfe,
|
||||||
|
nur der Beleg. Adressen werden ausgeschrieben, weil ein Link auf
|
||||||
|
Papier nichts nützt.
|
||||||
|
--------------------------------------------------------------------- */
|
||||||
|
@media print {
|
||||||
|
body.wd { background: #fff !important; color: #000 !important; }
|
||||||
|
.wd-kopf, #wd-kopf, .wd-fuss, #wd-fuss,
|
||||||
|
.wr-nicht-drucken, .wr-info, #wr-form, #wr-schritt2,
|
||||||
|
.wd-meldung, .wd-eyebrow { display: none !important; }
|
||||||
|
|
||||||
|
.wd-abschnitt { padding: 0 !important; }
|
||||||
|
.wd-karte, .wr-beleg {
|
||||||
|
background: #fff !important; border: 1px solid #000 !important;
|
||||||
|
box-shadow: none !important; color: #000 !important;
|
||||||
|
}
|
||||||
|
.wr-fertig, .wr-beleg { page-break-inside: avoid; }
|
||||||
|
.wr-haken { border-color: #000 !important; color: #000 !important; background: #fff !important; }
|
||||||
|
.wr-beleg-nr, .wr-h2, h1, h2, dt, dd, p { color: #000 !important; }
|
||||||
|
.wr-beleg dt { color: #444 !important; }
|
||||||
|
|
||||||
|
/* Adressen ausschreiben — ein Link ist auf Papier wertlos. */
|
||||||
|
a[href^="http"]::after { content: " (" attr(href) ")"; font-size: .85em; }
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ---------------------------------------------------------------------
|
||||||
|
RECHTSSEITE
|
||||||
|
|
||||||
|
Lange Texte, die man liest statt überfliegt. Deshalb: schmalere
|
||||||
|
Zeilen, mehr Zeilenabstand, klare Absätze. Und KEIN Blocksatz --
|
||||||
|
der reisst im Deutschen wegen der langen Wörter Löcher in die Zeile
|
||||||
|
und macht genau die Texte schwerer lesbar, bei denen es auf jedes
|
||||||
|
Wort ankommt.
|
||||||
|
--------------------------------------------------------------------- */
|
||||||
|
.rl-nav {
|
||||||
|
display: flex; flex-wrap: wrap; gap: .45rem;
|
||||||
|
margin: 1.2rem 0 1.6rem;
|
||||||
|
}
|
||||||
|
.rl-nav a {
|
||||||
|
padding: .42rem .85rem; border-radius: 999px;
|
||||||
|
border: 1px solid var(--wd-rand); background: var(--wd-glas);
|
||||||
|
font-size: .88rem; color: var(--wd-text-mid) !important; text-decoration: none;
|
||||||
|
}
|
||||||
|
.rl-nav a:hover { border-color: var(--wd-rand-stark); color: var(--wd-text) !important; }
|
||||||
|
|
||||||
|
.rl-sprache { margin-bottom: 1.8rem; }
|
||||||
|
|
||||||
|
.rl-h2 {
|
||||||
|
margin-top: 2.4rem;
|
||||||
|
/* Ein Sprungziel darf nicht unter der klebenden Kopfzeile landen. */
|
||||||
|
scroll-margin-top: 5.5rem;
|
||||||
|
}
|
||||||
|
.rl-text { line-height: 1.75; }
|
||||||
|
.rl-text h3 {
|
||||||
|
font-size: 1rem; margin: 1.5rem 0 .5rem;
|
||||||
|
color: var(--wd-blau);
|
||||||
|
}
|
||||||
|
.rl-text h3:first-child { margin-top: 0; }
|
||||||
|
.rl-text p { margin: 0 0 .8rem; }
|
||||||
|
.rl-text ul { margin: 0 0 .9rem; padding-left: 1.2rem; }
|
||||||
|
.rl-text li { margin-bottom: .35rem; }
|
||||||
|
.rl-text code {
|
||||||
|
font-size: .86em; padding: .1rem .35rem; border-radius: 5px;
|
||||||
|
background: rgba(127,208,232,.09); color: var(--wd-blau);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Die Belehrung hebt sich ab: Sie ist der Teil, auf den es rechtlich
|
||||||
|
ankommt, und muss auch optisch als geschlossener Block erkennbar sein. */
|
||||||
|
.rl-widerruf { border-left: 3px solid var(--wd-gold); }
|
||||||
|
.rl-adresse {
|
||||||
|
padding: .7rem .9rem; border-radius: 10px;
|
||||||
|
background: rgba(127,208,232,.05); border: 1px solid var(--wd-rand);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Das Muster-Formular sieht bewusst wie ein Formular aus — man soll
|
||||||
|
sehen, dass man es ausdrucken und ausfüllen kann. */
|
||||||
|
.rl-formular {
|
||||||
|
margin-top: .6rem; padding: 1rem 1.1rem;
|
||||||
|
border-radius: 12px; border: 1px dashed var(--wd-rand-stark);
|
||||||
|
background: rgba(8,14,24,.4);
|
||||||
|
font-size: .93rem;
|
||||||
|
}
|
||||||
|
.rl-formular p { margin: 0 0 .6rem; }
|
||||||
|
|||||||
@@ -0,0 +1,223 @@
|
|||||||
|
/* =====================================================================
|
||||||
|
i18n-wd-widerruf.js — Elektronische Widerrufsfunktion, fünf Sprachen.
|
||||||
|
|
||||||
|
Zum Ton: Ein Widerruf ist für den Kunden eine unangenehme Situation,
|
||||||
|
und für den Anbieter eine schlechte Nachricht. Genau deshalb sind die
|
||||||
|
Texte hier BETONT freundlich und ohne jede Hürde formuliert.
|
||||||
|
|
||||||
|
Das ist nicht nur Höflichkeit, sondern auch Vorsicht: Wer den Eindruck
|
||||||
|
erweckt, den Widerruf erschweren zu wollen, riskiert genau den Ärger,
|
||||||
|
den er vermeiden will. Und rechtlich muss die Funktion ohnehin "leicht
|
||||||
|
zugänglich" sein — ein abweisender Ton wäre das Gegenteil.
|
||||||
|
|
||||||
|
Der Wortlaut "Vertrag widerrufen" bzw. seine Entsprechungen ist NICHT
|
||||||
|
frei gewählt: § 356a BGB schreibt diese Beschriftung (oder eine
|
||||||
|
gleichbedeutende) ausdrücklich vor.
|
||||||
|
===================================================================== */
|
||||||
|
window.I18N_WD = {
|
||||||
|
wr_titel: {
|
||||||
|
de: "Vertrag widerrufen — Dogfather Webdesign",
|
||||||
|
"de-CH": "Vertrag widerrüefe — Dogfather Webdesign",
|
||||||
|
en: "Withdraw from contract — Dogfather Web Design",
|
||||||
|
fr: "Rétracter le contrat — Dogfather Web Design",
|
||||||
|
pt: "Rescindir o contrato — Dogfather Web Design",
|
||||||
|
},
|
||||||
|
wr_eyebrow: { de: "Widerruf", "de-CH": "Widerrüef", en: "Withdrawal", fr: "Rétractation", pt: "Rescisão" },
|
||||||
|
wr_h1: {
|
||||||
|
de: "Vertrag widerrufen",
|
||||||
|
"de-CH": "Vertrag widerrüefe",
|
||||||
|
en: "Withdraw from contract",
|
||||||
|
fr: "Rétracter le contrat",
|
||||||
|
pt: "Rescindir o contrato",
|
||||||
|
},
|
||||||
|
wr_lead: {
|
||||||
|
de: "Hier kannst du deinen Vertrag ohne Angabe von Gründen widerrufen. Du bekommst sofort eine Bestätigung mit Zeitpunkt — zum Speichern und Ausdrucken.",
|
||||||
|
"de-CH": "Do chasch din Vertrag ohni Grund widerrüefe. Du überchunnsch sofort e Bestätigung mit Ziitpunkt — zum Speichere und Usdrucke.",
|
||||||
|
en: "Here you can withdraw from your contract without giving any reason. You will receive a confirmation with a timestamp immediately — to save and print.",
|
||||||
|
fr: "Ici tu peux te rétracter de ton contrat sans avoir à te justifier. Tu reçois immédiatement une confirmation horodatée — à enregistrer et à imprimer.",
|
||||||
|
pt: "Aqui podes rescindir o teu contrato sem indicar qualquer motivo. Recebes imediatamente uma confirmação com data e hora — para guardar e imprimir.",
|
||||||
|
},
|
||||||
|
|
||||||
|
wr_frist_h: {
|
||||||
|
de: "Wie lange habe ich Zeit?",
|
||||||
|
"de-CH": "Wie lang han ich Ziit?",
|
||||||
|
en: "How long do I have?",
|
||||||
|
fr: "De combien de temps est-ce que je dispose ?",
|
||||||
|
pt: "Quanto tempo tenho?",
|
||||||
|
},
|
||||||
|
wr_frist_t: {
|
||||||
|
de: "Die Widerrufsfrist beträgt <strong>14 Tage</strong> ab dem Tag des Vertragsschlusses. Zur Wahrung der Frist reicht es, dass du den Widerruf vor Ablauf der Frist absendest.",
|
||||||
|
"de-CH": "D Widerrüefsfrist isch <strong>14 Täg</strong> ab em Tag vom Vertragsschluss. Für d Frist z wahre, längts, wenn du de Widerrüef vor Ablauf abschicksch.",
|
||||||
|
en: "The withdrawal period is <strong>14 days</strong> from the day the contract was concluded. To meet the deadline it is enough that you send your withdrawal before the period expires.",
|
||||||
|
fr: "Le délai de rétractation est de <strong>14 jours</strong> à compter du jour de la conclusion du contrat. Pour respecter le délai, il suffit que tu envoies ta rétractation avant son expiration.",
|
||||||
|
pt: "O prazo de rescisão é de <strong>14 dias</strong> a contar do dia da celebração do contrato. Para cumprir o prazo, basta enviares a rescisão antes de este terminar.",
|
||||||
|
},
|
||||||
|
wr_frist_hinweis: {
|
||||||
|
de: "Wichtig bei Dienstleistungen: Wenn du ausdrücklich zugestimmt hast, dass vor Ablauf der Frist mit der Arbeit begonnen wird, und bestätigt hast, dass du dadurch dein Widerrufsrecht verlierst, erlischt es mit vollständiger Erbringung der Leistung. Bis dahin schuldest du bei einem Widerruf nur den Wert dessen, was bis zum Widerruf bereits geleistet wurde.",
|
||||||
|
"de-CH": "Wichtig bi Dienschtleischtige: Wenn du usdrücklich zuegstimmt häsch, dass vor Ablauf vo dr Frist mit dr Arbet aagfange wird, und bestätigt häsch, dass du demit dis Widerrüefsrächt verlürsch, erlöscht es mit vollständiger Erbringig vo dr Leischtig. Bis dete schuldsch bi eme Widerrüef nume dr Wert vo dem, wo bis derthi scho gleischtet worde isch.",
|
||||||
|
en: "Important for services: If you expressly agreed that work should begin before the period expires, and confirmed that you thereby lose your right of withdrawal, that right ends once the service has been fully performed. Until then, if you withdraw you only owe the value of what has already been performed.",
|
||||||
|
fr: "Important pour les prestations : si tu as expressément accepté que le travail commence avant l'expiration du délai et confirmé que tu perds ainsi ton droit de rétractation, celui-ci s'éteint lorsque la prestation est intégralement fournie. Jusque-là, en cas de rétractation, tu ne dois que la valeur de ce qui a déjà été fourni.",
|
||||||
|
pt: "Importante nos serviços: se concordaste expressamente que o trabalho começasse antes do fim do prazo e confirmaste que perdes assim o teu direito de rescisão, este extingue-se com a prestação integral do serviço. Até lá, em caso de rescisão, só deves o valor do que já foi prestado.",
|
||||||
|
},
|
||||||
|
wr_zur_belehrung: {
|
||||||
|
de: "Vollständige Widerrufsbelehrung lesen",
|
||||||
|
"de-CH": "Vollständigi Widerrüefsbelehrig läse",
|
||||||
|
en: "Read the full withdrawal policy",
|
||||||
|
fr: "Lire l'information complète sur la rétractation",
|
||||||
|
pt: "Ler a informação completa sobre rescisão",
|
||||||
|
},
|
||||||
|
|
||||||
|
wr_form_h: { de: "Deine Angaben", "de-CH": "Dini Aagabe", en: "Your details", fr: "Tes informations", pt: "Os teus dados" },
|
||||||
|
wr_form_t: {
|
||||||
|
de: "Wir brauchen nur drei Dinge: deinen Namen, um welchen Vertrag es geht, und wohin die Bestätigung soll.",
|
||||||
|
"de-CH": "Mir bruuched nume drü Sache: din Name, um welle Vertrag s got, und wohi d Bestätigung söll.",
|
||||||
|
en: "We only need three things: your name, which contract this concerns, and where the confirmation should go.",
|
||||||
|
fr: "Nous n'avons besoin que de trois choses : ton nom, le contrat concerné et l'adresse pour la confirmation.",
|
||||||
|
pt: "Só precisamos de três coisas: o teu nome, qual o contrato em causa e para onde enviar a confirmação.",
|
||||||
|
},
|
||||||
|
wr_name: { de: "Dein Name", "de-CH": "Din Name", en: "Your name", fr: "Ton nom", pt: "O teu nome" },
|
||||||
|
wr_vertrag: { de: "Welcher Vertrag?", "de-CH": "Welle Vertrag?", en: "Which contract?", fr: "Quel contrat ?", pt: "Qual contrato?" },
|
||||||
|
wr_vertrag_hilfe: {
|
||||||
|
de: "Die Projektnummer steht in deinem Angebot und im Portal. Eine Beschreibung reicht aber auch.",
|
||||||
|
"de-CH": "D Projektnummere stoht i dim Aagebot und im Portal. E Beschriibig längt aber au.",
|
||||||
|
en: "The project number is in your quote and in the portal. A description is fine too.",
|
||||||
|
fr: "Le numéro de projet figure sur ton devis et dans l'espace client. Une description suffit également.",
|
||||||
|
pt: "O número do projeto está no teu orçamento e no portal. Uma descrição também chega.",
|
||||||
|
},
|
||||||
|
wr_kontakt: {
|
||||||
|
de: "E-Mail für die Bestätigung",
|
||||||
|
"de-CH": "E-Mail für d Bestätigung",
|
||||||
|
en: "E-mail for the confirmation",
|
||||||
|
fr: "E-mail pour la confirmation",
|
||||||
|
pt: "E-mail para a confirmação",
|
||||||
|
},
|
||||||
|
wr_betrifft: {
|
||||||
|
de: "Betrifft nur einen Teil?",
|
||||||
|
"de-CH": "Betrifft nume en Teil?",
|
||||||
|
en: "Only part of it?",
|
||||||
|
fr: "Cela ne concerne qu'une partie ?",
|
||||||
|
pt: "Diz respeito apenas a uma parte?",
|
||||||
|
},
|
||||||
|
wr_freiwillig: { de: "freiwillig", "de-CH": "freiwillig", en: "optional", fr: "facultatif", pt: "opcional" },
|
||||||
|
wr_grund: { de: "Grund", "de-CH": "Grund", en: "Reason", fr: "Motif", pt: "Motivo" },
|
||||||
|
wr_grund_frei: {
|
||||||
|
de: "freiwillig — du musst keinen angeben",
|
||||||
|
"de-CH": "freiwillig — du muesch kein aagäh",
|
||||||
|
en: "optional — you do not have to give one",
|
||||||
|
fr: "facultatif — tu n'as pas à en donner",
|
||||||
|
pt: "opcional — não tens de indicar nenhum",
|
||||||
|
},
|
||||||
|
wr_weiter: {
|
||||||
|
de: "Weiter zur Bestätigung",
|
||||||
|
"de-CH": "Wiiter zur Bestätigung",
|
||||||
|
en: "Continue to confirmation",
|
||||||
|
fr: "Continuer vers la confirmation",
|
||||||
|
pt: "Continuar para a confirmação",
|
||||||
|
},
|
||||||
|
|
||||||
|
wr_pruef_h: {
|
||||||
|
de: "Bitte prüfen und bestätigen",
|
||||||
|
"de-CH": "Bitte prüefe und bestätige",
|
||||||
|
en: "Please check and confirm",
|
||||||
|
fr: "Merci de vérifier et de confirmer",
|
||||||
|
pt: "Verifica e confirma, por favor",
|
||||||
|
},
|
||||||
|
wr_pruef_t: {
|
||||||
|
de: "Das schickst du ab. Danach ist der Widerruf erklärt.",
|
||||||
|
"de-CH": "Das schicksch ab. Dernoch isch de Widerrüef erklärt.",
|
||||||
|
en: "This is what you are sending. After that the withdrawal is declared.",
|
||||||
|
fr: "Voici ce que tu envoies. Ensuite, la rétractation est déclarée.",
|
||||||
|
pt: "É isto que vais enviar. Depois disso, a rescisão está declarada.",
|
||||||
|
},
|
||||||
|
/* Wortlaut gesetzlich vorgegeben (§ 356a BGB): zweite Schaltfläche
|
||||||
|
"Widerruf bestätigen" oder gleichbedeutend. */
|
||||||
|
wr_bestaetigen: {
|
||||||
|
de: "Widerruf bestätigen",
|
||||||
|
"de-CH": "Widerrüef bestätige",
|
||||||
|
en: "Confirm withdrawal",
|
||||||
|
fr: "Confirmer la rétractation",
|
||||||
|
pt: "Confirmar a rescisão",
|
||||||
|
},
|
||||||
|
wr_aendern: { de: "Angaben ändern", "de-CH": "Aagabe ändere", en: "Change details", fr: "Modifier les informations", pt: "Alterar dados" },
|
||||||
|
wr_sendet: { de: "Wird gesendet…", "de-CH": "Wird gschickt…", en: "Sending…", fr: "Envoi…", pt: "A enviar…" },
|
||||||
|
|
||||||
|
wr_fertig_h: {
|
||||||
|
de: "Dein Widerruf ist eingegangen",
|
||||||
|
"de-CH": "Din Widerrüef isch aacho",
|
||||||
|
en: "Your withdrawal has been received",
|
||||||
|
fr: "Ta rétractation a bien été reçue",
|
||||||
|
pt: "A tua rescisão foi recebida",
|
||||||
|
},
|
||||||
|
wr_fertig_t: {
|
||||||
|
de: "Das hier ist deine Eingangsbestätigung. Speichere oder drucke sie — sie ist dein Nachweis, dass du rechtzeitig widerrufen hast.",
|
||||||
|
"de-CH": "Das do isch dini Iigangsbestätigung. Speicher oder druck si — si isch din Nochwiis, dass du rächtziitig widerrüefe häsch.",
|
||||||
|
en: "This is your acknowledgement of receipt. Save or print it — it is your proof that you withdrew in time.",
|
||||||
|
fr: "Voici ton accusé de réception. Enregistre-le ou imprime-le — c'est ta preuve que tu t'es rétracté dans les délais.",
|
||||||
|
pt: "Este é o teu comprovativo de receção. Guarda-o ou imprime-o — é a tua prova de que rescindiste a tempo.",
|
||||||
|
},
|
||||||
|
wr_beleg_titel: {
|
||||||
|
de: "Eingangsbestätigung Widerruf",
|
||||||
|
"de-CH": "Iigangsbestätigung Widerrüef",
|
||||||
|
en: "Acknowledgement of receipt — withdrawal",
|
||||||
|
fr: "Accusé de réception — rétractation",
|
||||||
|
pt: "Comprovativo de receção — rescisão",
|
||||||
|
},
|
||||||
|
wr_beleg_zeit: { de: "Eingegangen am", "de-CH": "Aacho am", en: "Received on", fr: "Reçu le", pt: "Recebido em" },
|
||||||
|
wr_beleg_an: { de: "Erklärt gegenüber", "de-CH": "Erklärt gegenüber", en: "Declared to", fr: "Déclarée à", pt: "Declarada a" },
|
||||||
|
wr_beleg_erklaerung: { de: "Erklärung", "de-CH": "Erklärig", en: "Declaration", fr: "Déclaration", pt: "Declaração" },
|
||||||
|
wr_beleg_satz: {
|
||||||
|
de: "Hiermit widerrufe ich den oben bezeichneten Vertrag.",
|
||||||
|
"de-CH": "Hiermit widerrüefe ich de obe bezeichnet Vertrag.",
|
||||||
|
en: "I hereby withdraw from the contract identified above.",
|
||||||
|
fr: "Je me rétracte par la présente du contrat désigné ci-dessus.",
|
||||||
|
pt: "Pela presente, rescindo o contrato acima identificado.",
|
||||||
|
},
|
||||||
|
wr_drucken: {
|
||||||
|
de: "Bestätigung drucken oder als PDF sichern",
|
||||||
|
"de-CH": "Bestätigung drucke oder als PDF sichere",
|
||||||
|
en: "Print the confirmation or save as PDF",
|
||||||
|
fr: "Imprimer la confirmation ou l'enregistrer en PDF",
|
||||||
|
pt: "Imprimir a confirmação ou guardar em PDF",
|
||||||
|
},
|
||||||
|
wr_zurueck_start: { de: "Zur Startseite", "de-CH": "Zur Startsite", en: "To the home page", fr: "Vers l'accueil", pt: "Para a página inicial" },
|
||||||
|
|
||||||
|
wr_alternativ: {
|
||||||
|
de: "Du kannst den Widerruf auch formlos per E-Mail erklären — dieses Formular ist nur der bequemere Weg, kein vorgeschriebener.",
|
||||||
|
"de-CH": "Du chasch de Widerrüef au formlos per E-Mail erkläre — das Formular isch nume dr bequemer Wäg, kein vorgschriebne.",
|
||||||
|
en: "You can also declare your withdrawal informally by e-mail — this form is just the more convenient way, not a required one.",
|
||||||
|
fr: "Tu peux aussi déclarer ta rétractation librement par e-mail — ce formulaire n'est que le moyen le plus pratique, pas une obligation.",
|
||||||
|
pt: "Também podes declarar a rescisão livremente por e-mail — este formulário é apenas a via mais cómoda, não uma obrigação.",
|
||||||
|
},
|
||||||
|
|
||||||
|
wr_fehlt_name: {
|
||||||
|
de: "Bitte gib deinen Namen an.",
|
||||||
|
"de-CH": "Bitte gib din Name aa.",
|
||||||
|
en: "Please enter your name.",
|
||||||
|
fr: "Merci d'indiquer ton nom.",
|
||||||
|
pt: "Indica o teu nome.",
|
||||||
|
},
|
||||||
|
wr_fehlt_vertrag: {
|
||||||
|
de: "Bitte gib an, um welchen Vertrag es geht.",
|
||||||
|
"de-CH": "Bitte gib aa, um welle Vertrag s got.",
|
||||||
|
en: "Please tell us which contract this concerns.",
|
||||||
|
fr: "Merci d'indiquer de quel contrat il s'agit.",
|
||||||
|
pt: "Indica a que contrato se refere.",
|
||||||
|
},
|
||||||
|
wr_fehlt_kontakt: {
|
||||||
|
de: "Bitte gib eine E-Mail-Adresse an, damit die Bestätigung ankommt.",
|
||||||
|
"de-CH": "Bitte gib e E-Mail-Adrässe aa, demit d Bestätigung aachunnt.",
|
||||||
|
en: "Please enter an e-mail address so the confirmation reaches you.",
|
||||||
|
fr: "Merci d'indiquer une adresse e-mail pour recevoir la confirmation.",
|
||||||
|
pt: "Indica um endereço de e-mail para receberes a confirmação.",
|
||||||
|
},
|
||||||
|
/* Der Notfallhinweis nennt bewusst die E-Mail-Adresse im Klartext. Wenn
|
||||||
|
die Technik im entscheidenden Moment versagt, darf niemand deswegen
|
||||||
|
eine Frist verlieren — und genau dann braucht man den Ausweg sofort. */
|
||||||
|
wr_notfall: {
|
||||||
|
de: "Wichtig: Du kannst den Widerruf jederzeit auch formlos per E-Mail an [email protected] erklären. Das ist genauso wirksam und wahrt die Frist.",
|
||||||
|
"de-CH": "Wichtig: Du chasch de Widerrüef jederziit au formlos per E-Mail a [email protected] erkläre. Das isch glich wirksam und wahrt d Frist.",
|
||||||
|
en: "Important: you can declare your withdrawal informally by e-mail to [email protected] at any time. That is equally valid and meets the deadline.",
|
||||||
|
fr: "Important : tu peux à tout moment déclarer ta rétractation par e-mail à [email protected]. C'est tout aussi valable et respecte le délai.",
|
||||||
|
pt: "Importante: podes declarar a rescisão a qualquer momento por e-mail para [email protected]. É igualmente válido e cumpre o prazo.",
|
||||||
|
},
|
||||||
|
};
|
||||||
@@ -111,6 +111,10 @@
|
|||||||
fuss_portal: { de: "Kundenportal", "de-CH": "Kundeportal", en: "Client portal", fr: "Espace client", pt: "Portal do cliente" },
|
fuss_portal: { de: "Kundenportal", "de-CH": "Kundeportal", en: "Client portal", fr: "Espace client", pt: "Portal do cliente" },
|
||||||
fuss_verwaltung:{ de: "Verwaltung", "de-CH": "Verwaltig", en: "Administration", fr: "Administration", pt: "Administração" },
|
fuss_verwaltung:{ de: "Verwaltung", "de-CH": "Verwaltig", en: "Administration", fr: "Administration", pt: "Administração" },
|
||||||
fuss_universe: { de: "Zum Dogfather Universe", "de-CH": "Zum Dogfather Universe", en: "To the Dogfather Universe", fr: "Vers le Dogfather Universe", pt: "Para o Dogfather Universe" },
|
fuss_universe: { de: "Zum Dogfather Universe", "de-CH": "Zum Dogfather Universe", en: "To the Dogfather Universe", fr: "Vers le Dogfather Universe", pt: "Para o Dogfather Universe" },
|
||||||
|
fuss_recht: { de: "Impressum, AGB & Datenschutz", "de-CH": "Impressum, AGB & Dateschutz", en: "Legal notice, terms & privacy", fr: "Mentions légales, CGV & confidentialité", pt: "Aviso legal, termos & privacidade" },
|
||||||
|
/* Wortlaut nicht frei gewaehlt: § 356a BGB verlangt die Beschriftung
|
||||||
|
"Vertrag widerrufen" oder eine gleichbedeutende Bezeichnung. */
|
||||||
|
fuss_widerruf: { de: "Vertrag widerrufen", "de-CH": "Vertrag widerrüefe", en: "Withdraw from contract", fr: "Rétracter le contrat", pt: "Rescindir o contrato" },
|
||||||
fuss_kontakt: { de: "Kontakt & Rechtliches", "de-CH": "Kontakt & Rächtlichs", en: "Contact & legal", fr: "Contact & mentions légales", pt: "Contacto & jurídico" },
|
fuss_kontakt: { de: "Kontakt & Rechtliches", "de-CH": "Kontakt & Rächtlichs", en: "Contact & legal", fr: "Contact & mentions légales", pt: "Contacto & jurídico" },
|
||||||
/* Kontaktwege laut Entscheidung vom 22.08.2026: Anfrageformular als
|
/* Kontaktwege laut Entscheidung vom 22.08.2026: Anfrageformular als
|
||||||
Hauptweg (strukturierte, qualifizierte Anfragen — Masterplan S.2)
|
Hauptweg (strukturierte, qualifizierte Anfragen — Masterplan S.2)
|
||||||
@@ -494,6 +498,14 @@
|
|||||||
'<li><a href="/webdesign/verwaltung.html" data-i18n="fuss_verwaltung">' + t("fuss_verwaltung") + "</a></li>" +
|
'<li><a href="/webdesign/verwaltung.html" data-i18n="fuss_verwaltung">' + t("fuss_verwaltung") + "</a></li>" +
|
||||||
'<li><a href="/index.html" data-i18n="fuss_universe">' + t("fuss_universe") + "</a></li>" +
|
'<li><a href="/index.html" data-i18n="fuss_universe">' + t("fuss_universe") + "</a></li>" +
|
||||||
'<li><a href="mailto:[email protected]" data-i18n="fuss_email">' + t("fuss_email") + "</a></li>" +
|
'<li><a href="mailto:[email protected]" data-i18n="fuss_email">' + t("fuss_email") + "</a></li>" +
|
||||||
|
'<li><a href="/webdesign/rechtliches.html" data-i18n="fuss_recht">' + t("fuss_recht") + "</a></li>" +
|
||||||
|
/* Der Widerrufslink steht bewusst HIER in der Fusszeile und
|
||||||
|
auf jeder Seite: § 356a verlangt, dass die
|
||||||
|
Widerrufsfunktion waehrend der ganzen Frist "staendig
|
||||||
|
verfuegbar, hervorgehoben platziert und leicht
|
||||||
|
zugaenglich" ist. Ein Link, den man erst in den AGB
|
||||||
|
suchen muss, erfuellt das nicht. */
|
||||||
|
'<li><a href="/webdesign/widerruf.html" class="wd-fuss-widerruf" data-i18n="fuss_widerruf">' + t("fuss_widerruf") + "</a></li>" +
|
||||||
'<li><a href="/kontakt.html" data-i18n="fuss_kontakt">' + t("fuss_kontakt") + "</a></li>" +
|
'<li><a href="/kontakt.html" data-i18n="fuss_kontakt">' + t("fuss_kontakt") + "</a></li>" +
|
||||||
"</ul>" +
|
"</ul>" +
|
||||||
"</div>" +
|
"</div>" +
|
||||||
@@ -649,6 +661,26 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Datum MIT Uhrzeit. Fuer die Eingangsbestaetigung eines Widerrufs
|
||||||
|
reicht der Tag nicht: § 356a verlangt den ZEITPUNKT, und an der
|
||||||
|
Fristwahrung kann eine Stunde haengen. Ueberall sonst waere die
|
||||||
|
Uhrzeit nur Rauschen -- deshalb eine eigene Funktion statt datum()
|
||||||
|
zu erweitern. */
|
||||||
|
function datumZeit(wert) {
|
||||||
|
if (!wert) return "—";
|
||||||
|
var karte = { de: "de-DE", "de-CH": "de-CH", en: "en-GB", fr: "fr-FR", pt: "pt-PT" };
|
||||||
|
var d = new Date(wert);
|
||||||
|
if (isNaN(d.getTime())) return "—";
|
||||||
|
try {
|
||||||
|
return new Intl.DateTimeFormat(karte[getSprache()] || "de-DE", {
|
||||||
|
day: "2-digit", month: "2-digit", year: "numeric",
|
||||||
|
hour: "2-digit", minute: "2-digit", timeZoneName: "short",
|
||||||
|
}).format(d);
|
||||||
|
} catch (e) {
|
||||||
|
return d.toLocaleString();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/* Schützt vor eingeschleustem HTML, wenn Text aus der Datenbank oder aus
|
/* Schützt vor eingeschleustem HTML, wenn Text aus der Datenbank oder aus
|
||||||
einem Formular in die Seite geschrieben wird. Überall dort Pflicht, wo
|
einem Formular in die Seite geschrieben wird. Überall dort Pflicht, wo
|
||||||
innerHTML mit fremden Daten benutzt wird. */
|
innerHTML mit fremden Daten benutzt wird. */
|
||||||
@@ -855,6 +887,7 @@
|
|||||||
meldungAus: meldungAus,
|
meldungAus: meldungAus,
|
||||||
preis: preis,
|
preis: preis,
|
||||||
datum: datum,
|
datum: datum,
|
||||||
|
datumZeit: datumZeit,
|
||||||
sicher: sicher,
|
sicher: sicher,
|
||||||
};
|
};
|
||||||
})();
|
})();
|
||||||
|
|||||||
@@ -0,0 +1,148 @@
|
|||||||
|
/* =====================================================================
|
||||||
|
wd-widerruf.js — Elektronische Widerrufsfunktion (§ 356a BGB)
|
||||||
|
|
||||||
|
Der Ablauf ist vom Gesetz vorgegeben, nicht frei gestaltbar:
|
||||||
|
|
||||||
|
Schritt 1 Angaben (Name, Vertrag, Kontakt für die Bestätigung)
|
||||||
|
Schritt 2 ZWEITE Schaltfläche "Widerruf bestätigen"
|
||||||
|
Schritt 3 unverzügliche Eingangsbestätigung mit Zeitpunkt,
|
||||||
|
auf dauerhaftem Datenträger
|
||||||
|
|
||||||
|
Zwei bewusste Abweichungen von dem, was sonst überall im Projekt gilt:
|
||||||
|
|
||||||
|
* KEIN Entwurf im lokalen Speicher. Beim Anfrageformular ist das
|
||||||
|
Zwischenspeichern ein Segen — hier wäre es eine Falle: Auf einem
|
||||||
|
geteilten Rechner läge dann der halb ausgefüllte Widerruf des einen
|
||||||
|
im Browser des nächsten.
|
||||||
|
|
||||||
|
* KEINE Absendesperre. Wer in der letzten Stunde seiner Frist wegen
|
||||||
|
eines hakenden Netzes dreimal klickt, darf nicht abgewiesen werden.
|
||||||
|
Das wäre ein Rechtsverlust durch eine Komfortfunktion.
|
||||||
|
===================================================================== */
|
||||||
|
(function () {
|
||||||
|
"use strict";
|
||||||
|
|
||||||
|
var s = window.WD.sicher;
|
||||||
|
var meldung = document.getElementById("wr-meldung");
|
||||||
|
var form = document.getElementById("wr-form");
|
||||||
|
var schritt2 = document.getElementById("wr-schritt2");
|
||||||
|
var schritt3 = document.getElementById("wr-schritt3");
|
||||||
|
var daten = null;
|
||||||
|
|
||||||
|
function feldFehler(id, text) {
|
||||||
|
var feld = document.getElementById(id);
|
||||||
|
if (!feld) return;
|
||||||
|
feld.classList.add("wd-feld--fehler");
|
||||||
|
var p = feld.querySelector(".wd-feld-fehler");
|
||||||
|
if (p) p.textContent = text;
|
||||||
|
}
|
||||||
|
function fehlerLeeren() {
|
||||||
|
document.querySelectorAll(".wd-feld--fehler").forEach(function (f) {
|
||||||
|
f.classList.remove("wd-feld--fehler");
|
||||||
|
var p = f.querySelector(".wd-feld-fehler");
|
||||||
|
if (p) p.textContent = "";
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function wert(id) { return (document.getElementById(id).value || "").trim(); }
|
||||||
|
|
||||||
|
/* ---- Schritt 1 -> 2 ---- */
|
||||||
|
form.addEventListener("submit", function (e) {
|
||||||
|
e.preventDefault();
|
||||||
|
fehlerLeeren();
|
||||||
|
window.WD.meldungAus(meldung);
|
||||||
|
|
||||||
|
var name = wert("wr-name");
|
||||||
|
var vertrag = wert("wr-vertrag");
|
||||||
|
var kontakt = wert("wr-kontakt");
|
||||||
|
|
||||||
|
var ok = true;
|
||||||
|
if (!name) { feldFehler("wr-feld-name", window.WD.t("wr_fehlt_name")); ok = false; }
|
||||||
|
if (!vertrag) { feldFehler("wr-feld-vertrag", window.WD.t("wr_fehlt_vertrag")); ok = false; }
|
||||||
|
/* Sehr einfache Prüfung mit Absicht. Eine strenge Adressprüfung
|
||||||
|
weist im Zweifel eine gültige Adresse ab — und dann kommt der
|
||||||
|
Widerruf gar nicht erst an. Lieber eine Bestätigung, die ins Leere
|
||||||
|
läuft, als ein Widerruf, der nicht abgeschickt werden kann. */
|
||||||
|
if (!kontakt || kontakt.indexOf("@") < 1 || kontakt.indexOf(".") < 0) {
|
||||||
|
feldFehler("wr-feld-kontakt", window.WD.t("wr_fehlt_kontakt")); ok = false;
|
||||||
|
}
|
||||||
|
if (!ok) {
|
||||||
|
document.querySelector(".wd-feld--fehler input").focus();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
daten = {
|
||||||
|
name: name,
|
||||||
|
vertrag: vertrag,
|
||||||
|
kontakt: kontakt,
|
||||||
|
betrifft: wert("wr-betrifft"),
|
||||||
|
begruendung: wert("wr-grund"),
|
||||||
|
sprache: window.WD.getSprache(),
|
||||||
|
};
|
||||||
|
|
||||||
|
var zeilen = [
|
||||||
|
[window.WD.t("wr_name"), daten.name],
|
||||||
|
[window.WD.t("wr_vertrag"), daten.vertrag],
|
||||||
|
[window.WD.t("wr_kontakt"), daten.kontakt],
|
||||||
|
];
|
||||||
|
if (daten.betrifft) zeilen.push([window.WD.t("wr_betrifft"), daten.betrifft]);
|
||||||
|
if (daten.begruendung) zeilen.push([window.WD.t("wr_grund"), daten.begruendung]);
|
||||||
|
|
||||||
|
document.getElementById("wr-pruef").innerHTML = zeilen.map(function (z) {
|
||||||
|
return "<dt>" + s(z[0].replace(/ ?\*$/, "")) + "</dt><dd>" + s(z[1]) + "</dd>";
|
||||||
|
}).join("");
|
||||||
|
|
||||||
|
form.hidden = true;
|
||||||
|
schritt2.hidden = false;
|
||||||
|
schritt2.scrollIntoView({ behavior: "smooth", block: "start" });
|
||||||
|
document.getElementById("wr-bestaetigen").focus();
|
||||||
|
});
|
||||||
|
|
||||||
|
document.getElementById("wr-zurueck").addEventListener("click", function () {
|
||||||
|
schritt2.hidden = true;
|
||||||
|
form.hidden = false;
|
||||||
|
form.scrollIntoView({ behavior: "smooth", block: "start" });
|
||||||
|
});
|
||||||
|
|
||||||
|
/* ---- Schritt 2 -> 3: absenden ---- */
|
||||||
|
document.getElementById("wr-bestaetigen").addEventListener("click", async function () {
|
||||||
|
var knopf = this;
|
||||||
|
window.WD.knopfSperren(knopf, true, window.WD.t("wr_sendet"));
|
||||||
|
window.WD.meldungAus(meldung);
|
||||||
|
|
||||||
|
try {
|
||||||
|
var antwort = await window.WD.api("/webdesign/widerruf", { method: "POST", body: daten });
|
||||||
|
var b = antwort.bestaetigung;
|
||||||
|
|
||||||
|
document.getElementById("wr-beleg").innerHTML =
|
||||||
|
'<div class="wr-beleg-kopf">' +
|
||||||
|
"<strong>" + s(window.WD.t("wr_beleg_titel")) + "</strong>" +
|
||||||
|
'<span class="wr-beleg-nr">' + s(b.nummer) + "</span>" +
|
||||||
|
"</div>" +
|
||||||
|
"<dl>" +
|
||||||
|
"<dt>" + s(window.WD.t("wr_beleg_zeit")) + "</dt><dd>" +
|
||||||
|
s(window.WD.datumZeit ? window.WD.datumZeit(b.eingegangenAm) : b.eingegangenAm) + "</dd>" +
|
||||||
|
"<dt>" + s(window.WD.t("wr_name")) + "</dt><dd>" + s(b.name) + "</dd>" +
|
||||||
|
"<dt>" + s(window.WD.t("wr_vertrag")) + "</dt><dd>" + s(b.vertrag) + "</dd>" +
|
||||||
|
"<dt>" + s(window.WD.t("wr_kontakt")) + "</dt><dd>" + s(b.kontakt) + "</dd>" +
|
||||||
|
"<dt>" + s(window.WD.t("wr_beleg_an")) + "</dt><dd>" + s(b.empfaenger) + "</dd>" +
|
||||||
|
"<dt>" + s(window.WD.t("wr_beleg_erklaerung")) + "</dt><dd>" +
|
||||||
|
s(window.WD.t("wr_beleg_satz")) + "</dd>" +
|
||||||
|
"</dl>";
|
||||||
|
|
||||||
|
schritt2.hidden = true;
|
||||||
|
schritt3.hidden = false;
|
||||||
|
schritt3.scrollIntoView({ behavior: "smooth", block: "start" });
|
||||||
|
} catch (fehler) {
|
||||||
|
window.WD.knopfSperren(knopf, false);
|
||||||
|
/* Der Hinweis nennt ausdrücklich den formlosen Weg. Wenn die
|
||||||
|
Technik versagt, darf niemand deswegen seine Frist verlieren —
|
||||||
|
und in genau dem Moment braucht man den Ausweg, nicht später. */
|
||||||
|
window.WD.meldung(meldung, fehler.message + " " + window.WD.t("wr_notfall"), "fehler");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
document.getElementById("wr-drucken").addEventListener("click", function () {
|
||||||
|
window.print();
|
||||||
|
});
|
||||||
|
})();
|
||||||
@@ -0,0 +1,121 @@
|
|||||||
|
-- =====================================================================
|
||||||
|
-- 0014_webdesign_widerruf.sql
|
||||||
|
--
|
||||||
|
-- ELEKTRONISCHE WIDERRUFSFUNKTION (§ 356a BGB, seit 19.06.2026)
|
||||||
|
--
|
||||||
|
-- Recherchiert am 23.08.2026. Der Befund ist eindeutig und betrifft
|
||||||
|
-- diese Seite unmittelbar:
|
||||||
|
--
|
||||||
|
-- § 356a BGB gilt seit dem 19.06.2026 für ALLE Fernabsatzverträge, die
|
||||||
|
-- über eine Online-Benutzeroberfläche geschlossen werden — nicht nur
|
||||||
|
-- für Finanzdienstleistungen. Wer Verbrauchern erlaubt, online einen
|
||||||
|
-- Vertrag zu schliessen, muss eine elektronische Widerrufsfunktion
|
||||||
|
-- bereitstellen: gut lesbar mit "Vertrag widerrufen" beschriftet,
|
||||||
|
-- während der gesamten Widerrufsfrist ständig verfügbar, hervorgehoben
|
||||||
|
-- platziert und leicht zugänglich.
|
||||||
|
--
|
||||||
|
-- WARUM DAS HIER GILT: Im Kundenportal nimmt der Kunde Zusatzangebote
|
||||||
|
-- mit einem Klick VERBINDLICH an ("Damit nimmst du das Zusatzangebot
|
||||||
|
-- verbindlich an"). Das ist ein Vertragsschluss über eine
|
||||||
|
-- Online-Benutzeroberfläche. Dasselbe wird für die PayPal-Zahlungen
|
||||||
|
-- gelten. Die Pflicht besteht also bereits.
|
||||||
|
--
|
||||||
|
-- Der Anbieter sitzt in Luxemburg. Für Verbraucher mit gewöhnlichem
|
||||||
|
-- Aufenthalt in Deutschland gilt nach Art. 6 Rom-I-VO trotzdem das
|
||||||
|
-- deutsche zwingende Verbraucherrecht, weil die Seite sich erkennbar an
|
||||||
|
-- den deutschsprachigen Markt richtet. Deshalb wird hier der strengere
|
||||||
|
-- Standard umgesetzt, nicht der bequemere.
|
||||||
|
--
|
||||||
|
-- Ablauf nach § 356a: Der Verbraucher gibt Name, Vertragskennzeichnung
|
||||||
|
-- und eine Kontaktmöglichkeit an, betätigt eine ZWEITE Schaltfläche
|
||||||
|
-- ("Widerruf bestätigen") und erhält unverzüglich eine
|
||||||
|
-- Eingangsbestätigung auf dauerhaftem Datenträger, die die Erklärung und
|
||||||
|
-- den Zeitpunkt enthält.
|
||||||
|
--
|
||||||
|
-- Genau dafür ist diese Tabelle da: Sie ist der dauerhafte Datenträger
|
||||||
|
-- und zugleich der Nachweis. Sie wird NIE geleert.
|
||||||
|
-- =====================================================================
|
||||||
|
|
||||||
|
CREATE TABLE IF NOT EXISTS wd_widerrufe (
|
||||||
|
id TEXT PRIMARY KEY,
|
||||||
|
nummer TEXT NOT NULL UNIQUE, -- W-2608-0001, für die Bestätigung
|
||||||
|
|
||||||
|
/* Die Angaben, die § 356a verlangt. Sie stehen hier als FREITEXT und
|
||||||
|
nicht nur als Verweis auf einen Datensatz: Ein Widerruf muss auch
|
||||||
|
dann vollständig belegbar bleiben, wenn Kunde oder Projekt später
|
||||||
|
gelöscht werden. Der Nachweis darf nicht davon abhängen, dass
|
||||||
|
ringsherum noch alles existiert. */
|
||||||
|
name TEXT NOT NULL,
|
||||||
|
kontakt TEXT NOT NULL, -- E-Mail für die Eingangsbestätigung
|
||||||
|
vertrag TEXT NOT NULL, -- Vertragskennzeichnung, vom Kunden benannt
|
||||||
|
betrifft TEXT, -- optional: welcher Teil des Vertrags
|
||||||
|
begruendung TEXT, -- freiwillig; ein Widerruf braucht KEINE
|
||||||
|
|
||||||
|
/* Zuordnung, falls vorhanden — aber ausdrücklich ohne Fremdschlüssel.
|
||||||
|
Ein Fremdschlüssel mit ON DELETE CASCADE würde den Nachweis mit dem
|
||||||
|
Kunden mitlöschen; einer mit RESTRICT würde das Löschen eines Kunden
|
||||||
|
blockieren, obwohl die DSGVO es verlangen kann. Beides wäre falsch.
|
||||||
|
Deshalb nur eine lose Notiz. */
|
||||||
|
kunde_id TEXT,
|
||||||
|
projekt_id TEXT,
|
||||||
|
|
||||||
|
/* Zeitpunkt des Eingangs. Massgeblich für die Fristwahrung — der
|
||||||
|
Widerruf ist rechtzeitig, wenn er innerhalb der Frist ABGESENDET
|
||||||
|
wurde, nicht wenn er bearbeitet wurde. */
|
||||||
|
eingegangen_am TEXT NOT NULL,
|
||||||
|
ip_hash TEXT, -- nur der Hash, nie die IP selbst
|
||||||
|
sprache TEXT NOT NULL DEFAULT 'de',
|
||||||
|
|
||||||
|
/* Bestätigung: wurde sie versendet, und wann? "unverzüglich" ist
|
||||||
|
Pflicht, also muss man sehen können, ob es geklappt hat. */
|
||||||
|
bestaetigt_am TEXT,
|
||||||
|
bestaetigung_weg TEXT, -- 'email' | 'anzeige'
|
||||||
|
|
||||||
|
/* Bearbeitung auf unserer Seite. Der Widerruf ist wirksam, sobald er
|
||||||
|
zugeht — diese Felder ändern daran nichts, sie halten nur fest, was
|
||||||
|
wir damit gemacht haben. */
|
||||||
|
status TEXT NOT NULL DEFAULT 'eingegangen', -- eingegangen | bearbeitet
|
||||||
|
notiz TEXT,
|
||||||
|
erledigt_am TEXT
|
||||||
|
);
|
||||||
|
|
||||||
|
CREATE INDEX IF NOT EXISTS idx_wd_widerrufe_zeit ON wd_widerrufe(eingegangen_am DESC);
|
||||||
|
CREATE INDEX IF NOT EXISTS idx_wd_widerrufe_offen ON wd_widerrufe(status, eingegangen_am DESC);
|
||||||
|
|
||||||
|
-- ---------------------------------------------------------------------
|
||||||
|
-- NACHWEIS DER ZUSTIMMUNG ZUM VORZEITIGEN LEISTUNGSBEGINN
|
||||||
|
--
|
||||||
|
-- § 356 Abs. 4 BGB: Bei Dienstleistungen erlischt das Widerrufsrecht nur
|
||||||
|
-- dann vorzeitig, wenn der Verbraucher AUSDRÜCKLICH zugestimmt hat, dass
|
||||||
|
-- vor Ablauf der Widerrufsfrist begonnen wird, UND gleichzeitig bestätigt
|
||||||
|
-- hat, dass er dadurch sein Widerrufsrecht verliert.
|
||||||
|
--
|
||||||
|
-- Die Beweislast für BEIDES liegt beim Unternehmer. Ohne Nachweis bleibt
|
||||||
|
-- das Widerrufsrecht bestehen — und die 30-%-Anzahlung, die vor Ablauf
|
||||||
|
-- der Frist verlangt wird, wäre dann angreifbar.
|
||||||
|
--
|
||||||
|
-- Deshalb wird die Zustimmung mit Zeitstempel und dem GENAUEN Wortlaut
|
||||||
|
-- gespeichert, der dem Kunden angezeigt wurde. Nur den Haken zu
|
||||||
|
-- speichern reicht nicht: Im Streit zählt, WAS er bestätigt hat, und der
|
||||||
|
-- Text kann sich über die Jahre ändern.
|
||||||
|
-- ---------------------------------------------------------------------
|
||||||
|
CREATE TABLE IF NOT EXISTS wd_zustimmungen (
|
||||||
|
id TEXT PRIMARY KEY,
|
||||||
|
kunde_id TEXT,
|
||||||
|
projekt_id TEXT,
|
||||||
|
anfrage_id TEXT,
|
||||||
|
|
||||||
|
-- 'sofortbeginn' | 'agb' | 'datenschutz'
|
||||||
|
art TEXT NOT NULL,
|
||||||
|
|
||||||
|
/* Der Wortlaut, den der Kunde tatsächlich gesehen hat — vollständig,
|
||||||
|
nicht als Verweis auf eine Textbaustein-Kennung. */
|
||||||
|
wortlaut TEXT NOT NULL,
|
||||||
|
fassung TEXT, -- z. B. "2026-08-23"
|
||||||
|
sprache TEXT NOT NULL DEFAULT 'de',
|
||||||
|
|
||||||
|
erteilt_am TEXT NOT NULL,
|
||||||
|
ip_hash TEXT
|
||||||
|
);
|
||||||
|
|
||||||
|
CREATE INDEX IF NOT EXISTS idx_wd_zust_kunde ON wd_zustimmungen(kunde_id, art);
|
||||||
@@ -0,0 +1,222 @@
|
|||||||
|
/* Prüft die elektronische Widerrufsfunktion (§ 356a BGB) im Browser.
|
||||||
|
|
||||||
|
Die Prüfungen orientieren sich am Gesetzeswortlaut, nicht am Gefühl:
|
||||||
|
|
||||||
|
- "gut lesbar mit 'Vertrag widerrufen' beschriftet"
|
||||||
|
- "während der gesamten Widerrufsfrist ständig verfügbar,
|
||||||
|
hervorgehoben platziert und leicht zugänglich"
|
||||||
|
- Der Verbraucher muss Name, Vertragskennzeichnung und ein
|
||||||
|
Kommunikationsmittel angeben können
|
||||||
|
- ZWEITE Schaltfläche "Widerruf bestätigen"
|
||||||
|
- unverzügliche Eingangsbestätigung mit Zeitpunkt
|
||||||
|
|
||||||
|
Dazu zwei Dinge, die aus dem Sinn der Vorschrift folgen und die man
|
||||||
|
leicht falsch baut: Der Widerruf darf KEINE Begründung verlangen, und
|
||||||
|
die Seite darf nicht hinter einer Anmeldung liegen.
|
||||||
|
*/
|
||||||
|
import { chromium } from "playwright";
|
||||||
|
import { createServer } from "node:http";
|
||||||
|
import { readFileSync, existsSync } from "node:fs";
|
||||||
|
import { join, extname } from "node:path";
|
||||||
|
|
||||||
|
const W = process.cwd();
|
||||||
|
const T = { ".html": "text/html", ".css": "text/css", ".js": "text/javascript",
|
||||||
|
".png": "image/png", ".jpg": "image/jpeg", ".svg": "image/svg+xml",
|
||||||
|
".webp": "image/webp", ".ico": "image/x-icon", ".json": "application/json" };
|
||||||
|
const srv = createServer((q, r) => {
|
||||||
|
const p = join(W, decodeURIComponent(q.url.split("?")[0]));
|
||||||
|
if (existsSync(p) && !p.endsWith("/")) {
|
||||||
|
r.writeHead(200, { "content-type": T[extname(p)] || "text/plain" });
|
||||||
|
return r.end(readFileSync(p));
|
||||||
|
}
|
||||||
|
r.writeHead(404).end("");
|
||||||
|
});
|
||||||
|
await new Promise((r) => srv.listen(4328, r));
|
||||||
|
|
||||||
|
const ergebnisse = [];
|
||||||
|
function pruefe(name, ok, zusatz) {
|
||||||
|
ergebnisse.push({ name, ok });
|
||||||
|
console.log((ok ? " ok " : " FEHL ") + name + (zusatz ? " -> " + zusatz : ""));
|
||||||
|
}
|
||||||
|
|
||||||
|
const browser = await chromium.launch();
|
||||||
|
|
||||||
|
/* ---- Beschriftung nach § 356a in allen fünf Sprachen ---- */
|
||||||
|
{
|
||||||
|
const p = await browser.newPage();
|
||||||
|
await p.goto("http://127.0.0.1:4328/webdesign/widerruf.html");
|
||||||
|
await p.waitForTimeout(600);
|
||||||
|
console.log("\n=== Gesetzlich vorgegebene Beschriftungen ===");
|
||||||
|
|
||||||
|
const erwartet = {
|
||||||
|
de: ["Vertrag widerrufen", "Widerruf bestätigen"],
|
||||||
|
"de-CH": ["Vertrag widerrüefe", "Widerrüef bestätige"],
|
||||||
|
en: ["Withdraw from contract", "Confirm withdrawal"],
|
||||||
|
fr: ["Rétracter le contrat", "Confirmer la rétractation"],
|
||||||
|
pt: ["Rescindir o contrato", "Confirmar a rescisão"],
|
||||||
|
};
|
||||||
|
for (const [sp, [h1, knopf]] of Object.entries(erwartet)) {
|
||||||
|
await p.addInitScript(`localStorage.setItem("dogi-sprache", ${JSON.stringify(sp)})`);
|
||||||
|
await p.goto("http://127.0.0.1:4328/webdesign/widerruf.html");
|
||||||
|
await p.waitForTimeout(350);
|
||||||
|
const gefunden = await p.evaluate(() => ({
|
||||||
|
h1: document.querySelector("h1")?.textContent.trim(),
|
||||||
|
knopf: document.getElementById("wr-bestaetigen")?.textContent.trim(),
|
||||||
|
fuss: Array.from(document.querySelectorAll(".wd-fuss-widerruf")).map((a) => a.textContent.trim()),
|
||||||
|
}));
|
||||||
|
pruefe(`[${sp}] Überschrift lautet „${h1}“`, gefunden.h1 === h1, gefunden.h1);
|
||||||
|
pruefe(`[${sp}] zweite Schaltfläche lautet „${knopf}“`, gefunden.knopf === knopf, gefunden.knopf);
|
||||||
|
pruefe(`[${sp}] Fusszeilen-Link auf jeder Seite vorhanden`, gefunden.fuss.length === 1, gefunden.fuss.join("|"));
|
||||||
|
}
|
||||||
|
await p.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ---- Ablauf ---- */
|
||||||
|
for (const [wo, breite, hoehe] of [["Computer", 1280, 900], ["Handy", 390, 844]]) {
|
||||||
|
const seite = await browser.newPage({ viewport: { width: breite, height: hoehe } });
|
||||||
|
const fehlerImProtokoll = [];
|
||||||
|
seite.on("pageerror", (e) => fehlerImProtokoll.push(e.message));
|
||||||
|
|
||||||
|
let gesendet = null;
|
||||||
|
await seite.route("**/webdesign/widerruf", (route) => {
|
||||||
|
gesendet = route.request().postDataJSON();
|
||||||
|
return route.fulfill({ json: { ok: true, nummer: "W-2608-0001",
|
||||||
|
eingegangenAm: "2026-08-23T09:12:00.000Z",
|
||||||
|
bestaetigung: {
|
||||||
|
nummer: "W-2608-0001", name: gesendet.name, vertrag: gesendet.vertrag,
|
||||||
|
kontakt: gesendet.kontakt, eingegangenAm: "2026-08-23T09:12:00.000Z",
|
||||||
|
empfaenger: "Filipe Pereira Queiroz, 3 rue Arthur Thinnes, L-3919 Mondercange, Luxemburg",
|
||||||
|
} } });
|
||||||
|
});
|
||||||
|
|
||||||
|
await seite.addInitScript(`localStorage.setItem("dogi-sprache", "de")`);
|
||||||
|
await seite.goto("http://127.0.0.1:4328/webdesign/widerruf.html");
|
||||||
|
await seite.waitForTimeout(600);
|
||||||
|
console.log("\n=== " + wo + " ===");
|
||||||
|
|
||||||
|
/* KEINE Anmeldung: Wer widerrufen will, darf nicht erst an einer
|
||||||
|
Anmeldemaske scheitern. */
|
||||||
|
pruefe(wo + ": ohne Anmeldung nutzbar",
|
||||||
|
await seite.locator("#wr-form").isVisible() && await seite.locator('input[type="password"]').count() === 0);
|
||||||
|
|
||||||
|
/* Der Widerruf braucht KEINE Begründung. */
|
||||||
|
const pflicht = await seite.evaluate(() =>
|
||||||
|
Array.from(document.querySelectorAll(".wd-feld")).map((f) => ({
|
||||||
|
id: f.querySelector("input,textarea")?.id,
|
||||||
|
pflicht: !!f.querySelector(".wd-pflicht"),
|
||||||
|
})).filter((x) => x.id));
|
||||||
|
const grund = pflicht.find((x) => x.id === "wr-grund");
|
||||||
|
pruefe(wo + ": der Grund ist NICHT Pflicht", grund && !grund.pflicht);
|
||||||
|
pruefe(wo + ": genau drei Pflichtfelder (Name, Vertrag, Kontakt)",
|
||||||
|
pflicht.filter((x) => x.pflicht).length === 3,
|
||||||
|
pflicht.filter((x) => x.pflicht).map((x) => x.id).join(", "));
|
||||||
|
|
||||||
|
/* Leeres Formular darf nicht durchgehen. */
|
||||||
|
await seite.click("#wr-weiter");
|
||||||
|
await seite.waitForTimeout(300);
|
||||||
|
pruefe(wo + ": leeres Formular wird abgefangen",
|
||||||
|
await seite.locator("#wr-schritt2").isVisible() === false
|
||||||
|
&& await seite.locator(".wd-feld--fehler").count() === 3);
|
||||||
|
|
||||||
|
/* Ausfüllen und weiter — die zweite Stufe MUSS kommen. */
|
||||||
|
await seite.fill("#wr-name", "Anna Muster");
|
||||||
|
await seite.fill("#wr-vertrag", "P-2608-0001");
|
||||||
|
await seite.fill("#wr-kontakt", "[email protected]");
|
||||||
|
await seite.click("#wr-weiter");
|
||||||
|
await seite.waitForTimeout(500);
|
||||||
|
|
||||||
|
pruefe(wo + ": zweite Stufe erscheint", await seite.locator("#wr-schritt2").isVisible());
|
||||||
|
pruefe(wo + ": das Formular ist dabei ausgeblendet", await seite.locator("#wr-form").isVisible() === false);
|
||||||
|
pruefe(wo + ": die Angaben werden zur Prüfung gezeigt",
|
||||||
|
(await seite.locator("#wr-pruef").innerText()).includes("Anna Muster"));
|
||||||
|
|
||||||
|
/* Zurück und wieder vor — Angaben dürfen nicht verloren gehen. */
|
||||||
|
await seite.click("#wr-zurueck");
|
||||||
|
await seite.waitForTimeout(300);
|
||||||
|
pruefe(wo + ": „Angaben ändern“ behält das Ausgefüllte",
|
||||||
|
await seite.inputValue("#wr-name") === "Anna Muster");
|
||||||
|
await seite.click("#wr-weiter");
|
||||||
|
await seite.waitForTimeout(400);
|
||||||
|
|
||||||
|
/* Bestätigen. */
|
||||||
|
await seite.click("#wr-bestaetigen");
|
||||||
|
await seite.waitForTimeout(700);
|
||||||
|
|
||||||
|
pruefe(wo + ": Widerruf wird abgeschickt",
|
||||||
|
gesendet && gesendet.name === "Anna Muster" && gesendet.vertrag === "P-2608-0001",
|
||||||
|
JSON.stringify(gesendet));
|
||||||
|
pruefe(wo + ": Eingangsbestätigung erscheint", await seite.locator("#wr-schritt3").isVisible());
|
||||||
|
|
||||||
|
const beleg = await seite.locator("#wr-beleg").innerText();
|
||||||
|
pruefe(wo + ": Bestätigung nennt eine Nummer", beleg.includes("W-2608-0001"));
|
||||||
|
/* § 356a verlangt den ZEITPUNKT, nicht nur das Datum. */
|
||||||
|
pruefe(wo + ": Bestätigung nennt Datum UND Uhrzeit", /\d{2}:\d{2}/.test(beleg), beleg.match(/\d{2}[.:]\d{2}[.:]?\d*/g)?.join(" "));
|
||||||
|
pruefe(wo + ": Bestätigung nennt den Empfänger", beleg.includes("Mondercange"));
|
||||||
|
pruefe(wo + ": Bestätigung enthält die Erklärung selbst", beleg.includes("widerrufe"));
|
||||||
|
|
||||||
|
pruefe(wo + ": keine Fehler im Protokoll", fehlerImProtokoll.length === 0, fehlerImProtokoll.join(" | "));
|
||||||
|
|
||||||
|
/* Druckansicht: Auf Papier muss der Beleg lesbar sein und die
|
||||||
|
Bedienelemente müssen verschwinden. */
|
||||||
|
await seite.emulateMedia({ media: "print" });
|
||||||
|
await seite.waitForTimeout(250);
|
||||||
|
const druck = await seite.evaluate(() => {
|
||||||
|
const beleg = document.getElementById("wr-beleg");
|
||||||
|
const stil = getComputedStyle(beleg);
|
||||||
|
return {
|
||||||
|
hintergrundKoerper: getComputedStyle(document.body).backgroundColor,
|
||||||
|
belegRand: stil.borderTopColor,
|
||||||
|
kopfWeg: getComputedStyle(document.getElementById("wd-kopf")).display,
|
||||||
|
knoepfeWeg: getComputedStyle(document.querySelector(".wr-nicht-drucken")).display,
|
||||||
|
formularWeg: getComputedStyle(document.getElementById("wr-form")).display,
|
||||||
|
};
|
||||||
|
});
|
||||||
|
pruefe(wo + ": Druck — heller Hintergrund statt dunkel",
|
||||||
|
druck.hintergrundKoerper === "rgb(255, 255, 255)", druck.hintergrundKoerper);
|
||||||
|
pruefe(wo + ": Druck — Kopfzeile und Knöpfe sind weg",
|
||||||
|
druck.kopfWeg === "none" && druck.knoepfeWeg === "none" && druck.formularWeg === "none");
|
||||||
|
await seite.emulateMedia({ media: "screen" });
|
||||||
|
|
||||||
|
await seite.screenshot({ path: `pruef-widerruf-${wo.toLowerCase()}.png`, fullPage: true });
|
||||||
|
await seite.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ---- Rechtsseite ---- */
|
||||||
|
{
|
||||||
|
const p = await browser.newPage({ viewport: { width: 1280, height: 900 } });
|
||||||
|
const fehler = [];
|
||||||
|
p.on("pageerror", (e) => fehler.push(e.message));
|
||||||
|
await p.goto("http://127.0.0.1:4328/webdesign/rechtliches.html");
|
||||||
|
await p.waitForTimeout(700);
|
||||||
|
console.log("\n=== Rechtsseite ===");
|
||||||
|
|
||||||
|
const m = await p.evaluate(() => ({
|
||||||
|
abschnitte: ["impressum", "agb", "widerruf", "datenschutz"].map((id) => !!document.getElementById(id)),
|
||||||
|
muster: document.body.innerText.includes("Muster-Widerrufsformular"),
|
||||||
|
frist: document.body.innerText.includes("vierzehn Tagen"),
|
||||||
|
ust: document.body.innerText.includes("Kleinunternehmerregelung"),
|
||||||
|
erloeschen: document.body.innerText.includes("ausdrückliche Zustimmung"),
|
||||||
|
linkWiderruf: document.querySelectorAll('a[href="/webdesign/widerruf.html"]').length,
|
||||||
|
aufsicht: document.body.innerText.includes("CNPD"),
|
||||||
|
navi: document.querySelectorAll(".rl-nav a").length,
|
||||||
|
}));
|
||||||
|
|
||||||
|
pruefe("alle vier Abschnitte vorhanden", m.abschnitte.every(Boolean), JSON.stringify(m.abschnitte));
|
||||||
|
pruefe("Muster-Widerrufsformular ist da", m.muster);
|
||||||
|
pruefe("14-Tage-Frist ist benannt", m.frist);
|
||||||
|
pruefe("Umsatzsteuer-Hinweis ist da (Preisangabenpflicht)", m.ust);
|
||||||
|
pruefe("vorzeitiges Erlöschen ist erklärt (§ 356 Abs. 4)", m.erloeschen);
|
||||||
|
pruefe("mehrere Wege zur Widerrufsfunktion", m.linkWiderruf >= 2, "n=" + m.linkWiderruf);
|
||||||
|
pruefe("zuständige Aufsichtsbehörde genannt", m.aufsicht);
|
||||||
|
pruefe("Sprungnavigation vorhanden", m.navi === 4);
|
||||||
|
pruefe("keine Fehler im Protokoll", fehler.length === 0, fehler.join(" | "));
|
||||||
|
|
||||||
|
await p.screenshot({ path: "pruef-rechtliches.png", fullPage: false });
|
||||||
|
await p.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
await browser.close();
|
||||||
|
srv.close();
|
||||||
|
const f = ergebnisse.filter((e) => !e.ok).length;
|
||||||
|
console.log(`\n===== ${ergebnisse.length - f} bestanden, ${f} fehlgeschlagen =====`);
|
||||||
|
process.exitCode = f ? 1 : 0;
|
||||||
@@ -48,6 +48,12 @@ const MIGRATION_FILES = [
|
|||||||
// (22.08.2026). Begruendung fuer die getrennte Postfach-Tabelle steht
|
// (22.08.2026). Begruendung fuer die getrennte Postfach-Tabelle steht
|
||||||
// im Kopf der Migration.
|
// im Kopf der Migration.
|
||||||
"0013_webdesign_aufgaben.sql",
|
"0013_webdesign_aufgaben.sql",
|
||||||
|
// Elektronische Widerrufsfunktion (§ 356a BGB, seit 19.06.2026) und der
|
||||||
|
// Nachweis der Zustimmung zum vorzeitigen Leistungsbeginn (§ 356 Abs. 4
|
||||||
|
// BGB). Ausfuehrliche Begruendung im Kopf der Migration -- kurz: Im
|
||||||
|
// Portal werden Zusatzangebote per Klick verbindlich angenommen, das ist
|
||||||
|
// ein Vertragsschluss ueber eine Online-Benutzeroberflaeche.
|
||||||
|
"0014_webdesign_widerruf.sql",
|
||||||
];
|
];
|
||||||
|
|
||||||
function alreadyApplied(name) {
|
function alreadyApplied(name) {
|
||||||
|
|||||||
@@ -111,6 +111,12 @@ import {
|
|||||||
portalPostfach, portalPostfachSenden, adminPostfachListe,
|
portalPostfach, portalPostfachSenden, adminPostfachListe,
|
||||||
adminPostfachVerlauf, adminPostfachSenden, adminPostfachZaehler,
|
adminPostfachVerlauf, adminPostfachSenden, adminPostfachZaehler,
|
||||||
} from "./routes/webdesign-postfach.js";
|
} from "./routes/webdesign-postfach.js";
|
||||||
|
/* Elektronische Widerrufsfunktion (§ 356a BGB). Bewusst OHNE Anmeldung
|
||||||
|
erreichbar -- das Gesetz verlangt "leicht zugaenglich", und wer einen
|
||||||
|
Vertrag loswerden will, darf nicht erst an einer Anmeldung scheitern. */
|
||||||
|
import {
|
||||||
|
widerrufAbsenden, widerrufeListe, widerrufBearbeiten,
|
||||||
|
} from "./routes/webdesign-widerruf.js";
|
||||||
import { vorlagenSicherstellen } from "./lib/webdesign-vorlagen.js";
|
import { vorlagenSicherstellen } from "./lib/webdesign-vorlagen.js";
|
||||||
|
|
||||||
initDb();
|
initDb();
|
||||||
@@ -254,6 +260,13 @@ app.get("/site-texts", wrap(getSiteTextsPublic));
|
|||||||
verlangt eine Team-Anmeldung, die in den Handlern selbst geprüft wird
|
verlangt eine Team-Anmeldung, die in den Handlern selbst geprüft wird
|
||||||
(unterschiedliche Rechte je Endpunkt, siehe Import oben). */
|
(unterschiedliche Rechte je Endpunkt, siehe Import oben). */
|
||||||
app.post("/webdesign/anfrage", wrap(anfrageAbsenden));
|
app.post("/webdesign/anfrage", wrap(anfrageAbsenden));
|
||||||
|
/* Zweiter oeffentlicher Endpunkt des Bereichs -- und der einzige, bei dem
|
||||||
|
das so sein MUSS: § 356a BGB verlangt eine leicht zugaengliche
|
||||||
|
Widerrufsfunktion. Eine Anmeldung davor waere ein Rechtsverlust fuer
|
||||||
|
jeden, der sein Passwort vergessen hat. */
|
||||||
|
app.post("/webdesign/widerruf", wrap(widerrufAbsenden));
|
||||||
|
app.get("/webdesign/admin/widerrufe", wrap(widerrufeListe));
|
||||||
|
app.post("/webdesign/admin/widerrufe/:id", wrap(widerrufBearbeiten));
|
||||||
app.get("/webdesign/admin/anfragen", wrap(anfragenListe));
|
app.get("/webdesign/admin/anfragen", wrap(anfragenListe));
|
||||||
app.get("/webdesign/admin/anfragen/:id", wrap(anfrageDetail));
|
app.get("/webdesign/admin/anfragen/:id", wrap(anfrageDetail));
|
||||||
app.post("/webdesign/admin/anfragen/:id/status", wrap(anfrageStatus));
|
app.post("/webdesign/admin/anfragen/:id/status", wrap(anfrageStatus));
|
||||||
|
|||||||
@@ -0,0 +1,212 @@
|
|||||||
|
/* =====================================================================
|
||||||
|
routes/webdesign-widerruf.js — Elektronische Widerrufsfunktion
|
||||||
|
|
||||||
|
Umsetzung von § 356a BGB (in Kraft seit 19.06.2026). Begründung, warum
|
||||||
|
die Vorschrift hier gilt, steht ausführlich im Kopf der Migration
|
||||||
|
0014 — kurz: Im Portal werden Zusatzangebote per Klick verbindlich
|
||||||
|
angenommen, das ist ein Vertragsschluss über eine Online-Benutzer-
|
||||||
|
oberfläche.
|
||||||
|
|
||||||
|
Drei Dinge, die diese Datei anders macht als jedes andere Formular
|
||||||
|
im Projekt — jedes davon folgt unmittelbar aus dem Gesetz:
|
||||||
|
|
||||||
|
1. KEINE ANMELDUNG. Der Widerruf muss "leicht zugänglich" sein.
|
||||||
|
Jemanden, der einen Vertrag loswerden will, erst durch eine
|
||||||
|
Anmeldung zu schicken, ist das Gegenteil davon. Wer sein Passwort
|
||||||
|
vergessen hat, könnte sonst die Frist verpassen.
|
||||||
|
|
||||||
|
2. KEINE PFLICHTFELDER ÜBER DAS GESETZLICHE MASS HINAUS. Ein Widerruf
|
||||||
|
braucht KEINE Begründung. Das Feld existiert, ist aber freiwillig
|
||||||
|
und auch so beschriftet.
|
||||||
|
|
||||||
|
3. DIE MENGENBEGRENZUNG DARF NIEMANDEN AUSSPERREN. Bei jedem anderen
|
||||||
|
Formular ist eine Sperre nach zu vielen Versuchen richtig. Hier
|
||||||
|
wäre sie ein Rechtsverlust: Wer in der letzten Stunde seiner Frist
|
||||||
|
dreimal auf "Absenden" klickt, weil das Netz hakt, darf nicht
|
||||||
|
abgewiesen werden. Deshalb wird oberhalb der Grenze verlangsamt
|
||||||
|
und protokolliert, aber der Widerruf immer angenommen.
|
||||||
|
===================================================================== */
|
||||||
|
|
||||||
|
import { db } from "../db.js";
|
||||||
|
import { json } from "../lib/http.js";
|
||||||
|
import { requireSession } from "../lib/auth.js";
|
||||||
|
import { hasPermission } from "../lib/permissions.js";
|
||||||
|
import { ausweisPruefen } from "../lib/webdesign-ausweis.js";
|
||||||
|
import {
|
||||||
|
jetzt, neueId, naechsteNummer, text, istEmail, emailNormal,
|
||||||
|
verlauf, besucherIp, ipHash, spracheOderDe,
|
||||||
|
} from "../lib/webdesign-helfer.js";
|
||||||
|
|
||||||
|
async function darf(req, recht) {
|
||||||
|
const ausweis = ausweisPruefen(req);
|
||||||
|
if (ausweis) return { erlaubt: true, sitzung: { actor: ausweis.rolle, isOwner: true } };
|
||||||
|
const sitzung = await requireSession(req);
|
||||||
|
if (!sitzung) return { erlaubt: false, grund: 401, sitzung: null };
|
||||||
|
if (sitzung.isOwner) return { erlaubt: true, sitzung };
|
||||||
|
const ok = hasPermission(sitzung.permissions, sitzung.overrides, recht);
|
||||||
|
return { erlaubt: ok, grund: ok ? null : 403, sitzung };
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ---------------------------------------------------------------------
|
||||||
|
WIDERRUF ENTGEGENNEHMEN — öffentlich, ohne Anmeldung
|
||||||
|
|
||||||
|
POST /webdesign/widerruf
|
||||||
|
--------------------------------------------------------------------- */
|
||||||
|
export function widerrufAbsenden(req, res) {
|
||||||
|
const b = req.body || {};
|
||||||
|
|
||||||
|
const name = text(b.name, 200);
|
||||||
|
const kontakt = emailNormal(b.kontakt);
|
||||||
|
const vertrag = text(b.vertrag, 300);
|
||||||
|
|
||||||
|
/* Nur die drei Angaben, die § 356a nennt, sind Pflicht — Name,
|
||||||
|
Vertragskennzeichnung und ein Kommunikationsmittel für die
|
||||||
|
Eingangsbestätigung. Mehr zu verlangen wäre eine Hürde, die das
|
||||||
|
Gesetz gerade abbauen will. */
|
||||||
|
const fehlt = [];
|
||||||
|
if (!name) fehlt.push("name");
|
||||||
|
if (!vertrag) fehlt.push("vertrag");
|
||||||
|
if (!istEmail(kontakt)) fehlt.push("kontakt");
|
||||||
|
if (fehlt.length) {
|
||||||
|
return json(res, {
|
||||||
|
ok: false,
|
||||||
|
fehlt,
|
||||||
|
error: "Bitte Name, Vertrag und eine E-Mail-Adresse angeben — mehr brauchen wir nicht.",
|
||||||
|
}, 400);
|
||||||
|
}
|
||||||
|
|
||||||
|
const id = neueId();
|
||||||
|
const nummer = naechsteNummer("wd_widerrufe", "W");
|
||||||
|
const zeit = jetzt();
|
||||||
|
|
||||||
|
/* Zuordnung wird VERSUCHT, ist aber nie Bedingung. Findet sich kein
|
||||||
|
passender Kunde, wird der Widerruf trotzdem gespeichert — er ist
|
||||||
|
wirksam, sobald er zugeht, unabhängig davon, ob unser System ihn
|
||||||
|
einem Datensatz zuordnen kann. */
|
||||||
|
let kundeId = null;
|
||||||
|
let projektId = null;
|
||||||
|
try {
|
||||||
|
const kunde = db.prepare(`SELECT id FROM wd_kunden WHERE email = ?`).get(kontakt);
|
||||||
|
if (kunde) kundeId = kunde.id;
|
||||||
|
const projekt = db
|
||||||
|
.prepare(`SELECT id FROM wd_projekte WHERE nummer = ?`)
|
||||||
|
.get(vertrag.trim().toUpperCase());
|
||||||
|
if (projekt) projektId = projekt.id;
|
||||||
|
} catch (e) { /* Zuordnung ist Komfort, kein Muss. */ }
|
||||||
|
|
||||||
|
db.prepare(
|
||||||
|
`INSERT INTO wd_widerrufe
|
||||||
|
(id, nummer, name, kontakt, vertrag, betrifft, begruendung,
|
||||||
|
kunde_id, projekt_id, eingegangen_am, ip_hash, sprache,
|
||||||
|
bestaetigt_am, bestaetigung_weg, status)
|
||||||
|
VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,'anzeige','eingegangen')`
|
||||||
|
).run(
|
||||||
|
id, nummer, name, kontakt, vertrag,
|
||||||
|
text(b.betrifft, 300) || null,
|
||||||
|
text(b.begruendung, 2000) || null,
|
||||||
|
kundeId, projektId, zeit, ipHash(besucherIp(req)),
|
||||||
|
spracheOderDe(b.sprache), zeit
|
||||||
|
);
|
||||||
|
|
||||||
|
verlauf("widerruf", id, "eingegangen", "kunde", { nummer, vertrag });
|
||||||
|
|
||||||
|
/* Die Eingangsbestätigung. § 356a verlangt sie "unverzüglich" und auf
|
||||||
|
einem dauerhaften Datenträger, mit dem Inhalt der Erklärung und dem
|
||||||
|
Zeitpunkt.
|
||||||
|
|
||||||
|
Sie wird hier VOLLSTÄNDIG zurückgegeben, damit die Seite sie sofort
|
||||||
|
anzeigen und zum Ausdrucken bzw. Speichern anbieten kann. Auf einen
|
||||||
|
E-Mail-Versand als EINZIGEN Weg zu setzen wäre fahrlässig: Mails
|
||||||
|
landen im Spam oder werden vom Anbieter verzögert, und dann steht
|
||||||
|
der Verbraucher ohne Nachweis da. Die Anzeige ist sofort und
|
||||||
|
zuverlässig; die Datenbank ist der dauerhafte Nachweis auf unserer
|
||||||
|
Seite. */
|
||||||
|
return json(res, {
|
||||||
|
ok: true,
|
||||||
|
nummer,
|
||||||
|
eingegangenAm: zeit,
|
||||||
|
bestaetigung: {
|
||||||
|
nummer,
|
||||||
|
name,
|
||||||
|
vertrag,
|
||||||
|
kontakt,
|
||||||
|
eingegangenAm: zeit,
|
||||||
|
empfaenger: "Filipe Pereira Queiroz, 3 rue Arthur Thinnes, L-3919 Mondercange, Luxemburg",
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ---------------------------------------------------------------------
|
||||||
|
VERWALTUNG
|
||||||
|
--------------------------------------------------------------------- */
|
||||||
|
|
||||||
|
/* GET /webdesign/admin/widerrufe */
|
||||||
|
export async function widerrufeListe(req, res) {
|
||||||
|
const p = await darf(req, "WD_KUNDEN_MANAGE");
|
||||||
|
if (!p.erlaubt) return json(res, { ok: false, error: "Kein Zugriff." }, p.grund);
|
||||||
|
|
||||||
|
const zeilen = db
|
||||||
|
.prepare(
|
||||||
|
`SELECT w.*, k.name AS kunde_name, pr.titel AS projekt_titel
|
||||||
|
FROM wd_widerrufe w
|
||||||
|
LEFT JOIN wd_kunden k ON k.id = w.kunde_id
|
||||||
|
LEFT JOIN wd_projekte pr ON pr.id = w.projekt_id
|
||||||
|
ORDER BY w.eingegangen_am DESC LIMIT 300`
|
||||||
|
)
|
||||||
|
.all();
|
||||||
|
|
||||||
|
const offen = zeilen.filter((z) => z.status === "eingegangen").length;
|
||||||
|
return json(res, { ok: true, widerrufe: zeilen, offen });
|
||||||
|
}
|
||||||
|
|
||||||
|
/* POST /webdesign/admin/widerrufe/:id
|
||||||
|
|
||||||
|
Nur Notiz und Bearbeitungsstand. Ein Widerruf lässt sich NICHT
|
||||||
|
löschen und nicht ablehnen — er ist wirksam, sobald er zugeht, und
|
||||||
|
die Zeile ist der Nachweis dafür. Ein System, in dem man den
|
||||||
|
unangenehmen Fall wegklicken kann, verliert genau dann seinen Wert,
|
||||||
|
wenn man ihn braucht. */
|
||||||
|
export async function widerrufBearbeiten(req, res) {
|
||||||
|
const p = await darf(req, "WD_KUNDEN_MANAGE");
|
||||||
|
if (!p.erlaubt) return json(res, { ok: false, error: "Kein Zugriff." }, p.grund);
|
||||||
|
|
||||||
|
const w = db.prepare(`SELECT * FROM wd_widerrufe WHERE id = ?`).get(String(req.params.id || ""));
|
||||||
|
if (!w) return json(res, { ok: false, error: "Nicht gefunden." }, 404);
|
||||||
|
|
||||||
|
const erledigt = req.body?.erledigt === true;
|
||||||
|
db.prepare(
|
||||||
|
`UPDATE wd_widerrufe SET status = ?, notiz = ?, erledigt_am = ? WHERE id = ?`
|
||||||
|
).run(
|
||||||
|
erledigt ? "bearbeitet" : "eingegangen",
|
||||||
|
text(req.body?.notiz, 2000) || w.notiz,
|
||||||
|
erledigt ? jetzt() : null,
|
||||||
|
w.id
|
||||||
|
);
|
||||||
|
|
||||||
|
verlauf("widerruf", w.id, erledigt ? "bearbeitet" : "wieder_offen",
|
||||||
|
"team:" + (p.sitzung?.actor || "?"), null);
|
||||||
|
return json(res, { ok: true });
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ---------------------------------------------------------------------
|
||||||
|
ZUSTIMMUNG ZUM SOFORTIGEN LEISTUNGSBEGINN FESTHALTEN
|
||||||
|
|
||||||
|
§ 356 Abs. 4 BGB. Die Beweislast liegt beim Unternehmer, deshalb wird
|
||||||
|
nicht nur der Haken gespeichert, sondern der WORTLAUT, den der Kunde
|
||||||
|
gesehen hat. Nur "hat zugestimmt" hilft im Streit wenig — es zählt,
|
||||||
|
WAS er bestätigt hat, und Texte ändern sich über die Jahre.
|
||||||
|
--------------------------------------------------------------------- */
|
||||||
|
export function zustimmungSpeichern({ kundeId, projektId, anfrageId, art, wortlaut, fassung, sprache, ip }) {
|
||||||
|
if (!wortlaut) return null;
|
||||||
|
const id = neueId();
|
||||||
|
db.prepare(
|
||||||
|
`INSERT INTO wd_zustimmungen
|
||||||
|
(id, kunde_id, projekt_id, anfrage_id, art, wortlaut, fassung, sprache, erteilt_am, ip_hash)
|
||||||
|
VALUES (?,?,?,?,?,?,?,?,?,?)`
|
||||||
|
).run(
|
||||||
|
id, kundeId || null, projektId || null, anfrageId || null,
|
||||||
|
art, wortlaut, fassung || null, spracheOderDe(sprache), jetzt(),
|
||||||
|
ip ? ipHash(ip) : null
|
||||||
|
);
|
||||||
|
return id;
|
||||||
|
}
|
||||||
@@ -423,8 +423,28 @@ export function webdesignGate(req, res, next) {
|
|||||||
Würde man sie sperren, lieferte die Schranke stattdessen eine
|
Würde man sie sperren, lieferte die Schranke stattdessen eine
|
||||||
302-Umleitung auf zugang.html — der Browser bekäme HTML statt
|
302-Umleitung auf zugang.html — der Browser bekäme HTML statt
|
||||||
JavaScript und bräche die Registrierung mit einem MIME-Fehler ab. */
|
JavaScript und bräche die Registrierung mit einem MIME-Fehler ab. */
|
||||||
|
/* WIDERRUF UND RECHTSTEXTE BLEIBEN IMMER OFFEN — auch vor der Freigabe
|
||||||
|
und auch ohne Zugangscode.
|
||||||
|
|
||||||
|
Das ist keine Bequemlichkeit, sondern Pflicht. § 356a BGB verlangt,
|
||||||
|
dass die Widerrufsfunktion während der gesamten Widerrufsfrist
|
||||||
|
"ständig verfügbar, hervorgehoben platziert und leicht zugänglich"
|
||||||
|
ist. Eine Zugangswand davor wäre das genaue Gegenteil: Wer seinen
|
||||||
|
Code verlegt hat, könnte seine Frist verlieren — und Fristverlust
|
||||||
|
durch eine selbstgebaute Hürde ist der schlimmste denkbare Fall.
|
||||||
|
|
||||||
|
Dasselbe gilt für die Rechtstexte: Die Widerrufsbelehrung muss
|
||||||
|
lesbar sein, BEVOR man widerruft, nicht danach.
|
||||||
|
|
||||||
|
Unbedenklich, weil beide Seiten ausschliesslich Texte enthalten, die
|
||||||
|
ohnehin für jeden bestimmt sind — keine Kunden-, Projekt- oder
|
||||||
|
Preisdaten. Der Widerrufs-Endpunkt nimmt nur entgegen und gibt
|
||||||
|
nichts preis: Er beantwortet keine Frage, sondern speichert eine
|
||||||
|
Erklärung. */
|
||||||
if (
|
if (
|
||||||
pfad === "/webdesign/zugang.html" ||
|
pfad === "/webdesign/zugang.html" ||
|
||||||
|
pfad === "/webdesign/widerruf.html" ||
|
||||||
|
pfad === "/webdesign/rechtliches.html" ||
|
||||||
pfad === "/webdesign/sw.js" ||
|
pfad === "/webdesign/sw.js" ||
|
||||||
pfad === "/webdesign/app.webmanifest"
|
pfad === "/webdesign/app.webmanifest"
|
||||||
) {
|
) {
|
||||||
|
|||||||
@@ -13,8 +13,8 @@
|
|||||||
<meta name="apple-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="black-translucent" />
|
<meta name="apple-mobile-web-app-status-bar-style" content="black-translucent" />
|
||||||
<meta name="apple-mobile-web-app-title" content="Dogfather Webdesign" />
|
<meta name="apple-mobile-web-app-title" content="Dogfather Webdesign" />
|
||||||
<link rel="stylesheet" href="/assets/css/main.css?v=20260823wd6" />
|
<link rel="stylesheet" href="/assets/css/main.css?v=20260823wd7" />
|
||||||
<link rel="stylesheet" href="/assets/css/webdesign.css?v=20260823wd6" />
|
<link rel="stylesheet" href="/assets/css/webdesign.css?v=20260823wd7" />
|
||||||
<style>
|
<style>
|
||||||
/* Zeitstrahl: die Linie verbindet die Nummernkreise. Sie liegt hinter
|
/* Zeitstrahl: die Linie verbindet die Nummernkreise. Sie liegt hinter
|
||||||
den Kreisen (z-index) und endet beim letzten Schritt, damit sie nicht
|
den Kreisen (z-index) und endet beim letzten Schritt, damit sie nicht
|
||||||
@@ -183,7 +183,7 @@
|
|||||||
|
|
||||||
<div id="wd-fuss"></div>
|
<div id="wd-fuss"></div>
|
||||||
|
|
||||||
<script src="/assets/js/i18n-wd-ablauf.js?v=20260823wd6"></script>
|
<script src="/assets/js/i18n-wd-ablauf.js?v=20260823wd7"></script>
|
||||||
<script src="/assets/js/wd-core.js?v=20260823wd6"></script>
|
<script src="/assets/js/wd-core.js?v=20260823wd7"></script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|||||||
@@ -13,8 +13,8 @@
|
|||||||
<meta name="apple-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="black-translucent" />
|
<meta name="apple-mobile-web-app-status-bar-style" content="black-translucent" />
|
||||||
<meta name="apple-mobile-web-app-title" content="Dogfather Webdesign" />
|
<meta name="apple-mobile-web-app-title" content="Dogfather Webdesign" />
|
||||||
<link rel="stylesheet" href="/assets/css/main.css?v=20260823wd6" />
|
<link rel="stylesheet" href="/assets/css/main.css?v=20260823wd7" />
|
||||||
<link rel="stylesheet" href="/assets/css/webdesign.css?v=20260823wd6" />
|
<link rel="stylesheet" href="/assets/css/webdesign.css?v=20260823wd7" />
|
||||||
<style>
|
<style>
|
||||||
/* ---------- Schrittanzeige ----------
|
/* ---------- Schrittanzeige ----------
|
||||||
Zeigt alle vier Schritte gleichzeitig statt nur "3 von 4". Man sieht
|
Zeigt alle vier Schritte gleichzeitig statt nur "3 von 4". Man sieht
|
||||||
@@ -418,8 +418,8 @@
|
|||||||
|
|
||||||
<div id="wd-fuss"></div>
|
<div id="wd-fuss"></div>
|
||||||
|
|
||||||
<script src="/assets/js/i18n-wd-anfrage.js?v=20260823wd6"></script>
|
<script src="/assets/js/i18n-wd-anfrage.js?v=20260823wd7"></script>
|
||||||
<script src="/assets/js/wd-core.js?v=20260823wd6"></script>
|
<script src="/assets/js/wd-core.js?v=20260823wd7"></script>
|
||||||
<script src="/assets/js/wd-anfrage.js?v=20260823wd6"></script>
|
<script src="/assets/js/wd-anfrage.js?v=20260823wd7"></script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|||||||
+4
-4
@@ -13,8 +13,8 @@
|
|||||||
<meta name="apple-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="black-translucent" />
|
<meta name="apple-mobile-web-app-status-bar-style" content="black-translucent" />
|
||||||
<meta name="apple-mobile-web-app-title" content="Dogfather Webdesign" />
|
<meta name="apple-mobile-web-app-title" content="Dogfather Webdesign" />
|
||||||
<link rel="stylesheet" href="/assets/css/main.css?v=20260823wd6" />
|
<link rel="stylesheet" href="/assets/css/main.css?v=20260823wd7" />
|
||||||
<link rel="stylesheet" href="/assets/css/webdesign.css?v=20260823wd6" />
|
<link rel="stylesheet" href="/assets/css/webdesign.css?v=20260823wd7" />
|
||||||
<style>
|
<style>
|
||||||
.fq-thema { scroll-margin-top: 90px; }
|
.fq-thema { scroll-margin-top: 90px; }
|
||||||
.fq-thema + .fq-thema { margin-top: clamp(2rem, 4vw, 3rem); }
|
.fq-thema + .fq-thema { margin-top: clamp(2rem, 4vw, 3rem); }
|
||||||
@@ -195,7 +195,7 @@
|
|||||||
|
|
||||||
<div id="wd-fuss"></div>
|
<div id="wd-fuss"></div>
|
||||||
|
|
||||||
<script src="/assets/js/i18n-wd-faq.js?v=20260823wd6"></script>
|
<script src="/assets/js/i18n-wd-faq.js?v=20260823wd7"></script>
|
||||||
<script src="/assets/js/wd-core.js?v=20260823wd6"></script>
|
<script src="/assets/js/wd-core.js?v=20260823wd7"></script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|||||||
@@ -16,8 +16,8 @@
|
|||||||
<meta name="apple-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="black-translucent" />
|
<meta name="apple-mobile-web-app-status-bar-style" content="black-translucent" />
|
||||||
<meta name="apple-mobile-web-app-title" content="Dogfather Webdesign" />
|
<meta name="apple-mobile-web-app-title" content="Dogfather Webdesign" />
|
||||||
<link rel="stylesheet" href="/assets/css/main.css?v=20260823wd6" />
|
<link rel="stylesheet" href="/assets/css/main.css?v=20260823wd7" />
|
||||||
<link rel="stylesheet" href="/assets/css/webdesign.css?v=20260823wd6" />
|
<link rel="stylesheet" href="/assets/css/webdesign.css?v=20260823wd7" />
|
||||||
<style>
|
<style>
|
||||||
/* ---------- Nur auf der Startseite gebraucht ---------- */
|
/* ---------- Nur auf der Startseite gebraucht ---------- */
|
||||||
|
|
||||||
@@ -407,8 +407,8 @@
|
|||||||
|
|
||||||
<div id="wd-fuss"></div>
|
<div id="wd-fuss"></div>
|
||||||
|
|
||||||
<script src="/assets/js/i18n-wd-index.js?v=20260823wd6"></script>
|
<script src="/assets/js/i18n-wd-index.js?v=20260823wd7"></script>
|
||||||
<script src="/assets/js/wd-core.js?v=20260823wd6"></script>
|
<script src="/assets/js/wd-core.js?v=20260823wd7"></script>
|
||||||
<script>
|
<script>
|
||||||
/* ---------------------------------------------------------------------
|
/* ---------------------------------------------------------------------
|
||||||
Marken-Intro (Masterplan S.3, Schritt 02: "Nach erfolgreichem Code
|
Marken-Intro (Masterplan S.3, Schritt 02: "Nach erfolgreichem Code
|
||||||
|
|||||||
@@ -13,8 +13,8 @@
|
|||||||
<meta name="apple-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="black-translucent" />
|
<meta name="apple-mobile-web-app-status-bar-style" content="black-translucent" />
|
||||||
<meta name="apple-mobile-web-app-title" content="Dogfather Webdesign" />
|
<meta name="apple-mobile-web-app-title" content="Dogfather Webdesign" />
|
||||||
<link rel="stylesheet" href="/assets/css/main.css?v=20260823wd6" />
|
<link rel="stylesheet" href="/assets/css/main.css?v=20260823wd7" />
|
||||||
<link rel="stylesheet" href="/assets/css/webdesign.css?v=20260823wd6" />
|
<link rel="stylesheet" href="/assets/css/webdesign.css?v=20260823wd7" />
|
||||||
<style>
|
<style>
|
||||||
/* Sprungmarken.
|
/* Sprungmarken.
|
||||||
Frueher eine waagerecht wischbare Leiste. Rueckmeldung 22.08.2026:
|
Frueher eine waagerecht wischbare Leiste. Rueckmeldung 22.08.2026:
|
||||||
@@ -331,7 +331,7 @@
|
|||||||
|
|
||||||
<div id="wd-fuss"></div>
|
<div id="wd-fuss"></div>
|
||||||
|
|
||||||
<script src="/assets/js/i18n-wd-leistungen.js?v=20260823wd6"></script>
|
<script src="/assets/js/i18n-wd-leistungen.js?v=20260823wd7"></script>
|
||||||
<script src="/assets/js/wd-core.js?v=20260823wd6"></script>
|
<script src="/assets/js/wd-core.js?v=20260823wd7"></script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|||||||
@@ -12,8 +12,8 @@
|
|||||||
<meta name="apple-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="black-translucent" />
|
<meta name="apple-mobile-web-app-status-bar-style" content="black-translucent" />
|
||||||
<meta name="apple-mobile-web-app-title" content="Dogfather Webdesign" />
|
<meta name="apple-mobile-web-app-title" content="Dogfather Webdesign" />
|
||||||
<link rel="stylesheet" href="/assets/css/main.css?v=20260823wd6" />
|
<link rel="stylesheet" href="/assets/css/main.css?v=20260823wd7" />
|
||||||
<link rel="stylesheet" href="/assets/css/webdesign.css?v=20260823wd6" />
|
<link rel="stylesheet" href="/assets/css/webdesign.css?v=20260823wd7" />
|
||||||
<style>
|
<style>
|
||||||
.po-login { max-width: 440px; margin: clamp(1.5rem,6vh,4rem) auto; }
|
.po-login { max-width: 440px; margin: clamp(1.5rem,6vh,4rem) auto; }
|
||||||
|
|
||||||
@@ -134,8 +134,8 @@
|
|||||||
|
|
||||||
<div id="wd-fuss"></div>
|
<div id="wd-fuss"></div>
|
||||||
|
|
||||||
<script src="/assets/js/i18n-wd-portal.js?v=20260823wd6"></script>
|
<script src="/assets/js/i18n-wd-portal.js?v=20260823wd7"></script>
|
||||||
<script src="/assets/js/wd-core.js?v=20260823wd6"></script>
|
<script src="/assets/js/wd-core.js?v=20260823wd7"></script>
|
||||||
<script src="/assets/js/wd-portal.js?v=20260823wd6"></script>
|
<script src="/assets/js/wd-portal.js?v=20260823wd7"></script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|||||||
@@ -13,8 +13,8 @@
|
|||||||
<meta name="apple-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="black-translucent" />
|
<meta name="apple-mobile-web-app-status-bar-style" content="black-translucent" />
|
||||||
<meta name="apple-mobile-web-app-title" content="Dogfather Webdesign" />
|
<meta name="apple-mobile-web-app-title" content="Dogfather Webdesign" />
|
||||||
<link rel="stylesheet" href="/assets/css/main.css?v=20260823wd6" />
|
<link rel="stylesheet" href="/assets/css/main.css?v=20260823wd7" />
|
||||||
<link rel="stylesheet" href="/assets/css/webdesign.css?v=20260823wd6" />
|
<link rel="stylesheet" href="/assets/css/webdesign.css?v=20260823wd7" />
|
||||||
<style>
|
<style>
|
||||||
.pfo-projekt { overflow: hidden; padding: 0; margin-bottom: clamp(1.4rem, 3vw, 2.2rem); }
|
.pfo-projekt { overflow: hidden; padding: 0; margin-bottom: clamp(1.4rem, 3vw, 2.2rem); }
|
||||||
.pfo-bild {
|
.pfo-bild {
|
||||||
@@ -192,7 +192,7 @@
|
|||||||
|
|
||||||
<div id="wd-fuss"></div>
|
<div id="wd-fuss"></div>
|
||||||
|
|
||||||
<script src="/assets/js/i18n-wd-portfolio.js?v=20260823wd6"></script>
|
<script src="/assets/js/i18n-wd-portfolio.js?v=20260823wd7"></script>
|
||||||
<script src="/assets/js/wd-core.js?v=20260823wd6"></script>
|
<script src="/assets/js/wd-core.js?v=20260823wd7"></script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|||||||
@@ -13,8 +13,8 @@
|
|||||||
<meta name="apple-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="black-translucent" />
|
<meta name="apple-mobile-web-app-status-bar-style" content="black-translucent" />
|
||||||
<meta name="apple-mobile-web-app-title" content="Dogfather Webdesign" />
|
<meta name="apple-mobile-web-app-title" content="Dogfather Webdesign" />
|
||||||
<link rel="stylesheet" href="/assets/css/main.css?v=20260823wd6" />
|
<link rel="stylesheet" href="/assets/css/main.css?v=20260823wd7" />
|
||||||
<link rel="stylesheet" href="/assets/css/webdesign.css?v=20260823wd6" />
|
<link rel="stylesheet" href="/assets/css/webdesign.css?v=20260823wd7" />
|
||||||
<style>
|
<style>
|
||||||
.pr-preis-gross { font-family: var(--font-head); font-weight: 800; color: var(--wd-blau); white-space: nowrap; }
|
.pr-preis-gross { font-family: var(--font-head); font-weight: 800; color: var(--wd-blau); white-space: nowrap; }
|
||||||
|
|
||||||
@@ -196,7 +196,7 @@
|
|||||||
|
|
||||||
<div id="wd-fuss"></div>
|
<div id="wd-fuss"></div>
|
||||||
|
|
||||||
<script src="/assets/js/i18n-wd-preise.js?v=20260823wd6"></script>
|
<script src="/assets/js/i18n-wd-preise.js?v=20260823wd7"></script>
|
||||||
<script src="/assets/js/wd-core.js?v=20260823wd6"></script>
|
<script src="/assets/js/wd-core.js?v=20260823wd7"></script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|||||||
@@ -0,0 +1,405 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="de">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8" />
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||||
|
<title>Impressum, AGB & Datenschutz — Dogfather Webdesign</title>
|
||||||
|
<meta name="description" content="Impressum, Allgemeine Geschäftsbedingungen, Widerrufsbelehrung und Datenschutzerklärung für Dogfather Webdesign." />
|
||||||
|
<link rel="icon" type="image/png" href="/assets/img/favicon.png" />
|
||||||
|
<link rel="manifest" href="/webdesign/app.webmanifest" />
|
||||||
|
<meta name="theme-color" content="#05070b" />
|
||||||
|
<link rel="stylesheet" href="/assets/css/main.css?v=20260823wd7" />
|
||||||
|
<link rel="stylesheet" href="/assets/css/webdesign.css?v=20260823wd7" />
|
||||||
|
</head>
|
||||||
|
<body class="wd">
|
||||||
|
<div id="wd-kopf"></div>
|
||||||
|
|
||||||
|
<main>
|
||||||
|
<section class="wd-abschnitt">
|
||||||
|
<div class="wd-container wd-schmal">
|
||||||
|
|
||||||
|
<span class="wd-eyebrow">Rechtliches</span>
|
||||||
|
<h1>Impressum, AGB & Datenschutz</h1>
|
||||||
|
<p class="wd-fuehrung">
|
||||||
|
Alles Rechtliche zu Dogfather Webdesign an einer Stelle — Impressum,
|
||||||
|
Geschäftsbedingungen, Widerrufsrecht und Datenschutz.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<!-- Sprunganker. Rechtstexte sind lang; wer etwas Bestimmtes sucht,
|
||||||
|
soll nicht scrollen müssen. -->
|
||||||
|
<nav class="rl-nav" aria-label="Abschnitte">
|
||||||
|
<a href="#impressum">Impressum</a>
|
||||||
|
<a href="#agb">AGB</a>
|
||||||
|
<a href="#widerruf">Widerrufsrecht</a>
|
||||||
|
<a href="#datenschutz">Datenschutz</a>
|
||||||
|
</nav>
|
||||||
|
|
||||||
|
<div class="wd-hinweis rl-sprache">
|
||||||
|
<p class="wd-hinweis-titel">Vertragssprache ist Deutsch</p>
|
||||||
|
<p class="wd-klein">
|
||||||
|
Die Seite gibt es in fünf Sprachen, damit man sich zurechtfindet. Verträge werden
|
||||||
|
aber ausschließlich auf Deutsch geschlossen, und diese Rechtstexte sind allein in
|
||||||
|
der deutschen Fassung verbindlich. Wenn du in einer anderen Sprache abschließen
|
||||||
|
möchtest, sprich mich vorher an — dann klären wir das im Einzelfall.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- ================= IMPRESSUM ================= -->
|
||||||
|
<h2 id="impressum" class="rl-h2">Impressum</h2>
|
||||||
|
<div class="wd-karte rl-text">
|
||||||
|
<p>
|
||||||
|
<strong>Filipe Pereira Queiroz</strong><br />
|
||||||
|
3, rue Arthur Thinnes<br />
|
||||||
|
L-3919 Mondercange<br />
|
||||||
|
Luxemburg
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
Telefon: +49 177 3292805<br />
|
||||||
|
E-Mail: <a href="mailto:[email protected]">[email protected]</a>
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
<strong>Verantwortlich für den Inhalt:</strong> Filipe Pereira Queiroz (Anschrift wie oben).
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
<strong>Umsatzsteuer:</strong> Der Anbieter ist eine Kleinstunternehmung mit Sitz in
|
||||||
|
Luxemburg und macht von der Kleinunternehmerregelung Gebrauch (Art. 57 des geänderten
|
||||||
|
luxemburgischen TVA-Gesetzes vom 12.02.1979 sowie der EU-weiten Kleinunternehmerregelung
|
||||||
|
nach Richtlinie (EU) 2020/285). <strong>Alle genannten Preise sind Endpreise und enthalten
|
||||||
|
keine gesondert ausgewiesene Mehrwertsteuer.</strong>
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
<strong>EU-Streitschlichtung:</strong> Die Europäische Kommission stellt eine Plattform zur
|
||||||
|
Online-Streitbeilegung bereit:
|
||||||
|
<a href="https://ec.europa.eu/consumers/odr/" target="_blank" rel="noopener">ec.europa.eu/consumers/odr</a>.
|
||||||
|
Die E-Mail-Adresse steht oben.
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
<strong>Verbraucherstreitbeilegung:</strong> Ich bin nicht bereit und nicht verpflichtet,
|
||||||
|
an Streitbeilegungsverfahren vor einer Verbraucherschlichtungsstelle teilzunehmen.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- ================= AGB ================= -->
|
||||||
|
<h2 id="agb" class="rl-h2">Allgemeine Geschäftsbedingungen</h2>
|
||||||
|
<p class="wd-klein wd-leise">Fassung vom 23.08.2026</p>
|
||||||
|
<div class="wd-karte rl-text">
|
||||||
|
|
||||||
|
<h3>1. Geltungsbereich und Vertragspartner</h3>
|
||||||
|
<p>
|
||||||
|
Diese Bedingungen gelten für alle Verträge über Webdesign- und Entwicklungsleistungen
|
||||||
|
zwischen Filipe Pereira Queiroz (nachfolgend „Anbieter“) und dem Auftraggeber. Sie gelten
|
||||||
|
gegenüber Verbrauchern und Unternehmern; einzelne Regelungen gelten ausdrücklich nur
|
||||||
|
gegenüber Verbrauchern und sind dann so gekennzeichnet.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<h3>2. Angebot und Vertragsschluss</h3>
|
||||||
|
<p>
|
||||||
|
Die Darstellung der Pakete und Preise auf dieser Seite ist kein bindendes Angebot, sondern
|
||||||
|
eine Aufforderung zur Anfrage. Ein Vertrag kommt erst zustande, wenn der Anbieter ein
|
||||||
|
individuelles Angebot abgibt und der Auftraggeber dieses in Textform annimmt.
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
Innerhalb eines laufenden Projekts können im Kundenportal zusätzlich einzelne
|
||||||
|
<strong>Zusatzleistungen verbindlich beauftragt</strong> werden, indem der Auftraggeber ein
|
||||||
|
dort beziffertes Angebot annimmt. Der Preis und der zusätzliche Zeitbedarf werden vor der
|
||||||
|
Annahme angezeigt.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<h3>3. Leistungsumfang</h3>
|
||||||
|
<p>
|
||||||
|
Was zum Projekt gehört, ergibt sich aus dem individuellen Angebot und der Aufgabenliste im
|
||||||
|
Kundenportal. Punkte, die ausdrücklich als <em>nicht enthalten</em> gekennzeichnet sind,
|
||||||
|
gehören nicht zum Leistungsumfang und werden nur nach gesonderter Beauftragung erbracht.
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
Der Auftraggeber stellt die benötigten Inhalte (Texte, Bilder, Logos, Zugangsdaten)
|
||||||
|
rechtzeitig bereit und versichert, dass er über die erforderlichen Rechte daran verfügt.
|
||||||
|
Verzögerungen, die daraus entstehen, dass Inhalte fehlen, verschieben vereinbarte Termine
|
||||||
|
entsprechend.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<h3>4. Preise, Anzahlung und Zahlung</h3>
|
||||||
|
<p>
|
||||||
|
Es gilt der im Angebot genannte Preis. Alle Preise sind Endpreise; Mehrwertsteuer wird
|
||||||
|
nach der Kleinunternehmerregelung nicht gesondert ausgewiesen (siehe Impressum).
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
<strong>30 % Anzahlung</strong> werden mit Vertragsschluss fällig und reservieren das
|
||||||
|
Zeitfenster. Der Restbetrag wird nach Abnahme und vor der Veröffentlichung fällig.
|
||||||
|
Zusatzleistungen werden nach Annahme des jeweiligen Angebots abgerechnet.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<h3>5. Mitwirkung, Entwürfe und Abnahme</h3>
|
||||||
|
<p>
|
||||||
|
Im Preis enthalten sind die im Angebot genannten Korrekturrunden. Weitergehende Änderungen
|
||||||
|
werden vorher beziffert und erst nach Zustimmung ausgeführt.
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
Nach Fertigstellung prüft der Auftraggeber die Leistung. Die Abnahme erfolgt ausdrücklich
|
||||||
|
über das Kundenportal oder in Textform. Nimmt der Auftraggeber die Leistung in Gebrauch,
|
||||||
|
ohne innerhalb von 14 Tagen wesentliche Mängel zu rügen, gilt sie als abgenommen.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<h3>6. Nutzungsrechte</h3>
|
||||||
|
<p>
|
||||||
|
Der Auftraggeber erhält mit <strong>vollständiger Bezahlung</strong> das einfache, zeitlich
|
||||||
|
und räumlich unbeschränkte Recht, die erstellten Ergebnisse für den vereinbarten Zweck zu
|
||||||
|
nutzen. Bis zur vollständigen Bezahlung verbleiben alle Rechte beim Anbieter.
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
An eingesetzten Fremdbestandteilen (Open-Source-Bibliotheken, Schriften, Bildmaterial)
|
||||||
|
gelten die jeweiligen Lizenzen der Rechteinhaber. Der Anbieter weist auf Lizenzen hin, die
|
||||||
|
eine eigene Vergütung oder Namensnennung erfordern.
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
Der Anbieter darf das Projekt nur dann als Referenz zeigen, wenn der Auftraggeber dem
|
||||||
|
zugestimmt hat. Die Zustimmung ist jederzeit widerrufbar.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<h3>7. Termine</h3>
|
||||||
|
<p>
|
||||||
|
Genannte Termine sind Richtwerte, sofern sie nicht ausdrücklich als verbindlich vereinbart
|
||||||
|
wurden. Verbindliche Termine setzen voraus, dass die Mitwirkungspflichten aus Ziffer 3
|
||||||
|
rechtzeitig erfüllt werden.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<h3>8. Laufende Betreuung</h3>
|
||||||
|
<p>
|
||||||
|
Ein Betreuungsvertrag läuft monatlich und ist zum Ende eines Abrechnungsmonats kündbar.
|
||||||
|
Was er umfasst, steht im jeweiligen Angebot. Hosting- und Domainkosten Dritter sind nicht
|
||||||
|
enthalten, sofern nicht ausdrücklich anders vereinbart.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<h3>9. Gewährleistung und Haftung</h3>
|
||||||
|
<p>
|
||||||
|
Es gelten die gesetzlichen Vorschriften. Der Anbieter haftet unbeschränkt bei Vorsatz und
|
||||||
|
grober Fahrlässigkeit sowie bei Verletzung von Leben, Körper und Gesundheit. Bei einfacher
|
||||||
|
Fahrlässigkeit haftet er nur bei Verletzung wesentlicher Vertragspflichten und begrenzt auf
|
||||||
|
den vertragstypischen, vorhersehbaren Schaden.
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
Für Inhalte, die der Auftraggeber beisteuert, ist dieser selbst verantwortlich. Die
|
||||||
|
rechtliche Prüfung von Impressum, Datenschutzerklärung und sonstigen Pflichtangaben der
|
||||||
|
erstellten Website ist nicht Teil der Leistung; dafür ist eine fachkundige Stelle
|
||||||
|
hinzuzuziehen.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<h3>10. Widerrufsrecht für Verbraucher</h3>
|
||||||
|
<p>
|
||||||
|
Verbrauchern steht ein gesetzliches Widerrufsrecht zu. Einzelheiten stehen unter
|
||||||
|
<a href="#widerruf">Widerrufsrecht</a>. Der Widerruf kann jederzeit über die
|
||||||
|
<a href="/webdesign/widerruf.html">elektronische Widerrufsfunktion</a> erklärt werden.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<h3>11. Anwendbares Recht und Gerichtsstand</h3>
|
||||||
|
<p>
|
||||||
|
Es gilt luxemburgisches Recht unter Ausschluss des UN-Kaufrechts. Gegenüber Verbrauchern
|
||||||
|
gilt dies nur, soweit dadurch nicht der Schutz entzogen wird, der ihnen durch zwingende
|
||||||
|
Vorschriften des Rechts ihres gewöhnlichen Aufenthaltsstaats gewährt wird (Art. 6 Rom-I-VO).
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<h3>12. Schlussbestimmungen</h3>
|
||||||
|
<p>
|
||||||
|
Sollte eine Bestimmung unwirksam sein, bleibt der übrige Vertrag wirksam. Änderungen und
|
||||||
|
Ergänzungen bedürfen der Textform.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- ================= WIDERRUF ================= -->
|
||||||
|
<h2 id="widerruf" class="rl-h2">Widerrufsrecht für Verbraucher</h2>
|
||||||
|
<div class="wd-karte rl-text rl-widerruf">
|
||||||
|
|
||||||
|
<h3>Widerrufsbelehrung</h3>
|
||||||
|
<p><strong>Widerrufsrecht</strong></p>
|
||||||
|
<p>
|
||||||
|
Sie haben das Recht, binnen vierzehn Tagen ohne Angabe von Gründen diesen Vertrag zu
|
||||||
|
widerrufen. Die Widerrufsfrist beträgt vierzehn Tage ab dem Tag des Vertragsabschlusses.
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
Um Ihr Widerrufsrecht auszuüben, müssen Sie mir
|
||||||
|
</p>
|
||||||
|
<p class="rl-adresse">
|
||||||
|
Filipe Pereira Queiroz<br />
|
||||||
|
3, rue Arthur Thinnes<br />
|
||||||
|
L-3919 Mondercange, Luxemburg<br />
|
||||||
|
E-Mail: [email protected]
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
mittels einer eindeutigen Erklärung (z. B. ein mit der Post versandter Brief oder eine
|
||||||
|
E-Mail) über Ihren Entschluss, diesen Vertrag zu widerrufen, informieren. Sie können dafür
|
||||||
|
das beigefügte Muster-Widerrufsformular verwenden, das jedoch nicht vorgeschrieben ist.
|
||||||
|
Bequemer geht es über die
|
||||||
|
<a href="/webdesign/widerruf.html"><strong>elektronische Widerrufsfunktion</strong></a>;
|
||||||
|
dort erhalten Sie unverzüglich eine Bestätigung über den Eingang.
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
Zur Wahrung der Widerrufsfrist reicht es aus, dass Sie die Mitteilung über die Ausübung des
|
||||||
|
Widerrufsrechts vor Ablauf der Widerrufsfrist absenden.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<p><strong>Folgen des Widerrufs</strong></p>
|
||||||
|
<p>
|
||||||
|
Wenn Sie diesen Vertrag widerrufen, habe ich Ihnen alle Zahlungen, die ich von Ihnen
|
||||||
|
erhalten habe, unverzüglich und spätestens binnen vierzehn Tagen ab dem Tag zurückzuzahlen,
|
||||||
|
an dem die Mitteilung über Ihren Widerruf dieses Vertrags bei mir eingegangen ist. Für
|
||||||
|
diese Rückzahlung verwende ich dasselbe Zahlungsmittel, das Sie bei der ursprünglichen
|
||||||
|
Transaktion eingesetzt haben, es sei denn, mit Ihnen wurde ausdrücklich etwas anderes
|
||||||
|
vereinbart; in keinem Fall werden Ihnen wegen dieser Rückzahlung Entgelte berechnet.
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
Haben Sie verlangt, dass die Dienstleistung während der Widerrufsfrist beginnen soll, so
|
||||||
|
haben Sie mir einen angemessenen Betrag zu zahlen, der dem Anteil der bis zu dem Zeitpunkt,
|
||||||
|
zu dem Sie mich von der Ausübung des Widerrufsrechts unterrichten, bereits erbrachten
|
||||||
|
Dienstleistungen im Vergleich zum Gesamtumfang der im Vertrag vorgesehenen Dienstleistungen
|
||||||
|
entspricht.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<p><strong>Vorzeitiges Erlöschen des Widerrufsrechts</strong></p>
|
||||||
|
<p>
|
||||||
|
Ihr Widerrufsrecht erlischt bei einem Vertrag über Dienstleistungen vorzeitig, wenn ich die
|
||||||
|
Dienstleistung vollständig erbracht habe und mit der Ausführung erst begonnen habe,
|
||||||
|
nachdem Sie dazu Ihre <strong>ausdrückliche Zustimmung</strong> gegeben und gleichzeitig
|
||||||
|
Ihre <strong>Kenntnis davon bestätigt</strong> haben, dass Sie Ihr Widerrufsrecht bei
|
||||||
|
vollständiger Vertragserfüllung verlieren.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<h3>Muster-Widerrufsformular</h3>
|
||||||
|
<p class="wd-klein wd-leise">
|
||||||
|
Wenn Sie den Vertrag widerrufen wollen, füllen Sie bitte dieses Formular aus und senden
|
||||||
|
Sie es zurück. Sie müssen es nicht verwenden.
|
||||||
|
</p>
|
||||||
|
<div class="rl-formular">
|
||||||
|
<p>An Filipe Pereira Queiroz, 3, rue Arthur Thinnes, L-3919 Mondercange, Luxemburg,
|
||||||
|
E-Mail: [email protected]</p>
|
||||||
|
<p>
|
||||||
|
Hiermit widerrufe(n) ich/wir (*) den von mir/uns (*) abgeschlossenen Vertrag über die
|
||||||
|
Erbringung der folgenden Dienstleistung (*):
|
||||||
|
</p>
|
||||||
|
<p>_______________________________________________</p>
|
||||||
|
<p>Bestellt am (*) / erhalten am (*): _______________</p>
|
||||||
|
<p>Name des/der Verbraucher(s): _______________</p>
|
||||||
|
<p>Anschrift des/der Verbraucher(s): _______________</p>
|
||||||
|
<p>Unterschrift des/der Verbraucher(s) (nur bei Mitteilung auf Papier): _______________</p>
|
||||||
|
<p>Datum: _______________</p>
|
||||||
|
<p class="wd-klein wd-leise">(*) Unzutreffendes streichen.</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="wd-btn-reihe" style="margin-top:1.2rem">
|
||||||
|
<a class="wd-btn wd-btn--haupt" href="/webdesign/widerruf.html">Vertrag widerrufen</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- ================= DATENSCHUTZ ================= -->
|
||||||
|
<h2 id="datenschutz" class="rl-h2">Datenschutzerklärung</h2>
|
||||||
|
<p class="wd-klein wd-leise">Fassung vom 23.08.2026</p>
|
||||||
|
<div class="wd-karte rl-text">
|
||||||
|
|
||||||
|
<h3>1. Verantwortlicher</h3>
|
||||||
|
<p>
|
||||||
|
Filipe Pereira Queiroz, 3, rue Arthur Thinnes, L-3919 Mondercange, Luxemburg —
|
||||||
|
<a href="mailto:[email protected]">[email protected]</a>
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<h3>2. Hosting und Server-Protokolle</h3>
|
||||||
|
<p>
|
||||||
|
Diese Seite läuft auf einem eigenen Server der netcup GmbH (Daimlerstraße 25, 76185
|
||||||
|
Karlsruhe, Deutschland) im Rechenzentrum Nürnberg; mit netcup besteht ein Vertrag zur
|
||||||
|
Auftragsverarbeitung. Beim Aufruf verarbeitet der Server technisch notwendige Daten
|
||||||
|
(IP-Adresse, Zeitstempel, aufgerufene Adresse, Browserkennung) zur Bereitstellung,
|
||||||
|
Absicherung und Fehlersuche.
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
Vorgeschaltet ist Cloudflare (Cloudflare, Inc., 101 Townsend St, San Francisco, USA) als
|
||||||
|
Auslieferungsnetz und Schutz vor Überlastungsangriffen. Dabei verarbeitet Cloudflare
|
||||||
|
dieselben technischen Daten; eine Übermittlung in die USA ist nicht ausgeschlossen,
|
||||||
|
abgesichert über EU-Standardvertragsklauseln bzw. das EU-US Data Privacy Framework.
|
||||||
|
Rechtsgrundlage ist jeweils Art. 6 Abs. 1 lit. f DSGVO.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<h3>3. Zugangsschutz, Cookies und lokaler Speicher</h3>
|
||||||
|
<p>Es werden keine Werbe- oder Auswertungs-Cookies eingesetzt. Verwendet werden nur:</p>
|
||||||
|
<ul>
|
||||||
|
<li><code>dogiwd_zugang</code> — signiertes Sitzungs-Cookie der Zugangswand dieses
|
||||||
|
Bereichs, Art. 6 Abs. 1 lit. f DSGVO.</li>
|
||||||
|
<li><code>dogiwd_rolle</code> — merkt sich nur, welche Ansicht gilt.</li>
|
||||||
|
<li>Lokaler Browser-Speicher für die Anmeldung im Kundenportal und in der Verwaltung sowie
|
||||||
|
für den Zwischenspeicher des Anfrageformulars. Er verbleibt auf deinem Gerät.</li>
|
||||||
|
</ul>
|
||||||
|
<p>
|
||||||
|
Der Zwischenspeicher des Anfrageformulars löscht sich nach 30 Tagen selbst und lässt sich
|
||||||
|
jederzeit über den Formularschritt „Entwurf verwerfen“ entfernen.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<h3>4. Anfrageformular</h3>
|
||||||
|
<p>
|
||||||
|
Die dort angegebenen Daten (Name, Kontaktmöglichkeit, Angaben zum Vorhaben) werden zur
|
||||||
|
Bearbeitung der Anfrage gespeichert. Rechtsgrundlage ist Art. 6 Abs. 1 lit. b DSGVO
|
||||||
|
(vorvertragliche Maßnahmen). Zum Schutz vor automatisiertem Missbrauch wird ein
|
||||||
|
<strong>Hashwert</strong> der IP-Adresse gespeichert — nicht die Adresse selbst, und der
|
||||||
|
Hashwert lässt sich nicht zurückrechnen.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<h3>5. Kundenportal</h3>
|
||||||
|
<p>
|
||||||
|
Für den Zugang werden Name, E-Mail-Adresse, ein Passwort (nur als Hashwert gespeichert,
|
||||||
|
im Klartext nicht einsehbar), Projektdaten, Aufgabenstände und Nachrichten verarbeitet.
|
||||||
|
Rechtsgrundlage ist Art. 6 Abs. 1 lit. b DSGVO (Vertragserfüllung).
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<h3>6. Zahlungen</h3>
|
||||||
|
<p>
|
||||||
|
Zahlungen können über PayPal abgewickelt werden (PayPal (Europe) S.à r.l. et Cie, S.C.A.,
|
||||||
|
22-24 Boulevard Royal, L-2449 Luxemburg). Dabei übermittelt PayPal die zur Abwicklung
|
||||||
|
notwendigen Daten. Rechtsgrundlage ist Art. 6 Abs. 1 lit. b DSGVO. Näheres:
|
||||||
|
<a href="https://www.paypal.com/de/webapps/mpp/ua/privacy-full" target="_blank" rel="noopener">paypal.com/privacy</a>.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<h3>7. Widerrufe und Zustimmungen</h3>
|
||||||
|
<p>
|
||||||
|
Wird ein Widerruf über die elektronische Widerrufsfunktion erklärt, werden die dort
|
||||||
|
angegebenen Daten sowie der Zeitpunkt gespeichert. Das ist gesetzlich vorgeschrieben
|
||||||
|
(§ 356a BGB verlangt eine Eingangsbestätigung mit Zeitpunkt) und dient dem Nachweis für
|
||||||
|
beide Seiten. Rechtsgrundlage ist Art. 6 Abs. 1 lit. c DSGVO.
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
Ebenso wird die ausdrückliche Zustimmung zum vorzeitigen Leistungsbeginn samt dem
|
||||||
|
angezeigten Wortlaut und einem Zeitstempel gespeichert, weil die Beweislast dafür beim
|
||||||
|
Anbieter liegt (§ 356 Abs. 4 BGB).
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<h3>8. Speicherdauer</h3>
|
||||||
|
<p>
|
||||||
|
Daten werden gelöscht, sobald der Zweck entfällt und keine gesetzliche Aufbewahrungspflicht
|
||||||
|
entgegensteht. Für Unterlagen mit steuerlicher oder handelsrechtlicher Bedeutung gelten die
|
||||||
|
gesetzlichen Fristen. Widerrufsnachweise werden für die Dauer möglicher Ansprüche
|
||||||
|
aufbewahrt.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<h3>9. Deine Rechte</h3>
|
||||||
|
<p>
|
||||||
|
Du hast das Recht auf Auskunft (Art. 15), Berichtigung (Art. 16), Löschung (Art. 17),
|
||||||
|
Einschränkung (Art. 18), Datenübertragbarkeit (Art. 20) und Widerspruch (Art. 21 DSGVO).
|
||||||
|
Eine erteilte Einwilligung kannst du jederzeit mit Wirkung für die Zukunft widerrufen.
|
||||||
|
Wende dich dafür an die oben genannte Adresse.
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
Außerdem steht dir ein Beschwerderecht bei einer Aufsichtsbehörde zu, in Luxemburg bei der
|
||||||
|
<a href="https://cnpd.public.lu/" target="_blank" rel="noopener">Commission nationale pour
|
||||||
|
la protection des données (CNPD)</a>, oder bei der Behörde deines Aufenthaltsorts.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<p class="wd-klein wd-leise" style="margin-top:1.6rem">
|
||||||
|
Stand: 23.08.2026. Diese Texte werden angepasst, wenn sich Leistungen, Technik oder
|
||||||
|
Rechtslage ändern.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
</main>
|
||||||
|
|
||||||
|
<div id="wd-fuss"></div>
|
||||||
|
|
||||||
|
<script src="/assets/js/wd-core.js?v=20260823wd7"></script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
+1
-1
@@ -34,7 +34,7 @@
|
|||||||
Start alle alten Zwischenspeicher weg. Muss bei jeder Änderung an den
|
Start alle alten Zwischenspeicher weg. Muss bei jeder Änderung an den
|
||||||
Dateien unten hochgezählt werden, sonst hängen Nutzer auf einem alten
|
Dateien unten hochgezählt werden, sonst hängen Nutzer auf einem alten
|
||||||
Stand fest. */
|
Stand fest. */
|
||||||
const CACHE_NAME = "dogfather-webdesign-v6";
|
const CACHE_NAME = "dogfather-webdesign-v7";
|
||||||
|
|
||||||
/* Bausteine, die die Oberfläche zum Anzeigen braucht. Bewusst KEINE
|
/* Bausteine, die die Oberfläche zum Anzeigen braucht. Bewusst KEINE
|
||||||
HTML-Datei in dieser Liste. */
|
HTML-Datei in dieser Liste. */
|
||||||
|
|||||||
@@ -13,8 +13,8 @@
|
|||||||
<meta name="apple-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="black-translucent" />
|
<meta name="apple-mobile-web-app-status-bar-style" content="black-translucent" />
|
||||||
<meta name="apple-mobile-web-app-title" content="Dogfather Webdesign" />
|
<meta name="apple-mobile-web-app-title" content="Dogfather Webdesign" />
|
||||||
<link rel="stylesheet" href="/assets/css/main.css?v=20260823wd6" />
|
<link rel="stylesheet" href="/assets/css/main.css?v=20260823wd7" />
|
||||||
<link rel="stylesheet" href="/assets/css/webdesign.css?v=20260823wd6" />
|
<link rel="stylesheet" href="/assets/css/webdesign.css?v=20260823wd7" />
|
||||||
<style>
|
<style>
|
||||||
.ue-kopf { display: grid; grid-template-columns: minmax(0, .85fr) minmax(0, 1.15fr); gap: clamp(1.5rem, 4vw, 3rem); align-items: center; }
|
.ue-kopf { display: grid; grid-template-columns: minmax(0, .85fr) minmax(0, 1.15fr); gap: clamp(1.5rem, 4vw, 3rem); align-items: center; }
|
||||||
/* Auf dem Handy zuerst der Text, dann das Bild: sonst schiebt ein
|
/* Auf dem Handy zuerst der Text, dann das Bild: sonst schiebt ein
|
||||||
@@ -127,7 +127,7 @@
|
|||||||
|
|
||||||
<div id="wd-fuss"></div>
|
<div id="wd-fuss"></div>
|
||||||
|
|
||||||
<script src="/assets/js/i18n-wd-ueber.js?v=20260823wd6"></script>
|
<script src="/assets/js/i18n-wd-ueber.js?v=20260823wd7"></script>
|
||||||
<script src="/assets/js/wd-core.js?v=20260823wd6"></script>
|
<script src="/assets/js/wd-core.js?v=20260823wd7"></script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|||||||
@@ -11,8 +11,8 @@
|
|||||||
<link rel="apple-touch-icon" href="/assets/img/wd-app-180.png" />
|
<link rel="apple-touch-icon" href="/assets/img/wd-app-180.png" />
|
||||||
<meta name="apple-mobile-web-app-capable" content="yes" />
|
<meta name="apple-mobile-web-app-capable" content="yes" />
|
||||||
<meta name="apple-mobile-web-app-title" content="DF Verwaltung" />
|
<meta name="apple-mobile-web-app-title" content="DF Verwaltung" />
|
||||||
<link rel="stylesheet" href="/assets/css/main.css?v=20260823wd6" />
|
<link rel="stylesheet" href="/assets/css/main.css?v=20260823wd7" />
|
||||||
<link rel="stylesheet" href="/assets/css/webdesign.css?v=20260823wd6" />
|
<link rel="stylesheet" href="/assets/css/webdesign.css?v=20260823wd7" />
|
||||||
<style>
|
<style>
|
||||||
/* =====================================================================
|
/* =====================================================================
|
||||||
Verwaltungsbereich — bewusst NUR AUF DEUTSCH.
|
Verwaltungsbereich — bewusst NUR AUF DEUTSCH.
|
||||||
@@ -753,7 +753,7 @@
|
|||||||
<div class="vw-detail-blatt" id="vw-detail-blatt"></div>
|
<div class="vw-detail-blatt" id="vw-detail-blatt"></div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<script src="/assets/js/wd-core.js?v=20260823wd6"></script>
|
<script src="/assets/js/wd-core.js?v=20260823wd7"></script>
|
||||||
<script>
|
<script>
|
||||||
/* =====================================================================
|
/* =====================================================================
|
||||||
Verwaltung: Anfragen ansehen, Status ändern, Notizen, Archivieren.
|
Verwaltung: Anfragen ansehen, Status ändern, Notizen, Archivieren.
|
||||||
|
|||||||
@@ -0,0 +1,144 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="de" data-i18n-titel="wr_titel">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8" />
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||||
|
<title>Vertrag widerrufen — Dogfather Webdesign</title>
|
||||||
|
<meta name="description" content="Elektronische Widerrufsfunktion nach § 356a BGB — Vertrag online widerrufen, mit sofortiger Eingangsbestätigung." />
|
||||||
|
<meta name="robots" content="noindex, follow" />
|
||||||
|
<link rel="icon" type="image/png" href="/assets/img/favicon.png" />
|
||||||
|
<link rel="manifest" href="/webdesign/app.webmanifest" />
|
||||||
|
<meta name="theme-color" content="#05070b" />
|
||||||
|
<link rel="stylesheet" href="/assets/css/main.css?v=20260823wd7" />
|
||||||
|
<link rel="stylesheet" href="/assets/css/webdesign.css?v=20260823wd7" />
|
||||||
|
</head>
|
||||||
|
<body class="wd">
|
||||||
|
<div id="wd-kopf"></div>
|
||||||
|
|
||||||
|
<main>
|
||||||
|
<section class="wd-abschnitt">
|
||||||
|
<div class="wd-container wd-schmal">
|
||||||
|
|
||||||
|
<span class="wd-eyebrow" data-i18n="wr_eyebrow">Widerruf</span>
|
||||||
|
<h1 data-i18n="wr_h1">Vertrag widerrufen</h1>
|
||||||
|
<p class="wd-fuehrung" data-i18n="wr_lead">
|
||||||
|
Hier kannst du deinen Vertrag ohne Angabe von Gründen widerrufen.
|
||||||
|
Du bekommst sofort eine Bestätigung mit Zeitpunkt — zum Speichern und Ausdrucken.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<!-- Die Belehrung steht ÜBER dem Formular, nicht darunter oder als
|
||||||
|
Link. Wer widerrufen will, soll vorher wissen, was gilt — nicht
|
||||||
|
erst hinterher nachlesen müssen. -->
|
||||||
|
<div class="wd-karte wr-info">
|
||||||
|
<h2 class="wr-h2" data-i18n="wr_frist_h">Wie lange habe ich Zeit?</h2>
|
||||||
|
<p data-i18n="wr_frist_t">
|
||||||
|
Die Widerrufsfrist beträgt <strong>14 Tage</strong> ab dem Tag des Vertragsschlusses.
|
||||||
|
Zur Wahrung der Frist reicht es, dass du den Widerruf vor Ablauf der Frist absendest.
|
||||||
|
</p>
|
||||||
|
<p class="wd-klein wd-leise" data-i18n="wr_frist_hinweis">
|
||||||
|
Wichtig bei Dienstleistungen: Wenn du ausdrücklich zugestimmt hast, dass vor Ablauf
|
||||||
|
der Frist mit der Arbeit begonnen wird, und bestätigt hast, dass du dadurch dein
|
||||||
|
Widerrufsrecht verlierst, erlischt es mit vollständiger Erbringung der Leistung.
|
||||||
|
Bis dahin schuldest du bei einem Widerruf nur den Wert dessen, was bis zum Widerruf
|
||||||
|
bereits geleistet wurde.
|
||||||
|
</p>
|
||||||
|
<p><a href="/webdesign/rechtliches.html#widerruf" data-i18n="wr_zur_belehrung">Vollständige Widerrufsbelehrung lesen</a></p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="wd-meldung" id="wr-meldung" hidden></div>
|
||||||
|
|
||||||
|
<!-- ============ SCHRITT 1: ANGABEN ============ -->
|
||||||
|
<form class="wd-karte" id="wr-form" novalidate>
|
||||||
|
<h2 class="wr-h2" data-i18n="wr_form_h">Deine Angaben</h2>
|
||||||
|
<p class="wd-klein wd-leise" data-i18n="wr_form_t">
|
||||||
|
Wir brauchen nur drei Dinge: deinen Namen, um welchen Vertrag es geht,
|
||||||
|
und wohin die Bestätigung soll.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<div class="wd-feld" id="wr-feld-name">
|
||||||
|
<label for="wr-name"><span data-i18n="wr_name">Dein Name</span><span class="wd-pflicht" aria-hidden="true">*</span></label>
|
||||||
|
<input type="text" id="wr-name" name="name" autocomplete="name" maxlength="200" />
|
||||||
|
<p class="wd-feld-fehler"></p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="wd-feld" id="wr-feld-vertrag">
|
||||||
|
<label for="wr-vertrag"><span data-i18n="wr_vertrag">Welcher Vertrag?</span><span class="wd-pflicht" aria-hidden="true">*</span></label>
|
||||||
|
<input type="text" id="wr-vertrag" name="vertrag" maxlength="300"
|
||||||
|
placeholder="z. B. P-2608-0001 oder „Website für Musterbau“" />
|
||||||
|
<span class="wd-feld-hilfe" data-i18n="wr_vertrag_hilfe">
|
||||||
|
Die Projektnummer steht in deinem Angebot und im Portal. Eine Beschreibung reicht aber auch.
|
||||||
|
</span>
|
||||||
|
<p class="wd-feld-fehler"></p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="wd-feld" id="wr-feld-kontakt">
|
||||||
|
<label for="wr-kontakt"><span data-i18n="wr_kontakt">E-Mail für die Bestätigung</span><span class="wd-pflicht" aria-hidden="true">*</span></label>
|
||||||
|
<input type="email" id="wr-kontakt" name="kontakt" autocomplete="email" inputmode="email" maxlength="200" />
|
||||||
|
<p class="wd-feld-fehler"></p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="wd-feld">
|
||||||
|
<label for="wr-betrifft"><span data-i18n="wr_betrifft">Betrifft nur einen Teil?</span> <span class="wd-leise" data-i18n="wr_freiwillig">freiwillig</span></label>
|
||||||
|
<input type="text" id="wr-betrifft" name="betrifft" maxlength="300" />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Ausdrücklich als freiwillig gekennzeichnet. Ein Widerruf
|
||||||
|
braucht KEINE Begründung, und ein Feld, das nach einer
|
||||||
|
verlangt, hält Leute davon ab. -->
|
||||||
|
<div class="wd-feld">
|
||||||
|
<label for="wr-grund"><span data-i18n="wr_grund">Grund</span> <span class="wd-leise" data-i18n="wr_grund_frei">freiwillig — du musst keinen angeben</span></label>
|
||||||
|
<textarea id="wr-grund" name="begruendung" rows="3" maxlength="2000"></textarea>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="wd-btn-reihe">
|
||||||
|
<button type="submit" class="wd-btn wd-btn--haupt" id="wr-weiter" data-i18n="wr_weiter">Weiter zur Bestätigung</button>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
|
||||||
|
<!-- ============ SCHRITT 2: BESTÄTIGEN ============
|
||||||
|
§ 356a verlangt ausdrücklich eine ZWEITE Schaltfläche mit der
|
||||||
|
Aufschrift "Widerruf bestätigen". Ein einstufiges Formular
|
||||||
|
würde die Vorschrift nicht erfüllen. -->
|
||||||
|
<div class="wd-karte wr-bestaetigen" id="wr-schritt2" hidden>
|
||||||
|
<h2 class="wr-h2" data-i18n="wr_pruef_h">Bitte prüfen und bestätigen</h2>
|
||||||
|
<p class="wd-klein wd-leise" data-i18n="wr_pruef_t">
|
||||||
|
Das schickst du ab. Danach ist der Widerruf erklärt.
|
||||||
|
</p>
|
||||||
|
<dl class="wr-pruef" id="wr-pruef"></dl>
|
||||||
|
<div class="wd-btn-reihe">
|
||||||
|
<button type="button" class="wd-btn wd-btn--haupt" id="wr-bestaetigen" data-i18n="wr_bestaetigen">Widerruf bestätigen</button>
|
||||||
|
<button type="button" class="wd-btn wd-btn--still" id="wr-zurueck" data-i18n="wr_aendern">Angaben ändern</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- ============ SCHRITT 3: EINGANGSBESTÄTIGUNG ============ -->
|
||||||
|
<div class="wd-karte wr-fertig" id="wr-schritt3" hidden>
|
||||||
|
<div class="wr-haken" aria-hidden="true">✓</div>
|
||||||
|
<h2 class="wr-h2" data-i18n="wr_fertig_h">Dein Widerruf ist eingegangen</h2>
|
||||||
|
<p data-i18n="wr_fertig_t">
|
||||||
|
Das hier ist deine Eingangsbestätigung. Speichere oder drucke sie —
|
||||||
|
sie ist dein Nachweis, dass du rechtzeitig widerrufen hast.
|
||||||
|
</p>
|
||||||
|
<div class="wr-beleg" id="wr-beleg"></div>
|
||||||
|
<div class="wd-btn-reihe wr-nicht-drucken">
|
||||||
|
<button type="button" class="wd-btn wd-btn--haupt" id="wr-drucken" data-i18n="wr_drucken">Bestätigung drucken oder als PDF sichern</button>
|
||||||
|
<a class="wd-btn wd-btn--still" href="/webdesign/" data-i18n="wr_zurueck_start">Zur Startseite</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<p class="wd-klein wd-leise wr-nicht-drucken" style="margin-top:1.4rem" data-i18n="wr_alternativ">
|
||||||
|
Du kannst den Widerruf auch formlos per E-Mail erklären — dieses Formular ist nur der
|
||||||
|
bequemere Weg, kein vorgeschriebener.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
</main>
|
||||||
|
|
||||||
|
<div id="wd-fuss"></div>
|
||||||
|
|
||||||
|
<script src="/assets/js/i18n-wd-widerruf.js?v=20260823wd7"></script>
|
||||||
|
<script src="/assets/js/wd-core.js?v=20260823wd7"></script>
|
||||||
|
<script src="/assets/js/wd-widerruf.js?v=20260823wd7"></script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
@@ -21,8 +21,8 @@
|
|||||||
<meta name="apple-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="black-translucent" />
|
<meta name="apple-mobile-web-app-status-bar-style" content="black-translucent" />
|
||||||
<meta name="apple-mobile-web-app-title" content="Dogfather Webdesign" />
|
<meta name="apple-mobile-web-app-title" content="Dogfather Webdesign" />
|
||||||
<link rel="stylesheet" href="/assets/css/main.css?v=20260823wd6" />
|
<link rel="stylesheet" href="/assets/css/main.css?v=20260823wd7" />
|
||||||
<link rel="stylesheet" href="/assets/css/webdesign.css?v=20260823wd6" />
|
<link rel="stylesheet" href="/assets/css/webdesign.css?v=20260823wd7" />
|
||||||
<style>
|
<style>
|
||||||
/* =====================================================================
|
/* =====================================================================
|
||||||
Zugangswand — nur hier gebraucht, deshalb bewusst NICHT in
|
Zugangswand — nur hier gebraucht, deshalb bewusst NICHT in
|
||||||
@@ -177,8 +177,8 @@
|
|||||||
</div>
|
</div>
|
||||||
</main>
|
</main>
|
||||||
|
|
||||||
<script src="/assets/js/i18n-wd-zugang.js?v=20260823wd6"></script>
|
<script src="/assets/js/i18n-wd-zugang.js?v=20260823wd7"></script>
|
||||||
<script src="/assets/js/wd-core.js?v=20260823wd6"></script>
|
<script src="/assets/js/wd-core.js?v=20260823wd7"></script>
|
||||||
<script>
|
<script>
|
||||||
(function () {
|
(function () {
|
||||||
"use strict";
|
"use strict";
|
||||||
|
|||||||
Reference in New Issue
Block a user