<main class="calc" role="application" aria-label="Calculator">
<section class="display" aria-live="polite">
<div class="top-row">
<div class="mem-indicator" id="mem">M: —</div>
<div class="mem-indicator" id="error" role="status" aria-atomic="true"></div>
</div>
<div class="expression" id="expression">0</div>
<div class="result" id="result">0</div>
</section>
<section class="btn-grid" role="group">
<button class="btn-fn" data-action="clear">C</button>
<button class="btn-fn" data-action="plusminus">±</button>
<button class="btn-fn" data-action="percent">%</button>
<button class="btn-operator" data-value="/">÷</button>
<button data-value="7">7</button>
<button data-value="8">8</button>
<button data-value="9">9</button>
<button class="btn-operator" data-value="*">×</button>
<button data-value="4">4</button>
<button data-value="5">5</button>
<button data-value="6">6</button>
<button class="btn-operator" data-value="-">−</button>
<button data-value="1">1</button>
<button data-value="2">2</button>
<button data-value="3">3</button>
<button class="btn-operator" data-value="+">+</button>
<button data-value="0" class="btn-wide">0</button>
<button data-value=".">.</button>
<button class="btn-equal" data-action="equals">=</button>
</section>
<footer>Keyboard: numbers, + - * / . Enter =, Backspace to delete</footer>
</main>
:root{
--bg:#0f1724; /* deep navy */
--panel:#0b1220;
--accent:#60a5fa;
--muted:#94a3b8;
--glass: rgba(255,255,255,0.04);
}
*{box-sizing:border-box}
html,body{height:100%;margin:0;font-family:Inter,ui-sans-serif,system-ui,-apple-system,"Segoe UI",Roboto,"Helvetica Neue",Arial}
body{
background: linear-gradient(180deg,#071025 0%, #091426 60%);
color:#e6eef8;display:flex;align-items:center;justify-content:center;padding:24px
}
.calc{
width:360px;max-width:96vw;background:linear-gradient(180deg,var(--panel), rgba(255,255,255,0.02));border-radius:18px;padding:18px;box-shadow:0 10px 30px rgba(2,6,23,0.6);
backdrop-filter: blur(6px);
}
.display{
background:var(--glass);padding:18px;border-radius:12px;display:flex;flex-direction:column;gap:6px;min-height:84px;margin-bottom:14px;
}
.top-row{display:flex;justify-content:space-between;align-items:center}
.mem-indicator{font-size:12px;color:var(--muted)}
.expression{font-size:14px;color:var(--muted);word-break:break-all}
.result{font-size:28px;font-weight:600;word-break:break-all}
.btn-grid{display:grid;grid-template-columns:repeat(4,1fr);gap:10px}
button{
border:0;padding:14px;border-radius:10px;font-size:18px;cursor:pointer;user-select:none;background:transparent;color:inherit;transition:transform .06s ease, box-shadow .06s ease;
}
button:active{transform:translateY(1px)}
.btn-operator{background:linear-gradient(180deg, rgba(96,165,250,0.12), rgba(96,165,250,0.06));color:var(--accent);box-shadow:inset 0 -2px 0 rgba(96,165,250,0.04)}
.btn-equal{grid-column:span 2;background:linear-gradient(90deg,var(--accent),#3b82f6);color:#04233b;font-weight:700}
.btn-fn{background:rgba(255,255,255,0.02);color:var(--muted)}
.btn-wide{grid-column:span 2}
.small{font-size:13px;padding:10px}
footer{margin-top:12px;font-size:12px;color:var(--muted);text-align:center}
@media (max-width:420px){.calc{width:320px}}
(function(){
const expressionEl = document.getElementById('expression');
const resultEl = document.getElementById('result');
const memEl = document.getElementById('mem');
const errorEl = document.getElementById('error');
let expr = '';
let memory = null;
function sanitizeForEval(s){
// allow only digits, parentheses, decimal point and operators +-*/% and spaces
if(!/^[0-9+\-*/().% \u00B1]*$/.test(s)) return null;
// convert unicode minus (if any) to ascii
return s.replace(/×/g,'*').replace(/÷/g,'/').replace(/−/g,'-');
}
function updateDisplay(){
expressionEl.textContent = expr === '' ? '0' : expr;
// try to compute a preview result
const safe = sanitizeForEval(expr);
if(safe === null){
resultEl.textContent = 'Err';
return;
}
try{
// small preview: do not evaluate partial trailing operators
const trimmed = safe.replace(/[+\-*/.% ]+$/,'');
if(trimmed === ''){ resultEl.textContent = '0'; return }
// evaluate using Function in a safe-ish way
// eslint-disable-next-line no-new-func
const val = Function('return (' + trimmed + ')')();
resultEl.textContent = (Number.isFinite(val) ? +parseFloat(val.toFixed(12)) : 'Err');
}catch(e){ resultEl.textContent = 'Err' }
}
function pressValue(v){
errorEl.textContent = '';
// prevent multiple decimals in one number
if(v === '.'){
// find last number chunk
const parts = expr.split(/[^0-9.]/);
const last = parts[parts.length-1];
if(last && last.includes('.')) return;
if(expr === '' || /[+\-*/(]$/.test(expr)) expr += '0';
expr += '.';
} else if(/[0-9]/.test(v)){
expr += v;
} else if(/[+\-*/%]/.test(v)){
if(expr === '' && v === '-') { expr = '-'; }
else if(/[+\-*/%]$/.test(expr)){
// replace trailing operator
expr = expr.replace(/[+\-*/%]+$/, v);
} else {
expr += v;
}
}
updateDisplay();
}
function clearAll(){ expr = ''; updateDisplay(); }
function backspace(){ expr = expr.slice(0,-1); updateDisplay(); }
function togglePlusMinus(){
// attempt to toggle sign of last number
const match = expr.match(/(.*?)([-]?\d*\.?\d+)([^\d.]*)$/);
if(!match) return;
const before = match[1] || '';
const number = match[2];
const after = match[3] || '';
if(number.startsWith('-')){
expr = before + number.slice(1) + after;
} else {
expr = before + '-' + number + after;
}
updateDisplay();
}
function percent(){
// convert last number to percentage
const match = expr.match(/(.*?)(-?\d*\.?\d+)([^\d.]*)$/);
if(!match) return;
const before = match[1] || '';
const number = parseFloat(match[2]);
const after = match[3] || '';
if(Number.isNaN(number)) return;
expr = before + (number/100) + after;
updateDisplay();
}
function calculate(){
const safe = sanitizeForEval(expr);
if(safe === null) { errorEl.textContent = 'Invalid characters'; return }
try{
// eslint-disable-next-line no-new-func
const val = Function('return (' + safe + ')')();
if(!Number.isFinite(val)) { errorEl.textContent = 'Math error'; return }
expr = String(+parseFloat(val.toFixed(12)));
updateDisplay();
}catch(e){ errorEl.textContent = 'Syntax error' }
}
function setMemory(val){ memory = val; memEl.textContent = memory === null ? 'M: —' : 'M: ' + memory; }
// wire buttons
document.querySelectorAll('button').forEach(btn => {
btn.addEventListener('click', ()=>{
const action = btn.dataset.action;
const value = btn.dataset.value;
if(action === 'clear') { clearAll(); }
else if(action === 'equals') { calculate(); }
else if(action === 'plusminus') { togglePlusMinus(); }
else if(action === 'percent') { percent(); }
else if(value) { pressValue(value); }
})
});
// keyboard support
window.addEventListener('keydown', (e)=>{
if(e.key === 'Enter' || e.key === '='){ e.preventDefault(); calculate(); return }
if(e.key === 'Backspace'){ e.preventDefault(); backspace(); return }
if(e.key === 'Escape'){ clearAll(); return }
if(e.key === '%'){ percent(); return }
if(/[0-9]/.test(e.key) || e.key === '.') { pressValue(e.key); return }
if(['+','-','*','/','(',')'].includes(e.key)) { pressValue(e.key); return }
});
// init
clearAll();
})();
zabardaat janab