diff --git a/Projects/calculator-js/README.md b/Projects/calculator-js/README.md
new file mode 100644
index 00000000..f004e504
--- /dev/null
+++ b/Projects/calculator-js/README.md
@@ -0,0 +1,20 @@
+# Calculator (mini project)
+
+Simple calculator implemented with HTML, CSS and JavaScript.
+
+## Features
+- Basic operations: +, -, *, /
+- Decimal support
+- Percent handling (e.g., 50% → 0.5)
+- Handles division by zero gracefully (shows "Error")
+- Input and result displayed separately
+
+## How to use
+1. Open `index.html` in a browser.
+2. Use the buttons or keyboard to enter the expression.
+3. Press `=` or Enter to evaluate.
+4. Input remains visible; result appears below input.
+
+## Notes for maintainers
+- Files added under `Projects/calculator-js/`
+- No external dependencies
diff --git a/Projects/calculator-js/index.html b/Projects/calculator-js/index.html
new file mode 100644
index 00000000..26292d24
--- /dev/null
+++ b/Projects/calculator-js/index.html
@@ -0,0 +1,44 @@
+
+
+
+
+
+ Calculator
+
+
+
+
+
+
+
+
+ C
+ ⌫
+ %
+ ÷
+
+ 7
+ 8
+ 9
+ ×
+
+ 4
+ 5
+ 6
+ −
+
+ 1
+ 2
+ 3
+ +
+
+ 0
+ .
+ =
+
+
+
+
+
+
+
diff --git a/Projects/calculator-js/script.js b/Projects/calculator-js/script.js
new file mode 100644
index 00000000..05cbd4a5
--- /dev/null
+++ b/Projects/calculator-js/script.js
@@ -0,0 +1,319 @@
+// const display = document.getElementById('display');
+// const resultDisplay = document.getElementById('result');
+// const buttons = document.querySelectorAll('.buttons button');
+
+// let currentExpression = '';
+
+// function updateDisplay() {
+// display.value = currentExpression;
+// }
+
+// function sanitizeExpression(expr) {
+// return expr.replace(/[^0-9+\-*/().%]/g, '');
+// }
+
+// // function calculateExpression(expr) {
+// // const withPercent = expr.replace(/(\d+(\.\d+)?)%/g, '($1/100)');
+// // try {
+// // const result = eval(withPercent);
+// // if (typeof result === 'number') {
+// // if (!isFinite(result)) return 'Error';
+// // return String(Math.round(result * 1e10) / 1e10);
+// // }
+// // return 'Error';
+// // } catch (e) {
+// // return 'Error';
+// // }
+// // }
+
+// +// Option 1: Use math.js library (add to index.html: )
+// +// Or install via npm: npm install mathjs
+// +
+
+// +// Option 1: Use math.js library (add to index.html: )
+// +// Or install via npm: npm install mathjs
+// +
+
+
+
+
+
+// buttons.forEach(btn => {
+// btn.addEventListener('click', () => {
+// const val = btn.dataset.value;
+// const action = btn.dataset.action;
+
+// if (action === 'clear') {
+// currentExpression = '';
+// updateDisplay();
+// resultDisplay.value = '';
+// return;
+// }
+// if (action === 'back') {
+// currentExpression = currentExpression.slice(0, -1);
+// updateDisplay();
+// resultDisplay.value = '';
+// return;
+// }
+// if (action === 'equals') {
+// const sanitized = sanitizeExpression(currentExpression);
+// const result = calculateExpression(sanitized);
+// resultDisplay.value = result;
+// return;
+// }
+
+// currentExpression += val;
+// updateDisplay();
+// resultDisplay.value = '';
+// });
+// });
+
+// document.addEventListener('keydown', (e) => {
+// // Only digits, operators, and decimal
+// if ("0123456789+-*/.%".includes(e.key)) {
+// currentExpression += e.key;
+// updateDisplay();
+// resultDisplay.value = '';
+// }
+// // Evaluate on Enter
+// else if (e.key === 'Enter') {
+// e.preventDefault(); // Prevent form submission
+// const sanitized = sanitizeExpression(currentExpression);
+// resultDisplay.value = calculateExpression(sanitized);
+// }
+// // Delete last character
+// else if (e.key === 'Backspace') {
+// e.preventDefault(); // Prevent browser back navigation
+// currentExpression = currentExpression.slice(0, -1);
+// updateDisplay();
+// resultDisplay.value = '';
+// }
+// // Clear using Escape key
+// else if (e.key === 'Escape') {
+// e.preventDefault();
+// currentExpression = '';
+// updateDisplay();
+// resultDisplay.value = '';
+// }
+// });
+
+const display = document.getElementById('display');
+const resultDisplay = document.getElementById('result');
+const buttons = document.querySelectorAll('.buttons button');
+
+let currentExpression = '';
+
+function updateDisplay() {
+ display.value = currentExpression;
+}
+
+function sanitizeExpression(expr) {
+ return expr.replace(/[^0-9+\-*/().%]/g, '');
+}
+
+// Custom expression parser - replaces eval()
+function calculateExpression(expr) {
+ // Convert percentages to division by 100
+ const withPercent = expr.replace(/(\d+(\.\d+)?)%/g, '($1/100)');
+
+ try {
+ const result = evaluateExpression(withPercent);
+ if (typeof result === 'number') {
+ if (!isFinite(result)) return 'Error';
+ return String(Math.round(result * 1e10) / 1e10);
+ }
+ return 'Error';
+ } catch (e) {
+ return 'Error';
+ }
+}
+
+// Tokenizer: converts string expression into tokens
+function tokenize(expr) {
+ const tokens = [];
+ let i = 0;
+
+ while (i < expr.length) {
+ const char = expr[i];
+
+ // Skip whitespace
+ if (char === ' ') {
+ i++;
+ continue;
+ }
+
+ // Handle numbers (including decimals)
+ if (char >= '0' && char <= '9' || char === '.') {
+ let num = '';
+ while (i < expr.length && (expr[i] >= '0' && expr[i] <= '9' || expr[i] === '.')) {
+ num += expr[i];
+ i++;
+ }
+ tokens.push({ type: 'number', value: parseFloat(num) });
+ continue;
+ }
+
+ // Handle operators
+ if (char === '+' || char === '-' || char === '*' || char === '/') {
+ tokens.push({ type: 'operator', value: char });
+ i++;
+ continue;
+ }
+
+ // Handle parentheses
+ if (char === '(') {
+ tokens.push({ type: 'leftParen' });
+ i++;
+ continue;
+ }
+
+ if (char === ')') {
+ tokens.push({ type: 'rightParen' });
+ i++;
+ continue;
+ }
+
+ // Unknown character - skip it
+ i++;
+ }
+
+ return tokens;
+}
+
+// Evaluator: uses Shunting Yard algorithm to handle operator precedence
+function evaluateExpression(expr) {
+ const tokens = tokenize(expr);
+ const outputQueue = [];
+ const operatorStack = [];
+
+ // Define operator precedence
+ const precedence = {
+ '+': 1,
+ '-': 1,
+ '*': 2,
+ '/': 2
+ };
+
+ // Convert to Reverse Polish Notation (RPN) using Shunting Yard
+ for (const token of tokens) {
+ if (token.type === 'number') {
+ outputQueue.push(token);
+ } else if (token.type === 'operator') {
+ while (
+ operatorStack.length > 0 &&
+ operatorStack[operatorStack.length - 1].type === 'operator' &&
+ precedence[operatorStack[operatorStack.length - 1].value] >= precedence[token.value]
+ ) {
+ outputQueue.push(operatorStack.pop());
+ }
+ operatorStack.push(token);
+ } else if (token.type === 'leftParen') {
+ operatorStack.push(token);
+ } else if (token.type === 'rightParen') {
+ while (
+ operatorStack.length > 0 &&
+ operatorStack[operatorStack.length - 1].type !== 'leftParen'
+ ) {
+ outputQueue.push(operatorStack.pop());
+ }
+ // Pop the left parenthesis
+ if (operatorStack.length > 0) {
+ operatorStack.pop();
+ }
+ }
+ }
+
+ // Pop remaining operators
+ while (operatorStack.length > 0) {
+ outputQueue.push(operatorStack.pop());
+ }
+
+ // Evaluate RPN expression
+ const evalStack = [];
+
+ for (const token of outputQueue) {
+ if (token.type === 'number') {
+ evalStack.push(token.value);
+ } else if (token.type === 'operator') {
+ if (evalStack.length < 2) {
+ throw new Error('Invalid expression');
+ }
+
+ const b = evalStack.pop();
+ const a = evalStack.pop();
+
+ let result;
+ switch (token.value) {
+ case '+':
+ result = a + b;
+ break;
+ case '-':
+ result = a - b;
+ break;
+ case '*':
+ result = a * b;
+ break;
+ case '/':
+ if (b === 0) {
+ return Infinity; // Will be caught as 'Error' by !isFinite check
+ }
+ result = a / b;
+ break;
+ default:
+ throw new Error('Unknown operator');
+ }
+
+ evalStack.push(result);
+ }
+ }
+
+ if (evalStack.length !== 1) {
+ throw new Error('Invalid expression');
+ }
+
+ return evalStack[0];
+}
+
+// Rest of your button handling code remains the same
+buttons.forEach(button => {
+ button.addEventListener('click', () => {
+ const action = button.dataset.action;
+ const value = button.dataset.value;
+
+ if (action === 'clear') {
+ currentExpression = '';
+ resultDisplay.value = '';
+ updateDisplay();
+ } else if (action === 'backspace') {
+ currentExpression = currentExpression.slice(0, -1);
+ updateDisplay();
+ } else if (action === 'equals') {
+ const sanitized = sanitizeExpression(currentExpression);
+ const result = calculateExpression(sanitized);
+ resultDisplay.value = result;
+ } else {
+ currentExpression += value;
+ updateDisplay();
+ }
+ });
+});
+
+// Keyboard support
+document.addEventListener('keydown', (e) => {
+ if (e.key >= '0' && e.key <= '9' || '+-*/.()%'.includes(e.key)) {
+ currentExpression += e.key;
+ updateDisplay();
+ } else if (e.key === 'Enter') {
+ const sanitized = sanitizeExpression(currentExpression);
+ const result = calculateExpression(sanitized);
+ resultDisplay.value = result;
+ } else if (e.key === 'Backspace') {
+ e.preventDefault();
+ currentExpression = currentExpression.slice(0, -1);
+ updateDisplay();
+ } else if (e.key.toLowerCase() === 'c') {
+ currentExpression = '';
+ resultDisplay.value = '';
+ updateDisplay();
+ }
+});
\ No newline at end of file
diff --git a/Projects/calculator-js/style.css b/Projects/calculator-js/style.css
new file mode 100644
index 00000000..2e9f7e3f
--- /dev/null
+++ b/Projects/calculator-js/style.css
@@ -0,0 +1,73 @@
+:root{
+ --bg:#f4f7fb;
+ --panel:#ffffff;
+ --accent:#3b82f6;
+ --text:#111827;
+ --muted:#6b7280;
+}
+
+*{box-sizing:border-box}
+html,body{height:100%}
+body{
+ margin:0;
+ display:flex;
+ align-items:center;
+ justify-content:center;
+ background:linear-gradient(180deg,#e6eefc,#f8fafc);
+ font-family:Inter,system-ui,-apple-system,Segoe UI,Roboto,"Helvetica Neue",Arial;
+ color:var(--text);
+ padding:24px;
+}
+
+.calculator{
+ width:320px;
+ background:var(--panel);
+ border-radius:12px;
+ box-shadow:0 6px 24px rgba(16,24,40,0.08);
+ padding:18px;
+}
+
+.display{
+ width:100%;
+ height:56px;
+ border-radius:8px;
+ border:1px solid #e6eefc;
+ padding:8px 12px;
+ font-size:20px;
+ text-align:right;
+ margin-bottom:6px;
+ background:linear-gradient(180deg,#fff,#fbfdff);
+}
+
+.display + .display {
+ background: #e5e7eb;
+ color: #111827;
+ font-weight: 600;
+ margin-bottom:12px;
+}
+
+.buttons{
+ display:grid;
+ grid-template-columns:repeat(4,1fr);
+ gap:10px;
+}
+
+button{
+ height:48px;
+ border-radius:8px;
+ border:none;
+ background:#f3f4f6;
+ font-size:16px;
+ cursor:pointer;
+ transition:transform .08s ease, box-shadow .08s;
+}
+button:active{transform:translateY(1px)}
+button[data-action="equals"]{
+ background:var(--accent);
+ color:#fff;
+ grid-column:4;
+ grid-row:5;
+ height:100%;
+}
+button.zero{grid-column:1 / span 2}
+button[data-action="clear"]{background:#fef3f2;color:#991b1b}