diff --git a/projects/text-encryption-decryption/index.html b/projects/text-encryption-decryption/index.html
index 8fe3990..0bcf532 100644
--- a/projects/text-encryption-decryption/index.html
+++ b/projects/text-encryption-decryption/index.html
@@ -9,11 +9,43 @@
Text Encryption / Decryption Tool
diff --git a/projects/text-encryption-decryption/main.js b/projects/text-encryption-decryption/main.js
index b57141e..dbe0663 100644
--- a/projects/text-encryption-decryption/main.js
+++ b/projects/text-encryption-decryption/main.js
@@ -1,14 +1,152 @@
-// TODO: Encrypt input text (e.g., Caesar cipher)
-// TODO: Decrypt input text
-// TODO: Handle input and output display
-// TODO: Copy result to clipboard
-// TODO: Select encryption algorithm
+// Basic algorithms: Caesar, ROT13, Atbash
+
+function normalizeShift(n) {
+ // Normalize to range [0, 25]
+ const mod = ((n % 26) + 26) % 26;
+ return mod;
+}
+
+function shiftChar(c, shift) {
+ const code = c.charCodeAt(0);
+ if (code >= 65 && code <= 90) {
+ // A-Z
+ const base = 65;
+ return String.fromCharCode(((code - base + shift + 26) % 26) + base);
+ }
+ if (code >= 97 && code <= 122) {
+ // a-z
+ const base = 97;
+ return String.fromCharCode(((code - base + shift + 26) % 26) + base);
+ }
+ return c; // non-letters unchanged
+}
+
+function caesarEncrypt(text, shift) {
+ const s = normalizeShift(shift);
+ return Array.from(text).map(ch => shiftChar(ch, s)).join("");
+}
+
+function caesarDecrypt(text, shift) {
+ const s = normalizeShift(shift);
+ return Array.from(text).map(ch => shiftChar(ch, -s)).join("");
+}
+
+function rot13(text) {
+ return Array.from(text).map(ch => shiftChar(ch, 13)).join("");
+}
+
+function atbash(text) {
+ return Array.from(text).map(ch => {
+ const code = ch.charCodeAt(0);
+ if (code >= 65 && code <= 90) {
+ // A <-> Z mapping
+ return String.fromCharCode(65 + (25 - (code - 65)));
+ }
+ if (code >= 97 && code <= 122) {
+ return String.fromCharCode(97 + (25 - (code - 97)));
+ }
+ return ch;
+ }).join("");
+}
function initTextEncryptionDecryption() {
- // TODO: Handle text input
- // TODO: Encrypt and decrypt functions
- // TODO: Display result
- // TODO: Copy to clipboard
+ const inputEl = document.getElementById('inputText');
+ const outputEl = document.getElementById('outputText');
+ const encryptBtn = document.getElementById('encryptBtn');
+ const decryptBtn = document.getElementById('decryptBtn');
+ const clearBtn = document.getElementById('clearBtn');
+ const copyBtn = document.getElementById('copyBtn');
+ const copyStatus = document.getElementById('copyStatus');
+ const algorithmEl = document.getElementById('algorithm');
+ const shiftGroup = document.getElementById('shiftGroup');
+ const shiftEl = document.getElementById('shift');
+ const charCount = document.getElementById('charCount');
+
+ function updateCharCount() {
+ const len = inputEl.value.length;
+ charCount.textContent = `${len} ${len === 1 ? 'character' : 'characters'}`;
+ }
+
+ function setOutput(text) {
+ outputEl.value = text;
+ }
+
+ function getAlgorithm() {
+ return algorithmEl.value;
+ }
+
+ function isCaesar() {
+ return getAlgorithm() === 'caesar';
+ }
+
+ function updateShiftVisibility() {
+ // Show shift only for Caesar
+ shiftGroup.style.display = isCaesar() ? 'flex' : 'none';
+ }
+
+ function getShiftValue() {
+ const v = parseInt(shiftEl.value, 10);
+ if (Number.isNaN(v)) return 0;
+ // Clamp within [-25, 25] for UX; algorithm normalizes anyway
+ return Math.max(-25, Math.min(25, v));
+ }
+
+ function transform(action) {
+ const text = inputEl.value || '';
+ const algo = getAlgorithm();
+ let result = '';
+ if (algo === 'caesar') {
+ const shift = getShiftValue();
+ result = action === 'encrypt' ? caesarEncrypt(text, shift) : caesarDecrypt(text, shift);
+ } else if (algo === 'rot13') {
+ // ROT13 is symmetric
+ result = rot13(text);
+ } else if (algo === 'atbash') {
+ // Atbash is symmetric
+ result = atbash(text);
+ }
+ setOutput(result);
+ }
+
+ async function copyToClipboard() {
+ const val = outputEl.value;
+ if (!val) {
+ copyStatus.textContent = 'Nothing to copy';
+ return;
+ }
+ try {
+ await navigator.clipboard.writeText(val);
+ copyStatus.textContent = 'Copied!';
+ } catch (e) {
+ // Fallback
+ outputEl.select();
+ document.execCommand && document.execCommand('copy');
+ copyStatus.textContent = 'Copied (fallback)';
+ }
+ setTimeout(() => (copyStatus.textContent = ''), 1500);
+ }
+
+ // Event hookups
+ encryptBtn.addEventListener('click', () => transform('encrypt'));
+ decryptBtn.addEventListener('click', () => transform('decrypt'));
+ clearBtn.addEventListener('click', () => {
+ inputEl.value = '';
+ setOutput('');
+ updateCharCount();
+ });
+ copyBtn.addEventListener('click', copyToClipboard);
+ algorithmEl.addEventListener('change', () => {
+ updateShiftVisibility();
+ });
+ inputEl.addEventListener('input', updateCharCount);
+ shiftEl.addEventListener('input', () => {
+ // normalize shown value to be within range
+ shiftEl.value = String(getShiftValue());
+ });
+
+ // Init UI state
+ updateShiftVisibility();
+ updateCharCount();
}
window.addEventListener('DOMContentLoaded', initTextEncryptionDecryption);
\ No newline at end of file
diff --git a/projects/text-encryption-decryption/styles.css b/projects/text-encryption-decryption/styles.css
index b05f384..7743230 100644
--- a/projects/text-encryption-decryption/styles.css
+++ b/projects/text-encryption-decryption/styles.css
@@ -1 +1,144 @@
-/* TODO: Style tool container, textarea, buttons, result display, algorithm selector */
\ No newline at end of file
+:root {
+ --bg: #0b1020;
+ --card: #111933;
+ --text: #e6eaf3;
+ --muted: #9aa3b2;
+ --primary: #5b8cff;
+ --primary-700: #4878f2;
+ --border: #253153;
+ --success: #22c55e;
+}
+
+html, body {
+ height: 100%;
+}
+
+body {
+ margin: 0;
+ font-family: ui-sans-serif, system-ui, -apple-system, Segoe UI, Roboto, Helvetica, Arial, "Apple Color Emoji", "Segoe UI Emoji";
+ background: radial-gradient(1200px 1200px at 80% -10%, #1a2452 0%, transparent 40%), var(--bg);
+ color: var(--text);
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ padding: 32px 16px 48px;
+}
+
+h1 {
+ font-size: 1.8rem;
+ font-weight: 700;
+ letter-spacing: 0.2px;
+ margin: 8px 0 20px;
+}
+
+#tool-container {
+ width: 100%;
+ max-width: 860px;
+ background: var(--card);
+ border: 1px solid var(--border);
+ border-radius: 14px;
+ padding: 20px;
+ box-shadow: 0 8px 24px rgba(0,0,0,0.35);
+}
+
+.field-group {
+ display: flex;
+ flex-direction: column;
+ gap: 8px;
+ margin-bottom: 16px;
+}
+
+label {
+ font-size: 0.95rem;
+ color: var(--muted);
+}
+
+textarea {
+ width: 100%;
+ min-height: 120px;
+ resize: vertical;
+ background: #0e1630;
+ color: var(--text);
+ border: 1px solid var(--border);
+ border-radius: 10px;
+ padding: 12px 12px;
+ line-height: 1.4;
+ font-size: 0.98rem;
+}
+
+.meta-row {
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+}
+
+.options-row {
+ display: flex;
+ gap: 12px;
+ align-items: flex-end;
+ flex-wrap: wrap;
+ margin-bottom: 8px;
+}
+
+.option {
+ display: flex;
+ flex-direction: column;
+ gap: 6px;
+}
+
+select, input[type="number"] {
+ background: #0e1630;
+ color: var(--text);
+ border: 1px solid var(--border);
+ border-radius: 10px;
+ padding: 10px 12px;
+ font-size: 0.95rem;
+}
+
+.actions {
+ display: flex;
+ gap: 10px;
+ align-items: center;
+ margin: 10px 0 18px;
+}
+
+.actions.compact {
+ margin-top: 6px;
+}
+
+.btn {
+ appearance: none;
+ border: 1px solid var(--border);
+ background: #101a36;
+ color: var(--text);
+ padding: 10px 14px;
+ border-radius: 10px;
+ cursor: pointer;
+ font-weight: 600;
+ transition: transform 0.06s ease, background 0.2s ease, border-color 0.2s ease;
+}
+
+.btn:hover {
+ transform: translateY(-1px);
+ border-color: #31406f;
+}
+
+.btn.primary {
+ background: linear-gradient(180deg, var(--primary) 0%, var(--primary-700) 100%);
+ border: none;
+}
+
+.btn.ghost {
+ background: transparent;
+}
+
+#copyStatus {
+ color: var(--success);
+ margin-left: 6px;
+}
+
+@media (max-width: 600px) {
+ h1 { font-size: 1.4rem; }
+ #tool-container { padding: 14px; }
+ textarea { min-height: 100px; }
+}
\ No newline at end of file