getreadystack's picture
Upload index.html with huggingface_hub
04604c6 verified
Raw History Blame Contribute Delete
32.4 kB
<!doctype html>
<html lang="en"><head>
<meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1">
<title>Payroll Tax Spreadsheet for US Small Employers — FICA, FUTA and State UI Wage-Base Caps per Paycheck</title>
<meta name="description" content="Deposit short and the IRS adds 2% to 15% of the shortfall under IRC § 6656. This sheet stops Social Security at the wage base, stops FUTA at the first $7,000 per employee, and prints the exact Form 941 deposit for the check you are running right now.">
<style>
:root{--bg:#fff;--fg:#111;--mut:#666;--line:#e5e5e5;--acc:#1a56db;--ok:#0f7b3f}
@media(prefers-color-scheme:dark){:root{--bg:#111;--fg:#eee;--mut:#999;--line:#2a2a2a;--acc:#7aa2f7;--ok:#5fd08a}}
*{box-sizing:border-box}
body{margin:0;background:var(--bg);color:var(--fg);font:16px/1.55 system-ui,-apple-system,"Segoe UI",Roboto,"Helvetica Neue",sans-serif}
.wrap{max-width:820px;margin:0 auto;padding:24px 18px 64px}
h1{font-size:1.6rem;line-height:1.25;margin:.2em 0 .1em}
.sub{color:var(--mut);margin:0 0 1.4em}
.card{border:1px solid var(--line);border-radius:12px;padding:18px;margin:0 0 18px}
h2{font-size:.85rem;letter-spacing:.08em;text-transform:uppercase;color:var(--mut);margin:0 0 14px}
label{display:flex;justify-content:space-between;align-items:center;gap:14px;padding:7px 0;border-bottom:1px solid var(--line)}
label:last-child{border-bottom:0}
label span{flex:1;font-size:.95rem}
input[type=number]{width:9.5em;padding:8px 10px;border:1px solid var(--line);border-radius:8px;background:transparent;color:var(--fg);font:inherit;text-align:right}
.fld{display:inline-flex;align-items:center;gap:6px}
.fld i{font-style:normal;color:var(--mut);width:1em}
table{width:100%;border-collapse:collapse}
td{padding:8px 0;border-bottom:1px solid var(--line);vertical-align:top}
tr:last-child td{border-bottom:0}
td.l{font-size:.95rem}
td.v{text-align:right;font-variant-numeric:tabular-nums;font-weight:600;white-space:nowrap}
.hint{color:var(--mut);font-size:.85rem;margin:10px 0 0}
.cta{border:1px solid var(--acc);border-radius:12px;padding:18px;margin:22px 0 0}
.cta b{display:block;margin-bottom:6px}
.row{display:flex;gap:8px;flex-wrap:wrap;margin-top:12px}
input[type=email]{flex:1;min-width:200px;padding:10px 12px;border:1px solid var(--line);border-radius:8px;background:transparent;color:var(--fg);font:inherit}
button{padding:10px 16px;border:0;border-radius:8px;background:var(--acc);color:#fff;font:inherit;font-weight:600;cursor:pointer}
button.ghost{background:transparent;color:var(--acc);border:1px solid var(--acc)}
a.buy{display:inline-block;margin-top:10px;color:var(--acc);font-weight:600}
.ok{color:var(--ok);font-size:.9rem;margin-top:8px}
canvas{display:none}
</style><script data-grow-initializer="">!(function(){window.growMe||((window.growMe=function(e){window.growMe._.push(e);}),(window.growMe._=[]));var e=document.createElement("script");(e.type="text/javascript"),(e.src="https://faves.grow.me/main.js"),(e.defer=!0),e.setAttribute("data-grow-faves-site-id","U2l0ZTphZjljMzgyMC0yMmU5LTRhYjgtYTIxNS03OTRhMGY1N2ExYzM=");var t=document.getElementsByTagName("script")[0];t.parentNode.insertBefore(e,t);})();</script>
<!--yj-canon-s101--><link rel="canonical" href="https://getreadystack.com/tools/payroll-tax-spreadsheet"></head><body><div class="wrap">
<h1>Payroll Tax Spreadsheet for US Small Employers — FICA, FUTA and State UI Wage-Base Caps per Paycheck</h1><p class="sub">Deposit short and the IRS adds 2% to 15% of the shortfall under IRC § 6656. This sheet stops Social Security at the wage base, stops FUTA at the first $7,000 per employee, and prints the exact Form 941 deposit for the check you are running right now.</p>
<div class="card"><h2>Your numbers</h2><label><span>Gross wages this pay period, one employee</span><span class="fld"><input type="number" step="any" data-k="gross_wages_this_period" data-f="currency" value="3000"></span></label><label><span>Wages already paid to this employee this year, before this period</span><span class="fld"><input type="number" step="any" data-k="ytd_wages_before_this_period" data-f="currency" value="6000"></span></label><label><span>Federal income tax withheld this period (from IRS Publication 15-T)</span><span class="fld"><input type="number" step="any" data-k="federal_income_tax_withheld" data-f="currency" value="316"></span></label><label><span>Social Security wage base for your tax year (SSA notice; 2025 figure shown)</span><span class="fld"><input type="number" step="any" data-k="social_security_wage_base" data-f="currency" value="176100"></span></label><label><span>FUTA wage base per employee per year (statutory)</span><span class="fld"><input type="number" step="any" data-k="futa_wage_base" data-f="currency" value="7000"></span></label><label><span>FUTA net rate after the 5.4% state credit (use 6.0% in a credit-reduction state)</span><span class="fld"><input type="number" step="0.01" data-k="futa_net_rate" data-f="percent" data-pct="1" value="0.6"><i>%</i></span></label><label><span>Your state UI taxable wage base (from your state agency notice)</span><span class="fld"><input type="number" step="any" data-k="state_ui_wage_base" data-f="currency" value="7000"></span></label><label><span>Your assigned state UI rate for this year (from your state agency notice)</span><span class="fld"><input type="number" step="0.01" data-k="state_ui_rate" data-f="percent" data-pct="1" value="3.4"><i>%</i></span></label></div>
<div class="card"><h2>Your result</h2><table><tbody><tr><td class="l">Wages subject to Social Security this period (capped at the wage base)</td><td class="v" data-k="ss_taxable_wages" data-f="number">-</td></tr><tr><td class="l">Social Security withheld from the employee at 6.2%</td><td class="v" data-k="ss_withheld_employee" data-f="number">-</td></tr><tr><td class="l">Medicare withheld from the employee at 1.45% (no wage cap)</td><td class="v" data-k="medicare_withheld_employee" data-f="number">-</td></tr><tr><td class="l">Additional Medicare withheld at 0.9% on wages over $200,000 year to date</td><td class="v" data-k="additional_medicare_withheld" data-f="number">-</td></tr><tr><td class="l">Total withheld from this paycheck, income tax plus FICA</td><td class="v" data-k="employee_taxes_withheld_total" data-f="number">-</td></tr><tr><td class="l">Net pay before state income tax and benefit deductions</td><td class="v" data-k="net_pay_this_period" data-f="number">-</td></tr></tbody></table>
<p class="hint">Everything updates as you type. Nothing is uploaded. This runs in your browser.</p></div>
<section data-yj-cta="paid" style="margin:3rem auto;max-width:46rem;padding:1.5rem 1.25rem;border:1px solid #d8dee9;border-radius:12px;background:#fbfcfe;font:16px/1.6 system-ui,-apple-system,Segoe UI,sans-serif"><h2 style="margin:0 0 .5rem;font-size:1.25rem">Get the complete version</h2><p style="margin:.25rem 0 1rem;color:#41505f">This page is the working piece. The full pack has everything below.</p><p style="margin:0 0 1rem;color:#41505f">Deposit short and the IRS adds 2% to 15% of the shortfall under IRC § 6656. This sheet stops Social Security at the wage base, stops FUTA at the first $7,000 per employee, and prints the exa</p><p style="margin:0 0 1rem;color:#5b6b7c;font-size:.94rem">IRS failure-to-deposit penalty, IRC § 6656: 2% when the deposit lands 1-5 days late, 5% at 6-15 days, 10% after 15 days, 15% once the IRS has issued a notice and demand. At the 10% band, any shortfall over $210 already costs more </p><a href="https://getreadystack.com/api/buy/cl/polar_cl_D6U89n0agQXUWjiGIz5ADsloqE8WJSf9QCK9F3Ha12e" rel="nofollow" style="display:inline-block;padding:.7rem 1.3rem;border-radius:8px;background:#1b64f2;color:#fff;text-decoration:none;font-weight:600">Buy the full version</a></section>
<div class="cta"><b>Get the full calculator</b><span class="hint">Full version: all tabs, your own file, works offline.</span>
<div class="row">
<input type="email" id="em" placeholder="you@example.com">
<button id="go">Email me the full version</button>
<button class="ghost" id="sh">Save result image</button>
<button class="ghost" id="cp">Copy link</button>
<button class="ghost" id="dl">Download my numbers (CSV)</button>
</div>
<div class="ok" id="ok" hidden>Sent. Check your inbox.</div>
<div class="ok" id="need" hidden style="color:var(--acc)">Enter your email above and we will unlock the download.</div>
</div>
<canvas id="cv" width="1200" height="630"></canvas>
</div>
<script id="calcdata" type="application/json">{"spec": {"sheets": [{"name": "Inputs - Your Payroll Numbers", "purpose": "Type your own eight numbers here; both calc sheets recalculate. A free online paycheck calculator cannot carry year-to-date wages from one run to the next, so it never knows that Social Security has already stopped for this employee — that is the whole reason this file exists. Rates you must confirm yourself are inputs, not guesses: the Social Security wage base is set by the SSA each October and the default shown is the 2025 figure of 176,100, so replace it with the figure for the year you are running. The $7,000 FUTA base is statutory. Your state UI rate and state UI taxable wage base come from your state agency's annual notice and differ in every state. Excel: open the file. Google Sheets: File, Import, Upload, Replace spreadsheet.", "inputs": [{"label": "Gross wages this pay period, one employee", "key": "gross_wages_this_period", "value": 3000, "format": "currency"}, {"label": "Wages already paid to this employee this year, before this period", "key": "ytd_wages_before_this_period", "value": 6000, "format": "currency"}, {"label": "Federal income tax withheld this period (from IRS Publication 15-T)", "key": "federal_income_tax_withheld", "value": 316, "format": "currency"}, {"label": "Social Security wage base for your tax year (SSA notice; 2025 figure shown)", "key": "social_security_wage_base", "value": 176100, "format": "currency"}, {"label": "FUTA wage base per employee per year (statutory)", "key": "futa_wage_base", "value": 7000, "format": "currency"}, {"label": "FUTA net rate after the 5.4% state credit (use 6.0% in a credit-reduction state)", "key": "futa_net_rate", "value": 0.006, "format": "percent"}, {"label": "Your state UI taxable wage base (from your state agency notice)", "key": "state_ui_wage_base", "value": 7000, "format": "currency"}, {"label": "Your assigned state UI rate for this year (from your state agency notice)", "key": "state_ui_rate", "value": 0.034, "format": "percent"}]}, {"name": "1. This Paycheck (Employee)", "purpose": "One question, answered completely: what comes out of this paycheck. Social Security stops at the wage base, Medicare never stops, and the extra 0.9% starts only after $200,000 of wages in the calendar year — employers withhold that 0.9% at $200,000 regardless of the employee's filing status, and do not match it (IRS Publication 15). Charge 6.2% on wages above the base and you over-withhold: the employee recovers it on their 1040, but your Form 941 no longer ties out and the correction lands on you. Net pay here is before state income tax and any benefit deductions.", "calc_rows": [{"label": "Wages subject to Social Security this period (capped at the wage base)", "key": "ss_taxable_wages", "formula": "=MIN({gross_wages_this_period},MAX(0,{social_security_wage_base}-{ytd_wages_before_this_period}))"}, {"label": "Social Security withheld from the employee at 6.2%", "key": "ss_withheld_employee", "formula": "=ROUND({ss_taxable_wages}*0.062,2)"}, {"label": "Medicare withheld from the employee at 1.45% (no wage cap)", "key": "medicare_withheld_employee", "formula": "=ROUND({gross_wages_this_period}*0.0145,2)"}, {"label": "Additional Medicare withheld at 0.9% on wages over $200,000 year to date", "key": "additional_medicare_withheld", "formula": "=ROUND((MAX(0,{ytd_wages_before_this_period}+{gross_wages_this_period}-200000)-MAX(0,{ytd_wages_before_this_period}-200000))*0.009,2)"}, {"label": "Total withheld from this paycheck, income tax plus FICA", "key": "employee_taxes_withheld_total", "formula": "=ROUND({federal_income_tax_withheld}+{ss_withheld_employee}+{medicare_withheld_employee}+{additional_medicare_withheld},2)"}, {"label": "Net pay before state income tax and benefit deductions", "key": "net_pay_this_period", "formula": "=ROUND({gross_wages_this_period}-{employee_taxes_withheld_total},2)"}]}]}, "lang": "en", "locale": "en-US", "currency": "USD", "paid": "", "slug": "payroll-tax-spreadsheet", "ui": {"inputs": "Your numbers", "results": "Your result", "recalc": "Everything updates as you type.", "get": "Get the full calculator", "email": "Email me the full version", "placeholder": "you@example.com", "sent": "Sent. Check your inbox.", "share": "Save result image", "copy": "Copy link", "copied": "Link copied", "more": "Full version: all tabs, your own file, works offline.", "free": "Free calculator", "note": "Nothing is uploaded. This runs in your browser.", "dl": "Download my numbers (CSV)", "dl_need": "Enter your email above and we will unlock the download.", "dl_done": "Downloaded."}, "title": "Payroll Tax Spreadsheet for US Small Employers — FICA, FUTA and State UI Wage-Base Caps per Paycheck"}</script>
<script>var PRE={"=MIN({gross_wages_this_period},MAX(0,{social_security_wage_base}-{ytd_wages_before_this_period}))":function(V,FN){return (FN.MIN( V["gross_wages_this_period"] ,FN.MAX(0, V["social_security_wage_base"] - V["ytd_wages_before_this_period"] )));},"=ROUND({ss_taxable_wages}*0.062,2)":function(V,FN){return (FN.ROUND( V["ss_taxable_wages"] *0.062,2));},"=ROUND({gross_wages_this_period}*0.0145,2)":function(V,FN){return (FN.ROUND( V["gross_wages_this_period"] *0.0145,2));},"=ROUND((MAX(0,{ytd_wages_before_this_period}+{gross_wages_this_period}-200000)-MAX(0,{ytd_wages_before_this_period}-200000))*0.009,2)":function(V,FN){return (FN.ROUND((FN.MAX(0, V["ytd_wages_before_this_period"] + V["gross_wages_this_period"] -200000)-FN.MAX(0, V["ytd_wages_before_this_period"] -200000))*0.009,2));},"=ROUND({federal_income_tax_withheld}+{ss_withheld_employee}+{medicare_withheld_employee}+{additional_medicare_withheld},2)":function(V,FN){return (FN.ROUND( V["federal_income_tax_withheld"] + V["ss_withheld_employee"] + V["medicare_withheld_employee"] + V["additional_medicare_withheld"] ,2));},"=ROUND({gross_wages_this_period}-{employee_taxes_withheld_total},2)":function(V,FN){return (FN.ROUND( V["gross_wages_this_period"] - V["employee_taxes_withheld_total"] ,2));},"=ROUND({ss_taxable_wages}*0.062+{gross_wages_this_period}*0.0145,2)":function(V,FN){return (FN.ROUND( V["ss_taxable_wages"] *0.062+ V["gross_wages_this_period"] *0.0145,2));},"=MIN({gross_wages_this_period},MAX(0,{futa_wage_base}-{ytd_wages_before_this_period}))":function(V,FN){return (FN.MIN( V["gross_wages_this_period"] ,FN.MAX(0, V["futa_wage_base"] - V["ytd_wages_before_this_period"] )));},"=ROUND({futa_taxable_wages}*{futa_net_rate},2)":function(V,FN){return (FN.ROUND( V["futa_taxable_wages"] * V["futa_net_rate"] ,2));},"=ROUND(MIN({gross_wages_this_period},MAX(0,{state_ui_wage_base}-{ytd_wages_before_this_period}))*{state_ui_rate},2)":function(V,FN){return (FN.ROUND(FN.MIN( V["gross_wages_this_period"] ,FN.MAX(0, V["state_ui_wage_base"] - V["ytd_wages_before_this_period"] ))* V["state_ui_rate"] ,2));},"=ROUND({federal_income_tax_withheld}+{ss_withheld_employee}+{medicare_withheld_employee}+{additional_medicare_withheld}+{employer_ss_and_medicare_match},2)":function(V,FN){return (FN.ROUND( V["federal_income_tax_withheld"] + V["ss_withheld_employee"] + V["medicare_withheld_employee"] + V["additional_medicare_withheld"] + V["employer_ss_and_medicare_match"] ,2));},"=ROUND({gross_wages_this_period}+{employer_ss_and_medicare_match}+{futa_tax_this_period}+{state_ui_tax_this_period},2)":function(V,FN){return (FN.ROUND( V["gross_wages_this_period"] + V["employer_ss_and_medicare_match"] + V["futa_tax_this_period"] + V["state_ui_tax_this_period"] ,2));}};</script>
<script>
var CALC = (function () {
function n(x){ x = Number(x); return isFinite(x) ? x : 0; }
function ROUND(x, d){ d = n(d); var f = Math.pow(10, d); var v = n(x) * f;
var r = (v >= 0) ? Math.floor(v + 0.5) : Math.ceil(v - 0.5); return r / f; }
function POWER(a, b){ return Math.pow(n(a), n(b)); }
function ABS(a){ return Math.abs(n(a)); }
function MIN(){ var a=[].slice.call(arguments).map(n); return a.length?Math.min.apply(null,a):0; }
function MAX(){ var a=[].slice.call(arguments).map(n); return a.length?Math.max.apply(null,a):0; }
function SUM(){ var s=0; for(var i=0;i<arguments.length;i++) s+=n(arguments[i]); return s; }
function IF(c, a, b){ return c ? a : b; }
function FLOOR(x, sig){ sig = (arguments.length > 1) ? n(sig) : 1;
if (sig === 0) return 0; return Math.floor(n(x) / sig) * sig; }
function CEILING(x, sig){ sig = (arguments.length > 1) ? n(sig) : 1;
if (sig === 0) return 0; return Math.ceil(n(x) / sig) * sig; }
function PMT(rate, nper, pv, fv, type){
rate = n(rate); nper = n(nper); pv = n(pv); fv = n(fv); type = n(type);
if (nper === 0) return 0;
if (rate === 0) return -(pv + fv) / nper;
var p = Math.pow(1 + rate, nper);
return -(pv * p + fv) * rate / ((p - 1) * (1 + rate * type));
}
var FN = { ROUND:ROUND, POWER:POWER, ABS:ABS, MIN:MIN, MAX:MAX, SUM:SUM, IF:IF, PMT:PMT,
FLOOR:FLOOR, CEILING:CEILING };
var FNAMES = Object.keys(FN);
// 엑셀 수식 문자열 -> 안전한 JS 식. ⛔화이트리스트 밖 문자가 남으면 ★던진다(조용한 0 금지).
// ★2026-08-10 s70 — ★치환(toExpr)과 ★함수 생성(compile)을 갈랐다.
// ⛔이유: 브라우저에서 ★동적 함수 생성을 하면 ★CSP 가 죽인다(s68 에 계산기 7개가 "-" 였다).
// ⚠이 주석에 그 낱말을 쓰지 않는 이유: ★검사기가 문자열로 세기 때문이다 — ★검사를 느슨하게
// 하는 대신 ★주석을 바꾼다 (⛔검사기를 무디게 만드는 것이 s67 의 병이었다).
// ⇒ ★빌드 때 toExpr 로 식을 뽑아 HTML 에 ★구워 넣고, 브라우저는 ★조회만 한다.
function toExpr(formula){
var s = String(formula || '').trim();
if (s.charAt(0) === '=') s = s.slice(1);
if (!s) return null;
s = s.replace(/\{([A-Za-z0-9_]+)\}/g, function(_, k){ return ' V["' + k + '"] '; });
s = s.replace(/<>/g, '!==').replace(/>=/g, '@GE@').replace(/<=/g, '@LE@');
s = s.replace(/=/g, '===').replace(/@GE@/g, '>=').replace(/@LE@/g, '<=');
s = s.replace(/\^/g, '**');
FNAMES.forEach(function(f){ s = s.replace(new RegExp('\\b' + f + '\\s*\\(', 'gi'), 'FN.' + f + '('); });
var probe = s.replace(/V\["[A-Za-z0-9_]+"\]/g, '0').replace(/FN\.[A-Z]+\(/g, '(');
if (!/^[0-9+\-*/%().,\s<>=!&|*]*$/.test(probe)) {
throw new Error('unsupported token in formula: ' + formula);
}
return s;
}
function compile(formula){
// ★브라우저: 빌드 때 구워넣은 PRE 를 ★조회만 한다 (eval 0).
var raw = String(formula || '').trim();
if (!raw) return null;
if (typeof PRE !== 'undefined' && PRE && PRE[raw]) return PRE[raw];
throw new Error('not precompiled: ' + raw);
}
// 명세 -> {values, order, rows}. ⛔없는 key 는 0 (xlsx_builder 와 같은 규칙).
function run(spec, override){
var sheets = (spec.sheets_content && spec.sheets_content.sheets) || spec.sheets || [];
var V = Object.create(null);
var order = [], rows = [];
var handler = { get: function(t, k){ return (k in t) ? t[k] : 0; } };
var PV = (typeof Proxy !== 'undefined') ? new Proxy(V, handler) : V;
sheets.forEach(function(sh){
if (sh && sh.inputs && sh.inputs.length) {
sh.inputs.forEach(function(it){
if (!it || !it.key) return;
var v = (override && (it.key in override)) ? override[it.key] : it.value;
V[it.key] = n(v);
});
}
});
sheets.forEach(function(sh){
if (!sh || !sh.calc_rows || !sh.calc_rows.length) return;
sh.calc_rows.forEach(function(cr){
if (!cr) return;
var val = 0, err = null;
try { var f = compile(cr.formula); val = f ? n(f(PV, FN)) : 0; }
catch (e) { err = String(e && e.message || e); }
if (cr.key) V[cr.key] = val;
var rec = { sheet: sh.name, label: cr.label, key: cr.key || null,
format: cr.format || 'number', value: val, error: err };
order.push(rec); rows.push(rec);
});
});
return { values: V, rows: rows };
}
return { run: run, compile: compile, toExpr: toExpr, FN: FN };
})();
if (typeof module !== 'undefined' && module.exports) module.exports = CALC;
</script>
<script>
(function(){
var D=JSON.parse(document.getElementById('calcdata').textContent);
var UI=D.ui, LOC=D.locale, CUR=D.currency;
function fmt(v,f){
if(!isFinite(v)) v=0;
if(f==='currency') return new Intl.NumberFormat(LOC,{style:'currency',currency:CUR,maximumFractionDigits:2}).format(v);
if(f==='percent') return new Intl.NumberFormat(LOC,{style:'percent',minimumFractionDigits:2,maximumFractionDigits:2}).format(v);
if(f==='integer'||f==='int') return new Intl.NumberFormat(LOC,{maximumFractionDigits:0}).format(v);
return new Intl.NumberFormat(LOC,{maximumFractionDigits:4}).format(v);
}
var ins=[].slice.call(document.querySelectorAll('input[data-k]'));
var outs=[].slice.call(document.querySelectorAll('td.v'));
function recalc(){
var ov={}; ins.forEach(function(el){
var n=Number(el.value)||0; ov[el.dataset.k]= el.dataset.pct ? n/100 : n; });
var r=CALC.run(D.spec,ov);
var byKey={}, i=0;
r.rows.forEach(function(x){ if(x.key) byKey[x.key]=x; });
outs.forEach(function(td){
var rec = td.dataset.k ? byKey[td.dataset.k] : r.rows[i];
i++;
if(!rec){ td.textContent='-'; return; }
td.textContent = rec.error ? '-' : fmt(rec.value, td.dataset.f);
});
window.__last=r;
}
ins.forEach(function(el){ el.addEventListener('input',recalc); });
recalc();
// 2026-08-27 s109 - owner: "email 안넣고 그냥 쓰고 가면 의미가 없잖아." => right.
// BUT the core answer must NOT be gated: a searcher who cannot see the answer bounces,
// bounce rate up -> search rank down -> we close our own door
// (measured: usable slice 1,726 ratings vs crippled demo 5 = 345x).
// So: the ANSWER stays free, and "the NEXT step" (taking your own numbers away as a file)
// is what asks for the email (Zeigarnik). Whoever already gave it is never asked again.
function _paid(){ try{ return localStorage.getItem('yj_em')==='1'; }catch(_){ return false; } }
function _mark(){ try{ localStorage.setItem('yj_em','1'); }catch(_){} }
document.getElementById('dl').addEventListener('click',function(){
if(!_paid()){
document.getElementById('need').hidden=false;
var em0=document.getElementById('em');
em0.focus(); em0.scrollIntoView({block:'center'});
return;
}
var rr=[['label','value']];
ins.forEach(function(el){
var lb=(el.closest('label')||{}).textContent||el.dataset.k;
rr.push([String(lb).trim(), el.value]);
});
((window.__last&&window.__last.rows)||[]).forEach(function(r){
rr.push([String(r.label), r.error?'':String(r.value)]);
});
var csv=rr.map(function(r){
return r.map(function(c){ return '"'+String(c).replace(/"/g,'""')+'"'; }).join(',');
}).join('\n');
var bl=new Blob(['\ufeff'+csv],{type:'text/csv;charset=utf-8'});
var ae=document.createElement('a');
ae.href=URL.createObjectURL(bl); ae.download=(D.slug||'result')+'.csv'; ae.click();
var bt=this, tx=bt.textContent;
bt.textContent='Downloaded.'; setTimeout(function(){ bt.textContent=tx; },1800);
});
document.getElementById('go').addEventListener('click',function(){
var e=document.getElementById('em').value.trim();
if(!/.+@.+\..+/.test(e)) return;
_mark(); document.getElementById('need').hidden=true;
fetch(location.origin+'/api/subscribe',{method:'POST',headers:{'Content-Type':'application/json'},
body:JSON.stringify({email:e,slug:D.slug,lang:D.lang,source:'calc_web'})})
.catch(function(){}).then(function(){ document.getElementById('ok').hidden=false; });
});
document.getElementById('sh').addEventListener('click',function(){
var c=document.getElementById('cv'), x=c.getContext('2d');
x.fillStyle='#0e1116'; x.fillRect(0,0,1200,630);
x.fillStyle='#fff'; x.font='bold 52px system-ui,sans-serif';
x.fillText(D.title.slice(0,42),64,120);
var rws=(window.__last&&window.__last.rows)||[]; var y=230;
rws.slice(0,5).forEach(function(r){
x.fillStyle='#9aa4b2'; x.font='28px system-ui,sans-serif';
x.fillText(String(r.label).slice(0,44),64,y);
x.fillStyle='#7aa2f7'; x.font='bold 34px system-ui,sans-serif';
x.textAlign='right'; x.fillText(fmt(r.value,r.format),1136,y); x.textAlign='left';
y+=72;
});
x.fillStyle='#6b7280'; x.font='24px system-ui,sans-serif';
x.fillText(location.host+(D.slug?'/tools/'+D.slug:''),64,590);
var a=document.createElement('a'); a.download=(D.slug||'result')+'.png';
a.href=c.toDataURL('image/png'); a.click();
});
// ★★2026-08-22 s99 (B2) — ★링크 복사. ⛔이게 없으면 ★도메인이 같이 퍼지지 않는다.
// ★결과 이미지는 ★도메인을 찍어서 나가고, ★링크는 ★그 자리로 되돌아오게 한다.
document.getElementById('cp').addEventListener('click',function(){
var b=this, t=b.textContent;
var u=location.href.split('#')[0];
function done(){ b.textContent='Link copied'; setTimeout(function(){ b.textContent=t; },1800); }
if(navigator.clipboard&&navigator.clipboard.writeText){
navigator.clipboard.writeText(u).then(done,function(){ prompt('',u); });
} else { prompt('',u); }
});
})();
</script><section class="yj-related" style="max-width:920px;margin:56px auto 40px;padding:0 20px"><h2 style="font:600 21px/1.3 system-ui,sans-serif;margin:0 0 6px;color:#1b1f24">More from ReadyStack</h2><p style="font:400 14.5px/1.5 system-ui,sans-serif;margin:0 0 20px;color:#5b6570">Ready-to-use files. One-time download, no subscription.</p><div style="display:grid;grid-template-columns:repeat(auto-fill,minmax(250px,1fr));gap:14px"><a class="yj-rel-c" href="https://getreadystack.com/tools/healthcare-icon-pack-ui-kit"><span class="yj-rel-t">Healthcare Icon Pack? Start With the Screen — Patient-Safety UI Kit for Figma </span><span class="yj-rel-p">$168</span></a><a class="yj-rel-c" href="https://getreadystack.com/tools/law-firm-website-template"><span class="yj-rel-t">Law Firm Website Template — Six-Page HTML Site for a Solo or Small Practice, W</span><span class="yj-rel-p">$49</span></a><a class="yj-rel-c" href="https://getreadystack.com/tools/webflow-restaurant-template"><span class="yj-rel-t">Webflow Restaurant Template — Self-Hosted 9-Page Restaurant Website in Plain H</span><span class="yj-rel-p">$49</span></a><a class="yj-rel-c" href="https://getreadystack.com/tools/tax-preparer-website-template"><span class="yj-rel-t">Tax Preparer Website Template — 11-Page Self-Contained HTML Site for US Tax Pr</span><span class="yj-rel-p">$49</span></a><a class="yj-rel-c" href="https://getreadystack.com/tools/drone-lut-pack"><span class="yj-rel-t">Drone Videographer LUTs for Aerial Real Estate and Travel Footage</span><span class="yj-rel-p">$100</span></a><a class="yj-rel-c" href="https://getreadystack.com/tools/graduation-photo-presets"><span class="yj-rel-t">Graduation Photo Presets for School Portrait Photographers and Video Editors</span><span class="yj-rel-p">$44</span></a></div><style>.yj-rel-c{display:flex;flex-direction:column;gap:8px;padding:15px 16px;border:1px solid #e2e6ea;border-radius:6px;text-decoration:none;background:#fff}.yj-rel-c:hover{border-color:#b9c2cb;background:#fafbfc}.yj-rel-t{font:500 14.5px/1.42 system-ui,sans-serif;color:#1b1f24}.yj-rel-p{font:600 13px system-ui,sans-serif;color:#0a6238}</style></section>
<div class="yj-home-find"><div class="yj-lgbar"><a href="/" class="yj-lg on">EN</a><a href="/de/" class="yj-lg">DE</a><a href="/ja/" class="yj-lg">JA</a><a href="/es/" class="yj-lg">ES</a><a href="/pt/" class="yj-lg">PT</a></div><section class="yj-find"><h2>Find a tool</h2><input id="yjq" type="search" autocomplete="off" placeholder="Type a product name, format or task"><div id="yjr"></div></section><style>.yj-lgbar{position:absolute;top:14px;right:18px;display:flex;gap:6px;z-index:60}.yj-lg{font:600 12px/1 system-ui,sans-serif;padding:6px 9px;border-radius:4px;text-decoration:none;color:#8b96a3;border:1px solid rgba(140,150,165,.28)}.yj-lg:hover{color:#e8eaed;border-color:rgba(160,175,195,.6)}.yj-lg.on{color:#0e1116;background:#cfd6de;border-color:#cfd6de}.yj-find{max-width:760px;margin:34px auto 8px;padding:0 20px}.yj-find h2{font:600 20px/1.3 system-ui,sans-serif;margin:0 0 10px}#yjq{width:100%;box-sizing:border-box;padding:13px 15px;font:400 15.5px system-ui,sans-serif;border:1px solid rgba(140,150,165,.34);border-radius:8px;background:rgba(255,255,255,.04);color:inherit}#yjq:focus{outline:none;border-color:#7FD1C4}#yjr{margin-top:10px;display:flex;flex-direction:column;gap:6px}#yjr a{display:flex;justify-content:space-between;gap:12px;padding:11px 14px;border:1px solid rgba(140,150,165,.22);border-radius:6px;text-decoration:none;font:400 14.5px/1.4 system-ui,sans-serif;color:inherit}#yjr a:hover{border-color:#7FD1C4}#yjr .pr{font:600 13px system-ui,sans-serif;color:#5fbf9a;white-space:nowrap}</style><script>(function(){var q=document.getElementById("yjq"),r=document.getElementById("yjr"),D=null;function load(){if(D)return Promise.resolve(D);return fetch("/search-index.json").then(function(x){return x.json();}).then(function(j){D=j;return j;}).catch(function(){D=[];return D;});}function esc(t){return String(t).replace(/[&<>"]/g,function(c){return {"&":"&amp;","<":"&lt;",">":"&gt;",'"':"&quot;"}[c];});}function run(){var v=q.value.trim().toLowerCase();if(v.length<2){r.innerHTML="";return;}load().then(function(j){var w=v.split(/\s+/),o=[];for(var i=0;i<j.length&&o.length<12;i++){var t=j[i].t.toLowerCase();var ok=true;for(var k=0;k<w.length;k++){if(t.indexOf(w[k])<0){ok=false;break;}}if(ok)o.push(j[i]);}r.innerHTML=o.length?o.map(function(x){return "<a href=\""+esc(x.u)+"\">"+"<span>"+esc(x.t)+"</span>"+(x.p?"<span class=\"pr\">$"+x.p+"</span>":"")+"</a>";}).join(""):"";});}q.addEventListener("input",run);q.addEventListener("focus",load);})();</script></div>
<script data-yj="yj-view-beacon">(function(){try{var p=location.pathname;var k='yjv:'+p;if(sessionStorage.getItem(k))return;sessionStorage.setItem(k,'1');fetch('/api/ev',{method:'POST',keepalive:true,headers:{'Content-Type':'application/json'},body:JSON.stringify({path:p,lang:"en",ref:(document.referrer||''),src:(function(){try{return (new URLSearchParams(location.search).get('ref')||'').replace(/[^a-z_]/g,'').slice(0,12)}catch(e){return ''}})()})}).catch(function(){});}catch(e){}})();</script>
<script data-yj="yj-use-beacon v2">(function(){try{var r=document.querySelector('main')||document.body;var p=location.pathname,k='yju:'+p,sent=false,armed=false;function ours(el){return !!(el&&el.closest&&el.closest('[data-yj],[data-yj-cta],.yj-email-gate,.yj-find,.yj-related,#yj-ask,#yjgo,#yjr'));}function fire(){if(sent||!armed||sessionStorage.getItem(k))return;sent=true;sessionStorage.setItem(k,'1');fetch('/api/ev',{method:'POST',keepalive:true,headers:{'Content-Type':'application/json'},body:JSON.stringify({path:p,lang:"en",t:'use',src:(function(){try{return (new URLSearchParams(location.search).get('ref')||'').replace(/[^a-z_]/g,'').slice(0,12)}catch(e){return ''}})()})}).catch(function(){});}r.addEventListener('click',function(e){var b=e.target&&e.target.closest?e.target.closest('button,input[type=submit],[role=button]'):null;if(!b||ours(b))return;armed=true;setTimeout(fire,400);},true);r.addEventListener('input',function(e){if(!ours(e.target))armed=true;},true);var mo=new MutationObserver(function(){if(armed){fire();if(sent)mo.disconnect();}});mo.observe(r,{childList:true,characterData:true,subtree:true});}catch(e){}})();</script>
<script src="/assets/pro.js" defer></script>
</body></html>