<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8"/>
<meta name="viewport" content="width=device-width, initial-scale=1.0"/>
<title>PTO Calculator</title>
<style>
@import url('https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&display=swap');
*,*::before,*::after{box-sizing:border-box;margin:0;padding:0;}
body{font-family:'Inter',sans-serif;background:#f0f4ff;min-height:100vh;display:flex;align-items:flex-start;justify-content:center;padding:2rem 1rem;}
.card{background:#fff;border-radius:16px;box-shadow:0 4px 32px rgba(37,99,235,0.10);width:100%;max-width:520px;overflow:hidden;}
.card-header{background:linear-gradient(135deg,#2563eb,#0ea5e9);padding:2rem 2rem 1.5rem;color:#fff;}
.card-header h1{font-size:1.5rem;font-weight:700;margin-bottom:0.25rem;}
.card-header p{font-size:0.88rem;opacity:0.85;}
.card-body{padding:1.75rem 2rem;}
.field{margin-bottom:1.1rem;}
.field label{display:block;font-size:0.83rem;font-weight:600;color:#374151;margin-bottom:0.35rem;text-transform:uppercase;letter-spacing:0.5px;}
.field input,.field select{width:100%;padding:0.65rem 0.9rem;border:1.5px solid #e2e8f0;border-radius:8px;font-family:'Inter',sans-serif;font-size:0.93rem;color:#1e293b;background:#fff;outline:none;transition:border-color 0.2s,box-shadow 0.2s;}
.field input:focus,.field select:focus{border-color:#2563eb;box-shadow:0 0 0 3px rgba(37,99,235,0.12);}
.row{display:grid;grid-template-columns:1fr 1fr;gap:0.85rem;}
@media(max-width:440px){.row{grid-template-columns:1fr;}}
.input-unit{display:flex;gap:0.5rem;}
.input-unit input{flex:1;}
.input-unit select{width:100px;flex-shrink:0;}
.toggle-row{display:flex;align-items:center;gap:0.65rem;margin-bottom:1.1rem;}
.switch{position:relative;width:38px;height:21px;flex-shrink:0;}
.switch input{opacity:0;width:0;height:0;}
.slider{position:absolute;inset:0;background:#cbd5e1;border-radius:21px;cursor:pointer;transition:.2s;}
.slider::before{content:'';position:absolute;width:15px;height:15px;left:3px;top:3px;background:#fff;border-radius:50%;transition:.2s;}
.switch input:checked~.slider{background:#2563eb;}
.switch input:checked~.slider::before{transform:translateX(17px);}
.toggle-label{font-size:0.85rem;font-weight:600;color:#374151;}
.divider{height:1px;background:#f1f5f9;margin:1.25rem 0;}
.btn{width:100%;padding:0.9rem;background:#2563eb;color:#fff;border:none;border-radius:9px;font-family:'Inter',sans-serif;font-size:1rem;font-weight:700;cursor:pointer;transition:background 0.18s,transform 0.1s;}
.btn:hover{background:#1d4ed8;}
.btn:active{transform:scale(0.98);}
.result{display:none;margin-top:1.25rem;background:#f0fdf4;border:1.5px solid #86efac;border-radius:12px;padding:1.4rem;text-align:center;}
.result.show{display:block;}
.result-label{font-size:0.72rem;font-weight:700;letter-spacing:2px;text-transform:uppercase;color:#16a34a;margin-bottom:0.25rem;}
.result-value{font-size:2.8rem;font-weight:700;color:#15803d;line-height:1.1;margin-bottom:0.25rem;}
.result-breakdown{font-size:0.82rem;color:#166534;margin-top:0.5rem;line-height:1.7;}
.result-note{font-size:0.78rem;color:#6b7280;margin-top:0.75rem;font-style:italic;}
.error{display:none;margin-top:0.75rem;background:#fef2f2;border:1.5px solid #fecaca;border-radius:8px;padding:0.75rem 1rem;color:#b91c1c;font-size:0.86rem;}
.error.show{display:block;}
#limitField{display:none;margin-bottom:1.1rem;}
</style>
</head>
<body>
<div class="card">
<div class="card-header">
<h1>🗓️ PTO Calculator</h1>
<p>Estimate your paid time off accrual over any period</p>
</div>
<div class="card-body">
<div class="row">
<div class="field">
<label>Start Date</label>
<input type="date" id="startDate"/>
</div>
<div class="field">
<label>End Date</label>
<input type="date" id="endDate"/>
</div>
</div>
<div class="field">
<label>Starting PTO Balance</label>
<div class="input-unit">
<input type="number" id="startBal" placeholder="0" min="0" step="0.5"/>
<select id="startBalUnit">
<option value="hours">Hours</option>
<option value="days">Days</option>
</select>
</div>
</div>
<div class="field">
<label>PTO Accrued per Period</label>
<div class="input-unit">
<input type="number" id="accrueAmt" placeholder="e.g. 4" min="0" step="0.5"/>
<select id="accrueUnit">
<option value="hours">Hours</option>
<option value="days">Days</option>
</select>
</div>
</div>
<div class="field">
<label>Accrual Frequency</label>
<select id="freq">
<option value="daily">Daily</option>
<option value="weekly">Weekly</option>
<option value="biweekly" selected>Bi-weekly (every 2 weeks)</option>
<option value="semimonthly">Semi-monthly (twice a month)</option>
<option value="monthly">Monthly</option>
</select>
</div>
<div class="field">
<label>Show Result In</label>
<select id="displayUnit">
<option value="hours">Hours</option>
<option value="days">Days</option>
</select>
</div>
<div class="divider"></div>
<div class="toggle-row">
<label class="switch">
<input type="checkbox" id="limitOn" onchange="document.getElementById('limitField').style.display=this.checked?'block':'none'"/>
<span class="slider"></span>
</label>
<span class="toggle-label">Set Maximum PTO Cap</span>
</div>
<div id="limitField">
<div class="field" style="margin:0;">
<label>PTO Cap (Maximum Limit)</label>
<div class="input-unit">
<input type="number" id="capAmt" placeholder="e.g. 120" min="0" step="0.5"/>
<select id="capUnit">
<option value="hours">Hours</option>
<option value="days">Days</option>
</select>
</div>
</div>
</div>
<button class="btn" onclick="calc()">Calculate PTO →</button>
<div class="error" id="err"></div>
<div class="result" id="res">
<div class="result-label">Total PTO Balance</div>
<div class="result-value" id="resVal">—</div>
<div class="result-breakdown" id="resBrk"></div>
<div class="result-note">* Estimate only. Actual PTO may vary based on your employer's exact payroll method.</div>
</div>
</div>
</div>
<script>
(function(){
var t=new Date(),ny=new Date(t);
ny.setFullYear(t.getFullYear()+1);
var f=d=>d.toISOString().split('T')[0];
document.getElementById('startDate').value=f(t);
document.getElementById('endDate').value=f(ny);
})();
function toHrs(v,u){return u==='days'?v*8:v;}
function fromHrs(v,u){return u==='days'?v/8:v;}
function fmt(v){return Number.isInteger(v)?v:v.toFixed(1);}
function calc(){
var err=document.getElementById('err');
var res=document.getElementById('res');
err.className='error';res.className='result';
var s=document.getElementById('startDate').value;
var e=document.getElementById('endDate').value;
if(!s||!e){err.textContent='⚠️ Please enter both start and end dates.';err.className='error show';return;}
var sd=new Date(s),ed=new Date(e);
if(ed<=sd){err.textContent='⚠️ End date must be after start date.';err.className='error show';return;}
var amt=parseFloat(document.getElementById('accrueAmt').value);
if(isNaN(amt)||amt<=0){err.textContent='⚠️ Please enter a valid PTO accrual amount.';err.className='error show';return;}
var days=(ed-sd)/(1000*60*60*24);
var freq=document.getElementById('freq').value;
var pd={daily:1,weekly:7,biweekly:14,semimonthly:15.218,monthly:30.437};
var periods=Math.floor(days/pd[freq]);
if(periods<1){err.textContent='⚠️ Date range too short. Try a longer period.';err.className='error show';return;}
var accrueUnit=document.getElementById('accrueUnit').value;
var startBal=parseFloat(document.getElementById('startBal').value)||0;
var startBalUnit=document.getElementById('startBalUnit').value;
var displayUnit=document.getElementById('displayUnit').value;
var startHrs=toHrs(startBal,startBalUnit);
var accrueHrs=toHrs(amt,accrueUnit)*periods;
var capApplied=false;
if(document.getElementById('limitOn').checked){
var capAmt=parseFloat(document.getElementById('capAmt').value);
var capUnit=document.getElementById('capUnit').value;
if(!isNaN(capAmt)&&capAmt>0){
var capHrs=toHrs(capAmt,capUnit);
if(startHrs+accrueHrs>capHrs){accrueHrs=Math.max(0,capHrs-startHrs);capApplied=true;}
}
}
var totalHrs=startHrs+accrueHrs;
var ul=displayUnit==='hours'?'hrs':'days';
var fl={daily:'daily',weekly:'weekly',biweekly:'bi-weekly',semimonthly:'semi-monthly',monthly:'monthly'}[freq];
document.getElementById('resVal').textContent=fmt(fromHrs(totalHrs,displayUnit))+' '+ul;
document.getElementById('resBrk').innerHTML=
'📌 Starting balance: <b>'+fmt(fromHrs(startHrs,displayUnit))+' '+ul+'</b><br>'+
'➕ Accrued: <b>'+fmt(fromHrs(accrueHrs,displayUnit))+' '+ul+'</b> ('+periods+' '+fl+' periods)<br>'+
'🟰 Total PTO: <b>'+fmt(fromHrs(totalHrs,displayUnit))+' '+ul+'</b>'+
(capApplied?'<br>🔒 <i>Cap applied — maximum limit reached</i>':'');
res.className='result show';
res.scrollIntoView({behavior:'smooth',block:'nearest'});
}
</script>
</body>
</html>