(() => {
// — Parametri base —
const K_2021_TODAY = 0.196; // +19,6% dal 2021 ad oggi
const RAISE_STEP = 0.03; // +3% ogni 3 anni// — Utility —
const fmtEUR = (n) => new Intl.NumberFormat(“it-IT”, { style: “currency”, currency: “EUR”, maximumFractionDigits: 0 }).format(n);
const fmtPct = (r) => new Intl.NumberFormat(“it-IT”, { style: “percent”, minimumFractionDigits: 1, maximumFractionDigits: 1 }).format(r);function parseMoney(raw) {
if (raw == null) return 0;
let s = String(raw).trim();
if (!s) return 0;
s = s.replace(/[€\s]/g, “”);
if (s.includes(“,”)) s = s.replace(/\./g, “”).replace(“,”, “.”);
else s = s.replace(/,/g, “”);
const n = Number.parseFloat(s);
return Number.isFinite(n) ? n : 0;
}
function clampInt(n, min, max) {
n = Math.trunc(Number(n));
if (!Number.isFinite(n)) n = min;
return Math.max(min, Math.min(max, n));
}
function pow(a, b) { return Math.pow(a, b); }function statusForMargin(v) {
if (v < 1000) return { cls: "uwt-red uwt-blink", badge: { cls: "red", text: "Critico ❗" }, msg: "Con questo margine, le rinunce non sono una scelta: sono una conseguenza." };
if (v <= 1500) return { cls: "uwt-yellow", badge: { cls: "yellow", text: "Teso 😬" }, msg: "Regge finché non succede nulla: basta un imprevisto e salta tutto." };
return { cls: "uwt-blue", badge: { cls: "blue", text: "Sostenibile ✅" }, msg: "Hai un margine decente. La domanda è: quanto dura se i prezzi corrono?" };
}function buildTradeoffs(margin) {
const items = [];
const t1 = 1000, t2 = 1500;
const need1 = Math.max(0, t1 - margin);
const need2 = Math.max(0, t2 - margin);if (margin <= 0) {
items.push(`Sei in negativo: per tornare a zero servono circa ${fmtEUR(Math.abs(margin))} al mese (tagli o entrate in più).`);
items.push(`Per arrivare a ${fmtEUR(t1)} ti mancano circa ${fmtEUR(need1)} al mese.`);
items.push(`Per essere “Sostenibile” (${fmtEUR(t2)}) ti mancano circa ${fmtEUR(need2)} al mese.`);
items.push(`Traduzione: rinunci già oggi a risparmio, serenità e imprevisti.`);
return items;
}
if (margin < t1) {
items.push(`Margine sotto ${fmtEUR(t1)}: servono circa ${fmtEUR(need1)} al mese solo per “respirare”.`);
items.push(`Per essere “Sostenibile” servono circa ${fmtEUR(need2)} in più ogni mese.`);
items.push(`Rinunce tipiche: fondo emergenza rimandato, manutenzioni, cure, tempo libero “a singhiozzo”.`);
return items;
}
if (margin <= t2) {
items.push(`Se vuoi essere “Sostenibile”, mancano circa ${fmtEUR(need2)} al mese.`);
items.push(`Rinunce tipiche: vacanze ridotte, spese per figli compressa, rinvio acquisti importanti.`);
items.push(`Basta un aumento (auto/bollette) per tornare in rosso.`);
return items;
}
items.push(`Sei sopra soglia: margine extra ≈ ${fmtEUR(margin - t2)} rispetto a ${fmtEUR(t2)}.`);
items.push(`Se i prezzi salgono, proteggi questo margine: è la tua “armatura”.`);
items.push(`Usalo per fondo emergenza / debiti / risparmio: così l’inflazione fa meno male.`);
return items;
}// Stima spese familiari (modello semplice e modificabile)
function estimateFamilyExpenses(spouseYes, children, infants) {
// base:
// coniuge a carico: +240
// per figlio: +200
// extra neonato (oltre al figlio): +140
// extra gestione casa (se famiglia > 0): +90
const spouse = spouseYes ? 240 : 0;
const child = 200 * children;
const infantExtra = 140 * infants;
const misc = (spouseYes || children > 0) ? 90 : 0;
const est = spouse + child + infantExtra + misc;
return Math.round(est / 10) * 10;
}function initTool(root) {
if (root.dataset.uwtInit === “1”) return;
root.dataset.uwtInit = “1”;const $ = (sel) => root.querySelector(sel);const salaryEl = $(“.uwt-salary”);
const fixedEl = $(“.uwt-fixed”);
const familyEl = $(“.uwt-family”);const spouseEl = $(“.uwt-spouse”);
const childrenEl = $(“.uwt-children”);
const infantsEl = $(“.uwt-infants”);const yearsEl = $(“.uwt-years”);
const inflEl = $(“.uwt-infl”);const btnCalc = $(“.uwt-calc”);
const btnEstimate = $(“.uwt-estimate”);
const btnGoToday = $(“.uwt-go-today”);
const btnGoFuture = $(“.uwt-go-future”);
const modeBtns = root.querySelectorAll(“.uwt-mode”);const errorBox = $(“.uwt-errorbox”);const panelToday = $(“.uwt-today”);
const panelFuture = $(“.uwt-future”);// Outputs TODAY
const outTodayMissing = $(“.uwt-out-today-missing”);
const outTodayShould = $(“.uwt-out-today-should”);
const outTodayLeft = $(“.uwt-out-today-left”);
const outTodayLeftRow = $(“.uwt-out-today-left-row”);
const outTodayBadge = $(“.uwt-out-today-badge”);
const outTodayMsgSlot = root.querySelector(“.uwt-out-today-msg”) || null;
const outTodayList = $(“.uwt-out-today-tradeoffs”);// Outputs FUTURE
const outYears = $(“.uwt-out-years”);
const outYears2 = $(“.uwt-out-years2”);
const outYears3 = $(“.uwt-out-years3”);const outFuturePres = $(“.uwt-out-future-pres”);
const outFutureShould = $(“.uwt-out-future-should”);
const outFutureMissing= $(“.uwt-out-future-missing”);
const outFutureMissingHero = $(“.uwt-out-future-missing-hero”);
const outFutureLeft = $(“.uwt-out-future-left”);
const outFutureLeftRow= $(“.uwt-out-future-left-row”);
const outFutureList = $(“.uwt-out-future-tradeoffs”);
const outFutureMessage= $(“.uwt-out-future-message”);// Share
const shareWrap = $(“.uwt-share”);
const btnCopy = $(“.uwt-copy”);
const waLink = $(“.uwt-wa”);
const btnNative = $(“.uwt-native”);// How-to values
const vSalary = $(“.uwt-v-salary”);
const vFixed = $(“.uwt-v-fixed”);
const vFamily = $(“.uwt-v-family”);
const vYears = $(“.uwt-v-years”);
const vInfl = $(“.uwt-v-infl”);
const vStep = $(“.uwt-v-step”);
const vRaise = $(“.uwt-v-raise”);
const vInflFac = $(“.uwt-v-inflfac”);let mode = “step”; // step | all
let screen = “today”;// Evita Enter/submit
root.addEventListener(“keydown”, (e) => { if (e.key === “Enter”) e.preventDefault(); }, true);const showError = (m) => { errorBox.style.display = “block”; errorBox.textContent = m; };
const clearError = () => { errorBox.style.display = “none”; errorBox.textContent = “”; };function setMode(next) {
mode = next;
modeBtns.forEach(b => b.setAttribute(“aria-pressed”, b.dataset.mode === mode ? “true” : “false”));if (mode === “all”) {
btnGoToday.classList.add(“uwt-hidden”);
btnGoFuture.classList.add(“uwt-hidden”);
panelToday.classList.remove(“uwt-hidden”);
panelFuture.classList.remove(“uwt-hidden”);
} else {
btnGoToday.classList.remove(“uwt-hidden”);
btnGoFuture.classList.remove(“uwt-hidden”);
setScreen(screen);
}
}
function setScreen(next) {
screen = next;
if (mode === “all”) return;
if (screen === “today”) {
panelToday.classList.remove(“uwt-hidden”);
panelFuture.classList.add(“uwt-hidden”);
} else {
panelToday.classList.add(“uwt-hidden”);
panelFuture.classList.remove(“uwt-hidden”);
}
}function normalizeCounts() {
const ch = (childrenEl.value === “5”) ? 5 : clampInt(childrenEl.value, 0, 5);
let inf = (infantsEl.value === “3”) ? 3 : clampInt(infantsEl.value, 0, 3);
if (inf > ch) { inf = Math.min(ch, 3); infantsEl.value = String(inf); }
return { ch, inf };
}function renderList(ul, items) {
ul.innerHTML = “”;
items.forEach(t => { const li = document.createElement(“li”); li.textContent = t; ul.appendChild(li); });
}function applyRowClass(rowEl, cls) {
rowEl.classList.remove(“uwt-red”,”uwt-yellow”,”uwt-blue”,”uwt-blink”);
cls.split(” “).forEach(c => rowEl.classList.add(c));
}function setBadge(el, badge) {
el.classList.remove(“red”,”yellow”,”blue”);
el.classList.add(badge.cls);
el.textContent = badge.text;
}function buildShareText(obj) {
return [
“Quanto valgono i tuoi soldi oggi e domani:”,
“”,
`💶 Stipendio: ${fmtEUR(obj.salary)}`,
`🚨 OGGI MANCANO (dal 2021 +19,6%): ${fmtEUR(obj.todayMissing)}`,
`✅ Dovrebbe essere oggi: ${fmtEUR(obj.shouldToday)}`,
`🏚️ Ti resta oggi dopo spese: ${fmtEUR(obj.leftToday)}`,
“”,
`⏳ Tra ${obj.years} anni (scatti +3% ogni 3 anni):`,
`📈 Stipendio presunto: ${fmtEUR(obj.presumed)}`,
`📌 Dovrebbe essere: ${fmtEUR(obj.shouldFuture)}`,
`🔥 DOMANI MANCANO: ${fmtEUR(obj.missingFuture)}`,
`🧾 Ti resterebbe dopo spese: ${fmtEUR(obj.leftFuture)}`
].join(“\n”);
}function doEstimate() {
const spouseYes = spouseEl.value === “si”;
const { ch, inf } = normalizeCounts();
familyEl.value = String(estimateFamilyExpenses(spouseYes, ch, inf));
}function doCalc() {
clearError();const salary = parseMoney(salaryEl.value);
const fixed = parseMoney(fixedEl.value);
const family = parseMoney(familyEl.value);
const years = clampInt(yearsEl.value, 0, 60);
const infl = Number(inflEl.value);if (!(salary > 0)) return showError(“Inserisci uno stipendio valido (maggiore di 0).”);
if (!Number.isFinite(infl) || infl < 0) return showError("Seleziona un tasso di inflazione valido.");const expNow = Math.max(0, fixed + family);// --- OGGI ---
const todayMissing = salary * K_2021_TODAY;
const shouldToday = salary * (1 + K_2021_TODAY);
const leftToday = salary - expNow;outTodayMissing.textContent = fmtEUR(todayMissing);
outTodayShould.textContent = fmtEUR(shouldToday);
outTodayLeft.textContent = fmtEUR(leftToday);const stToday = statusForMargin(leftToday);
applyRowClass(outTodayLeftRow, stToday.cls);
setBadge(outTodayBadge, stToday.badge);
if (outTodayMsgSlot) outTodayMsgSlot.textContent = stToday.msg;
renderList(outTodayList, buildTradeoffs(leftToday));// --- DOMANI ---
const step = Math.floor(years / 3);
const raiseFactor = pow(1 + RAISE_STEP, step);
const inflFactor = pow(1 + infl, years);const presumed = salary * raiseFactor;
const shouldFuture= salary * (1 + K_2021_TODAY) * inflFactor;
const missingFuture = shouldFuture - presumed;const expFuture = expNow * inflFactor;
const leftFuture = presumed - expFuture;outYears.textContent = String(years);
outYears2.textContent= String(years);
outYears3.textContent= String(years);outFuturePres.textContent = fmtEUR(presumed);
outFutureShould.textContent = fmtEUR(shouldFuture);
outFutureMissing.textContent= fmtEUR(missingFuture);
outFutureLeft.textContent = fmtEUR(leftFuture);// HERO "DOMANI MANCANO" (lampeggia se > 0)
outFutureMissingHero.classList.remove(“uwt-red”,”uwt-blue”,”uwt-yellow”,”uwt-blink”);
if (missingFuture > 0) outFutureMissingHero.classList.add(“uwt-red”,”uwt-blink”);
else outFutureMissingHero.classList.add(“uwt-blue”);// Margine futuro (colori come oggi)
const stF = statusForMargin(leftFuture);
applyRowClass(outFutureLeftRow, stF.cls);const msg =
missingFuture > 0
? `Con scatti (+3% ogni 3 anni) e inflazione ${fmtPct(infl)} per ${years} anni, il divario cresce: ti mancano circa ${fmtEUR(missingFuture)} al mese rispetto a uno stipendio “in pari”.`
: `In questo scenario sei sopra: margine ≈ ${fmtEUR(Math.abs(missingFuture))}. (Raro: significa che i prezzi crescono meno di quanto immagini o il netto è già alto).`;outFutureMessage.textContent = msg;renderList(outFutureList, [
`Scatti: step = floor(${years}/3) = ${step} → fattore scatti ≈ ${raiseFactor.toFixed(3)}.`,
`Inflazione: fattore ≈ ${(inflFactor).toFixed(3)} (tasso ${fmtPct(infl)} per ${years} anni).`,
…(missingFuture > 0
? [`“DOMANI MANCANO” = ${fmtEUR(missingFuture)} → il 3% ogni 3 anni non basta.`]
: [`“DOMANI MANCANO” ≤ 0 → sei sopra la curva in questo scenario.`]),
…buildTradeoffs(leftFuture)
]);// How-to numeric values
vSalary.textContent = fmtEUR(salary);
vFixed.textContent = fmtEUR(fixed);
vFamily.textContent = fmtEUR(family);
vYears.textContent = String(years);
vInfl.textContent = fmtPct(infl);
vStep.textContent = String(step);
vRaise.textContent = raiseFactor.toFixed(3);
vInflFac.textContent = inflFactor.toFixed(3);// mostra pannelli
panelToday.classList.remove(“uwt-hidden”);
panelFuture.classList.remove(“uwt-hidden”);// share
const shareText = buildShareText({ salary, todayMissing, shouldToday, leftToday, years, presumed, shouldFuture, missingFuture, leftFuture });
const waUrl = “https://wa.me/?text=” + encodeURIComponent(shareText);waLink.href = waUrl;
shareWrap.classList.remove(“uwt-hidden”);btnCopy.onclick = async () => {
try { await navigator.clipboard.writeText(shareText); btnCopy.textContent = “✅ Copiato”; setTimeout(()=>btnCopy.textContent=”📋 Copia testo”, 1400); }
catch { alert(shareText); }
};btnNative.onclick = async () => {
try {
if (navigator.share) await navigator.share({ title: “Oggi vs Domani”, text: shareText });
else btnCopy.click();
} catch { /* niente */ }
};if (mode === “step”) setScreen(“today”);
}// listeners
btnEstimate.addEventListener(“click”, doEstimate);
btnCalc.addEventListener(“click”, doCalc);
btnGoToday.addEventListener(“click”, () => setScreen(“today”));
btnGoFuture.addEventListener(“click”, () => setScreen(“future”));
modeBtns.forEach(b => b.addEventListener(“click”, () => setMode(b.dataset.mode)));childrenEl.addEventListener(“change”, normalizeCounts);
infantsEl.addEventListener(“change”, normalizeCounts);setMode(“step”);
}// Boot robusto (Elementor può inserire HTML dopo il load)
function initAll() {
document.querySelectorAll(“[data-usami-wage-tool]”).forEach(initTool);
}if (document.readyState === “loading”) {
document.addEventListener(“DOMContentLoaded”, indicate);
} else {
indicate();
}function indicate() {
initAll();
// MutationObserver: se Elementor carica il widget dopo, lo inizializziamo lo stesso
const obs = new MutationObserver(() => initAll());
obs.observe(document.documentElement, { childList: true, subtree: true });
}
})();
