/* Marken-Klarheits-Check — Quiz-State, geführte Navigation, deterministischer Reifegrad, * Ergebnis-Rendering. Die KI-Einordnung kommt (nur bei Freitext) vom serverseitigen Proxy * POST /klarheits-check/auswerten. Schlägt der Call fehl oder fehlt Freitext, greift das * deterministische Ergebnis — das Tool funktioniert immer. * Funktioniert auf /klarheits-check wie im Overlay (mehrere [data-kc]-Roots). */ (function () { 'use strict'; // Reifegrad je Skala-Index — Spiegel zu config/klarheits_check.php. const GRAD = ['scharf', 'unscharf', 'nicht entschieden', 'nicht entschieden']; const GRADCLASS = ['scharf', 'unscharf', 'offen', 'offen']; function escapeHtml(s) { return String(s).replace(/[&<>"]/g, (c) => ({ '&': '&', '<': '<', '>': '>', '"': '"' }[c])); } // Umami-Event (self-hosted). No-op, wenn der Tracker nicht geladen ist. // Wir senden ausschließlich Aggregat-Zahlen, niemals Freitext. let started = false; function track(event, data) { try { if (window.umami && typeof window.umami.track === 'function') window.umami.track(event, data); } catch (_) { /* Tracking darf das Tool nie blockieren */ } } function scrollToEl(el) { if (el) el.scrollIntoView({ behavior: 'smooth', block: 'start' }); } // Vier kanonische Stufen-Texte — Spiegel zu config/klarheits_check.php ('stufen') // und zum Block im KI-Prompt. So zeigt der No-Text-Pfad exakt // denselben Auftakt/Schluss wie KI-Lauf und serverseitiger Fallback. const STUFEN = { scharf: { auftakt: 'Das Markenfundament ist in weiten Teilen bereits scharf.', schluss: 'Ihr Fundament trägt in weiten Teilen schon. Das Marken-Klarheits-Audit hält fest, was hier bereits klar ist, und schließt die wenigen offenen Stellen zu einer dokumentierten Marken-DNA.', }, gemischt: { auftakt: 'Das Markenfundament steht in Teilen, ist aber noch nicht durchgängig scharf.', schluss: 'Einige Glieder Ihres Fundaments tragen bereits, andere warten noch auf eine klare Entscheidung. Das Marken-Klarheits-Audit ist der Ort, an dem aus dieser Diagnose eine dokumentierte Marken-DNA wird.', }, offen: { auftakt: 'Das Markenfundament ist überwiegend noch nicht scharf gestellt.', schluss: 'Die offenen Punkte liegen nicht in der Umsetzung, sondern in noch nicht getroffenen Entscheidungen. Genau diese Entscheidungen hält das Marken-Klarheits-Audit als dokumentierte Marken-DNA fest.', }, }; // Gesamtstufe aus der Anzahl „scharf"-Grade (>=4 scharf, 2–3 gemischt, sonst offen). function stufeFor(state) { let scharf = 0; state.forEach((s) => { if (s.sel != null && GRADCLASS[s.sel] === 'scharf') scharf++; }); if (scharf >= 4) return STUFEN.scharf; if (scharf >= 2) return STUFEN.gemischt; return STUFEN.offen; } function initCheck(root) { if (root.dataset.kcInit) return; // Doppel-Init vermeiden (Seite + Overlay teilen das Markup) root.dataset.kcInit = '1'; const questions = Array.from(root.querySelectorAll('.kc-q')); const state = questions.map(() => ({ sel: null, text: '' })); // Antwort wählen → markieren, Freitextfeld dezent einladen, Weiter-Button zeigen. root.querySelectorAll('[data-kc-opt]').forEach((btn) => { btn.addEventListener('click', () => { if (!started) { started = true; track('klarheits-check-gestartet'); } const q = +btn.dataset.q; const o = +btn.dataset.o; state[q].sel = o; root.querySelectorAll('[data-kc-opt][data-q="' + q + '"]').forEach((x) => x.setAttribute('aria-pressed', 'false')); btn.setAttribute('aria-pressed', 'true'); const qEl = questions[q]; const field = qEl.querySelector('[data-kc-field]'); if (field && !state[q].text) field.classList.add('is-invited'); // Hervorhebung, KEIN Fokus const next = qEl.querySelector('[data-kc-next]'); if (next) next.hidden = false; hideError(root); }); }); // Freitext → speichern; sobald etwas drinsteht, Einladungs-Hervorhebung lösen. root.querySelectorAll('[data-kc-text]').forEach((t) => { t.addEventListener('input', () => { const q = +t.dataset.q; state[q].text = t.value.trim(); const field = questions[q].querySelector('[data-kc-field]'); if (field) field.classList.toggle('is-invited', !state[q].text && state[q].sel != null); }); }); // Weiter → nächste Frage, bei der letzten zur Auswertung. root.querySelectorAll('[data-kc-next-btn]').forEach((btn) => { btn.addEventListener('click', () => { const qEl = btn.closest('.kc-q'); const idx = questions.indexOf(qEl); const target = questions[idx + 1] || root.querySelector('[data-kc-submit]'); scrollToEl(target); }); }); const submitBtn = root.querySelector('[data-kc-submit]'); if (submitBtn) submitBtn.addEventListener('click', () => auswerten(root, state, submitBtn, questions)); const jumpBtn = root.querySelector('[data-kc-error-jump]'); if (jumpBtn) { jumpBtn.addEventListener('click', () => { const firstOpen = state.findIndex((s) => s.sel == null); if (firstOpen !== -1) scrollToEl(questions[firstOpen]); }); } const resetBtn = root.querySelector('[data-kc-reset]'); if (resetBtn) resetBtn.addEventListener('click', () => window.location.reload()); } function hideError(root) { const err = root.querySelector('[data-kc-error]'); if (err) err.hidden = true; } async function auswerten(root, state, submitBtn, questions) { if (state.some((s) => s.sel == null)) { const err = root.querySelector('[data-kc-error]'); if (err) err.hidden = false; return; } hideError(root); submitBtn.disabled = true; const loading = root.querySelector('[data-kc-loading]'); const hatText = state.some((s) => s.text); if (loading && hatText) { submitBtn.hidden = true; loading.hidden = false; loading.scrollIntoView({ behavior: 'smooth', block: 'center' }); } let ki = null; let limitHinweis = null; if (hatText) { try { const meta = document.querySelector('meta[name="csrf-token"]'); const res = await fetch('/klarheits-check/auswerten', { method: 'POST', headers: { 'Content-Type': 'application/json', Accept: 'application/json', 'X-CSRF-TOKEN': meta ? meta.getAttribute('content') : '', }, body: JSON.stringify({ antworten: state.map((s) => ({ skala: s.sel, text: s.text })) }), }); if (res.ok) { const data = await res.json(); // Limit erreicht: statisches Ergebnis + Hinweis, nicht als KI behandeln. if (data && data.hinweis) { limitHinweis = data.hinweis; } else { ki = data; } } } catch (_) { ki = null; } } render(root, state, ki, questions, limitHinweis); // Abschluss: nur Reifegrad-Verteilung + Flags, kein Freitext. const verteilung = { scharf: 0, unscharf: 0, offen: 0 }; state.forEach((s) => { if (s.sel != null) verteilung[GRADCLASS[s.sel]]++; }); track('klarheits-check-abgeschlossen', { ...verteilung, mit_text: hatText, ki: !!ki, limit: !!limitHinweis }); if (loading) loading.hidden = true; // Ergebnis-Zustand: Fragen + kompletter Aktionsbereich weg, Hero-Start weg. const form = root.querySelector('[data-kc-form]'); if (form) form.hidden = true; const actions = root.querySelector('.kc-actions'); if (actions) actions.hidden = true; document.querySelectorAll('[data-kc-start-link]').forEach((el) => { el.hidden = true; }); // „Neu starten“ hinter den Audit-CTA ans Ende des Ergebnisses verschieben. const result = root.querySelector('[data-kc-result]'); const resetBtn = root.querySelector('[data-kc-reset]'); const lastBlock = result ? result.querySelector('.kc-r-block:last-child') : null; if (resetBtn && lastBlock) { resetBtn.hidden = false; lastBlock.appendChild(resetBtn); } scrollToEl(result); } // Belegter Grad (aus dem Wortlaut) → CSS-Klasse der ersten Spalte. function classForGrad(grad) { if (grad === 'scharf') return 'scharf'; if (grad === 'unscharf') return 'unscharf'; return 'offen'; // 'nicht entschieden' } function render(root, state, ki, questions, limitHinweis) { const kiSpiegelFor = (n) => { if (!ki || !ki.spiegel) return null; return ki.spiegel.find((s) => s.nr === n) || null; }; const hatText = state.some((s) => s.text); const stufe = stufeFor(state); let html = '
' + (ki ? 'Ihre Auswertung' : 'Statische Auswertung') + '
' + '

Erster Eindruck, keine fertige Diagnose. Ein Spiegel Ihrer Antworten, kein Urteil über Ihr Unternehmen.

' + '
' + (ki && ki.gesamt ? escapeHtml(ki.gesamt) : escapeHtml(stufe.auftakt)) + '
'; // KI-Limit erreicht: deutlich machen, dass dies die statische Auswertung ist. if (limitHinweis) { html += '
' + escapeHtml(limitHinweis) + '
'; } // Ohne Freitext greift nur der deterministische Reifegrad. Deutlich machen, // dass ausgefüllte Felder eine echte, am Wortlaut belegte Analyse ergeben. if (!hatText) { html += '
' + 'Möchten Sie mehr Klarheit und eine präzise Auswertung? ' + 'Das hier ist eine statische Auswertung allein aus Ihrer Skala. ' + 'Füllen Sie die Felder unter den Fragen aus – dann liest die Analyse Ihren Wortlaut, ' + 'belegt jede Einordnung am Beispiel und spiegelt zurück, wo Selbstbild und Formulierung auseinandergehen.' + '
'; } questions.forEach((q, i) => { const sel = state[i].sel; const bw = (q.querySelector('.kc-q-bw') || {}).textContent || ''; const selBtn = root.querySelector('[data-kc-opt][data-q="' + i + '"][aria-pressed="true"]'); const optText = selBtn ? selBtn.textContent.trim() : ''; const m = kiSpiegelFor(i + 1); // Standard: Skala-Stufe als Rückfall. let gradClass = GRADCLASS[sel]; let gradLabel = GRAD[sel]; let spiegelText = 'Einschätzung: ' + escapeHtml(optText) + '.'; let note = ''; if (m && m.grad) { // Belegter Grad aus dem Wortlaut steuert Label + Farbe. gradClass = classForGrad(m.grad); gradLabel = m.grad; if (m.text) spiegelText = escapeHtml(m.text); } else if (state[i].text && !limitHinweis) { // Freitext lag vor, aber kein belegter Grad (KI nicht verfügbar): // Skala zeigen, aber als ungeprüft kennzeichnen — kein geprüftes Urteil. // Bei erreichtem Limit erklärt das der Banner oben, nicht jede Zeile. note = '
Wortlaut-Prüfung nicht verfügbar
'; } html += '
' + '
' + gradLabel + '
' + note + '
' + '
' + escapeHtml(bw) + '
' + '
' + spiegelText + '
' + '
'; }); if (ki && ki.luecken && ki.luecken.length) { html += '

Lücke zwischen Selbstbild und Wortlaut

'; } if (ki && ki.stimmigkeit && ki.stimmigkeit.length) { html += '

Innere Stimmigkeit

'; } const schluss = ki && ki.schluss ? ki.schluss : stufe.schluss; html += '
' + escapeHtml(schluss) + '
' + 'Zum Marken-Klarheits-Audit
'; const result = root.querySelector('[data-kc-result]'); if (result) { result.innerHTML = html; result.hidden = false; const audit = result.querySelector('[data-kc-audit]'); if (audit) audit.addEventListener('click', () => track('klarheits-check-audit-klick')); } } function init() { document.querySelectorAll('[data-kc]').forEach(initCheck); // „Check starten“ im Hero: sanft zum Anker scrollen statt hart zu springen. document.querySelectorAll('[data-kc-start-link]').forEach((link) => { if (link.dataset.kcBound) return; link.dataset.kcBound = '1'; link.addEventListener('click', (e) => { const href = link.getAttribute('href') || ''; const target = href.charAt(0) === '#' ? document.querySelector(href) : null; if (target) { e.preventDefault(); target.scrollIntoView({ behavior: 'smooth', block: 'start' }); } }); }); } if (document.readyState !== 'loading') { init(); } else { document.addEventListener('DOMContentLoaded', init); } // Für dynamisch eingefügte Overlays (CP 2.x) erneut aufrufbar. window.MarkemachtKlarheitsCheck = { init: init }; })();