/* Diagnóstico Digital — UI React trilingüe (FR · DE · ES). Usa window.DIAG. */ const { useState, useEffect, useRef } = React; const LANGS = ["fr", "de", "es", "en"]; const STORE = "md_lang"; const UI = { fr: { back_site: "← Retour au site", wkicker: "Gratuit · Sans inscription", wt1: "Diagnostic", wt2: "digital gratuit", wlead: "Répondez à quelques questions simples sur votre activité et recevez aussitôt un rapport personnalisé : votre score digital sur 100, vos points forts, vos axes d'amélioration et des recommandations concrètes.", f_min: "minutes", f_blocks: "blocs", f_score: "score", start: "Commencer le diagnostic", fineprint: "Vos réponses ne sont pas partagées. Le rapport est généré automatiquement pour vous.", progress: (c, t) => "Étape " + c + " sur " + t, multi_hint: "· plusieurs choix", choose: "Choisissez une option…", back: "← Retour", next: "Suivant", see_report: "Voir mon rapport", rkicker: "Votre rapport digital", rtitle: e => "Diagnostic de " + e, your_biz: "votre activité", pts: "pts", strengths_h: "Points forts détectés", opp_h: "Axes d'amélioration", impact_h: "Impact estimé sur votre activité", rec_h: "Recommandations personnalisées", svc_kicker: "Comment Mariño Digital peut vous aider", svc_h: "Des services pour ce dont vous avez besoin maintenant", cta_h: "Je veux un audit personnalisé gratuit", cta_p: "Je vous analyse en profondeur, sans engagement, et je vous propose un plan sur mesure sans jargon.", cta_btn: "Demander l'audit gratuit", cta_more: "En savoir plus sur Mariño Digital", restart: "↺ Refaire le diagnostic", }, de: { back_site: "← Zurück zur Website", wkicker: "Kostenlos · Ohne Anmeldung", wt1: "Kostenlose", wt2: "Digital-Analyse", wlead: "Beantworten Sie ein paar einfache Fragen zu Ihrem Unternehmen und erhalten Sie sofort einen persönlichen Bericht: Ihr Digital-Score von 100, Ihre Stärken, Ihre Verbesserungspotenziale und konkrete Empfehlungen.", f_min: "Minuten", f_blocks: "Blöcke", f_score: "Score", start: "Analyse starten", fineprint: "Ihre Antworten werden nicht geteilt. Der Bericht wird automatisch für Sie erstellt.", progress: (c, t) => "Schritt " + c + " von " + t, multi_hint: "· Mehrfachauswahl", choose: "Wählen Sie eine Option…", back: "← Zurück", next: "Weiter", see_report: "Meinen Bericht ansehen", rkicker: "Ihr Digital-Bericht", rtitle: e => "Diagnose von " + e, your_biz: "Ihr Unternehmen", pts: "Pkt.", strengths_h: "Erkannte Stärken", opp_h: "Verbesserungspotenziale", impact_h: "Geschätzte Auswirkung auf Ihr Geschäft", rec_h: "Persönliche Empfehlungen", svc_kicker: "Wie Mariño Digital Ihnen helfen kann", svc_h: "Services für das, was Sie jetzt brauchen", cta_h: "Ich möchte ein kostenloses Audit", cta_p: "Ich analysiere Sie gründlich, unverbindlich, und schlage Ihnen einen maßgeschneiderten Plan ohne Fachjargon vor.", cta_btn: "Kostenloses Audit anfragen", cta_more: "Mehr über Mariño Digital", restart: "↺ Analyse wiederholen", }, es: { back_site: "← Volver a la web", wkicker: "Gratis · Sin registro", wt1: "Diagnóstico", wt2: "digital gratuito", wlead: "Responde unas preguntas sencillas sobre tu negocio y recibe al instante un informe personalizado: tu puntuación digital sobre 100, tus fortalezas, tus oportunidades de mejora y recomendaciones prácticas.", f_min: "minutos", f_blocks: "bloques", f_score: "puntuación", start: "Empezar diagnóstico", fineprint: "Tus respuestas no se comparten. El informe se genera automáticamente para ti.", progress: (c, t) => "Paso " + c + " de " + t, multi_hint: "· varias opciones", choose: "Elige una opción…", back: "← Atrás", next: "Siguiente", see_report: "Ver mi informe", rkicker: "Tu informe digital", rtitle: e => "Diagnóstico de " + e, your_biz: "tu negocio", pts: "pts", strengths_h: "Fortalezas detectadas", opp_h: "Oportunidades de mejora", impact_h: "Impacto estimado en tu negocio", rec_h: "Recomendaciones personalizadas", svc_kicker: "Cómo puede ayudarte Mariño Digital", svc_h: "Servicios para lo que tu negocio necesita ahora", cta_h: "Quiero una auditoría personalizada gratuita", cta_p: "Te reviso a fondo, sin compromiso, y te propongo un plan a medida sin tecnicismos.", cta_btn: "Pedir auditoría gratis", cta_more: "Ver más sobre Mariño Digital", restart: "↺ Repetir el diagnóstico", }, en: { back_site: "← Back to site", wkicker: "Free · No sign-up", wt1: "Free digital", wt2: "diagnostic", wlead: "Answer a few simple questions about your business and instantly get a personalised report: your digital score out of 100, your strengths, your areas for improvement and practical recommendations.", f_min: "minutes", f_blocks: "blocks", f_score: "score", start: "Start the diagnostic", fineprint: "Your answers aren't shared. The report is generated automatically for you.", progress: (c, t) => "Step " + c + " of " + t, multi_hint: "· multiple choice", choose: "Choose an option…", back: "← Back", next: "Next", see_report: "See my report", rkicker: "Your digital report", rtitle: e => "Diagnostic of " + e, your_biz: "your business", pts: "pts", strengths_h: "Strengths detected", opp_h: "Areas for improvement", impact_h: "Estimated impact on your business", rec_h: "Personalised recommendations", svc_kicker: "How Mariño Digital can help you", svc_h: "Services for what you need now", cta_h: "I want a free personalised audit", cta_p: "I'll review you in depth, with no commitment, and propose a tailored plan with no jargon.", cta_btn: "Request the free audit", cta_more: "More about Mariño Digital", restart: "↺ Retake the diagnostic", }, }; function ProgressBar({ current, total, ui }) { const pct = Math.round((current / total) * 100); return (
{ui.progress(current, total)}{pct}%
); } function Option({ type, checked, label, onClick }) { return ( ); } function Question({ q, value, onChange, ui }) { if (q.type === "text") { return (
onChange(e.target.value)} />
); } if (q.type === "select") { return (
); } if (q.type === "single") { return (
{q.label}
{q.options.map(o =>
); } const arr = Array.isArray(value) ? value : []; const toggle = v => { let next; if (q.none && v === q.none) next = arr.includes(v) ? [] : [v]; else next = arr.includes(v) ? arr.filter(x => x !== v) : [...arr.filter(x => x !== q.none), v]; onChange(next); }; return (
{q.label} {ui.multi_hint}
{q.options.map(o =>
); } function LangSwitch({ lang, setLang }) { return (
{LANGS.map((l, i) => ( {i ? / : null} ))}
); } function Welcome({ onStart, ui }) { return (
{ui.wkicker}

{ui.wt1}
{ui.wt2}

{ui.wlead}

3–5{ui.f_min}
10{ui.f_blocks}
/100{ui.f_score}

{ui.fineprint}

); } function Gauge({ score, level }) { const [val, setVal] = useState(0); useEffect(() => { const dur = 1100, t0 = Date.now(); const id = setInterval(() => { const p = Math.min(1, (Date.now() - t0) / dur), e = 1 - Math.pow(1 - p, 3); setVal(Math.round(e * score)); if (p >= 1) clearInterval(id); }, 30); const fb = setTimeout(() => setVal(score), dur + 300); return () => { clearInterval(id); clearTimeout(fb); }; }, [score]); const R = 86, C = 2 * Math.PI * R, off = C - (val / 100) * C; return (
{val}/ 100
); } function Report({ answers, onRestart, lang, ui }) { const r = window.DIAG.buildReport(answers, lang); const empresa = answers.empresa ? answers.empresa : ui.your_biz; return (
{ui.rkicker}

{ui.rtitle(empresa)}

{r.level.name} {r.level.range} {ui.pts}

{r.level.blurb}

{ui.strengths_h}

    {r.strengths.map((s, i) =>
  • {s}
  • )}

! {ui.opp_h}

    {r.opportunities.map((s, i) =>
  • {s}
  • )}

{ui.impact_h}

{r.impact}

{ui.rec_h}

    {r.recommendations.map((s, i) =>
  1. {String(i + 1).padStart(2, "0")}{s}
  2. )}
{ui.svc_kicker}

{ui.svc_h}

{r.services.map((s, i) =>

{s.name}

{s.desc}

)}

{ui.cta_h}

{ui.cta_p}

{ui.cta_btn} {ui.cta_more}
); } function App() { const [lang, setLangState] = useState(() => { const l = localStorage.getItem(STORE); return LANGS.includes(l) ? l : "fr"; }); const setLang = l => { setLangState(l); localStorage.setItem(STORE, l); document.documentElement.lang = l; }; useEffect(() => { document.documentElement.lang = lang; }, [lang]); const ui = UI[lang]; const steps = window.DIAG.localizedSteps(lang); const total = steps.length; const [stage, setStage] = useState("welcome"); const [answers, setAnswers] = useState({}); const [shake, setShake] = useState(false); const topRef = useRef(null); useEffect(() => { if (topRef.current) topRef.current.scrollTop = 0; window.scrollTo(0, 0); }, [stage]); const set = (id, v) => setAnswers(a => ({ ...a, [id]: v })); const visibleQs = step => step.questions.filter(q => window.DIAG.visible(q, answers)); const stepComplete = step => visibleQs(step).every(q => { const v = answers[q.id]; if (q.type === "multi") return Array.isArray(v) && v.length > 0; return v !== undefined && v !== null && String(v).trim() !== ""; }); const next = () => { const step = steps[stage]; if (!stepComplete(step)) { setShake(true); setTimeout(() => setShake(false), 500); return; } if (stage === total - 1) setStage("report"); else setStage(stage + 1); }; const back = () => { if (stage === 0) setStage("welcome"); else setStage(stage - 1); }; let content; if (stage === "welcome") content = setStage(0)} ui={ui} />; else if (stage === "report") content = { setAnswers({}); setStage("welcome"); }} lang={lang} ui={ui} />; else { const step = steps[stage]; content = (
{step.kicker}

{step.title}

{step.subtitle &&

{step.subtitle}

}
{visibleQs(step).map(q => set(q.id, v)} ui={ui} />)}
); } return (
MariñoDigital
{content}
); } ReactDOM.createRoot(document.getElementById("root")).render();