Commits vergleichen

..

5 Commits

Autor SHA1 Nachricht Datum
claude-dev
0492b11cf4 Testzugang: Freemail nicht mehr blockieren, nur Hinweis anzeigen
Alle plausiblen E-Mail-Adressen werden jetzt akzeptiert. Bei einer
Freemail-Adresse erscheint weiterhin der Hinweis, dass fuer einen
Testzugang ausschliesslich dienstliche Adressen von Behoerden oder
Unternehmen beruecksichtigt werden, aber die Anfrage wird nicht mehr
abgewiesen. Hinweis wird als tf-note-warn (Hinweis) statt tf-note-error
(Fehler) dargestellt, kein aria-invalid mehr. Texte de/en und
Modal-Subline entsprechend als Hinweis formuliert.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-23 10:51:51 +02:00
claude-dev
457f64ef0d Testzugang-Modal: Feld-Layout, Zulassungstext und State-Reset
- Formularfelder im Modal sauber ausgerichtet: Inputs einer Reihe per
  align-items:end buendig, fragiler min-height-Label-Hack entfernt,
  optional-Hinweise brechen nicht mehr unschoen um.
- Zulassungstext praezisiert: ausschliesslich dienstliche Adressen von
  Behoerden oder Unternehmen (de + en, Modal-Subline + Meldung).
- Datenschutz-Fix: Formular wird beim Oeffnen des Modals zurueckgesetzt
  (Felder, Erfolgsmeldung, Fehler), damit kein Nutzer die Eingaben eines
  vorherigen sieht.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-23 10:48:39 +02:00
claude-dev
8934b1a920 FAQ: DSGVO-Antwort praezisiert (KI-Analyse EU-Rechenzentrum, AVV, lokale Version)
Alte Aussage "keine Daten an Drittlaender uebermittelt" war mit
Cloud-KI-Nutzung nicht haltbar. Neue Fassung nennt EU-Verarbeitung,
kein Training, AVV nach Art. 28 und die lokale Version als souveraene
Option. JSON-LD und sichtbares HTML konsistent aktualisiert.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-23 10:32:33 +02:00
AegisSight
acb12e28b4 Formularfelder stehen wieder auf einer Linie
Der Zusatz "optional" stand als eigene Zeile unter dem Feldnamen. Dadurch
wurde das Label dreizeilig und das Feld rutschte gegenueber dem Nachbarn
um bis zu 48 Pixel nach unten, die Reihen wirkten schief. Der Zusatz
steht jetzt in derselben Zeile, und die Labels einer Reihe haben eine
gemeinsame Mindesthoehe, damit die Felder unabhaengig von der Textlaenge
auf einer Linie beginnen. Auf dem Telefon steht jedes Feld ohnehin allein,
dort entfaellt die Mindesthoehe.
2026-07-22 23:06:09 +02:00
AegisSight
d5851d8d99 Behoerde und Funktion optional, Pflichtfelder greifen jetzt wirklich
Behoerde oder Organisation und Funktion oder Dienststelle sind optional,
in beiden Sprachen mit Hinweis am Feld. Pflicht bleiben Name, dienstliche
E-Mail und die Einwilligung.

Dabei aufgefallen, das Formular traegt novalidate, damit die
Browsermeldungen die eigene Pruefung nicht ueberlagern. Die Pflichtfelder
wurden dadurch bisher gar nicht erzwungen, ein leerer Name waere
durchgegangen. Die Pruefung liegt jetzt im Skript, mit Meldung und Fokus
auf dem betroffenen Feld. Der Name steht ausserdem mit in der
Anfrage-Mail, da die Organisation leer bleiben kann.
2026-07-22 23:01:23 +02:00
5 geänderte Dateien mit 51 neuen und 22 gelöschten Zeilen

Datei anzeigen

@@ -294,7 +294,7 @@ a { color:inherit; text-decoration:none; }
.modal-close:hover { color:var(--navy); } .modal-close:hover { color:var(--navy); }
.modal-content h2 { font-size:1.5rem; font-weight:700; color:var(--navy); margin-bottom:8px; } .modal-content h2 { font-size:1.5rem; font-weight:700; color:var(--navy); margin-bottom:8px; }
.modal-sub { font-size:0.95rem; color:var(--text-light); margin-bottom:28px; } .modal-sub { font-size:0.95rem; color:var(--text-light); margin-bottom:28px; }
.form-row { display:grid; grid-template-columns:1fr 1fr; gap:16px; } .form-row { display:grid; grid-template-columns:1fr 1fr; gap:16px; align-items:end; }
.form-group { margin-bottom:16px; } .form-group { margin-bottom:16px; }
.form-group label { display:block; font-size:0.82rem; font-weight:600; color:var(--navy); margin-bottom:6px; text-transform:uppercase; letter-spacing:0.04em; } .form-group label { display:block; font-size:0.82rem; font-weight:600; color:var(--navy); margin-bottom:6px; text-transform:uppercase; letter-spacing:0.04em; }
.form-group input, .form-group textarea { width:100%; padding:10px 14px; border:1px solid var(--gray-200); border-radius:var(--radius); font-family:inherit; font-size:0.95rem; color:var(--text); background:var(--base); transition:border-color 0.2s; } .form-group input, .form-group textarea { width:100%; padding:10px 14px; border:1px solid var(--gray-200); border-radius:var(--radius); font-family:inherit; font-size:0.95rem; color:var(--text); background:var(--base); transition:border-color 0.2s; }
@@ -316,7 +316,10 @@ a { color:inherit; text-decoration:none; }
.tf-note { display:block; font-size:0.85rem; margin-top:6px; line-height:1.5; } .tf-note { display:block; font-size:0.85rem; margin-top:6px; line-height:1.5; }
.tf-note-error { color:#C0392B; } .tf-note-error { color:#C0392B; }
.tf-note-ok { color:#1E7A44; } .tf-note-ok { color:#1E7A44; }
.tf-hint { display:block; font-size:0.82rem; color:var(--text-light); margin-top:6px; font-weight:400; text-transform:none; letter-spacing:0; } .tf-note-warn { color:#8A6D1B; }
.tf-hint { font-size:0.82rem; color:var(--text-light); margin-left:5px; font-weight:400; text-transform:none; letter-spacing:0; white-space:nowrap; }
/* Kompakte Zeilenhoehe fuer mehrzeilige Labels; Felder einer Reihe per align-items:end buendig */
#trial-form .form-row label { line-height:1.35; }
.tf-consent { display:flex; align-items:flex-start; gap:12px; margin:6px 0 22px; } .tf-consent { display:flex; align-items:flex-start; gap:12px; margin:6px 0 22px; }
.tf-consent input { width:18px; height:18px; margin-top:2px; flex:0 0 auto; accent-color:var(--gold-dark); } .tf-consent input { width:18px; height:18px; margin-top:2px; flex:0 0 auto; accent-color:var(--gold-dark); }
.tf-consent label { font-size:0.9rem; color:var(--text-light); line-height:1.6; font-weight:400; text-transform:none; letter-spacing:0; margin:0; } .tf-consent label { font-size:0.9rem; color:var(--text-light); line-height:1.6; font-weight:400; text-transform:none; letter-spacing:0; margin:0; }

Datei anzeigen

@@ -748,7 +748,7 @@
<div class="modal-content"> <div class="modal-content">
<button class="modal-close" onclick="closeContactModal()" aria-label="Close">&times;</button> <button class="modal-close" onclick="closeContactModal()" aria-label="Close">&times;</button>
<h2>Request trial access</h2> <h2>Request trial access</h2>
<p class="modal-sub">Test the Monitor for one week with the full feature set, free of charge and without obligation. We need your work email address for the request.</p> <p class="modal-sub">Test the Monitor for one week with the full feature set, free of charge and without obligation. For trial access we only consider official email addresses from public authorities or companies.</p>
<div class="tf-error" id="tf-error" role="alert"></div> <div class="tf-error" id="tf-error" role="alert"></div>
@@ -759,14 +759,14 @@
<input type="text" id="tf-name" name="name" autocomplete="name" required> <input type="text" id="tf-name" name="name" autocomplete="name" required>
</div> </div>
<div class="form-group"> <div class="form-group">
<label for="tf-org">Authority or organisation</label> <label for="tf-org">Authority or organisation <span class="tf-hint">optional</span></label>
<input type="text" id="tf-org" name="organisation" autocomplete="organization" required> <input type="text" id="tf-org" name="organisation" autocomplete="organization">
</div> </div>
</div> </div>
<div class="form-row"> <div class="form-row">
<div class="form-group"> <div class="form-group">
<label for="tf-role">Role or department</label> <label for="tf-role">Role or department <span class="tf-hint">optional</span></label>
<input type="text" id="tf-role" name="role" autocomplete="organization-title" required> <input type="text" id="tf-role" name="role" autocomplete="organization-title">
</div> </div>
<div class="form-group"> <div class="form-group">
<label for="tf-phone">Phone <span class="tf-hint">optional</span></label> <label for="tf-phone">Phone <span class="tf-hint">optional</span></label>

Datei anzeigen

@@ -249,7 +249,7 @@
"name": "Ist AegisSight DSGVO-konform?", "name": "Ist AegisSight DSGVO-konform?",
"acceptedAnswer": { "acceptedAnswer": {
"@type": "Answer", "@type": "Answer",
"text": "AegisSight wird vollständig auf Servern in Deutschland betrieben und verarbeitet ausschließlich öffentlich zugängliche Quellen. Es werden keine Daten an Drittländer übermittelt." "text": "AegisSight wird auf Servern in Deutschland betrieben und verarbeitet ausschließlich öffentlich zugängliche Quellen. Für die KI-gestützte Analyse setzen wir ein Sprachmodell ein, das in einem Rechenzentrum in der EU (Frankfurt) läuft. Ihre Inhalte werden dabei nicht zum Training der Modelle verwendet, und es besteht ein Auftragsverarbeitungsvertrag nach Art. 28 DSGVO. Für Behörden und besonders sensible Einsätze bieten wir zusätzlich eine lokale Version an, die vollständig in Ihrer eigenen Infrastruktur läuft, ohne jede externe Datenübermittlung."
} }
}, },
{ {
@@ -707,7 +707,8 @@
<details class="faq-item"> <details class="faq-item">
<summary>Ist AegisSight DSGVO-konform?</summary> <summary>Ist AegisSight DSGVO-konform?</summary>
<p class="faq-answer">AegisSight wird vollständig auf Servern in Deutschland betrieben und verarbeitet ausschließlich öffentlich zugängliche Quellen. Es werden keine Daten an Drittländer übermittelt.</p> <p class="faq-answer">AegisSight wird auf Servern in Deutschland betrieben und verarbeitet ausschließlich öffentlich zugängliche Quellen. Für die KI-gestützte Analyse setzen wir ein Sprachmodell ein, das in einem Rechenzentrum in der EU (Frankfurt) läuft. Ihre Inhalte werden dabei nicht zum Training der Modelle verwendet, und es besteht ein Auftragsverarbeitungsvertrag nach Art. 28 DSGVO.</p>
<p class="faq-answer">Für Behörden und besonders sensible Einsätze bieten wir zusätzlich eine lokale Version an, die vollständig in Ihrer eigenen Infrastruktur läuft, ohne jede externe Datenübermittlung.</p>
</details> </details>
<details class="faq-item"> <details class="faq-item">
@@ -748,7 +749,7 @@
<div class="modal-content"> <div class="modal-content">
<button class="modal-close" onclick="closeContactModal()">&times;</button> <button class="modal-close" onclick="closeContactModal()">&times;</button>
<h2>Testzugang anfragen</h2> <h2>Testzugang anfragen</h2>
<p class="modal-sub">Eine Woche lang mit vollem Funktionsumfang testen, kostenfrei und ohne Verpflichtung. Für die Anfrage benötigen wir Ihre dienstliche E-Mail-Adresse.</p> <p class="modal-sub">Eine Woche lang mit vollem Funktionsumfang testen, kostenfrei und ohne Verpflichtung. Für einen Testzugang berücksichtigen wir ausschließlich dienstliche E-Mail-Adressen von Behörden oder Unternehmen.</p>
<div class="tf-error" id="tf-error" role="alert"></div> <div class="tf-error" id="tf-error" role="alert"></div>
@@ -759,14 +760,14 @@
<input type="text" id="tf-name" name="name" autocomplete="name" required> <input type="text" id="tf-name" name="name" autocomplete="name" required>
</div> </div>
<div class="form-group"> <div class="form-group">
<label for="tf-org">Behörde oder Organisation</label> <label for="tf-org">Behörde oder Organisation <span class="tf-hint">optional</span></label>
<input type="text" id="tf-org" name="organisation" autocomplete="organization" required> <input type="text" id="tf-org" name="organisation" autocomplete="organization">
</div> </div>
</div> </div>
<div class="form-row"> <div class="form-row">
<div class="form-group"> <div class="form-group">
<label for="tf-role">Funktion oder Dienststelle</label> <label for="tf-role">Funktion oder Dienststelle <span class="tf-hint">optional</span></label>
<input type="text" id="tf-role" name="role" autocomplete="organization-title" required> <input type="text" id="tf-role" name="role" autocomplete="organization-title">
</div> </div>
<div class="form-group"> <div class="form-group">
<label for="tf-phone">Telefon <span class="tf-hint">optional</span></label> <label for="tf-phone">Telefon <span class="tf-hint">optional</span></label>

Datei anzeigen

@@ -621,6 +621,7 @@ function mdToHtml(md) {
/* ==================== CONTACT MODAL ==================== */ /* ==================== CONTACT MODAL ==================== */
window.openContactModal = function () { window.openContactModal = function () {
if (typeof window.resetTrialForm === 'function') window.resetTrialForm();
document.getElementById('contact-modal').style.display = 'flex'; document.getElementById('contact-modal').style.display = 'flex';
document.body.style.overflow = 'hidden'; document.body.style.overflow = 'hidden';
if (window.umami) umami.track('contact_modal_open'); if (window.umami) umami.track('contact_modal_open');

Datei anzeigen

@@ -14,7 +14,8 @@
de: { de: {
sending: 'Wird gesendet...', sending: 'Wird gesendet...',
send: 'Testzugang anfragen', send: 'Testzugang anfragen',
freemail: 'Bitte geben Sie Ihre dienstliche E-Mail-Adresse an. Adressen bei Freemail-Anbietern wie Gmail, GMX oder web.de können wir für einen Testzugang nicht berücksichtigen.', nameFehlt: 'Bitte geben Sie Ihren Namen an.',
freemail: 'Für einen Testzugang berücksichtigen wir ausschließlich dienstliche E-Mail-Adressen von Behörden oder Unternehmen. Bitte verwenden Sie daher möglichst keine Freemail-Adresse wie Gmail, GMX oder web.de.',
invalidMail: 'Diese E-Mail-Adresse sieht nicht vollständig aus. Bitte prüfen Sie Ihre Eingabe.', invalidMail: 'Diese E-Mail-Adresse sieht nicht vollständig aus. Bitte prüfen Sie Ihre Eingabe.',
consent: 'Bitte bestätigen Sie die Datenschutzhinweise, damit wir Ihre Anfrage bearbeiten dürfen.', consent: 'Bitte bestätigen Sie die Datenschutzhinweise, damit wir Ihre Anfrage bearbeiten dürfen.',
tooFast: 'Bitte nehmen Sie sich einen Moment für das Formular. Versuchen Sie es gleich noch einmal.', tooFast: 'Bitte nehmen Sie sich einen Moment für das Formular. Versuchen Sie es gleich noch einmal.',
@@ -37,7 +38,8 @@
en: { en: {
sending: 'Sending...', sending: 'Sending...',
send: 'Request trial access', send: 'Request trial access',
freemail: 'Please enter your official work email address. Addresses from free providers such as Gmail, GMX or web.de cannot be accepted for trial access.', nameFehlt: 'Please enter your name.',
freemail: 'For trial access we only consider official email addresses from public authorities or companies. Please avoid free-provider addresses such as Gmail, GMX or web.de.',
invalidMail: 'This email address looks incomplete. Please check your entry.', invalidMail: 'This email address looks incomplete. Please check your entry.',
consent: 'Please confirm the privacy notice so that we may process your request.', consent: 'Please confirm the privacy notice so that we may process your request.',
tooFast: 'Please take a moment to fill in the form, then try again.', tooFast: 'Please take a moment to fill in the form, then try again.',
@@ -132,6 +134,23 @@
var mailField = document.getElementById('tf-email'); var mailField = document.getElementById('tf-email');
var mailNote = document.getElementById('tf-email-note'); var mailNote = document.getElementById('tf-email-note');
/* Formular in den Ausgangszustand zuruecksetzen. Wird beim Oeffnen
des Modals aufgerufen, damit niemand die Eingaben oder die
Erfolgsmeldung eines vorherigen Nutzers sieht. */
function resetTrialForm() {
form.reset();
form.style.display = '';
var ok = document.getElementById('tf-success');
if (ok) ok.style.display = 'none';
var errorBox = document.getElementById('tf-error');
if (errorBox) { errorBox.style.display = 'none'; errorBox.textContent = ''; }
if (mailNote) { mailNote.textContent = ''; mailNote.className = 'tf-note'; }
if (mailField) mailField.removeAttribute('aria-invalid');
var btn = form.querySelector('button[type="submit"]');
if (btn) { btn.disabled = false; btn.textContent = T.send; }
}
window.resetTrialForm = resetTrialForm;
/* Live-Rueckmeldung am E-Mail-Feld */ /* Live-Rueckmeldung am E-Mail-Feld */
if (mailField && mailNote) { if (mailField && mailNote) {
mailField.addEventListener('blur', function () { mailField.addEventListener('blur', function () {
@@ -140,8 +159,8 @@
if (!v) { mailNote.textContent = ''; return; } if (!v) { mailNote.textContent = ''; return; }
if (isBlocked(v)) { if (isBlocked(v)) {
mailNote.textContent = T.freemail; mailNote.textContent = T.freemail;
mailNote.className = 'tf-note tf-note-error'; mailNote.className = 'tf-note tf-note-warn';
mailField.setAttribute('aria-invalid', 'true'); mailField.removeAttribute('aria-invalid');
} else if (looksOfficial(v)) { } else if (looksOfficial(v)) {
mailNote.textContent = T.hintOfficial; mailNote.textContent = T.hintOfficial;
mailNote.className = 'tf-note tf-note-ok'; mailNote.className = 'tf-note tf-note-ok';
@@ -167,12 +186,14 @@
var errorBox = document.getElementById('tf-error'); var errorBox = document.getElementById('tf-error');
var btn = form.querySelector('button[type="submit"]'); var btn = form.querySelector('button[type="submit"]');
var fail = function (msg) { var fail = function (msg, feldId) {
if (errorBox) { if (errorBox) {
errorBox.textContent = msg; errorBox.textContent = msg;
errorBox.style.display = 'block'; errorBox.style.display = 'block';
errorBox.scrollIntoView({ behavior: 'smooth', block: 'center' }); errorBox.scrollIntoView({ behavior: 'smooth', block: 'center' });
} }
var feld = feldId && document.getElementById(feldId);
if (feld) { feld.focus(); }
if (btn) { btn.disabled = false; btn.textContent = T.send; } if (btn) { btn.disabled = false; btn.textContent = T.send; }
}; };
@@ -184,9 +205,11 @@
/* Zeitfalle gegen Bots */ /* Zeitfalle gegen Bots */
if (Date.now() - loadedAt < 4000) return fail(T.tooFast); if (Date.now() - loadedAt < 4000) return fail(T.tooFast);
if (!plausibleMail(email)) return fail(T.invalidMail); /* Das Formular traegt novalidate, damit die Browsermeldungen die
if (isBlocked(email)) return fail(T.freemail); eigene Pruefung nicht ueberlagern. Pflichtfelder deshalb hier. */
if (consent && !consent.checked) return fail(T.consent); if (!val('tf-name')) return fail(T.nameFehlt, 'tf-name');
if (!plausibleMail(email)) return fail(T.invalidMail, 'tf-email');
if (consent && !consent.checked) return fail(T.consent, 'tf-consent');
if (btn) { btn.disabled = true; btn.textContent = T.sending; } if (btn) { btn.disabled = true; btn.textContent = T.sending; }
@@ -195,6 +218,7 @@
var zeilen = [ var zeilen = [
L.head, L.head,
'', '',
L.name + ' = ' + val('tf-name'),
L.org + ' = ' + (val('tf-org') || e_), L.org + ' = ' + (val('tf-org') || e_),
L.role + ' = ' + (val('tf-role') || e_), L.role + ' = ' + (val('tf-role') || e_),
L.mail + ' = ' + email, L.mail + ' = ' + email,