commit 3ed14e71fa088e23203e30779f71448fe5ddbd51
parent 7b98974b95be8ffb77de7589ad8f56fb88221a33
Author: Amit Dutta <amitdutta4255@gmail.com>
Date: Mon, 9 Feb 2026 21:01:09 +0530
v2.11
Diffstat:
10 files changed, 6770 insertions(+), 0 deletions(-)
diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md
@@ -0,0 +1,14 @@
+# Contributor Covenant Code of Conduct
+
+## Our Pledge
+We as members, contributors, and leaders pledge to make participation in our community a harassment-free experience for everyone, regardless of age, body size, visible or invisible disability, ethnicity, sex characteristics, gender identity and expression, level of experience, education, socio-economic status, nationality, personal appearance, race, religion, or sexual identity and orientation.
+
+## Our Standards
+Examples of behavior that contributes to a positive environment include:
+* Using welcoming and inclusive language
+* Being respectful of differing viewpoints and experiences
+* Gracefully accepting constructive criticism
+* Focusing on what is best for the community
+* Showing empathy towards other community members
+
+[Full text available at: https://www.contributor-covenant.org/version/2/0/code_of_conduct/]
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
@@ -0,0 +1,23 @@
+# Contributing to NCT (Number Conversion Tool)
+
+First off, thank you for considering contributing! It's people like you that make the open-source community such a great place to learn, inspire, and create.
+
+## How Can I Contribute?
+
+### Reporting Bugs
+- Use the GitHub Issue Tracker.
+- Describe the bug and include steps to reproduce.
+
+### Suggesting Enhancements
+- Open an issue with the tag "enhancement".
+- Explain why this feature would be useful.
+
+### Pull Requests
+1. Fork the repo.
+2. Create a branch (`git checkout -b feature/AmazingFeature`).
+3. Commit your changes.
+4. Push to the branch.
+5. Open a Pull Request.
+
+## Questions?
+Contact the maintainer at: amit.is-a.dev
diff --git a/LICENSE b/LICENSE
@@ -0,0 +1,21 @@
+MIT License
+
+Copyright (c) 2026 Amit Dutta
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
diff --git a/README.md b/README.md
@@ -0,0 +1,31 @@
+# NCT (Number Conversion Tool) v2.11
+
+A high-performance, modern web application for number system conversion and arithmetic.
+
+[](https://amit.is-a.dev/nct-legacy)
+[](https://opensource.org/licenses/MIT)
+
+## 🚀 Features
+
+- **Multi-Base Support**: Convert between Binary, Octal, Decimal, and Hexadecimal.
+- **Dynamic Calculator**: Perform arithmetic operations (+, -, ×, ÷) across different bases.
+- **Visualizer Engine**: Bit-by-bit calculation animation for educational purposes.
+- **Calculation Steps**: Detailed breakdown of how conversions are performed.
+- **History Tape**: Keep track of your recent conversions locally.
+- **Material You UI**: Beautiful, responsive design with smooth animations.
+
+## 🛠️ Installation
+
+Simply clone the repository and open `nct-legacy.html` in any modern web browser.
+
+```bash
+git clone [https://github.com/notamitgamer/nct-legacy.git](https://github.com/notamitgamer/nct-legacy.git)
+```
+
+## 🌐 Hosting
+
+The project is officially hosted at: [https://amit.is-a.dev/nct-legacy](https://amit.is-a.dev/nct-legacy)
+
+## 📄 License
+
+This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details.
diff --git a/SECURITY.md b/SECURITY.md
@@ -0,0 +1,11 @@
+# Security Policy
+
+## Supported Versions
+Currently, only the latest version (v2.11) is actively supported with security updates.
+
+## Reporting a Vulnerability
+If you discover a security vulnerability within this project, please send an e-mail to:
+- **amit.is-a.dev**
+- **amitdutta4255@gmail.com**
+
+Please do not report security vulnerabilities through public GitHub issues. We will acknowledge your report within 48 hours.
diff --git a/code-nct-legacy.html b/code-nct-legacy.html
@@ -0,0 +1,1954 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+ <meta charset="UTF-8">
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
+ <title>NCT Legacy Code View</title>
+<link rel="icon" href="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIzMiIgaGVpZ2h0PSIzMiIgdmlld0JveD0iMCAwIDI0IDI0Ij48c3R5bGU+cGF0aHtmaWxsOiNEMEJDRkZ9QG1lZGlhIChwcmVmZXJzLWNvbG9yLXNjaGVtZTpkYXJrKXtwYXRoe2ZpbGw6I2ZmZnx9fTwvc3R5bGU+PHBhdGggZD0iTTcgMnYxMWgzdjlsNy0xMmgtNGw0LTh6Ii8+PC9zdmc+">
+ <!-- Link to Cascadia Code font from CDN -->
+ <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@xz/fonts@1/serve/cascadia-code.min.css">
+ <style>
+ /* Base styling for the entire page */
+ body {
+ background-color: black;
+ margin: 0;
+ padding: 20px;
+ font-family: 'Cascadia Code', 'Fira Code', 'Roboto Mono', monospace;
+ color: white;
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ min-height: 100vh;
+ }
+
+ /* --- CUSTOM SCROLLBAR --- */
+ ::-webkit-scrollbar { width: 8px; height: 8px; }
+ ::-webkit-scrollbar-track { background: #000; }
+ ::-webkit-scrollbar-thumb { background: #333; border-radius: 4px; }
+ ::-webkit-scrollbar-thumb:hover { background: #1abc9c; }
+
+ h1 {
+ color: #f0f0f0;
+ text-align: center;
+ margin-bottom: 20px;
+ }
+
+ /* Container for the tabs */
+ .tab-container {
+ background-color: #1a1a1a;
+ color: #f0f0f0;
+ padding: 15px;
+ border-radius: 8px;
+ width: 100%;
+ max-width: 1000px;
+ box-shadow: 0 4px 12px rgba(0, 0, 0, 0.4);
+ position: relative;
+ overflow: hidden;
+ border: 1px solid #333;
+ }
+
+ /* Tab buttons styling */
+ .tabs {
+ display: flex;
+ justify-content: center;
+ margin-bottom: 20px;
+ border-bottom: 1px solid #333;
+ }
+
+ .tab-button {
+ background-color: transparent;
+ border: none;
+ padding: 10px 20px;
+ font-size: 1.2em;
+ color: #f0f0f0;
+ cursor: pointer;
+ transition: color 0.3s ease, background-color 0.3s ease, transform 0.3s ease;
+ position: relative;
+ overflow: hidden;
+ border-radius: 5px 5px 0 0;
+ margin: 0 5px;
+ }
+
+ .tab-button:hover {
+ color: #1abc9c;
+ transform: translateY(-2px);
+ }
+
+ .tab-button.active {
+ color: #1abc9c;
+ font-weight: bold;
+ background-color: #333;
+ transform: translateY(-2px);
+ }
+
+ .tab-button.active::after {
+ content: '';
+ position: absolute;
+ bottom: 0;
+ left: 0;
+ width: 100%;
+ height: 3px;
+ background-color: #1abc9c;
+ animation: tabUnderline 0.3s forwards;
+ }
+
+ @keyframes tabUnderline {
+ from { width: 0; }
+ to { width: 100%; }
+ }
+
+ /* Tab content styling */
+ .tab-content {
+ display: none;
+ padding: 15px 0;
+ animation: fadeIn 0.5s ease-in-out;
+ }
+
+ .tab-content.active {
+ display: block;
+ }
+
+ @keyframes fadeIn {
+ from { opacity: 0; transform: translateY(10px); }
+ to { opacity: 1; transform: translateY(0); }
+ }
+
+ /* Header for Code Block */
+ .code-header {
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ background-color: #252525;
+ padding: 8px 15px;
+ border-radius: 8px 8px 0 0;
+ border: 1px solid #333;
+ border-bottom: none;
+ }
+
+ .file-info { font-size: 0.85em; color: #888; }
+
+ /* Copy Button Styling */
+ .copy-button {
+ background: transparent;
+ border: 1px solid #444;
+ color: #ccc;
+ padding: 5px 12px;
+ border-radius: 4px;
+ font-size: 0.8em;
+ cursor: pointer;
+ transition: 0.2s;
+ display: flex;
+ align-items: center;
+ gap: 6px;
+ }
+
+ .copy-button:hover {
+ border-color: #1abc9c;
+ color: #1abc9c;
+ }
+
+ .copy-button.success {
+ border-color: #2ecc71;
+ color: #2ecc71;
+ }
+
+ /* Styling for the code block container */
+ .code-block-container {
+ background-color: #1a1a1a;
+ border: 1px solid #333;
+ border-radius: 0 0 8px 8px;
+ overflow: hidden;
+ }
+
+ .code-block {
+ background-color: #1a1a1a;
+ color: #f0f0f0;
+ padding: 15px;
+ overflow-x: auto;
+ max-height: 70vh;
+ font-size: 0.95em;
+ line-height: 1.5;
+ tab-size: 4;
+ white-space: pre;
+ margin: 0;
+ }
+
+ /* Download section styling */
+ .download-item {
+ background-color: #2a2a2a;
+ padding: 15px;
+ border-radius: 8px;
+ margin-bottom: 15px;
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ border: 1px solid #333;
+ }
+
+ .download-info h3 {
+ color: #1abc9c;
+ margin: 0 0 5px 0;
+ }
+
+ .download-info p {
+ font-size: 0.9em;
+ color: #ccc;
+ margin: 0;
+ }
+
+ .download-button {
+ background-color: #007bff;
+ color: white;
+ border: none;
+ padding: 10px 15px;
+ border-radius: 5px;
+ cursor: pointer;
+ text-decoration: none;
+ font-size: 0.9em;
+ transition: background-color 0.3s ease, transform 0.2s ease;
+ }
+
+ .download-button:hover {
+ background-color: #0056b3;
+ transform: translateY(-1px);
+ }
+ </style>
+</head>
+<body>
+
+ <h1>--- NCT Legacy Explorer ---</h1>
+
+ <div class="tab-container">
+ <div class="tabs">
+ <button class="tab-button active" onclick="switchTab(event, 'code')">Code</button>
+ <button class="tab-button" onclick="switchTab(event, 'download')">Download</button>
+ </div>
+
+ <div id="code-tab-content" class="tab-content active">
+ <div class="code-header">
+ <span class="file-info">nct-legacy.html</span>
+ <button class="copy-button" onclick="copyToClipboard(this)">
+ <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><rect x="9" y="9" width="13" height="13" rx="2" ry="2"></rect><path d="M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1"></path></svg>
+ Copy Code
+ </button>
+ </div>
+ <div class="code-block-container">
+ <pre id="sourceCode" class="code-block"><!DOCTYPE html>
+<html lang="en">
+<head>
+ <meta charset="UTF-8">
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
+ <title>NCT v2.11 | Legacy Edition</title>
+ <link rel="icon" href="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIzMiIgaGVpZ2h0PSIzMiIgdmlld0JveD0iMCAwIDI0IDI0Ij48c3R5bGU+cGF0aHtmaWxsOiNEMEJDRkZ9QG1lZGlhIChwcmVmZXJzLWNvbG9yLXNjaGVtZTpkYXJrKXtwYXRoe2ZpbGw6I2ZmZnx9fTwvc3R5bGU+PHBhdGggZD0iTTcgMnYxMWgzdjlsNy0xMmgtNGw0LTh6Ii8+PC9zdmc+">
+ <link rel="preconnect" href="https://fonts.googleapis.com">
+ <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
+ <link href="https://fonts.googleapis.com/css2?family=Roboto:wght@400;500;700&family=Google+Sans:wght@400;500;700&family=Roboto+Mono:wght@400;500&display=swap" rel="stylesheet">
+ <script type="application/ld+json">
+ {
+ "@context": "https://schema.org",
+ "@type": "WebApplication",
+ "name": "NCT v2.11 | Legacy Edition",
+ "url": "https://amit.is-a.dev/nct-legacy",
+ "image": "https://amit.is-a.dev/nctlogo.png",
+ "description": "Number conversion tool",
+ "applicationCategory": "EducationalApplication",
+ "operatingSystem": "Any"
+ }
+</script>
+ <style>
+ /* --- MATERIAL DESIGN 3 TOKENS --- */
+ :root {
+ --md-sys-color-background: #141218;
+ --md-sys-color-surface: #1D1B20;
+ --md-sys-color-surface-container: #211F26;
+ --md-sys-color-surface-container-high: #2B2930;
+
+ /* Primary */
+ --md-sys-color-primary: #D0BCFF;
+ --md-sys-color-on-primary: #381E72;
+ --md-sys-color-primary-container: #4F378B;
+ --md-sys-color-on-primary-container: #EADDFF;
+
+ /* Text */
+ --md-sys-color-on-surface: #E6E1E5;
+ --md-sys-color-on-surface-variant: #CAC4D0;
+ --md-sys-color-outline: #938F99;
+
+ /* Error */
+ --md-sys-color-error: #F2B8B5;
+ --md-sys-color-error-container: #8C1D18;
+
+ /* Success/Accent */
+ --md-sys-color-tertiary: #B6C4FF;
+ --md-sys-color-success: #4ade80;
+
+ /* Dimensions */
+ --radius-xl: 28px;
+ --radius-l: 16px;
+ --radius-m: 12px;
+ --radius-s: 8px;
+
+ --font-main: 'Google Sans', 'Roboto', sans-serif;
+ --font-code: 'Roboto Mono', monospace;
+
+ /* Animation */
+ --ease-elastic: cubic-bezier(0.4, 0.0, 0.2, 1);
+ }
+
+ /* --- CUSTOM SCROLLBAR --- */
+ ::-webkit-scrollbar { width: 6px; height: 6px; }
+ ::-webkit-scrollbar-track { background: transparent; }
+ ::-webkit-scrollbar-thumb { background: rgba(255,255,255,0.2); border-radius: 3px; }
+ ::-webkit-scrollbar-thumb:hover { background: rgba(255,255,255,0.4); }
+
+ * { margin: 0; padding: 0; box-sizing: border-box; -webkit-tap-highlight-color: transparent; }
+
+ body {
+ font-family: var(--font-main);
+ background-color: var(--md-sys-color-background);
+ color: var(--md-sys-color-on-surface);
+ min-height: 100vh;
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ overflow-x: hidden;
+ background-image: radial-gradient(circle at 50% 0%, rgba(208, 188, 255, 0.05) 0%, transparent 60%);
+ }
+
+ /* --- LAYOUT --- */
+ .app-container {
+ width: 100%;
+ max-width: 1000px;
+ padding: 24px;
+ display: flex;
+ flex-direction: column;
+ gap: 24px;
+ flex-grow: 1;
+ }
+
+ /* --- HEADER & NAV --- */
+ header {
+ display: flex;
+ flex-direction: column;
+ gap: 20px;
+ padding: 16px 0;
+ }
+
+ .header-top {
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ }
+
+ .logo {
+ font-size: 1.75rem;
+ font-weight: 500;
+ color: var(--md-sys-color-on-surface);
+ display: flex;
+ align-items: center;
+ gap: 12px;
+ }
+
+ .logo span { color: var(--md-sys-color-primary); }
+
+ /* Navigation Tabs with Animation */
+ .nav-tabs {
+ position: relative;
+ display: flex;
+ background: var(--md-sys-color-surface-container);
+ padding: 4px;
+ border-radius: 32px;
+ width: fit-content;
+ margin: 0 auto;
+ border: 1px solid rgba(255,255,255,0.05);
+ }
+
+ .nav-glider {
+ position: absolute;
+ top: 4px;
+ left: 4px;
+ height: calc(100% - 8px);
+ width: 110px;
+ background: var(--md-sys-color-primary-container);
+ border-radius: 28px;
+ transition: transform 0.3s var(--ease-elastic);
+ z-index: 1;
+ }
+
+ .nav-tab {
+ position: relative;
+ padding: 10px 24px;
+ border-radius: 24px;
+ color: var(--md-sys-color-on-surface-variant);
+ font-weight: 500;
+ cursor: pointer;
+ transition: color 0.2s;
+ border: none;
+ background: none;
+ z-index: 2;
+ width: 110px;
+ }
+
+ .nav-tab.active { color: var(--md-sys-color-on-primary-container); }
+
+ .header-actions {
+ display: flex;
+ gap: 10px;
+ }
+
+ .tools-btn {
+ background: transparent;
+ border: 1px solid var(--md-sys-color-outline);
+ color: var(--md-sys-color-on-surface);
+ padding: 8px 16px;
+ border-radius: var(--radius-l);
+ cursor: pointer;
+ font-weight: 500;
+ display: flex;
+ align-items: center;
+ gap: 8px;
+ transition: transform 0.1s;
+ }
+ .tools-btn:active { transform: scale(0.95); }
+ .tools-btn:hover { background: rgba(255,255,255,0.05); }
+
+ /* --- SECTIONS --- */
+ .app-section {
+ display: none;
+ flex-direction: column;
+ gap: 24px;
+ animation: fadeIn 0.3s ease-out;
+ }
+ .app-section.active { display: flex; }
+
+ @keyframes fadeIn { from { opacity: 0; transform: translateY(10px); } to { opacity: 1; transform: translateY(0); } }
+
+ /* --- CONVERTER STAGE --- */
+ .converter-stage {
+ display: grid;
+ grid-template-columns: 1fr 64px 1fr;
+ gap: 16px;
+ align-items: stretch;
+ position: relative;
+ }
+
+ .material-card {
+ background-color: var(--md-sys-color-surface-container);
+ border-radius: var(--radius-xl);
+ padding: 24px;
+ display: flex;
+ flex-direction: column;
+ height: 380px;
+ position: relative;
+ transition: transform 0.4s var(--ease-elastic), box-shadow 0.2s;
+ border: 1px solid transparent;
+ z-index: 1;
+ }
+
+ .material-card:focus-within {
+ background-color: var(--md-sys-color-surface-container-high);
+ border-color: var(--md-sys-color-outline);
+ }
+
+ /* CARD HEADER & CUSTOM DROPDOWN */
+ .card-header {
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ margin-bottom: 20px;
+ border-bottom: 1px solid var(--md-sys-color-outline);
+ padding-bottom: 12px;
+ z-index: 10;
+ }
+
+ .input-label {
+ font-size: 0.875rem;
+ font-weight: 500;
+ color: var(--md-sys-color-primary);
+ text-transform: uppercase;
+ letter-spacing: 1px;
+ }
+
+ /* Custom Dropdown Styles */
+ .custom-select-wrapper {
+ position: relative;
+ min-width: 150px;
+ user-select: none;
+ }
+ .custom-select-trigger {
+ display: flex;
+ align-items: center;
+ justify-content: flex-end;
+ gap: 8px;
+ cursor: pointer;
+ color: var(--md-sys-color-on-surface);
+ font-weight: 500;
+ padding: 4px 0;
+ transition: color 0.2s;
+ }
+ .custom-select-trigger:hover { color: var(--md-sys-color-primary); }
+ .arrow { font-size: 0.7rem; transition: transform 0.3s; }
+
+ .custom-options {
+ position: absolute;
+ top: 120%;
+ right: 0;
+ background: var(--md-sys-color-surface-container-high);
+ border-radius: var(--radius-m);
+ box-shadow: 0 8px 16px rgba(0,0,0,0.4);
+ opacity: 0;
+ visibility: hidden;
+ pointer-events: none;
+ transform: translateY(-10px);
+ transition: all 0.2s var(--ease-elastic);
+ min-width: 180px;
+ overflow: hidden;
+ border: 1px solid rgba(255,255,255,0.1);
+ z-index: 1000;
+ }
+ .custom-select-wrapper.open .custom-options {
+ opacity: 1; visibility: visible; pointer-events: all; transform: translateY(0);
+ }
+ .custom-select-wrapper.open .arrow { transform: rotate(180deg); }
+
+ .custom-option {
+ padding: 12px 16px;
+ cursor: pointer;
+ color: var(--md-sys-color-on-surface-variant);
+ transition: 0.2s;
+ }
+ .custom-option:hover { background: rgba(255,255,255,0.05); color: var(--md-sys-color-on-surface); }
+ .custom-option.selected { color: var(--md-sys-color-primary); background: rgba(208, 188, 255, 0.1); }
+
+ /* INPUT AREA */
+ .input-area {
+ flex-grow: 1;
+ background: transparent;
+ border: none;
+ color: var(--md-sys-color-on-surface);
+ font-size: 2.2rem;
+ font-family: var(--font-main);
+ resize: none;
+ outline: none;
+ line-height: 1.2;
+ z-index: 1;
+ }
+ .input-area::placeholder { color: var(--md-sys-color-on-surface-variant); opacity: 0.3; }
+
+ /* Error Chip */
+ .error-chip {
+ position: absolute;
+ bottom: 80px;
+ left: 24px;
+ background: var(--md-sys-color-error-container);
+ color: var(--md-sys-color-error);
+ padding: 8px 16px;
+ border-radius: 8px;
+ font-size: 0.875rem;
+ display: flex;
+ align-items: center;
+ gap: 8px;
+ opacity: 0;
+ transform: translateY(10px);
+ transition: all 0.3s;
+ pointer-events: none;
+ }
+ .error-chip.visible { opacity: 1; transform: translateY(0); }
+
+ /* Card Footer */
+ .card-footer {
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ margin-top: auto;
+ }
+ .char-count { font-size: 0.75rem; color: var(--md-sys-color-on-surface-variant); }
+
+ /* Icons & Buttons */
+ .icon-btn {
+ background: transparent;
+ border: none;
+ color: var(--md-sys-color-on-surface-variant);
+ width: 40px; height: 40px;
+ border-radius: 50%;
+ cursor: pointer;
+ display: flex; align-items: center; justify-content: center;
+ transition: 0.2s;
+ }
+ .icon-btn:hover { background: rgba(255,255,255,0.1); color: var(--md-sys-color-on-surface); transform: scale(1.1); }
+ .icon-btn:active { transform: scale(0.9); }
+
+ /* Copy Button Animation */
+ .icon-btn.success svg {
+ animation: check-pop 0.4s cubic-bezier(0.175, 0.885, 0.32, 1.275);
+ color: var(--md-sys-color-success);
+ }
+ @keyframes check-pop {
+ 0% { transform: scale(0); }
+ 50% { transform: scale(1.3); }
+ 100% { transform: scale(1); }
+ }
+
+ /* Trash Shake Animation */
+ @keyframes trash-shake {
+ 0% { transform: rotate(0deg); }
+ 25% { transform: rotate(-15deg); }
+ 50% { transform: rotate(15deg); }
+ 75% { transform: rotate(-15deg); }
+ 100% { transform: rotate(0deg); }
+ }
+ .icon-btn.shaking {
+ animation: trash-shake 0.4s ease-in-out;
+ color: var(--md-sys-color-error);
+ }
+
+ /* Swap Button */
+ .swap-container { display: flex; justify-content: center; align-items: center; z-index: 2; }
+ .swap-fab {
+ width: 56px; height: 56px;
+ border-radius: 16px;
+ background-color: var(--md-sys-color-surface-container-high);
+ border: 1px solid var(--md-sys-color-outline);
+ color: var(--md-sys-color-on-surface);
+ cursor: pointer;
+ display: flex; align-items: center; justify-content: center;
+ transition: all 0.3s var(--ease-elastic);
+ box-shadow: 0 4px 8px rgba(0,0,0,0.2);
+ }
+ .swap-fab:hover {
+ background-color: var(--md-sys-color-primary);
+ color: var(--md-sys-color-on-primary);
+ transform: rotate(180deg) scale(1.1);
+ border-color: transparent;
+ }
+
+ /* --- SWAP ANIMATION CLASSES --- */
+ .anim-move-right { transform: translateX(calc(100% + 16px)); }
+ .anim-move-left { transform: translateX(calc(-100% - 16px)); }
+ .anim-move-down { transform: translateY(calc(100% + 16px)); }
+ .anim-move-up { transform: translateY(calc(-100% - 16px)); }
+
+ /* --- DYNAMIC CALCULATOR --- */
+ .calc-wrapper {
+ background: var(--md-sys-color-surface-container);
+ padding: 24px;
+ border-radius: var(--radius-xl);
+ max-width: 800px;
+ margin: 0 auto;
+ width: 100%;
+ }
+
+ .calc-header {
+ display: flex;
+ gap: 24px;
+ margin-bottom: 24px;
+ flex-wrap: wrap;
+ }
+
+ .calc-ctrl-group {
+ flex: 1;
+ min-width: 200px;
+ display: flex;
+ align-items: center;
+ gap: 12px;
+ background: rgba(0,0,0,0.2);
+ padding: 8px 16px;
+ border-radius: 12px;
+ }
+ .calc-ctrl-group label {
+ font-size: 0.8rem;
+ color: var(--md-sys-color-primary);
+ text-transform: uppercase;
+ white-space: nowrap;
+ margin: 0;
+ }
+
+ .calc-rows {
+ display: flex;
+ flex-direction: column;
+ gap: 12px;
+ margin-bottom: 24px;
+ }
+
+ .calc-row {
+ display: flex;
+ gap: 12px;
+ align-items: center;
+ animation: fadeIn 0.3s ease-out;
+ }
+
+ /* Simple Fade Out */
+ .calc-row.removing {
+ opacity: 0;
+ transition: opacity 0.3s ease;
+ }
+
+ .calc-input-wrapper {
+ flex-grow: 1;
+ position: relative;
+ }
+ .calc-input {
+ width: 100%;
+ background: rgba(0,0,0,0.2);
+ border: 1px solid transparent;
+ color: var(--md-sys-color-on-surface);
+ padding: 16px;
+ border-radius: 12px;
+ font-family: var(--font-code);
+ font-size: 1.1rem;
+ outline: none;
+ transition: 0.2s;
+ }
+ .calc-input:focus { border-color: var(--md-sys-color-primary); background: rgba(0,0,0,0.4); }
+ .calc-input.invalid { border-color: var(--md-sys-color-error); }
+
+ .calc-remove-btn {
+ background: var(--md-sys-color-surface-container-high);
+ border: none;
+ color: var(--md-sys-color-error);
+ width: 40px; height: 40px;
+ border-radius: 8px;
+ cursor: pointer;
+ display: flex; align-items: center; justify-content: center;
+ transition: 0.2s;
+ }
+ .calc-remove-btn:hover { background: var(--md-sys-color-error-container); color: #fff; }
+
+ /* The Divider Area */
+ .calc-actions {
+ display: flex;
+ align-items: center;
+ border-top: 1px solid var(--md-sys-color-outline);
+ padding-top: 20px;
+ margin-top: 20px;
+ }
+
+ .add-row-btn {
+ background: rgba(255,255,255,0.05);
+ border: 1px dashed var(--md-sys-color-outline);
+ color: var(--md-sys-color-on-surface-variant);
+ padding: 10px 20px;
+ border-radius: 12px;
+ cursor: pointer;
+ font-weight: 500;
+ width: fit-content;
+ text-align: left;
+ transition: 0.2s;
+ margin-bottom: 20px; /* Space before divider */
+ }
+ .add-row-btn:hover { border-color: var(--md-sys-color-primary); color: var(--md-sys-color-primary); background: rgba(208, 188, 255, 0.05); }
+
+ .calc-result-area {
+ display: flex;
+ align-items: center;
+ gap: 20px;
+ margin-left: auto; /* Forces result to the right */
+ }
+
+ .visualize-btn {
+ background: var(--md-sys-color-primary-container);
+ color: var(--md-sys-color-on-primary-container);
+ border: none;
+ padding: 8px 16px;
+ border-radius: 20px;
+ font-size: 0.9rem;
+ font-weight: 500;
+ cursor: pointer;
+ display: none; /* Hidden by default */
+ align-items: center;
+ gap: 6px;
+ transition: 0.2s;
+ animation: popIn 0.3s cubic-bezier(0.175, 0.885, 0.32, 1.275);
+ margin-right: auto; /* Keeps it left-aligned in actions */
+ }
+ .visualize-btn:hover { filter: brightness(1.2); }
+ .visualize-btn:active { transform: scale(0.95); }
+ @keyframes popIn { from{transform: scale(0.8); opacity:0} to{transform: scale(1); opacity:1} }
+
+ .calc-result-display { text-align: right; }
+ .res-label { font-size: 0.9rem; color: var(--md-sys-color-on-surface-variant); }
+ .res-value { font-size: 2.2rem; color: var(--md-sys-color-primary); font-family: var(--font-code); margin-top: 5px; font-weight: 700; }
+
+
+ /* --- VISUALIZER PANEL --- */
+ .calc-visualizer {
+ background: #111;
+ border-radius: var(--radius-m);
+ padding: 20px;
+ margin-top: 20px;
+ display: none;
+ border: 1px solid var(--md-sys-color-outline);
+ overflow-x: auto;
+ }
+ .calc-visualizer.active { display: block; animation: slideIn 0.4s var(--ease-elastic); }
+
+ .viz-grid {
+ display: grid;
+ font-family: var(--font-code);
+ font-size: 1.2rem;
+ color: var(--md-sys-color-on-surface);
+ gap: 4px;
+ }
+ .viz-row { display: contents; }
+ .viz-cell {
+ padding: 4px 8px;
+ text-align: center;
+ position: relative;
+ }
+ .viz-cell.carry { color: var(--md-sys-color-tertiary); font-size: 0.8rem; height: 20px; }
+ .viz-cell.operator { color: var(--md-sys-color-primary); }
+ .viz-cell.border-bottom { border-bottom: 2px solid var(--md-sys-color-outline); }
+ .viz-cell.active-col { background: rgba(208, 188, 255, 0.15); border-radius: 4px; }
+ .viz-cell.result { color: var(--md-sys-color-primary); font-weight: bold; }
+
+ .viz-info {
+ margin-top: 12px;
+ font-size: 0.9rem;
+ color: var(--md-sys-color-on-surface-variant);
+ font-family: var(--font-code);
+ min-height: 20px;
+ }
+
+ /* --- CONVERTER STEPS PANEL --- */
+ .steps-container {
+ margin-top: 20px;
+ background: rgba(0,0,0,0.2);
+ border-radius: var(--radius-l);
+ padding: 20px;
+ border: 1px solid rgba(255,255,255,0.05);
+ display: none;
+ }
+ .steps-container.visible { display: block; animation: fadeIn 0.5s; }
+
+ .steps-header {
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ margin-bottom: 12px;
+ border-bottom: 1px solid rgba(255,255,255,0.1);
+ padding-bottom: 8px;
+ }
+
+ .steps-content {
+ font-family: var(--font-code);
+ color: var(--md-sys-color-tertiary);
+ font-size: 0.9rem;
+ white-space: pre-wrap;
+ line-height: 1.5;
+ }
+
+ /* --- HISTORY TAPE --- */
+ .history-section {
+ margin-top: 20px;
+ border-top: 1px solid var(--md-sys-color-outline);
+ padding-top: 20px;
+ }
+ .history-header {
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ margin-bottom: 10px;
+ }
+ .history-list {
+ display: flex; flex-direction: column; gap: 8px;
+ max-height: 200px; overflow-y: auto;
+ transition: opacity 0.3s;
+ }
+
+ /* History Item Animation: Slide Out to Right */
+ .history-list.clearing .history-item {
+ animation: slide-out-right 0.3s forwards;
+ }
+ @keyframes slide-out-right {
+ to { transform: translateX(50px); opacity: 0; }
+ }
+
+ .history-item {
+ display: flex; justify-content: space-between;
+ padding: 12px;
+ background: rgba(255,255,255,0.03);
+ border-radius: 8px;
+ font-family: var(--font-code);
+ font-size: 0.9rem;
+ cursor: pointer;
+ transition: 0.2s;
+ align-items: center;
+ }
+ .history-item:hover { background: rgba(255,255,255,0.08); transform: translateX(4px); }
+ .hist-val { color: var(--md-sys-color-on-surface); }
+ .hist-meta { font-size: 0.75rem; color: var(--md-sys-color-outline); margin-right: 10px; }
+
+ /* --- FOOTER --- */
+ footer {
+ margin-top: 40px;
+ text-align: center;
+ padding: 20px;
+ color: var(--md-sys-color-on-surface-variant);
+ font-size: 0.85rem;
+ border-top: 1px solid var(--md-sys-color-outline);
+ width: 100%;
+ background: rgba(0,0,0,0.2);
+ }
+ .footer-content {
+ display: flex;
+ flex-direction: column;
+ gap: 8px;
+ }
+ .footer-links {
+ display: flex;
+ justify-content: center;
+ gap: 15px;
+ margin-top: 5px;
+ }
+ .footer-link {
+ color: var(--md-sys-color-primary);
+ text-decoration: none;
+ transition: 0.2s;
+ }
+ .footer-link:hover { text-decoration: underline; color: var(--md-sys-color-tertiary); }
+
+ /* --- MODAL (LEGACY & CHEATSHEET) --- */
+ .modal-overlay {
+ position: fixed;
+ top: 0; left: 0; width: 100%; height: 100%;
+ background: rgba(0,0,0,0.7);
+ backdrop-filter: blur(4px);
+ z-index: 2000;
+ display: none;
+ justify-content: center;
+ align-items: center;
+ opacity: 0;
+ transition: opacity 0.3s;
+ }
+ .modal-overlay.open { display: flex; opacity: 1; }
+
+ .modal-content {
+ background: var(--md-sys-color-surface-container);
+ width: 90%; max-width: 500px;
+ border-radius: var(--radius-xl);
+ padding: 24px;
+ max-height: 80vh;
+ display: flex;
+ flex-direction: column;
+ box-shadow: 0 10px 30px rgba(0,0,0,0.5);
+ border: 1px solid var(--md-sys-color-outline);
+ transform: scale(0.9);
+ transition: transform 0.3s var(--ease-elastic);
+ }
+ .modal-overlay.open .modal-content { transform: scale(1); }
+
+ .modal-header {
+ display: flex; justify-content: space-between; align-items: center;
+ margin-bottom: 20px;
+ border-bottom: 1px solid var(--md-sys-color-outline);
+ padding-bottom: 12px;
+ flex-shrink: 0;
+ }
+ .modal-header h3 { margin: 0; color: var(--md-sys-color-primary); }
+ .close-modal {
+ background: none; border: none; color: var(--md-sys-color-on-surface);
+ font-size: 1.5rem; cursor: pointer;
+ }
+
+ .modal-body {
+ overflow-y: auto;
+ padding-right: 5px; /* Space for scrollbar */
+ }
+
+ /* History Timeline */
+ .timeline-item {
+ position: relative;
+ padding-left: 20px;
+ border-left: 2px solid var(--md-sys-color-outline);
+ margin-bottom: 20px;
+ }
+ .timeline-item::before {
+ content: ''; position: absolute; left: -6px; top: 0;
+ width: 10px; height: 10px; border-radius: 50%;
+ background: var(--md-sys-color-primary);
+ }
+ .version-tag {
+ font-size: 0.8rem; font-weight: bold; color: var(--md-sys-color-tertiary);
+ margin-bottom: 4px; display: inline-block; background: rgba(255,255,255,0.1);
+ padding: 2px 8px; border-radius: 4px;
+ }
+ .version-tag.current { background: var(--md-sys-color-primary); color: var(--md-sys-color-on-primary); }
+ .timeline-desc { font-size: 0.9rem; color: var(--md-sys-color-on-surface-variant); margin: 0; }
+
+ /* Sidebar (Cheat Sheet) */
+ .sidebar {
+ position: fixed; top: 0; right: -320px;
+ width: 300px; height: 100%;
+ background: var(--md-sys-color-surface-container-high);
+ box-shadow: -5px 0 15px rgba(0,0,0,0.5);
+ padding: 24px;
+ transition: 0.3s ease;
+ z-index: 1000;
+ overflow-y: auto;
+ }
+ .sidebar.open { right: 0; }
+ .sidebar h3 { color: var(--md-sys-color-primary); margin-bottom: 16px; border-bottom: 1px solid rgba(255,255,255,0.1); padding-bottom: 8px; }
+ .cheat-table { width: 100%; border-collapse: collapse; font-family: var(--font-code); font-size: 0.8rem; }
+ .cheat-table th, .cheat-table td { text-align: left; padding: 6px; border-bottom: 1px solid rgba(255,255,255,0.05); }
+ .close-sidebar {
+ position: absolute; top: 10px; right: 10px;
+ background: none; border: none; color: var(--md-sys-color-on-surface);
+ font-size: 1.5rem; cursor: pointer;
+ }
+
+ /* --- RESPONSIVE --- */
+ @media (max-width: 768px) {
+ .converter-stage { grid-template-columns: 1fr; grid-template-rows: 1fr 64px 1fr; gap: 8px; }
+ .material-card { height: 260px; }
+ .swap-container { transform: rotate(90deg); }
+ .swap-fab:hover { transform: rotate(180deg); }
+ .header-top { flex-direction: column; gap: 10px; }
+ .calc-header { flex-direction: column; gap: 10px; }
+ .calc-result-area { margin-left: 0; justify-content: flex-end; width: 100%; }
+ .visualize-btn { margin-right: 0; margin-bottom: 10px; }
+ .calc-actions { flex-direction: column-reverse; align-items: stretch; }
+ }
+ </style>
+</head>
+<body>
+
+ <header class="app-container">
+ <div class="header-top">
+ <div class="logo">
+ <svg width="32" height="32" viewBox="0 0 24 24" fill="var(--md-sys-color-primary)"><path d="M7 2v11h3v9l7-12h-4l4-8z"/></svg>
+ NCT <span>v2.11</span>
+ </div>
+
+ <!-- Animated Navigation -->
+ <div class="nav-tabs">
+ <div class="nav-glider" id="navGlider"></div>
+ <button class="nav-tab active" onclick="switchTab('converter', 0)">Converter</button>
+ <button class="nav-tab" onclick="switchTab('calculator', 1)">Calculator</button>
+ </div>
+
+ <div class="header-actions">
+ <button class="tools-btn" onclick="toggleHistory()">
+ <svg width="18" height="18" viewBox="0 0 24 24" fill="currentColor"><path d="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm1 15h-2v-6h2v6zm0-8h-2V7h2v2z"/></svg>
+ Versions
+ </button>
+ <button class="tools-btn" onclick="toggleSidebar()">
+ <svg width="18" height="18" viewBox="0 0 24 24" fill="currentColor"><path d="M11 18h2v-2h-2v2zm1-16C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm0 18c-4.41 0-8-3.59-8-8s3.59-8 8-8 8 3.59 8 8-3.59 8-8 8zm0-14c-2.21 0-4 1.79-4 4h2c0-1.1.9-2 2-2s2 .9 2 2c0 2-3 1.75-3 5h2c0-2.25 3-2.5 3-5 0-2.21-1.79-4-4-4z"/></svg>
+ Cheat Sheet
+ </button>
+ </div>
+ </div>
+ </header>
+
+ <main class="app-container">
+
+ <!-- SECTION 1: CONVERTER -->
+ <section id="converter-section" class="app-section active">
+
+ <div class="converter-stage">
+ <!-- SOURCE CARD -->
+ <div class="material-card" id="cardSource">
+ <div class="card-header">
+ <span class="input-label">Input</span>
+ <div class="custom-select-wrapper" id="sourceSelectWrapper">
+ <div class="custom-select-trigger">
+ <span class="selection-text">Decimal</span>
+ <span class="arrow">▼</span>
+ </div>
+ <div class="custom-options">
+ <div class="custom-option" data-value="2">Binary</div>
+ <div class="custom-option" data-value="8">Octal</div>
+ <div class="custom-option selected" data-value="10">Decimal</div>
+ <div class="custom-option" data-value="16">Hexadecimal</div>
+ </div>
+ <select id="fromBase" style="display:none">
+ <option value="2">Binary</option>
+ <option value="8">Octal</option>
+ <option value="10" selected>Decimal</option>
+ <option value="16">Hexadecimal</option>
+ </select>
+ </div>
+ </div>
+
+ <textarea id="inputVal" class="input-area" placeholder="0" spellcheck="false"></textarea>
+ <div id="errorChip" class="error-chip">⚠️ Invalid character for this base</div>
+
+ <div class="card-footer">
+ <div class="char-count" id="sourceCount">0 chars</div>
+ <div class="icon-actions">
+ <button class="icon-btn" onclick="clearAll()" title="Clear">
+ <svg width="20" height="20" viewBox="0 0 24 24" fill="currentColor"><path d="M19 6.41L17.59 5 12 10.59 6.41 5 5 6.41 10.59 12 5 17.59 6.41 19 12 13.41 17.59 19 19 17.59 13.41 12z"/></svg>
+ </button>
+ </div>
+ </div>
+ </div>
+
+ <!-- SWAP -->
+ <div class="swap-container">
+ <button class="swap-fab" id="swapBtn" title="Swap (Alt+S)">
+ <svg width="24" height="24" viewBox="0 0 24 24" fill="currentColor"><path d="M6.99 11L3 15l3.99 4v-3H14v-2H6.99v-3zM21 9l-3.99-4v3H10v2h7.01v3L21 9z"/></svg>
+ </button>
+ </div>
+
+ <!-- TARGET CARD -->
+ <div class="material-card" id="cardTarget">
+ <div class="card-header">
+ <span class="input-label">Result</span>
+ <div class="custom-select-wrapper" id="targetSelectWrapper">
+ <div class="custom-select-trigger">
+ <span class="selection-text">Binary</span>
+ <span class="arrow">▼</span>
+ </div>
+ <div class="custom-options">
+ <div class="custom-option selected" data-value="2">Binary</div>
+ <div class="custom-option" data-value="8">Octal</div>
+ <div class="custom-option" data-value="10">Decimal</div>
+ <div class="custom-option" data-value="16">Hexadecimal</div>
+ </div>
+ <select id="toBase" style="display:none">
+ <option value="2" selected>Binary</option>
+ <option value="8">Octal</option>
+ <option value="10">Decimal</option>
+ <option value="16">Hexadecimal</option>
+ </select>
+ </div>
+ </div>
+
+ <textarea id="outputVal" class="input-area" readonly placeholder="..." spellcheck="false"></textarea>
+
+ <div class="card-footer">
+ <div class="char-count">Result</div>
+ <div class="icon-actions">
+ <button class="icon-btn" onclick="speakResult()" title="Speak">
+ <svg width="20" height="20" viewBox="0 0 24 24" fill="currentColor"><path d="M3 9v6h4l5 5V4L7 9H3zm13.5 3c0-1.77-1.02-3.29-2.5-4.03v8.05c1.48-.73 2.5-2.25 2.5-4.02zM14 3.23v2.06c2.89.86 5 3.54 5 6.71s-2.11 5.85-5 6.71v2.06c4.01-.91 7-4.49 7-8.77s-2.99-7.86-7-8.77z"/></svg>
+ </button>
+ <button class="icon-btn" id="copyBtn" onclick="copyToClipboard('outputVal', 'copyBtn')" title="Copy (Ctrl+Enter)">
+ <svg width="20" height="20" viewBox="0 0 24 24" fill="currentColor"><path d="M16 1H4c-1.1 0-2 .9-2 2v14h2V3h12V1zm3 4H8c-1.1 0-2 .9-2 2v14c0 1.1.9 2 2 2h11c1.1 0 2-.9 2-2V7c0-1.1-.9-2-2-2zm0 16H8V7h11v14z"/></svg>
+ </button>
+ </div>
+ </div>
+ </div>
+ </div>
+
+ <!-- STEPS PANEL WITH COPY BUTTON -->
+ <div class="steps-container" id="stepsContainer">
+ <div class="steps-header">
+ <h4 style="color:var(--md-sys-color-primary); margin:0;">Calculation Steps</h4>
+ <button class="icon-btn" id="copyStepsBtn" onclick="copyStepsText()" title="Copy Steps">
+ <svg width="18" height="18" viewBox="0 0 24 24" fill="currentColor"><path d="M16 1H4c-1.1 0-2 .9-2 2v14h2V3h12V1zm3 4H8c-1.1 0-2 .9-2 2v14c0 1.1.9 2 2 2h11c1.1 0 2-.9 2-2V7c0-1.1-.9-2-2-2zm0 16H8V7h11v14z"/></svg>
+ </button>
+ </div>
+ <div class="steps-content" id="stepsOutput">Waiting for input...</div>
+ </div>
+
+ <div class="history-section">
+ <div class="history-header" style="display:flex; justify-content:space-between; align-items:center; margin-bottom:10px;">
+ <h4 style="color:var(--md-sys-color-on-surface-variant); margin:0;">Recent History</h4>
+ <button class="icon-btn" id="clearHistBtn" onclick="clearHistory()" title="Clear History">
+ <svg width="18" height="18" viewBox="0 0 24 24" fill="currentColor"><path d="M6 19c0 1.1.9 2 2 2h8c1.1 0 2-.9 2-2V7H6v12zM19 4h-3.5l-1-1h-5l-1 1H5v2h14V4z"/></svg>
+ </button>
+ </div>
+ <div class="history-list" id="historyList">
+ <div style="padding:10px; color:#555; text-align:center;">No history yet</div>
+ </div>
+ </div>
+
+ </section>
+
+ <!-- SECTION 2: DYNAMIC CALCULATOR -->
+ <section id="calculator-section" class="app-section">
+ <div class="calc-wrapper">
+
+ <div class="calc-header">
+ <div class="calc-ctrl-group">
+ <label>Base Mode</label>
+ <div class="custom-select-wrapper" id="calcBaseWrapper" style="flex:1">
+ <div class="custom-select-trigger">
+ <span class="selection-text">Decimal (Base 10)</span>
+ <span class="arrow">▼</span>
+ </div>
+ <div class="custom-options">
+ <div class="custom-option" data-value="2">Binary (Base 2)</div>
+ <div class="custom-option" data-value="8">Octal (Base 8)</div>
+ <div class="custom-option selected" data-value="10">Decimal (Base 10)</div>
+ <div class="custom-option" data-value="16">Hex (Base 16)</div>
+ </div>
+ <select id="calcBase" style="display:none" onchange="validateAllRows()">
+ <option value="2">Binary</option>
+ <option value="8">Octal</option>
+ <option value="10" selected>Decimal</option>
+ <option value="16">Hex</option>
+ </select>
+ </div>
+ </div>
+ <div class="calc-ctrl-group">
+ <label>Operation</label>
+ <div class="custom-select-wrapper" id="calcOpWrapper" style="flex:1">
+ <div class="custom-select-trigger">
+ <span class="selection-text">Addition (+)</span>
+ <span class="arrow">▼</span>
+ </div>
+ <div class="custom-options">
+ <div class="custom-option selected" data-value="add">Addition (+)</div>
+ <div class="custom-option" data-value="sub">Subtraction (-)</div>
+ <div class="custom-option" data-value="mul">Multiplication (×)</div>
+ <div class="custom-option" data-value="div">Division (÷)</div>
+ </div>
+ <select id="calcOp" style="display:none" onchange="runCalculation()">
+ <option value="add" selected>Addition</option>
+ <option value="sub">Subtraction</option>
+ <option value="mul">Multiplication</option>
+ <option value="div">Division</option>
+ </select>
+ </div>
+ </div>
+ </div>
+
+ <div id="calcRows" class="calc-rows">
+ <!-- Dynamic Rows Injected Here -->
+ </div>
+
+ <!-- ADD ROW BUTTON (Left Aligned) -->
+ <button class="add-row-btn" onclick="addCalcRow()">+ Add Number</button>
+
+ <div class="calc-actions">
+ <button class="visualize-btn" id="vizBtn" onclick="visualizeCalculation()">
+ <svg width="18" height="18" viewBox="0 0 24 24" fill="currentColor"><path d="M12 4.5C7 4.5 2.73 7.61 1 12c1.73 4.39 6 7.5 11 7.5s9.27-3.11 11-7.5c-1.73-4.39-6-7.5-11-7.5zM12 17c-2.76 0-5-2.24-5-5s2.24-5 5-5 5 2.24 5 5-2.24 5-5 5zm0-8c-1.66 0-3 1.34-3 3s1.34 3 3 3 3-1.34 3-3-1.34-3-3-3z"/></svg>
+ Visualize
+ </button>
+
+ <div class="calc-result-area">
+ <div class="calc-result-display">
+ <div class="res-label">Result</div>
+ <div class="res-value" id="calcResult">0</div>
+ </div>
+ </div>
+ </div>
+
+ <!-- BIT-BY-BIT VISUALIZER PANEL -->
+ <div class="calc-visualizer" id="visualizerPanel">
+ <h4 style="color:var(--md-sys-color-primary); margin-bottom:15px; border-bottom:1px solid #333; padding-bottom:10px;">Calculation Steps</h4>
+ <div class="viz-grid" id="vizGrid">
+ <!-- Grid injected here -->
+ </div>
+ <div class="viz-info" id="vizInfo">Click visualize to start...</div>
+ </div>
+
+ </div>
+ </section>
+
+ </main>
+
+ <!-- PROJECT LEGACY MODAL -->
+ <div class="modal-overlay" id="historyModal">
+ <div class="modal-content">
+ <div class="modal-header">
+ <h3>Project Legacy</h3>
+ <button class="close-modal" onclick="toggleHistory()">×</button>
+ </div>
+ <div class="modal-body">
+ <div class="history-timeline">
+ <div class="timeline-item">
+ <div class="version-tag">v1.0 (2024)</div>
+ <p class="timeline-desc">Initial High School Project. Single file, manual conversion logic, animated background.</p>
+ </div>
+ <div class="timeline-item">
+ <div class="version-tag">v2.0</div>
+ <p class="timeline-desc">Modularized Logic & URL State. Moved away from switch cases to a Base-N engine.</p>
+ </div>
+ <div class="timeline-item">
+ <div class="version-tag">v2.1</div>
+ <p class="timeline-desc">Material You UI. Glassmorphism, animated cards, and custom dropdowns.</p>
+ </div>
+ <div class="timeline-item">
+ <div class="version-tag">v2.2</div>
+ <p class="timeline-desc">Dynamic Calculator. Added list-based arithmetic with strict validation.</p>
+ </div>
+ <div class="timeline-item">
+ <div class="version-tag">v2.3 - v2.8</div>
+ <p class="timeline-desc">Visualizer Engine. Bit-by-bit animation, step explanations, and history tape.</p>
+ </div>
+ <div class="timeline-item">
+ <div class="version-tag">v2.10</div>
+ <p class="timeline-desc">Professional Polish. Optimized UX layout, legacy tracking, and performance tuning.</p>
+ </div>
+ <div class="timeline-item">
+ <div class="version-tag current">v2.11 (2026)</div>
+ <p class="timeline-desc">Ultimate Refinement. Enhanced animations, layout stability, and smart controls.</p>
+ </div>
+ </div>
+ </div>
+ </div>
+ </div>
+
+ <!-- SIDEBAR CHEATSHEET -->
+ <aside class="sidebar" id="sidebar">
+ <button class="close-sidebar" onclick="toggleSidebar()">×</button>
+ <h3>Cheat Sheet</h3>
+
+ <h4>Powers of 2</h4>
+ <table class="cheat-table" style="margin-bottom:20px;">
+ <tr><td>2⁰</td><td>1</td></tr>
+ <tr><td>2¹</td><td>2</td></tr>
+ <tr><td>2²</td><td>4</td></tr>
+ <tr><td>2³</td><td>8</td></tr>
+ <tr><td>2⁴</td><td>16</td></tr>
+ <tr><td>2⁵</td><td>32</td></tr>
+ <tr><td>2⁶</td><td>64</td></tr>
+ <tr><td>2⁷</td><td>128</td></tr>
+ <tr><td>2⁸</td><td>256</td></tr>
+ <tr><td>2¹⁰</td><td>1024</td></tr>
+ </table>
+
+ <h4>Hex Lookup</h4>
+ <table class="cheat-table">
+ <tr><th>Dec</th><th>Hex</th><th>Bin</th></tr>
+ <tr><td>0</td><td>0</td><td>0000</td></tr>
+ <tr><td>1</td><td>1</td><td>0001</td></tr>
+ <tr><td>10</td><td>A</td><td>1010</td></tr>
+ <tr><td>11</td><td>B</td><td>1011</td></tr>
+ <tr><td>12</td><td>C</td><td>1100</td></tr>
+ <tr><td>13</td><td>D</td><td>1101</td></tr>
+ <tr><td>14</td><td>E</td><td>1110</td></tr>
+ <tr><td>15</td><td>F</td><td>1111</td></tr>
+ </table>
+ </aside>
+
+ <footer>
+ <div class="footer-content">
+ <p>&copy; 2026 <strong>Amit Dutta</strong>. All rights reserved.</p>
+ <div class="footer-links">
+ <a href="https://amit.is-a.dev" target="_blank" class="footer-link">amit.is-a.dev</a>
+ <span>•</span>
+ <a href="https://github.com/notamitgamer" target="_blank" class="footer-link">GitHub</a>
+ </div>
+ </div>
+ </footer>
+
+ <script>
+ document.addEventListener('DOMContentLoaded', () => {
+ setupCustomDropdown('sourceSelectWrapper', 'fromBase');
+ setupCustomDropdown('targetSelectWrapper', 'toBase');
+ setupCustomDropdown('calcBaseWrapper', 'calcBase');
+ setupCustomDropdown('calcOpWrapper', 'calcOp');
+
+ setupEventListeners();
+
+ // Initialize Calculator
+ addCalcRow();
+ addCalcRow();
+
+ renderHistory();
+
+ try {
+ const params = new URLSearchParams(window.location.search);
+ if(params.has('val')) {
+ document.getElementById('inputVal').value = params.get('val');
+ if(params.has('from')) {
+ const val = params.get('from');
+ document.getElementById('fromBase').value = val;
+ updateDropdownUI('sourceSelectWrapper', val);
+ }
+ if(params.has('to')) {
+ const val = params.get('to');
+ document.getElementById('toBase').value = val;
+ updateDropdownUI('targetSelectWrapper', val);
+ }
+ convert();
+ }
+ } catch(e) {}
+
+ document.addEventListener('click', (e) => {
+ const sidebar = document.getElementById('sidebar');
+ const historyModal = document.getElementById('historyModal');
+ const toolsBtns = document.querySelectorAll('.tools-btn');
+ let clickedBtn = false;
+ toolsBtns.forEach(btn => { if(btn.contains(e.target)) clickedBtn = true; });
+
+ if (!sidebar.contains(e.target) && !clickedBtn && sidebar.classList.contains('open')) {
+ toggleSidebar();
+ }
+ if (e.target === historyModal) {
+ toggleHistory();
+ }
+ });
+ });
+
+ /* --- NAVIGATION --- */
+ function switchTab(tabName, index) {
+ document.querySelectorAll('.app-section').forEach(el => el.classList.remove('active'));
+ document.getElementById(tabName + '-section').classList.add('active');
+
+ document.querySelectorAll('.nav-tab').forEach(el => el.classList.remove('active'));
+ const tabs = document.querySelectorAll('.nav-tab');
+ tabs[index].classList.add('active');
+
+ const glider = document.getElementById('navGlider');
+ glider.style.transform = `translateX(${index * 100}%)`;
+ }
+
+ function toggleSidebar() {
+ document.getElementById('sidebar').classList.toggle('open');
+ }
+
+ function toggleHistory() {
+ const modal = document.getElementById('historyModal');
+ modal.classList.toggle('open');
+ }
+
+ /* --- DROPDOWN LOGIC --- */
+ function setupCustomDropdown(wrapperId, nativeSelectId) {
+ const wrapper = document.getElementById(wrapperId);
+ const nativeSelect = document.getElementById(nativeSelectId);
+ const trigger = wrapper.querySelector('.custom-select-trigger');
+ const triggerText = trigger.querySelector('.selection-text');
+ const options = wrapper.querySelectorAll('.custom-option');
+
+ trigger.addEventListener('click', (e) => {
+ e.stopPropagation();
+ document.querySelectorAll('.custom-select-wrapper').forEach(w => {
+ if(w !== wrapper) w.classList.remove('open');
+ });
+ wrapper.classList.toggle('open');
+ });
+
+ options.forEach(opt => {
+ opt.addEventListener('click', (e) => {
+ e.stopPropagation();
+ wrapper.querySelectorAll('.custom-option').forEach(o => o.classList.remove('selected'));
+ opt.classList.add('selected');
+ triggerText.textContent = opt.textContent;
+ wrapper.classList.remove('open');
+
+ nativeSelect.value = opt.getAttribute('data-value');
+ nativeSelect.dispatchEvent(new Event('change'));
+ });
+ });
+
+ document.addEventListener('click', () => {
+ wrapper.classList.remove('open');
+ });
+ }
+
+ function updateDropdownUI(wrapperId, value) {
+ const wrapper = document.getElementById(wrapperId);
+ const triggerText = wrapper.querySelector('.selection-text');
+ const options = wrapper.querySelectorAll('.custom-option');
+
+ options.forEach(opt => {
+ if(opt.getAttribute('data-value') === value) {
+ opt.classList.add('selected');
+ triggerText.textContent = opt.textContent;
+ } else {
+ opt.classList.remove('selected');
+ }
+ });
+ }
+
+ /* --- CONVERTER LOGIC --- */
+ function convert() {
+ const inputVal = document.getElementById('inputVal').value;
+ const outputEl = document.getElementById('outputVal');
+ const errorChip = document.getElementById('errorChip');
+ const fromBase = parseInt(document.getElementById('fromBase').value);
+ const toBase = parseInt(document.getElementById('toBase').value);
+
+ document.getElementById('sourceCount').textContent = `${inputVal.length} chars`;
+ errorChip.classList.remove('visible');
+
+ if(!inputVal) {
+ outputEl.value = '';
+ document.getElementById('stepsContainer').classList.remove('visible');
+ updateURL('');
+ return;
+ }
+
+ const normalized = inputVal.trim().toUpperCase();
+ const validChars = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ.".substring(0, fromBase) + ".";
+ const regex = new RegExp(`^[${validChars}]+$`);
+
+ if(!regex.test(normalized)) {
+ errorChip.classList.add('visible');
+ outputEl.value = "Error";
+ document.getElementById('stepsContainer').classList.remove('visible');
+ return;
+ }
+
+ try {
+ updateURL(normalized, fromBase, toBase);
+
+ const parts = normalized.split('.');
+ const decInt = parseInt(parts[0], fromBase);
+ let decTotal = decInt;
+
+ const result = decTotal.toString(toBase).toUpperCase();
+ outputEl.value = result;
+
+ generateSteps(normalized, fromBase, toBase, decTotal, result);
+ addToHistory(normalized, result, fromBase, toBase);
+
+ } catch(e) {
+ outputEl.value = "Error";
+ }
+ }
+
+ function generateSteps(input, from, to, decimal, result) {
+ const container = document.getElementById('stepsContainer');
+ const output = document.getElementById('stepsOutput');
+ container.classList.add('visible');
+
+ let log = `Converting ${input} (Base ${from}) to Base ${to}\n`;
+ log += `------------------------------------------\n`;
+
+ if(from !== 10) {
+ log += `1. Convert to Decimal:\n `;
+ const digits = input.split('.')[0].split('').reverse();
+ digits.forEach((d, i) => {
+ log += `${d}×${from}^${i} + `;
+ });
+ log = log.slice(0, -3); // Remove last +
+ log += ` = ${decimal}\n\n`;
+ } else {
+ log += `1. Already Decimal: ${decimal}\n\n`;
+ }
+
+ if(to !== 10) {
+ log += `2. Convert Decimal ${decimal} to Base ${to}:\n`;
+ let temp = decimal;
+ while(temp > 0) {
+ let rem = temp % to;
+ let quot = Math.floor(temp / to);
+ log += ` ${temp} ÷ ${to} = ${quot} [Rem: ${rem.toString(to).toUpperCase()}]\n`;
+ temp = quot;
+ }
+ log += ` Read remainders bottom-up: ${result}`;
+ } else {
+ log += `2. Result is Decimal: ${result}`;
+ }
+ output.textContent = log;
+ }
+
+ function updateURL(val, from, to) {
+ try {
+ const url = new URL(window.location);
+ if(val) {
+ url.searchParams.set('val', val);
+ url.searchParams.set('from', from);
+ url.searchParams.set('to', to);
+ window.history.replaceState({}, '', url);
+ } else {
+ const cleanUrl = window.location.pathname;
+ window.history.replaceState({}, '', cleanUrl);
+ }
+ } catch(e) {}
+ }
+
+ /* --- HISTORY LOGIC --- */
+ function addToHistory(inVal, outVal, from, to) {
+ const key = "nct_history";
+ let history = JSON.parse(localStorage.getItem(key) || "[]");
+ if(history.length > 0 && history[0].inVal === inVal && history[0].to === to) return;
+ history.unshift({ inVal, outVal, from, to });
+ if(history.length > 10) history.pop();
+ localStorage.setItem(key, JSON.stringify(history));
+ renderHistory();
+ }
+
+ function renderHistory() {
+ const list = document.getElementById('historyList');
+ const history = JSON.parse(localStorage.getItem("nct_history") || "[]");
+ if(history.length === 0) {
+ list.innerHTML = '<div style="padding:10px; color:#555; text-align:center;">No history yet</div>';
+ return;
+ }
+ list.innerHTML = '';
+ history.forEach(item => {
+ const div = document.createElement('div');
+ div.className = 'history-item';
+ div.innerHTML = `<div><span class="hist-meta">Base ${item.from} → ${item.to}</span><span class="hist-val">${item.inVal} = ${item.outVal}</span></div>`;
+ div.onclick = () => {
+ document.getElementById('inputVal').value = item.inVal;
+ document.getElementById('fromBase').value = item.from;
+ document.getElementById('toBase').value = item.to;
+ updateDropdownUI('sourceSelectWrapper', item.from.toString());
+ updateDropdownUI('targetSelectWrapper', item.to.toString());
+ convert();
+ };
+ list.appendChild(div);
+ });
+ }
+
+ function clearHistory() {
+ const list = document.getElementById('historyList');
+ const btn = document.getElementById('clearHistBtn');
+
+ if(list.children.length === 0 || list.innerText.includes("No history")) return;
+
+ // Trigger Shake Animation on Button
+ btn.classList.add('shaking');
+
+ // Trigger Slide Out Animation on List
+ list.classList.add('clearing');
+
+ setTimeout(() => {
+ localStorage.removeItem("nct_history");
+ renderHistory();
+ list.classList.remove('clearing');
+ btn.classList.remove('shaking');
+ }, 400); // Duration matches CSS animation
+ }
+
+ /* --- DYNAMIC CALCULATOR LOGIC --- */
+ function addCalcRow() {
+ const container = document.getElementById('calcRows');
+ const div = document.createElement('div');
+ div.className = 'calc-row';
+ div.innerHTML = `
+ <div class="calc-input-wrapper">
+ <input type="text" class="calc-input" placeholder="Enter number..." oninput="validateRow(this); runCalculation()">
+ </div>
+ <button class="calc-remove-btn" onclick="removeRow(this)" title="Remove">×</button>
+ `;
+ container.appendChild(div);
+ div.querySelector('input').focus();
+ }
+
+ function removeRow(btn) {
+ const row = btn.parentElement;
+ if(document.querySelectorAll('.calc-row').length > 1) {
+ row.classList.add('removing');
+ setTimeout(() => {
+ row.remove();
+ runCalculation();
+ }, 300);
+ }
+ }
+
+ function validateRow(input) {
+ const base = parseInt(document.getElementById('calcBase').value);
+ const val = input.value.trim().toUpperCase();
+ if(!val) {
+ input.classList.remove('invalid');
+ return true;
+ }
+ const validChars = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ.".substring(0, base) + ".";
+ const regex = new RegExp(`^[${validChars}]+$`);
+ if(!regex.test(val)) {
+ input.classList.add('invalid');
+ return false;
+ } else {
+ input.classList.remove('invalid');
+ return true;
+ }
+ }
+
+ function validateAllRows() {
+ document.querySelectorAll('.calc-input').forEach(input => validateRow(input));
+ runCalculation();
+ }
+
+ function runCalculation() {
+ const base = parseInt(document.getElementById('calcBase').value);
+ const op = document.getElementById('calcOp').value;
+ const inputs = document.querySelectorAll('.calc-input');
+ const vizBtn = document.getElementById('vizBtn');
+ let total = null;
+ let error = false;
+
+ inputs.forEach(input => {
+ if(input.classList.contains('invalid')) { error = true; return; }
+ const valStr = input.value.trim().toUpperCase();
+ if(!valStr) return;
+ const val = parseInt(valStr, base);
+ if(isNaN(val)) return;
+ if(total === null) { total = val; } else {
+ if(op === 'add') total += val;
+ if(op === 'sub') total -= val;
+ if(op === 'mul') total *= val;
+ if(op === 'div') total = Math.floor(total / val);
+ }
+ });
+
+ const resEl = document.getElementById('calcResult');
+ if(error) {
+ resEl.textContent = "Invalid Input";
+ resEl.style.color = "var(--md-sys-color-error)";
+ vizBtn.style.display = 'none';
+ } else if(total === null) {
+ resEl.textContent = "0";
+ resEl.style.color = "var(--md-sys-color-primary)";
+ vizBtn.style.display = 'none';
+ } else {
+ resEl.textContent = total.toString(base).toUpperCase();
+ resEl.style.color = "var(--md-sys-color-primary)";
+ vizBtn.style.display = (op === 'add') ? 'flex' : 'none';
+ }
+ document.getElementById('visualizerPanel').classList.remove('active');
+ }
+
+ /* --- VISUALIZER LOGIC (Multi-Base) --- */
+ function visualizeCalculation() {
+ const base = parseInt(document.getElementById('calcBase').value);
+ const op = document.getElementById('calcOp').value;
+ const panel = document.getElementById('visualizerPanel');
+ const grid = document.getElementById('vizGrid');
+ const info = document.getElementById('vizInfo');
+
+ if(op !== 'add') return;
+
+ const inputs = [];
+ let isValid = true;
+ document.querySelectorAll('.calc-input').forEach(inp => {
+ if(inp.value.trim()) {
+ const val = parseInt(inp.value.trim(), base);
+ if(isNaN(val)) isValid = false;
+ inputs.push(val);
+ }
+ });
+
+ if(!isValid || inputs.length < 2) {
+ alert("Enter at least two numbers.");
+ return;
+ }
+
+ panel.classList.add('active');
+ grid.innerHTML = '';
+
+ const digitArrays = inputs.map(n => n.toString(base).toUpperCase().split(''));
+ const maxLen = Math.max(...digitArrays.map(arr => arr.length)) + 1;
+
+ const paddedArrays = digitArrays.map(arr => {
+ const padding = new Array(maxLen - arr.length).fill('0');
+ return padding.concat(arr);
+ });
+
+ grid.style.gridTemplateColumns = `repeat(${maxLen}, 1fr)`;
+
+ for(let i=0; i<maxLen; i++) {
+ const cell = document.createElement('div');
+ cell.className = 'viz-cell carry';
+ cell.id = `carry-${i}`;
+ grid.appendChild(cell);
+ }
+
+ paddedArrays.forEach((rowDigits, rIdx) => {
+ for(let i=0; i<maxLen; i++) {
+ const cell = document.createElement('div');
+ cell.className = 'viz-cell';
+ if(rIdx === paddedArrays.length -1) cell.classList.add('border-bottom');
+ const val = rowDigits[i];
+ cell.textContent = val;
+ if(i === 0 && rIdx > 0) {
+ cell.classList.add('operator');
+ cell.innerHTML = `+ ${val}`;
+ }
+ grid.appendChild(cell);
+ }
+ });
+
+ for(let i=0; i<maxLen; i++) {
+ const cell = document.createElement('div');
+ cell.className = 'viz-cell result';
+ cell.id = `res-${i}`;
+ cell.textContent = '?';
+ grid.appendChild(cell);
+ }
+
+ let carry = 0;
+ let colIdx = maxLen - 1;
+
+ function step() {
+ if(colIdx < 0) {
+ info.textContent = "Complete.";
+ return;
+ }
+ document.querySelectorAll('.active-col').forEach(el => el.classList.remove('active-col'));
+ const resCell = document.getElementById(`res-${colIdx}`);
+ resCell.classList.add('active-col');
+
+ let sum = carry;
+ const digitVals = [];
+ paddedArrays.forEach(row => {
+ const char = row[colIdx];
+ const val = parseInt(char, base);
+ sum += val;
+ digitVals.push(val.toString(base).toUpperCase());
+ });
+
+ const writeVal = sum % base;
+ const newCarry = Math.floor(sum / base);
+
+ resCell.textContent = writeVal.toString(base).toUpperCase();
+ if(newCarry > 0 && colIdx > 0) {
+ document.getElementById(`carry-${colIdx-1}`).textContent = newCarry.toString(base).toUpperCase();
+ }
+
+ const eqStr = `${digitVals.join('+')} + ${carry}(c) = ${sum}`;
+ const writeStr = writeVal.toString(base).toUpperCase();
+ const carryStr = newCarry.toString(base).toUpperCase();
+ info.textContent = `Col ${maxLen - colIdx}: ${eqStr} (Base ${base}). Write ${writeStr}, Carry ${carryStr}`;
+
+ carry = newCarry;
+ colIdx--;
+ setTimeout(step, 800);
+ }
+ step();
+ }
+
+ /* --- UTILS & SWAP --- */
+ function setupEventListeners() {
+ document.addEventListener('keydown', (e) => {
+ if(e.ctrlKey && e.key === 'Enter') copyToClipboard('outputVal', 'copyBtn');
+ if(e.altKey && e.key === 's') document.getElementById('swapBtn').click();
+ });
+
+ document.getElementById('inputVal').addEventListener('input', convert);
+ document.getElementById('fromBase').addEventListener('change', convert);
+ document.getElementById('toBase').addEventListener('change', convert);
+
+ document.getElementById('swapBtn').addEventListener('click', () => {
+ const cardSource = document.getElementById('cardSource');
+ const cardTarget = document.getElementById('cardTarget');
+ const isMobile = window.innerWidth <= 768;
+
+ if(isMobile) {
+ cardSource.classList.add('anim-move-down');
+ cardTarget.classList.add('anim-move-up');
+ } else {
+ cardSource.classList.add('anim-move-right');
+ cardTarget.classList.add('anim-move-left');
+ }
+
+ setTimeout(() => {
+ const inEl = document.getElementById('inputVal');
+ const outEl = document.getElementById('outputVal');
+ const from = document.getElementById('fromBase');
+ const to = document.getElementById('toBase');
+
+ const tempVal = inEl.value;
+ inEl.value = outEl.value;
+ const tempBase = from.value;
+ from.value = to.value;
+ to.value = tempBase;
+
+ updateDropdownUI('sourceSelectWrapper', from.value);
+ updateDropdownUI('targetSelectWrapper', to.value);
+
+ cardSource.classList.remove('anim-move-down', 'anim-move-right');
+ cardTarget.classList.remove('anim-move-up', 'anim-move-left');
+ convert();
+ }, 400);
+ });
+ }
+
+ function speakResult() {
+ const txt = document.getElementById('outputVal').value;
+ if(!txt) return;
+ let spoken = txt;
+ if(document.getElementById('toBase').value == 2) {
+ spoken = txt.split('').join(' ');
+ }
+ const utt = new SpeechSynthesisUtterance(spoken);
+ window.speechSynthesis.speak(utt);
+ }
+
+ function copyToClipboard(id, btnId) {
+ const el = document.getElementById(id);
+ if(el) {
+ el.select();
+ document.execCommand('copy');
+ }
+ const btn = document.getElementById(btnId);
+ if(btn) {
+ const originalHTML = btn.innerHTML;
+ btn.innerHTML = `<svg width="20" height="20" viewBox="0 0 24 24" fill="var(--md-sys-color-success)"><path d="M9 16.17L4.83 12l-1.42 1.41L9 19 21 7l-1.41-1.41z"/></svg>`;
+ btn.classList.add('success');
+ setTimeout(() => {
+ btn.innerHTML = originalHTML;
+ btn.classList.remove('success');
+ }, 2000);
+ }
+ }
+
+ function copyStepsText() {
+ const stepsText = document.getElementById('stepsOutput').innerText;
+ navigator.clipboard.writeText(stepsText).then(() => {
+ const btn = document.getElementById('copyStepsBtn');
+ const originalHTML = btn.innerHTML;
+ btn.innerHTML = `<svg width="18" height="18" viewBox="0 0 24 24" fill="var(--md-sys-color-success)"><path d="M9 16.17L4.83 12l-1.42 1.41L9 19 21 7l-1.41-1.41z"/></svg>`;
+ btn.classList.add('success');
+ setTimeout(() => {
+ btn.innerHTML = originalHTML;
+ btn.classList.remove('success');
+ }, 2000);
+ });
+ }
+
+ function clearAll() {
+ document.getElementById('inputVal').value = '';
+ document.getElementById('outputVal').value = '';
+ document.getElementById('sourceCount').textContent = '0 chars';
+ document.getElementById('errorChip').classList.remove('visible');
+ document.getElementById('stepsContainer').classList.remove('visible');
+ updateURL('');
+ }
+ </script>
+</body>
+</html></pre>
+ </div>
+ </div>
+
+ <div id="download-tab-content" class="tab-content">
+ <div class="download-item">
+ <div class="download-info">
+ <h3>nct-legacy.html</h3>
+ <p>Standalone production code of the Number Conversion Tool.</p>
+ </div>
+ <!-- Force download via download attribute -->
+ <a href="https://github.com/notamitgamer/NCT/releases/download/v2.11/nct-legacy.html" class="download-button">Download File</a>
+ </div>
+ <div class="download-item">
+ <div class="download-info">
+ <h3>GitHub Repository</h3>
+ <p>View source and contribute on GitHub.</p>
+ </div>
+ <a href="https://github.com/notamitgamer/nct-legacy" target="_blank" class="download-button" style="background-color: #333;">GitHub</a>
+ </div>
+ </div>
+ </div>
+
+ <script>
+ function switchTab(evt, tabId) {
+ // Hide all contents
+ document.querySelectorAll('.tab-content').forEach(content => {
+ content.classList.remove('active');
+ });
+ // Deactivate all buttons
+ document.querySelectorAll('.tab-button').forEach(button => {
+ button.classList.remove('active');
+ });
+
+ // Show current
+ document.getElementById(tabId + '-tab-content').classList.add('active');
+ evt.currentTarget.classList.add('active');
+ }
+
+ async function copyToClipboard(btn) {
+ const code = document.getElementById('sourceCode').innerText;
+ try {
+ await navigator.clipboard.writeText(code);
+ const originalHTML = btn.innerHTML;
+ btn.innerHTML = '<span>✓ Copied!</span>';
+ btn.classList.add('success');
+
+ setTimeout(() => {
+ btn.innerHTML = originalHTML;
+ btn.classList.remove('success');
+ }, 2000);
+ } catch (err) {
+ console.error('Failed to copy code: ', err);
+ }
+ }
+ </script>
+
+</body>
+</html>+
\ No newline at end of file
diff --git a/codenct-legacy.html b/codenct-legacy.html
@@ -0,0 +1,1953 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+ <meta charset="UTF-8">
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
+ <title>NCT Legacy Code View</title>
+ <!-- Link to Cascadia Code font from CDN -->
+ <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@xz/fonts@1/serve/cascadia-code.min.css">
+ <style>
+ /* Base styling for the entire page */
+ body {
+ background-color: black;
+ margin: 0;
+ padding: 20px;
+ font-family: 'Cascadia Code', 'Fira Code', 'Roboto Mono', monospace;
+ color: white;
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ min-height: 100vh;
+ }
+
+ /* --- CUSTOM SCROLLBAR --- */
+ ::-webkit-scrollbar { width: 8px; height: 8px; }
+ ::-webkit-scrollbar-track { background: #000; }
+ ::-webkit-scrollbar-thumb { background: #333; border-radius: 4px; }
+ ::-webkit-scrollbar-thumb:hover { background: #1abc9c; }
+
+ h1 {
+ color: #f0f0f0;
+ text-align: center;
+ margin-bottom: 20px;
+ }
+
+ /* Container for the tabs */
+ .tab-container {
+ background-color: #1a1a1a;
+ color: #f0f0f0;
+ padding: 15px;
+ border-radius: 8px;
+ width: 100%;
+ max-width: 1000px;
+ box-shadow: 0 4px 12px rgba(0, 0, 0, 0.4);
+ position: relative;
+ overflow: hidden;
+ border: 1px solid #333;
+ }
+
+ /* Tab buttons styling */
+ .tabs {
+ display: flex;
+ justify-content: center;
+ margin-bottom: 20px;
+ border-bottom: 1px solid #333;
+ }
+
+ .tab-button {
+ background-color: transparent;
+ border: none;
+ padding: 10px 20px;
+ font-size: 1.2em;
+ color: #f0f0f0;
+ cursor: pointer;
+ transition: color 0.3s ease, background-color 0.3s ease, transform 0.3s ease;
+ position: relative;
+ overflow: hidden;
+ border-radius: 5px 5px 0 0;
+ margin: 0 5px;
+ }
+
+ .tab-button:hover {
+ color: #1abc9c;
+ transform: translateY(-2px);
+ }
+
+ .tab-button.active {
+ color: #1abc9c;
+ font-weight: bold;
+ background-color: #333;
+ transform: translateY(-2px);
+ }
+
+ .tab-button.active::after {
+ content: '';
+ position: absolute;
+ bottom: 0;
+ left: 0;
+ width: 100%;
+ height: 3px;
+ background-color: #1abc9c;
+ animation: tabUnderline 0.3s forwards;
+ }
+
+ @keyframes tabUnderline {
+ from { width: 0; }
+ to { width: 100%; }
+ }
+
+ /* Tab content styling */
+ .tab-content {
+ display: none;
+ padding: 15px 0;
+ animation: fadeIn 0.5s ease-in-out;
+ }
+
+ .tab-content.active {
+ display: block;
+ }
+
+ @keyframes fadeIn {
+ from { opacity: 0; transform: translateY(10px); }
+ to { opacity: 1; transform: translateY(0); }
+ }
+
+ /* Header for Code Block */
+ .code-header {
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ background-color: #252525;
+ padding: 8px 15px;
+ border-radius: 8px 8px 0 0;
+ border: 1px solid #333;
+ border-bottom: none;
+ }
+
+ .file-info { font-size: 0.85em; color: #888; }
+
+ /* Copy Button Styling */
+ .copy-button {
+ background: transparent;
+ border: 1px solid #444;
+ color: #ccc;
+ padding: 5px 12px;
+ border-radius: 4px;
+ font-size: 0.8em;
+ cursor: pointer;
+ transition: 0.2s;
+ display: flex;
+ align-items: center;
+ gap: 6px;
+ }
+
+ .copy-button:hover {
+ border-color: #1abc9c;
+ color: #1abc9c;
+ }
+
+ .copy-button.success {
+ border-color: #2ecc71;
+ color: #2ecc71;
+ }
+
+ /* Styling for the code block container */
+ .code-block-container {
+ background-color: #1a1a1a;
+ border: 1px solid #333;
+ border-radius: 0 0 8px 8px;
+ overflow: hidden;
+ }
+
+ .code-block {
+ background-color: #1a1a1a;
+ color: #f0f0f0;
+ padding: 15px;
+ overflow-x: auto;
+ max-height: 70vh;
+ font-size: 0.95em;
+ line-height: 1.5;
+ tab-size: 4;
+ white-space: pre;
+ margin: 0;
+ }
+
+ /* Download section styling */
+ .download-item {
+ background-color: #2a2a2a;
+ padding: 15px;
+ border-radius: 8px;
+ margin-bottom: 15px;
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ border: 1px solid #333;
+ }
+
+ .download-info h3 {
+ color: #1abc9c;
+ margin: 0 0 5px 0;
+ }
+
+ .download-info p {
+ font-size: 0.9em;
+ color: #ccc;
+ margin: 0;
+ }
+
+ .download-button {
+ background-color: #007bff;
+ color: white;
+ border: none;
+ padding: 10px 15px;
+ border-radius: 5px;
+ cursor: pointer;
+ text-decoration: none;
+ font-size: 0.9em;
+ transition: background-color 0.3s ease, transform 0.2s ease;
+ }
+
+ .download-button:hover {
+ background-color: #0056b3;
+ transform: translateY(-1px);
+ }
+ </style>
+</head>
+<body>
+
+ <h1>--- NCT Legacy Explorer ---</h1>
+
+ <div class="tab-container">
+ <div class="tabs">
+ <button class="tab-button active" onclick="switchTab(event, 'code')">Code</button>
+ <button class="tab-button" onclick="switchTab(event, 'download')">Download</button>
+ </div>
+
+ <div id="code-tab-content" class="tab-content active">
+ <div class="code-header">
+ <span class="file-info">nct-legacy.html</span>
+ <button class="copy-button" onclick="copyToClipboard(this)">
+ <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><rect x="9" y="9" width="13" height="13" rx="2" ry="2"></rect><path d="M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1"></path></svg>
+ Copy Code
+ </button>
+ </div>
+ <div class="code-block-container">
+ <pre id="sourceCode" class="code-block"><!DOCTYPE html>
+<html lang="en">
+<head>
+ <meta charset="UTF-8">
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
+ <title>NCT v2.11 | Legacy Edition</title>
+ <link rel="icon" href="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIzMiIgaGVpZ2h0PSIzMiIgdmlld0JveD0iMCAwIDI0IDI0Ij48c3R5bGU+cGF0aHtmaWxsOiNEMEJDRkZ9QG1lZGlhIChwcmVmZXJzLWNvbG9yLXNjaGVtZTpkYXJrKXtwYXRoe2ZpbGw6I2ZmZnx9fTwvc3R5bGU+PHBhdGggZD0iTTcgMnYxMWgzdjlsNy0xMmgtNGw0LTh6Ii8+PC9zdmc+">
+ <link rel="preconnect" href="https://fonts.googleapis.com">
+ <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
+ <link href="https://fonts.googleapis.com/css2?family=Roboto:wght@400;500;700&family=Google+Sans:wght@400;500;700&family=Roboto+Mono:wght@400;500&display=swap" rel="stylesheet">
+ <script type="application/ld+json">
+ {
+ "@context": "https://schema.org",
+ "@type": "WebApplication",
+ "name": "NCT v2.11 | Legacy Edition",
+ "url": "https://amit.is-a.dev/nct-legacy",
+ "image": "https://amit.is-a.dev/nctlogo.png",
+ "description": "Number conversion tool",
+ "applicationCategory": "EducationalApplication",
+ "operatingSystem": "Any"
+ }
+</script>
+ <style>
+ /* --- MATERIAL DESIGN 3 TOKENS --- */
+ :root {
+ --md-sys-color-background: #141218;
+ --md-sys-color-surface: #1D1B20;
+ --md-sys-color-surface-container: #211F26;
+ --md-sys-color-surface-container-high: #2B2930;
+
+ /* Primary */
+ --md-sys-color-primary: #D0BCFF;
+ --md-sys-color-on-primary: #381E72;
+ --md-sys-color-primary-container: #4F378B;
+ --md-sys-color-on-primary-container: #EADDFF;
+
+ /* Text */
+ --md-sys-color-on-surface: #E6E1E5;
+ --md-sys-color-on-surface-variant: #CAC4D0;
+ --md-sys-color-outline: #938F99;
+
+ /* Error */
+ --md-sys-color-error: #F2B8B5;
+ --md-sys-color-error-container: #8C1D18;
+
+ /* Success/Accent */
+ --md-sys-color-tertiary: #B6C4FF;
+ --md-sys-color-success: #4ade80;
+
+ /* Dimensions */
+ --radius-xl: 28px;
+ --radius-l: 16px;
+ --radius-m: 12px;
+ --radius-s: 8px;
+
+ --font-main: 'Google Sans', 'Roboto', sans-serif;
+ --font-code: 'Roboto Mono', monospace;
+
+ /* Animation */
+ --ease-elastic: cubic-bezier(0.4, 0.0, 0.2, 1);
+ }
+
+ /* --- CUSTOM SCROLLBAR --- */
+ ::-webkit-scrollbar { width: 6px; height: 6px; }
+ ::-webkit-scrollbar-track { background: transparent; }
+ ::-webkit-scrollbar-thumb { background: rgba(255,255,255,0.2); border-radius: 3px; }
+ ::-webkit-scrollbar-thumb:hover { background: rgba(255,255,255,0.4); }
+
+ * { margin: 0; padding: 0; box-sizing: border-box; -webkit-tap-highlight-color: transparent; }
+
+ body {
+ font-family: var(--font-main);
+ background-color: var(--md-sys-color-background);
+ color: var(--md-sys-color-on-surface);
+ min-height: 100vh;
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ overflow-x: hidden;
+ background-image: radial-gradient(circle at 50% 0%, rgba(208, 188, 255, 0.05) 0%, transparent 60%);
+ }
+
+ /* --- LAYOUT --- */
+ .app-container {
+ width: 100%;
+ max-width: 1000px;
+ padding: 24px;
+ display: flex;
+ flex-direction: column;
+ gap: 24px;
+ flex-grow: 1;
+ }
+
+ /* --- HEADER & NAV --- */
+ header {
+ display: flex;
+ flex-direction: column;
+ gap: 20px;
+ padding: 16px 0;
+ }
+
+ .header-top {
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ }
+
+ .logo {
+ font-size: 1.75rem;
+ font-weight: 500;
+ color: var(--md-sys-color-on-surface);
+ display: flex;
+ align-items: center;
+ gap: 12px;
+ }
+
+ .logo span { color: var(--md-sys-color-primary); }
+
+ /* Navigation Tabs with Animation */
+ .nav-tabs {
+ position: relative;
+ display: flex;
+ background: var(--md-sys-color-surface-container);
+ padding: 4px;
+ border-radius: 32px;
+ width: fit-content;
+ margin: 0 auto;
+ border: 1px solid rgba(255,255,255,0.05);
+ }
+
+ .nav-glider {
+ position: absolute;
+ top: 4px;
+ left: 4px;
+ height: calc(100% - 8px);
+ width: 110px;
+ background: var(--md-sys-color-primary-container);
+ border-radius: 28px;
+ transition: transform 0.3s var(--ease-elastic);
+ z-index: 1;
+ }
+
+ .nav-tab {
+ position: relative;
+ padding: 10px 24px;
+ border-radius: 24px;
+ color: var(--md-sys-color-on-surface-variant);
+ font-weight: 500;
+ cursor: pointer;
+ transition: color 0.2s;
+ border: none;
+ background: none;
+ z-index: 2;
+ width: 110px;
+ }
+
+ .nav-tab.active { color: var(--md-sys-color-on-primary-container); }
+
+ .header-actions {
+ display: flex;
+ gap: 10px;
+ }
+
+ .tools-btn {
+ background: transparent;
+ border: 1px solid var(--md-sys-color-outline);
+ color: var(--md-sys-color-on-surface);
+ padding: 8px 16px;
+ border-radius: var(--radius-l);
+ cursor: pointer;
+ font-weight: 500;
+ display: flex;
+ align-items: center;
+ gap: 8px;
+ transition: transform 0.1s;
+ }
+ .tools-btn:active { transform: scale(0.95); }
+ .tools-btn:hover { background: rgba(255,255,255,0.05); }
+
+ /* --- SECTIONS --- */
+ .app-section {
+ display: none;
+ flex-direction: column;
+ gap: 24px;
+ animation: fadeIn 0.3s ease-out;
+ }
+ .app-section.active { display: flex; }
+
+ @keyframes fadeIn { from { opacity: 0; transform: translateY(10px); } to { opacity: 1; transform: translateY(0); } }
+
+ /* --- CONVERTER STAGE --- */
+ .converter-stage {
+ display: grid;
+ grid-template-columns: 1fr 64px 1fr;
+ gap: 16px;
+ align-items: stretch;
+ position: relative;
+ }
+
+ .material-card {
+ background-color: var(--md-sys-color-surface-container);
+ border-radius: var(--radius-xl);
+ padding: 24px;
+ display: flex;
+ flex-direction: column;
+ height: 380px;
+ position: relative;
+ transition: transform 0.4s var(--ease-elastic), box-shadow 0.2s;
+ border: 1px solid transparent;
+ z-index: 1;
+ }
+
+ .material-card:focus-within {
+ background-color: var(--md-sys-color-surface-container-high);
+ border-color: var(--md-sys-color-outline);
+ }
+
+ /* CARD HEADER & CUSTOM DROPDOWN */
+ .card-header {
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ margin-bottom: 20px;
+ border-bottom: 1px solid var(--md-sys-color-outline);
+ padding-bottom: 12px;
+ z-index: 10;
+ }
+
+ .input-label {
+ font-size: 0.875rem;
+ font-weight: 500;
+ color: var(--md-sys-color-primary);
+ text-transform: uppercase;
+ letter-spacing: 1px;
+ }
+
+ /* Custom Dropdown Styles */
+ .custom-select-wrapper {
+ position: relative;
+ min-width: 150px;
+ user-select: none;
+ }
+ .custom-select-trigger {
+ display: flex;
+ align-items: center;
+ justify-content: flex-end;
+ gap: 8px;
+ cursor: pointer;
+ color: var(--md-sys-color-on-surface);
+ font-weight: 500;
+ padding: 4px 0;
+ transition: color 0.2s;
+ }
+ .custom-select-trigger:hover { color: var(--md-sys-color-primary); }
+ .arrow { font-size: 0.7rem; transition: transform 0.3s; }
+
+ .custom-options {
+ position: absolute;
+ top: 120%;
+ right: 0;
+ background: var(--md-sys-color-surface-container-high);
+ border-radius: var(--radius-m);
+ box-shadow: 0 8px 16px rgba(0,0,0,0.4);
+ opacity: 0;
+ visibility: hidden;
+ pointer-events: none;
+ transform: translateY(-10px);
+ transition: all 0.2s var(--ease-elastic);
+ min-width: 180px;
+ overflow: hidden;
+ border: 1px solid rgba(255,255,255,0.1);
+ z-index: 1000;
+ }
+ .custom-select-wrapper.open .custom-options {
+ opacity: 1; visibility: visible; pointer-events: all; transform: translateY(0);
+ }
+ .custom-select-wrapper.open .arrow { transform: rotate(180deg); }
+
+ .custom-option {
+ padding: 12px 16px;
+ cursor: pointer;
+ color: var(--md-sys-color-on-surface-variant);
+ transition: 0.2s;
+ }
+ .custom-option:hover { background: rgba(255,255,255,0.05); color: var(--md-sys-color-on-surface); }
+ .custom-option.selected { color: var(--md-sys-color-primary); background: rgba(208, 188, 255, 0.1); }
+
+ /* INPUT AREA */
+ .input-area {
+ flex-grow: 1;
+ background: transparent;
+ border: none;
+ color: var(--md-sys-color-on-surface);
+ font-size: 2.2rem;
+ font-family: var(--font-main);
+ resize: none;
+ outline: none;
+ line-height: 1.2;
+ z-index: 1;
+ }
+ .input-area::placeholder { color: var(--md-sys-color-on-surface-variant); opacity: 0.3; }
+
+ /* Error Chip */
+ .error-chip {
+ position: absolute;
+ bottom: 80px;
+ left: 24px;
+ background: var(--md-sys-color-error-container);
+ color: var(--md-sys-color-error);
+ padding: 8px 16px;
+ border-radius: 8px;
+ font-size: 0.875rem;
+ display: flex;
+ align-items: center;
+ gap: 8px;
+ opacity: 0;
+ transform: translateY(10px);
+ transition: all 0.3s;
+ pointer-events: none;
+ }
+ .error-chip.visible { opacity: 1; transform: translateY(0); }
+
+ /* Card Footer */
+ .card-footer {
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ margin-top: auto;
+ }
+ .char-count { font-size: 0.75rem; color: var(--md-sys-color-on-surface-variant); }
+
+ /* Icons & Buttons */
+ .icon-btn {
+ background: transparent;
+ border: none;
+ color: var(--md-sys-color-on-surface-variant);
+ width: 40px; height: 40px;
+ border-radius: 50%;
+ cursor: pointer;
+ display: flex; align-items: center; justify-content: center;
+ transition: 0.2s;
+ }
+ .icon-btn:hover { background: rgba(255,255,255,0.1); color: var(--md-sys-color-on-surface); transform: scale(1.1); }
+ .icon-btn:active { transform: scale(0.9); }
+
+ /* Copy Button Animation */
+ .icon-btn.success svg {
+ animation: check-pop 0.4s cubic-bezier(0.175, 0.885, 0.32, 1.275);
+ color: var(--md-sys-color-success);
+ }
+ @keyframes check-pop {
+ 0% { transform: scale(0); }
+ 50% { transform: scale(1.3); }
+ 100% { transform: scale(1); }
+ }
+
+ /* Trash Shake Animation */
+ @keyframes trash-shake {
+ 0% { transform: rotate(0deg); }
+ 25% { transform: rotate(-15deg); }
+ 50% { transform: rotate(15deg); }
+ 75% { transform: rotate(-15deg); }
+ 100% { transform: rotate(0deg); }
+ }
+ .icon-btn.shaking {
+ animation: trash-shake 0.4s ease-in-out;
+ color: var(--md-sys-color-error);
+ }
+
+ /* Swap Button */
+ .swap-container { display: flex; justify-content: center; align-items: center; z-index: 2; }
+ .swap-fab {
+ width: 56px; height: 56px;
+ border-radius: 16px;
+ background-color: var(--md-sys-color-surface-container-high);
+ border: 1px solid var(--md-sys-color-outline);
+ color: var(--md-sys-color-on-surface);
+ cursor: pointer;
+ display: flex; align-items: center; justify-content: center;
+ transition: all 0.3s var(--ease-elastic);
+ box-shadow: 0 4px 8px rgba(0,0,0,0.2);
+ }
+ .swap-fab:hover {
+ background-color: var(--md-sys-color-primary);
+ color: var(--md-sys-color-on-primary);
+ transform: rotate(180deg) scale(1.1);
+ border-color: transparent;
+ }
+
+ /* --- SWAP ANIMATION CLASSES --- */
+ .anim-move-right { transform: translateX(calc(100% + 16px)); }
+ .anim-move-left { transform: translateX(calc(-100% - 16px)); }
+ .anim-move-down { transform: translateY(calc(100% + 16px)); }
+ .anim-move-up { transform: translateY(calc(-100% - 16px)); }
+
+ /* --- DYNAMIC CALCULATOR --- */
+ .calc-wrapper {
+ background: var(--md-sys-color-surface-container);
+ padding: 24px;
+ border-radius: var(--radius-xl);
+ max-width: 800px;
+ margin: 0 auto;
+ width: 100%;
+ }
+
+ .calc-header {
+ display: flex;
+ gap: 24px;
+ margin-bottom: 24px;
+ flex-wrap: wrap;
+ }
+
+ .calc-ctrl-group {
+ flex: 1;
+ min-width: 200px;
+ display: flex;
+ align-items: center;
+ gap: 12px;
+ background: rgba(0,0,0,0.2);
+ padding: 8px 16px;
+ border-radius: 12px;
+ }
+ .calc-ctrl-group label {
+ font-size: 0.8rem;
+ color: var(--md-sys-color-primary);
+ text-transform: uppercase;
+ white-space: nowrap;
+ margin: 0;
+ }
+
+ .calc-rows {
+ display: flex;
+ flex-direction: column;
+ gap: 12px;
+ margin-bottom: 24px;
+ }
+
+ .calc-row {
+ display: flex;
+ gap: 12px;
+ align-items: center;
+ animation: fadeIn 0.3s ease-out;
+ }
+
+ /* Simple Fade Out */
+ .calc-row.removing {
+ opacity: 0;
+ transition: opacity 0.3s ease;
+ }
+
+ .calc-input-wrapper {
+ flex-grow: 1;
+ position: relative;
+ }
+ .calc-input {
+ width: 100%;
+ background: rgba(0,0,0,0.2);
+ border: 1px solid transparent;
+ color: var(--md-sys-color-on-surface);
+ padding: 16px;
+ border-radius: 12px;
+ font-family: var(--font-code);
+ font-size: 1.1rem;
+ outline: none;
+ transition: 0.2s;
+ }
+ .calc-input:focus { border-color: var(--md-sys-color-primary); background: rgba(0,0,0,0.4); }
+ .calc-input.invalid { border-color: var(--md-sys-color-error); }
+
+ .calc-remove-btn {
+ background: var(--md-sys-color-surface-container-high);
+ border: none;
+ color: var(--md-sys-color-error);
+ width: 40px; height: 40px;
+ border-radius: 8px;
+ cursor: pointer;
+ display: flex; align-items: center; justify-content: center;
+ transition: 0.2s;
+ }
+ .calc-remove-btn:hover { background: var(--md-sys-color-error-container); color: #fff; }
+
+ /* The Divider Area */
+ .calc-actions {
+ display: flex;
+ align-items: center;
+ border-top: 1px solid var(--md-sys-color-outline);
+ padding-top: 20px;
+ margin-top: 20px;
+ }
+
+ .add-row-btn {
+ background: rgba(255,255,255,0.05);
+ border: 1px dashed var(--md-sys-color-outline);
+ color: var(--md-sys-color-on-surface-variant);
+ padding: 10px 20px;
+ border-radius: 12px;
+ cursor: pointer;
+ font-weight: 500;
+ width: fit-content;
+ text-align: left;
+ transition: 0.2s;
+ margin-bottom: 20px; /* Space before divider */
+ }
+ .add-row-btn:hover { border-color: var(--md-sys-color-primary); color: var(--md-sys-color-primary); background: rgba(208, 188, 255, 0.05); }
+
+ .calc-result-area {
+ display: flex;
+ align-items: center;
+ gap: 20px;
+ margin-left: auto; /* Forces result to the right */
+ }
+
+ .visualize-btn {
+ background: var(--md-sys-color-primary-container);
+ color: var(--md-sys-color-on-primary-container);
+ border: none;
+ padding: 8px 16px;
+ border-radius: 20px;
+ font-size: 0.9rem;
+ font-weight: 500;
+ cursor: pointer;
+ display: none; /* Hidden by default */
+ align-items: center;
+ gap: 6px;
+ transition: 0.2s;
+ animation: popIn 0.3s cubic-bezier(0.175, 0.885, 0.32, 1.275);
+ margin-right: auto; /* Keeps it left-aligned in actions */
+ }
+ .visualize-btn:hover { filter: brightness(1.2); }
+ .visualize-btn:active { transform: scale(0.95); }
+ @keyframes popIn { from{transform: scale(0.8); opacity:0} to{transform: scale(1); opacity:1} }
+
+ .calc-result-display { text-align: right; }
+ .res-label { font-size: 0.9rem; color: var(--md-sys-color-on-surface-variant); }
+ .res-value { font-size: 2.2rem; color: var(--md-sys-color-primary); font-family: var(--font-code); margin-top: 5px; font-weight: 700; }
+
+
+ /* --- VISUALIZER PANEL --- */
+ .calc-visualizer {
+ background: #111;
+ border-radius: var(--radius-m);
+ padding: 20px;
+ margin-top: 20px;
+ display: none;
+ border: 1px solid var(--md-sys-color-outline);
+ overflow-x: auto;
+ }
+ .calc-visualizer.active { display: block; animation: slideIn 0.4s var(--ease-elastic); }
+
+ .viz-grid {
+ display: grid;
+ font-family: var(--font-code);
+ font-size: 1.2rem;
+ color: var(--md-sys-color-on-surface);
+ gap: 4px;
+ }
+ .viz-row { display: contents; }
+ .viz-cell {
+ padding: 4px 8px;
+ text-align: center;
+ position: relative;
+ }
+ .viz-cell.carry { color: var(--md-sys-color-tertiary); font-size: 0.8rem; height: 20px; }
+ .viz-cell.operator { color: var(--md-sys-color-primary); }
+ .viz-cell.border-bottom { border-bottom: 2px solid var(--md-sys-color-outline); }
+ .viz-cell.active-col { background: rgba(208, 188, 255, 0.15); border-radius: 4px; }
+ .viz-cell.result { color: var(--md-sys-color-primary); font-weight: bold; }
+
+ .viz-info {
+ margin-top: 12px;
+ font-size: 0.9rem;
+ color: var(--md-sys-color-on-surface-variant);
+ font-family: var(--font-code);
+ min-height: 20px;
+ }
+
+ /* --- CONVERTER STEPS PANEL --- */
+ .steps-container {
+ margin-top: 20px;
+ background: rgba(0,0,0,0.2);
+ border-radius: var(--radius-l);
+ padding: 20px;
+ border: 1px solid rgba(255,255,255,0.05);
+ display: none;
+ }
+ .steps-container.visible { display: block; animation: fadeIn 0.5s; }
+
+ .steps-header {
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ margin-bottom: 12px;
+ border-bottom: 1px solid rgba(255,255,255,0.1);
+ padding-bottom: 8px;
+ }
+
+ .steps-content {
+ font-family: var(--font-code);
+ color: var(--md-sys-color-tertiary);
+ font-size: 0.9rem;
+ white-space: pre-wrap;
+ line-height: 1.5;
+ }
+
+ /* --- HISTORY TAPE --- */
+ .history-section {
+ margin-top: 20px;
+ border-top: 1px solid var(--md-sys-color-outline);
+ padding-top: 20px;
+ }
+ .history-header {
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ margin-bottom: 10px;
+ }
+ .history-list {
+ display: flex; flex-direction: column; gap: 8px;
+ max-height: 200px; overflow-y: auto;
+ transition: opacity 0.3s;
+ }
+
+ /* History Item Animation: Slide Out to Right */
+ .history-list.clearing .history-item {
+ animation: slide-out-right 0.3s forwards;
+ }
+ @keyframes slide-out-right {
+ to { transform: translateX(50px); opacity: 0; }
+ }
+
+ .history-item {
+ display: flex; justify-content: space-between;
+ padding: 12px;
+ background: rgba(255,255,255,0.03);
+ border-radius: 8px;
+ font-family: var(--font-code);
+ font-size: 0.9rem;
+ cursor: pointer;
+ transition: 0.2s;
+ align-items: center;
+ }
+ .history-item:hover { background: rgba(255,255,255,0.08); transform: translateX(4px); }
+ .hist-val { color: var(--md-sys-color-on-surface); }
+ .hist-meta { font-size: 0.75rem; color: var(--md-sys-color-outline); margin-right: 10px; }
+
+ /* --- FOOTER --- */
+ footer {
+ margin-top: 40px;
+ text-align: center;
+ padding: 20px;
+ color: var(--md-sys-color-on-surface-variant);
+ font-size: 0.85rem;
+ border-top: 1px solid var(--md-sys-color-outline);
+ width: 100%;
+ background: rgba(0,0,0,0.2);
+ }
+ .footer-content {
+ display: flex;
+ flex-direction: column;
+ gap: 8px;
+ }
+ .footer-links {
+ display: flex;
+ justify-content: center;
+ gap: 15px;
+ margin-top: 5px;
+ }
+ .footer-link {
+ color: var(--md-sys-color-primary);
+ text-decoration: none;
+ transition: 0.2s;
+ }
+ .footer-link:hover { text-decoration: underline; color: var(--md-sys-color-tertiary); }
+
+ /* --- MODAL (LEGACY & CHEATSHEET) --- */
+ .modal-overlay {
+ position: fixed;
+ top: 0; left: 0; width: 100%; height: 100%;
+ background: rgba(0,0,0,0.7);
+ backdrop-filter: blur(4px);
+ z-index: 2000;
+ display: none;
+ justify-content: center;
+ align-items: center;
+ opacity: 0;
+ transition: opacity 0.3s;
+ }
+ .modal-overlay.open { display: flex; opacity: 1; }
+
+ .modal-content {
+ background: var(--md-sys-color-surface-container);
+ width: 90%; max-width: 500px;
+ border-radius: var(--radius-xl);
+ padding: 24px;
+ max-height: 80vh;
+ display: flex;
+ flex-direction: column;
+ box-shadow: 0 10px 30px rgba(0,0,0,0.5);
+ border: 1px solid var(--md-sys-color-outline);
+ transform: scale(0.9);
+ transition: transform 0.3s var(--ease-elastic);
+ }
+ .modal-overlay.open .modal-content { transform: scale(1); }
+
+ .modal-header {
+ display: flex; justify-content: space-between; align-items: center;
+ margin-bottom: 20px;
+ border-bottom: 1px solid var(--md-sys-color-outline);
+ padding-bottom: 12px;
+ flex-shrink: 0;
+ }
+ .modal-header h3 { margin: 0; color: var(--md-sys-color-primary); }
+ .close-modal {
+ background: none; border: none; color: var(--md-sys-color-on-surface);
+ font-size: 1.5rem; cursor: pointer;
+ }
+
+ .modal-body {
+ overflow-y: auto;
+ padding-right: 5px; /* Space for scrollbar */
+ }
+
+ /* History Timeline */
+ .timeline-item {
+ position: relative;
+ padding-left: 20px;
+ border-left: 2px solid var(--md-sys-color-outline);
+ margin-bottom: 20px;
+ }
+ .timeline-item::before {
+ content: ''; position: absolute; left: -6px; top: 0;
+ width: 10px; height: 10px; border-radius: 50%;
+ background: var(--md-sys-color-primary);
+ }
+ .version-tag {
+ font-size: 0.8rem; font-weight: bold; color: var(--md-sys-color-tertiary);
+ margin-bottom: 4px; display: inline-block; background: rgba(255,255,255,0.1);
+ padding: 2px 8px; border-radius: 4px;
+ }
+ .version-tag.current { background: var(--md-sys-color-primary); color: var(--md-sys-color-on-primary); }
+ .timeline-desc { font-size: 0.9rem; color: var(--md-sys-color-on-surface-variant); margin: 0; }
+
+ /* Sidebar (Cheat Sheet) */
+ .sidebar {
+ position: fixed; top: 0; right: -320px;
+ width: 300px; height: 100%;
+ background: var(--md-sys-color-surface-container-high);
+ box-shadow: -5px 0 15px rgba(0,0,0,0.5);
+ padding: 24px;
+ transition: 0.3s ease;
+ z-index: 1000;
+ overflow-y: auto;
+ }
+ .sidebar.open { right: 0; }
+ .sidebar h3 { color: var(--md-sys-color-primary); margin-bottom: 16px; border-bottom: 1px solid rgba(255,255,255,0.1); padding-bottom: 8px; }
+ .cheat-table { width: 100%; border-collapse: collapse; font-family: var(--font-code); font-size: 0.8rem; }
+ .cheat-table th, .cheat-table td { text-align: left; padding: 6px; border-bottom: 1px solid rgba(255,255,255,0.05); }
+ .close-sidebar {
+ position: absolute; top: 10px; right: 10px;
+ background: none; border: none; color: var(--md-sys-color-on-surface);
+ font-size: 1.5rem; cursor: pointer;
+ }
+
+ /* --- RESPONSIVE --- */
+ @media (max-width: 768px) {
+ .converter-stage { grid-template-columns: 1fr; grid-template-rows: 1fr 64px 1fr; gap: 8px; }
+ .material-card { height: 260px; }
+ .swap-container { transform: rotate(90deg); }
+ .swap-fab:hover { transform: rotate(180deg); }
+ .header-top { flex-direction: column; gap: 10px; }
+ .calc-header { flex-direction: column; gap: 10px; }
+ .calc-result-area { margin-left: 0; justify-content: flex-end; width: 100%; }
+ .visualize-btn { margin-right: 0; margin-bottom: 10px; }
+ .calc-actions { flex-direction: column-reverse; align-items: stretch; }
+ }
+ </style>
+</head>
+<body>
+
+ <header class="app-container">
+ <div class="header-top">
+ <div class="logo">
+ <svg width="32" height="32" viewBox="0 0 24 24" fill="var(--md-sys-color-primary)"><path d="M7 2v11h3v9l7-12h-4l4-8z"/></svg>
+ NCT <span>v2.11</span>
+ </div>
+
+ <!-- Animated Navigation -->
+ <div class="nav-tabs">
+ <div class="nav-glider" id="navGlider"></div>
+ <button class="nav-tab active" onclick="switchTab('converter', 0)">Converter</button>
+ <button class="nav-tab" onclick="switchTab('calculator', 1)">Calculator</button>
+ </div>
+
+ <div class="header-actions">
+ <button class="tools-btn" onclick="toggleHistory()">
+ <svg width="18" height="18" viewBox="0 0 24 24" fill="currentColor"><path d="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm1 15h-2v-6h2v6zm0-8h-2V7h2v2z"/></svg>
+ Versions
+ </button>
+ <button class="tools-btn" onclick="toggleSidebar()">
+ <svg width="18" height="18" viewBox="0 0 24 24" fill="currentColor"><path d="M11 18h2v-2h-2v2zm1-16C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm0 18c-4.41 0-8-3.59-8-8s3.59-8 8-8 8 3.59 8 8-3.59 8-8 8zm0-14c-2.21 0-4 1.79-4 4h2c0-1.1.9-2 2-2s2 .9 2 2c0 2-3 1.75-3 5h2c0-2.25 3-2.5 3-5 0-2.21-1.79-4-4-4z"/></svg>
+ Cheat Sheet
+ </button>
+ </div>
+ </div>
+ </header>
+
+ <main class="app-container">
+
+ <!-- SECTION 1: CONVERTER -->
+ <section id="converter-section" class="app-section active">
+
+ <div class="converter-stage">
+ <!-- SOURCE CARD -->
+ <div class="material-card" id="cardSource">
+ <div class="card-header">
+ <span class="input-label">Input</span>
+ <div class="custom-select-wrapper" id="sourceSelectWrapper">
+ <div class="custom-select-trigger">
+ <span class="selection-text">Decimal</span>
+ <span class="arrow">▼</span>
+ </div>
+ <div class="custom-options">
+ <div class="custom-option" data-value="2">Binary</div>
+ <div class="custom-option" data-value="8">Octal</div>
+ <div class="custom-option selected" data-value="10">Decimal</div>
+ <div class="custom-option" data-value="16">Hexadecimal</div>
+ </div>
+ <select id="fromBase" style="display:none">
+ <option value="2">Binary</option>
+ <option value="8">Octal</option>
+ <option value="10" selected>Decimal</option>
+ <option value="16">Hexadecimal</option>
+ </select>
+ </div>
+ </div>
+
+ <textarea id="inputVal" class="input-area" placeholder="0" spellcheck="false"></textarea>
+ <div id="errorChip" class="error-chip">⚠️ Invalid character for this base</div>
+
+ <div class="card-footer">
+ <div class="char-count" id="sourceCount">0 chars</div>
+ <div class="icon-actions">
+ <button class="icon-btn" onclick="clearAll()" title="Clear">
+ <svg width="20" height="20" viewBox="0 0 24 24" fill="currentColor"><path d="M19 6.41L17.59 5 12 10.59 6.41 5 5 6.41 10.59 12 5 17.59 6.41 19 12 13.41 17.59 19 19 17.59 13.41 12z"/></svg>
+ </button>
+ </div>
+ </div>
+ </div>
+
+ <!-- SWAP -->
+ <div class="swap-container">
+ <button class="swap-fab" id="swapBtn" title="Swap (Alt+S)">
+ <svg width="24" height="24" viewBox="0 0 24 24" fill="currentColor"><path d="M6.99 11L3 15l3.99 4v-3H14v-2H6.99v-3zM21 9l-3.99-4v3H10v2h7.01v3L21 9z"/></svg>
+ </button>
+ </div>
+
+ <!-- TARGET CARD -->
+ <div class="material-card" id="cardTarget">
+ <div class="card-header">
+ <span class="input-label">Result</span>
+ <div class="custom-select-wrapper" id="targetSelectWrapper">
+ <div class="custom-select-trigger">
+ <span class="selection-text">Binary</span>
+ <span class="arrow">▼</span>
+ </div>
+ <div class="custom-options">
+ <div class="custom-option selected" data-value="2">Binary</div>
+ <div class="custom-option" data-value="8">Octal</div>
+ <div class="custom-option" data-value="10">Decimal</div>
+ <div class="custom-option" data-value="16">Hexadecimal</div>
+ </div>
+ <select id="toBase" style="display:none">
+ <option value="2" selected>Binary</option>
+ <option value="8">Octal</option>
+ <option value="10">Decimal</option>
+ <option value="16">Hexadecimal</option>
+ </select>
+ </div>
+ </div>
+
+ <textarea id="outputVal" class="input-area" readonly placeholder="..." spellcheck="false"></textarea>
+
+ <div class="card-footer">
+ <div class="char-count">Result</div>
+ <div class="icon-actions">
+ <button class="icon-btn" onclick="speakResult()" title="Speak">
+ <svg width="20" height="20" viewBox="0 0 24 24" fill="currentColor"><path d="M3 9v6h4l5 5V4L7 9H3zm13.5 3c0-1.77-1.02-3.29-2.5-4.03v8.05c1.48-.73 2.5-2.25 2.5-4.02zM14 3.23v2.06c2.89.86 5 3.54 5 6.71s-2.11 5.85-5 6.71v2.06c4.01-.91 7-4.49 7-8.77s-2.99-7.86-7-8.77z"/></svg>
+ </button>
+ <button class="icon-btn" id="copyBtn" onclick="copyToClipboard('outputVal', 'copyBtn')" title="Copy (Ctrl+Enter)">
+ <svg width="20" height="20" viewBox="0 0 24 24" fill="currentColor"><path d="M16 1H4c-1.1 0-2 .9-2 2v14h2V3h12V1zm3 4H8c-1.1 0-2 .9-2 2v14c0 1.1.9 2 2 2h11c1.1 0 2-.9 2-2V7c0-1.1-.9-2-2-2zm0 16H8V7h11v14z"/></svg>
+ </button>
+ </div>
+ </div>
+ </div>
+ </div>
+
+ <!-- STEPS PANEL WITH COPY BUTTON -->
+ <div class="steps-container" id="stepsContainer">
+ <div class="steps-header">
+ <h4 style="color:var(--md-sys-color-primary); margin:0;">Calculation Steps</h4>
+ <button class="icon-btn" id="copyStepsBtn" onclick="copyStepsText()" title="Copy Steps">
+ <svg width="18" height="18" viewBox="0 0 24 24" fill="currentColor"><path d="M16 1H4c-1.1 0-2 .9-2 2v14h2V3h12V1zm3 4H8c-1.1 0-2 .9-2 2v14c0 1.1.9 2 2 2h11c1.1 0 2-.9 2-2V7c0-1.1-.9-2-2-2zm0 16H8V7h11v14z"/></svg>
+ </button>
+ </div>
+ <div class="steps-content" id="stepsOutput">Waiting for input...</div>
+ </div>
+
+ <div class="history-section">
+ <div class="history-header" style="display:flex; justify-content:space-between; align-items:center; margin-bottom:10px;">
+ <h4 style="color:var(--md-sys-color-on-surface-variant); margin:0;">Recent History</h4>
+ <button class="icon-btn" id="clearHistBtn" onclick="clearHistory()" title="Clear History">
+ <svg width="18" height="18" viewBox="0 0 24 24" fill="currentColor"><path d="M6 19c0 1.1.9 2 2 2h8c1.1 0 2-.9 2-2V7H6v12zM19 4h-3.5l-1-1h-5l-1 1H5v2h14V4z"/></svg>
+ </button>
+ </div>
+ <div class="history-list" id="historyList">
+ <div style="padding:10px; color:#555; text-align:center;">No history yet</div>
+ </div>
+ </div>
+
+ </section>
+
+ <!-- SECTION 2: DYNAMIC CALCULATOR -->
+ <section id="calculator-section" class="app-section">
+ <div class="calc-wrapper">
+
+ <div class="calc-header">
+ <div class="calc-ctrl-group">
+ <label>Base Mode</label>
+ <div class="custom-select-wrapper" id="calcBaseWrapper" style="flex:1">
+ <div class="custom-select-trigger">
+ <span class="selection-text">Decimal (Base 10)</span>
+ <span class="arrow">▼</span>
+ </div>
+ <div class="custom-options">
+ <div class="custom-option" data-value="2">Binary (Base 2)</div>
+ <div class="custom-option" data-value="8">Octal (Base 8)</div>
+ <div class="custom-option selected" data-value="10">Decimal (Base 10)</div>
+ <div class="custom-option" data-value="16">Hex (Base 16)</div>
+ </div>
+ <select id="calcBase" style="display:none" onchange="validateAllRows()">
+ <option value="2">Binary</option>
+ <option value="8">Octal</option>
+ <option value="10" selected>Decimal</option>
+ <option value="16">Hex</option>
+ </select>
+ </div>
+ </div>
+ <div class="calc-ctrl-group">
+ <label>Operation</label>
+ <div class="custom-select-wrapper" id="calcOpWrapper" style="flex:1">
+ <div class="custom-select-trigger">
+ <span class="selection-text">Addition (+)</span>
+ <span class="arrow">▼</span>
+ </div>
+ <div class="custom-options">
+ <div class="custom-option selected" data-value="add">Addition (+)</div>
+ <div class="custom-option" data-value="sub">Subtraction (-)</div>
+ <div class="custom-option" data-value="mul">Multiplication (×)</div>
+ <div class="custom-option" data-value="div">Division (÷)</div>
+ </div>
+ <select id="calcOp" style="display:none" onchange="runCalculation()">
+ <option value="add" selected>Addition</option>
+ <option value="sub">Subtraction</option>
+ <option value="mul">Multiplication</option>
+ <option value="div">Division</option>
+ </select>
+ </div>
+ </div>
+ </div>
+
+ <div id="calcRows" class="calc-rows">
+ <!-- Dynamic Rows Injected Here -->
+ </div>
+
+ <!-- ADD ROW BUTTON (Left Aligned) -->
+ <button class="add-row-btn" onclick="addCalcRow()">+ Add Number</button>
+
+ <div class="calc-actions">
+ <button class="visualize-btn" id="vizBtn" onclick="visualizeCalculation()">
+ <svg width="18" height="18" viewBox="0 0 24 24" fill="currentColor"><path d="M12 4.5C7 4.5 2.73 7.61 1 12c1.73 4.39 6 7.5 11 7.5s9.27-3.11 11-7.5c-1.73-4.39-6-7.5-11-7.5zM12 17c-2.76 0-5-2.24-5-5s2.24-5 5-5 5 2.24 5 5-2.24 5-5 5zm0-8c-1.66 0-3 1.34-3 3s1.34 3 3 3 3-1.34 3-3-1.34-3-3-3z"/></svg>
+ Visualize
+ </button>
+
+ <div class="calc-result-area">
+ <div class="calc-result-display">
+ <div class="res-label">Result</div>
+ <div class="res-value" id="calcResult">0</div>
+ </div>
+ </div>
+ </div>
+
+ <!-- BIT-BY-BIT VISUALIZER PANEL -->
+ <div class="calc-visualizer" id="visualizerPanel">
+ <h4 style="color:var(--md-sys-color-primary); margin-bottom:15px; border-bottom:1px solid #333; padding-bottom:10px;">Calculation Steps</h4>
+ <div class="viz-grid" id="vizGrid">
+ <!-- Grid injected here -->
+ </div>
+ <div class="viz-info" id="vizInfo">Click visualize to start...</div>
+ </div>
+
+ </div>
+ </section>
+
+ </main>
+
+ <!-- PROJECT LEGACY MODAL -->
+ <div class="modal-overlay" id="historyModal">
+ <div class="modal-content">
+ <div class="modal-header">
+ <h3>Project Legacy</h3>
+ <button class="close-modal" onclick="toggleHistory()">×</button>
+ </div>
+ <div class="modal-body">
+ <div class="history-timeline">
+ <div class="timeline-item">
+ <div class="version-tag">v1.0 (2024)</div>
+ <p class="timeline-desc">Initial High School Project. Single file, manual conversion logic, animated background.</p>
+ </div>
+ <div class="timeline-item">
+ <div class="version-tag">v2.0</div>
+ <p class="timeline-desc">Modularized Logic & URL State. Moved away from switch cases to a Base-N engine.</p>
+ </div>
+ <div class="timeline-item">
+ <div class="version-tag">v2.1</div>
+ <p class="timeline-desc">Material You UI. Glassmorphism, animated cards, and custom dropdowns.</p>
+ </div>
+ <div class="timeline-item">
+ <div class="version-tag">v2.2</div>
+ <p class="timeline-desc">Dynamic Calculator. Added list-based arithmetic with strict validation.</p>
+ </div>
+ <div class="timeline-item">
+ <div class="version-tag">v2.3 - v2.8</div>
+ <p class="timeline-desc">Visualizer Engine. Bit-by-bit animation, step explanations, and history tape.</p>
+ </div>
+ <div class="timeline-item">
+ <div class="version-tag">v2.10</div>
+ <p class="timeline-desc">Professional Polish. Optimized UX layout, legacy tracking, and performance tuning.</p>
+ </div>
+ <div class="timeline-item">
+ <div class="version-tag current">v2.11 (2026)</div>
+ <p class="timeline-desc">Ultimate Refinement. Enhanced animations, layout stability, and smart controls.</p>
+ </div>
+ </div>
+ </div>
+ </div>
+ </div>
+
+ <!-- SIDEBAR CHEATSHEET -->
+ <aside class="sidebar" id="sidebar">
+ <button class="close-sidebar" onclick="toggleSidebar()">×</button>
+ <h3>Cheat Sheet</h3>
+
+ <h4>Powers of 2</h4>
+ <table class="cheat-table" style="margin-bottom:20px;">
+ <tr><td>2⁰</td><td>1</td></tr>
+ <tr><td>2¹</td><td>2</td></tr>
+ <tr><td>2²</td><td>4</td></tr>
+ <tr><td>2³</td><td>8</td></tr>
+ <tr><td>2⁴</td><td>16</td></tr>
+ <tr><td>2⁵</td><td>32</td></tr>
+ <tr><td>2⁶</td><td>64</td></tr>
+ <tr><td>2⁷</td><td>128</td></tr>
+ <tr><td>2⁸</td><td>256</td></tr>
+ <tr><td>2¹⁰</td><td>1024</td></tr>
+ </table>
+
+ <h4>Hex Lookup</h4>
+ <table class="cheat-table">
+ <tr><th>Dec</th><th>Hex</th><th>Bin</th></tr>
+ <tr><td>0</td><td>0</td><td>0000</td></tr>
+ <tr><td>1</td><td>1</td><td>0001</td></tr>
+ <tr><td>10</td><td>A</td><td>1010</td></tr>
+ <tr><td>11</td><td>B</td><td>1011</td></tr>
+ <tr><td>12</td><td>C</td><td>1100</td></tr>
+ <tr><td>13</td><td>D</td><td>1101</td></tr>
+ <tr><td>14</td><td>E</td><td>1110</td></tr>
+ <tr><td>15</td><td>F</td><td>1111</td></tr>
+ </table>
+ </aside>
+
+ <footer>
+ <div class="footer-content">
+ <p>&copy; 2026 <strong>Amit Dutta</strong>. All rights reserved.</p>
+ <div class="footer-links">
+ <a href="https://amit.is-a.dev" target="_blank" class="footer-link">amit.is-a.dev</a>
+ <span>•</span>
+ <a href="https://github.com/notamitgamer" target="_blank" class="footer-link">GitHub</a>
+ </div>
+ </div>
+ </footer>
+
+ <script>
+ document.addEventListener('DOMContentLoaded', () => {
+ setupCustomDropdown('sourceSelectWrapper', 'fromBase');
+ setupCustomDropdown('targetSelectWrapper', 'toBase');
+ setupCustomDropdown('calcBaseWrapper', 'calcBase');
+ setupCustomDropdown('calcOpWrapper', 'calcOp');
+
+ setupEventListeners();
+
+ // Initialize Calculator
+ addCalcRow();
+ addCalcRow();
+
+ renderHistory();
+
+ try {
+ const params = new URLSearchParams(window.location.search);
+ if(params.has('val')) {
+ document.getElementById('inputVal').value = params.get('val');
+ if(params.has('from')) {
+ const val = params.get('from');
+ document.getElementById('fromBase').value = val;
+ updateDropdownUI('sourceSelectWrapper', val);
+ }
+ if(params.has('to')) {
+ const val = params.get('to');
+ document.getElementById('toBase').value = val;
+ updateDropdownUI('targetSelectWrapper', val);
+ }
+ convert();
+ }
+ } catch(e) {}
+
+ document.addEventListener('click', (e) => {
+ const sidebar = document.getElementById('sidebar');
+ const historyModal = document.getElementById('historyModal');
+ const toolsBtns = document.querySelectorAll('.tools-btn');
+ let clickedBtn = false;
+ toolsBtns.forEach(btn => { if(btn.contains(e.target)) clickedBtn = true; });
+
+ if (!sidebar.contains(e.target) && !clickedBtn && sidebar.classList.contains('open')) {
+ toggleSidebar();
+ }
+ if (e.target === historyModal) {
+ toggleHistory();
+ }
+ });
+ });
+
+ /* --- NAVIGATION --- */
+ function switchTab(tabName, index) {
+ document.querySelectorAll('.app-section').forEach(el => el.classList.remove('active'));
+ document.getElementById(tabName + '-section').classList.add('active');
+
+ document.querySelectorAll('.nav-tab').forEach(el => el.classList.remove('active'));
+ const tabs = document.querySelectorAll('.nav-tab');
+ tabs[index].classList.add('active');
+
+ const glider = document.getElementById('navGlider');
+ glider.style.transform = `translateX(${index * 100}%)`;
+ }
+
+ function toggleSidebar() {
+ document.getElementById('sidebar').classList.toggle('open');
+ }
+
+ function toggleHistory() {
+ const modal = document.getElementById('historyModal');
+ modal.classList.toggle('open');
+ }
+
+ /* --- DROPDOWN LOGIC --- */
+ function setupCustomDropdown(wrapperId, nativeSelectId) {
+ const wrapper = document.getElementById(wrapperId);
+ const nativeSelect = document.getElementById(nativeSelectId);
+ const trigger = wrapper.querySelector('.custom-select-trigger');
+ const triggerText = trigger.querySelector('.selection-text');
+ const options = wrapper.querySelectorAll('.custom-option');
+
+ trigger.addEventListener('click', (e) => {
+ e.stopPropagation();
+ document.querySelectorAll('.custom-select-wrapper').forEach(w => {
+ if(w !== wrapper) w.classList.remove('open');
+ });
+ wrapper.classList.toggle('open');
+ });
+
+ options.forEach(opt => {
+ opt.addEventListener('click', (e) => {
+ e.stopPropagation();
+ wrapper.querySelectorAll('.custom-option').forEach(o => o.classList.remove('selected'));
+ opt.classList.add('selected');
+ triggerText.textContent = opt.textContent;
+ wrapper.classList.remove('open');
+
+ nativeSelect.value = opt.getAttribute('data-value');
+ nativeSelect.dispatchEvent(new Event('change'));
+ });
+ });
+
+ document.addEventListener('click', () => {
+ wrapper.classList.remove('open');
+ });
+ }
+
+ function updateDropdownUI(wrapperId, value) {
+ const wrapper = document.getElementById(wrapperId);
+ const triggerText = wrapper.querySelector('.selection-text');
+ const options = wrapper.querySelectorAll('.custom-option');
+
+ options.forEach(opt => {
+ if(opt.getAttribute('data-value') === value) {
+ opt.classList.add('selected');
+ triggerText.textContent = opt.textContent;
+ } else {
+ opt.classList.remove('selected');
+ }
+ });
+ }
+
+ /* --- CONVERTER LOGIC --- */
+ function convert() {
+ const inputVal = document.getElementById('inputVal').value;
+ const outputEl = document.getElementById('outputVal');
+ const errorChip = document.getElementById('errorChip');
+ const fromBase = parseInt(document.getElementById('fromBase').value);
+ const toBase = parseInt(document.getElementById('toBase').value);
+
+ document.getElementById('sourceCount').textContent = `${inputVal.length} chars`;
+ errorChip.classList.remove('visible');
+
+ if(!inputVal) {
+ outputEl.value = '';
+ document.getElementById('stepsContainer').classList.remove('visible');
+ updateURL('');
+ return;
+ }
+
+ const normalized = inputVal.trim().toUpperCase();
+ const validChars = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ.".substring(0, fromBase) + ".";
+ const regex = new RegExp(`^[${validChars}]+$`);
+
+ if(!regex.test(normalized)) {
+ errorChip.classList.add('visible');
+ outputEl.value = "Error";
+ document.getElementById('stepsContainer').classList.remove('visible');
+ return;
+ }
+
+ try {
+ updateURL(normalized, fromBase, toBase);
+
+ const parts = normalized.split('.');
+ const decInt = parseInt(parts[0], fromBase);
+ let decTotal = decInt;
+
+ const result = decTotal.toString(toBase).toUpperCase();
+ outputEl.value = result;
+
+ generateSteps(normalized, fromBase, toBase, decTotal, result);
+ addToHistory(normalized, result, fromBase, toBase);
+
+ } catch(e) {
+ outputEl.value = "Error";
+ }
+ }
+
+ function generateSteps(input, from, to, decimal, result) {
+ const container = document.getElementById('stepsContainer');
+ const output = document.getElementById('stepsOutput');
+ container.classList.add('visible');
+
+ let log = `Converting ${input} (Base ${from}) to Base ${to}\n`;
+ log += `------------------------------------------\n`;
+
+ if(from !== 10) {
+ log += `1. Convert to Decimal:\n `;
+ const digits = input.split('.')[0].split('').reverse();
+ digits.forEach((d, i) => {
+ log += `${d}×${from}^${i} + `;
+ });
+ log = log.slice(0, -3); // Remove last +
+ log += ` = ${decimal}\n\n`;
+ } else {
+ log += `1. Already Decimal: ${decimal}\n\n`;
+ }
+
+ if(to !== 10) {
+ log += `2. Convert Decimal ${decimal} to Base ${to}:\n`;
+ let temp = decimal;
+ while(temp > 0) {
+ let rem = temp % to;
+ let quot = Math.floor(temp / to);
+ log += ` ${temp} ÷ ${to} = ${quot} [Rem: ${rem.toString(to).toUpperCase()}]\n`;
+ temp = quot;
+ }
+ log += ` Read remainders bottom-up: ${result}`;
+ } else {
+ log += `2. Result is Decimal: ${result}`;
+ }
+ output.textContent = log;
+ }
+
+ function updateURL(val, from, to) {
+ try {
+ const url = new URL(window.location);
+ if(val) {
+ url.searchParams.set('val', val);
+ url.searchParams.set('from', from);
+ url.searchParams.set('to', to);
+ window.history.replaceState({}, '', url);
+ } else {
+ const cleanUrl = window.location.pathname;
+ window.history.replaceState({}, '', cleanUrl);
+ }
+ } catch(e) {}
+ }
+
+ /* --- HISTORY LOGIC --- */
+ function addToHistory(inVal, outVal, from, to) {
+ const key = "nct_history";
+ let history = JSON.parse(localStorage.getItem(key) || "[]");
+ if(history.length > 0 && history[0].inVal === inVal && history[0].to === to) return;
+ history.unshift({ inVal, outVal, from, to });
+ if(history.length > 10) history.pop();
+ localStorage.setItem(key, JSON.stringify(history));
+ renderHistory();
+ }
+
+ function renderHistory() {
+ const list = document.getElementById('historyList');
+ const history = JSON.parse(localStorage.getItem("nct_history") || "[]");
+ if(history.length === 0) {
+ list.innerHTML = '<div style="padding:10px; color:#555; text-align:center;">No history yet</div>';
+ return;
+ }
+ list.innerHTML = '';
+ history.forEach(item => {
+ const div = document.createElement('div');
+ div.className = 'history-item';
+ div.innerHTML = `<div><span class="hist-meta">Base ${item.from} → ${item.to}</span><span class="hist-val">${item.inVal} = ${item.outVal}</span></div>`;
+ div.onclick = () => {
+ document.getElementById('inputVal').value = item.inVal;
+ document.getElementById('fromBase').value = item.from;
+ document.getElementById('toBase').value = item.to;
+ updateDropdownUI('sourceSelectWrapper', item.from.toString());
+ updateDropdownUI('targetSelectWrapper', item.to.toString());
+ convert();
+ };
+ list.appendChild(div);
+ });
+ }
+
+ function clearHistory() {
+ const list = document.getElementById('historyList');
+ const btn = document.getElementById('clearHistBtn');
+
+ if(list.children.length === 0 || list.innerText.includes("No history")) return;
+
+ // Trigger Shake Animation on Button
+ btn.classList.add('shaking');
+
+ // Trigger Slide Out Animation on List
+ list.classList.add('clearing');
+
+ setTimeout(() => {
+ localStorage.removeItem("nct_history");
+ renderHistory();
+ list.classList.remove('clearing');
+ btn.classList.remove('shaking');
+ }, 400); // Duration matches CSS animation
+ }
+
+ /* --- DYNAMIC CALCULATOR LOGIC --- */
+ function addCalcRow() {
+ const container = document.getElementById('calcRows');
+ const div = document.createElement('div');
+ div.className = 'calc-row';
+ div.innerHTML = `
+ <div class="calc-input-wrapper">
+ <input type="text" class="calc-input" placeholder="Enter number..." oninput="validateRow(this); runCalculation()">
+ </div>
+ <button class="calc-remove-btn" onclick="removeRow(this)" title="Remove">×</button>
+ `;
+ container.appendChild(div);
+ div.querySelector('input').focus();
+ }
+
+ function removeRow(btn) {
+ const row = btn.parentElement;
+ if(document.querySelectorAll('.calc-row').length > 1) {
+ row.classList.add('removing');
+ setTimeout(() => {
+ row.remove();
+ runCalculation();
+ }, 300);
+ }
+ }
+
+ function validateRow(input) {
+ const base = parseInt(document.getElementById('calcBase').value);
+ const val = input.value.trim().toUpperCase();
+ if(!val) {
+ input.classList.remove('invalid');
+ return true;
+ }
+ const validChars = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ.".substring(0, base) + ".";
+ const regex = new RegExp(`^[${validChars}]+$`);
+ if(!regex.test(val)) {
+ input.classList.add('invalid');
+ return false;
+ } else {
+ input.classList.remove('invalid');
+ return true;
+ }
+ }
+
+ function validateAllRows() {
+ document.querySelectorAll('.calc-input').forEach(input => validateRow(input));
+ runCalculation();
+ }
+
+ function runCalculation() {
+ const base = parseInt(document.getElementById('calcBase').value);
+ const op = document.getElementById('calcOp').value;
+ const inputs = document.querySelectorAll('.calc-input');
+ const vizBtn = document.getElementById('vizBtn');
+ let total = null;
+ let error = false;
+
+ inputs.forEach(input => {
+ if(input.classList.contains('invalid')) { error = true; return; }
+ const valStr = input.value.trim().toUpperCase();
+ if(!valStr) return;
+ const val = parseInt(valStr, base);
+ if(isNaN(val)) return;
+ if(total === null) { total = val; } else {
+ if(op === 'add') total += val;
+ if(op === 'sub') total -= val;
+ if(op === 'mul') total *= val;
+ if(op === 'div') total = Math.floor(total / val);
+ }
+ });
+
+ const resEl = document.getElementById('calcResult');
+ if(error) {
+ resEl.textContent = "Invalid Input";
+ resEl.style.color = "var(--md-sys-color-error)";
+ vizBtn.style.display = 'none';
+ } else if(total === null) {
+ resEl.textContent = "0";
+ resEl.style.color = "var(--md-sys-color-primary)";
+ vizBtn.style.display = 'none';
+ } else {
+ resEl.textContent = total.toString(base).toUpperCase();
+ resEl.style.color = "var(--md-sys-color-primary)";
+ vizBtn.style.display = (op === 'add') ? 'flex' : 'none';
+ }
+ document.getElementById('visualizerPanel').classList.remove('active');
+ }
+
+ /* --- VISUALIZER LOGIC (Multi-Base) --- */
+ function visualizeCalculation() {
+ const base = parseInt(document.getElementById('calcBase').value);
+ const op = document.getElementById('calcOp').value;
+ const panel = document.getElementById('visualizerPanel');
+ const grid = document.getElementById('vizGrid');
+ const info = document.getElementById('vizInfo');
+
+ if(op !== 'add') return;
+
+ const inputs = [];
+ let isValid = true;
+ document.querySelectorAll('.calc-input').forEach(inp => {
+ if(inp.value.trim()) {
+ const val = parseInt(inp.value.trim(), base);
+ if(isNaN(val)) isValid = false;
+ inputs.push(val);
+ }
+ });
+
+ if(!isValid || inputs.length < 2) {
+ alert("Enter at least two numbers.");
+ return;
+ }
+
+ panel.classList.add('active');
+ grid.innerHTML = '';
+
+ const digitArrays = inputs.map(n => n.toString(base).toUpperCase().split(''));
+ const maxLen = Math.max(...digitArrays.map(arr => arr.length)) + 1;
+
+ const paddedArrays = digitArrays.map(arr => {
+ const padding = new Array(maxLen - arr.length).fill('0');
+ return padding.concat(arr);
+ });
+
+ grid.style.gridTemplateColumns = `repeat(${maxLen}, 1fr)`;
+
+ for(let i=0; i<maxLen; i++) {
+ const cell = document.createElement('div');
+ cell.className = 'viz-cell carry';
+ cell.id = `carry-${i}`;
+ grid.appendChild(cell);
+ }
+
+ paddedArrays.forEach((rowDigits, rIdx) => {
+ for(let i=0; i<maxLen; i++) {
+ const cell = document.createElement('div');
+ cell.className = 'viz-cell';
+ if(rIdx === paddedArrays.length -1) cell.classList.add('border-bottom');
+ const val = rowDigits[i];
+ cell.textContent = val;
+ if(i === 0 && rIdx > 0) {
+ cell.classList.add('operator');
+ cell.innerHTML = `+ ${val}`;
+ }
+ grid.appendChild(cell);
+ }
+ });
+
+ for(let i=0; i<maxLen; i++) {
+ const cell = document.createElement('div');
+ cell.className = 'viz-cell result';
+ cell.id = `res-${i}`;
+ cell.textContent = '?';
+ grid.appendChild(cell);
+ }
+
+ let carry = 0;
+ let colIdx = maxLen - 1;
+
+ function step() {
+ if(colIdx < 0) {
+ info.textContent = "Complete.";
+ return;
+ }
+ document.querySelectorAll('.active-col').forEach(el => el.classList.remove('active-col'));
+ const resCell = document.getElementById(`res-${colIdx}`);
+ resCell.classList.add('active-col');
+
+ let sum = carry;
+ const digitVals = [];
+ paddedArrays.forEach(row => {
+ const char = row[colIdx];
+ const val = parseInt(char, base);
+ sum += val;
+ digitVals.push(val.toString(base).toUpperCase());
+ });
+
+ const writeVal = sum % base;
+ const newCarry = Math.floor(sum / base);
+
+ resCell.textContent = writeVal.toString(base).toUpperCase();
+ if(newCarry > 0 && colIdx > 0) {
+ document.getElementById(`carry-${colIdx-1}`).textContent = newCarry.toString(base).toUpperCase();
+ }
+
+ const eqStr = `${digitVals.join('+')} + ${carry}(c) = ${sum}`;
+ const writeStr = writeVal.toString(base).toUpperCase();
+ const carryStr = newCarry.toString(base).toUpperCase();
+ info.textContent = `Col ${maxLen - colIdx}: ${eqStr} (Base ${base}). Write ${writeStr}, Carry ${carryStr}`;
+
+ carry = newCarry;
+ colIdx--;
+ setTimeout(step, 800);
+ }
+ step();
+ }
+
+ /* --- UTILS & SWAP --- */
+ function setupEventListeners() {
+ document.addEventListener('keydown', (e) => {
+ if(e.ctrlKey && e.key === 'Enter') copyToClipboard('outputVal', 'copyBtn');
+ if(e.altKey && e.key === 's') document.getElementById('swapBtn').click();
+ });
+
+ document.getElementById('inputVal').addEventListener('input', convert);
+ document.getElementById('fromBase').addEventListener('change', convert);
+ document.getElementById('toBase').addEventListener('change', convert);
+
+ document.getElementById('swapBtn').addEventListener('click', () => {
+ const cardSource = document.getElementById('cardSource');
+ const cardTarget = document.getElementById('cardTarget');
+ const isMobile = window.innerWidth <= 768;
+
+ if(isMobile) {
+ cardSource.classList.add('anim-move-down');
+ cardTarget.classList.add('anim-move-up');
+ } else {
+ cardSource.classList.add('anim-move-right');
+ cardTarget.classList.add('anim-move-left');
+ }
+
+ setTimeout(() => {
+ const inEl = document.getElementById('inputVal');
+ const outEl = document.getElementById('outputVal');
+ const from = document.getElementById('fromBase');
+ const to = document.getElementById('toBase');
+
+ const tempVal = inEl.value;
+ inEl.value = outEl.value;
+ const tempBase = from.value;
+ from.value = to.value;
+ to.value = tempBase;
+
+ updateDropdownUI('sourceSelectWrapper', from.value);
+ updateDropdownUI('targetSelectWrapper', to.value);
+
+ cardSource.classList.remove('anim-move-down', 'anim-move-right');
+ cardTarget.classList.remove('anim-move-up', 'anim-move-left');
+ convert();
+ }, 400);
+ });
+ }
+
+ function speakResult() {
+ const txt = document.getElementById('outputVal').value;
+ if(!txt) return;
+ let spoken = txt;
+ if(document.getElementById('toBase').value == 2) {
+ spoken = txt.split('').join(' ');
+ }
+ const utt = new SpeechSynthesisUtterance(spoken);
+ window.speechSynthesis.speak(utt);
+ }
+
+ function copyToClipboard(id, btnId) {
+ const el = document.getElementById(id);
+ if(el) {
+ el.select();
+ document.execCommand('copy');
+ }
+ const btn = document.getElementById(btnId);
+ if(btn) {
+ const originalHTML = btn.innerHTML;
+ btn.innerHTML = `<svg width="20" height="20" viewBox="0 0 24 24" fill="var(--md-sys-color-success)"><path d="M9 16.17L4.83 12l-1.42 1.41L9 19 21 7l-1.41-1.41z"/></svg>`;
+ btn.classList.add('success');
+ setTimeout(() => {
+ btn.innerHTML = originalHTML;
+ btn.classList.remove('success');
+ }, 2000);
+ }
+ }
+
+ function copyStepsText() {
+ const stepsText = document.getElementById('stepsOutput').innerText;
+ navigator.clipboard.writeText(stepsText).then(() => {
+ const btn = document.getElementById('copyStepsBtn');
+ const originalHTML = btn.innerHTML;
+ btn.innerHTML = `<svg width="18" height="18" viewBox="0 0 24 24" fill="var(--md-sys-color-success)"><path d="M9 16.17L4.83 12l-1.42 1.41L9 19 21 7l-1.41-1.41z"/></svg>`;
+ btn.classList.add('success');
+ setTimeout(() => {
+ btn.innerHTML = originalHTML;
+ btn.classList.remove('success');
+ }, 2000);
+ });
+ }
+
+ function clearAll() {
+ document.getElementById('inputVal').value = '';
+ document.getElementById('outputVal').value = '';
+ document.getElementById('sourceCount').textContent = '0 chars';
+ document.getElementById('errorChip').classList.remove('visible');
+ document.getElementById('stepsContainer').classList.remove('visible');
+ updateURL('');
+ }
+ </script>
+</body>
+</html></pre>
+ </div>
+ </div>
+
+ <div id="download-tab-content" class="tab-content">
+ <div class="download-item">
+ <div class="download-info">
+ <h3>nct-legacy.html</h3>
+ <p>Standalone production code of the Number Conversion Tool.</p>
+ </div>
+ <!-- Force download via download attribute -->
+ <a href="nct-legacy.html" download="nct-legacy.html" class="download-button">Download File</a>
+ </div>
+ <div class="download-item">
+ <div class="download-info">
+ <h3>GitHub Repository</h3>
+ <p>View source and contribute on GitHub.</p>
+ </div>
+ <a href="https://github.com/notamitgamer/nct-legacy" target="_blank" class="download-button" style="background-color: #333;">GitHub</a>
+ </div>
+ </div>
+ </div>
+
+ <script>
+ function switchTab(evt, tabId) {
+ // Hide all contents
+ document.querySelectorAll('.tab-content').forEach(content => {
+ content.classList.remove('active');
+ });
+ // Deactivate all buttons
+ document.querySelectorAll('.tab-button').forEach(button => {
+ button.classList.remove('active');
+ });
+
+ // Show current
+ document.getElementById(tabId + '-tab-content').classList.add('active');
+ evt.currentTarget.classList.add('active');
+ }
+
+ async function copyToClipboard(btn) {
+ const code = document.getElementById('sourceCode').innerText;
+ try {
+ await navigator.clipboard.writeText(code);
+ const originalHTML = btn.innerHTML;
+ btn.innerHTML = '<span>✓ Copied!</span>';
+ btn.classList.add('success');
+
+ setTimeout(() => {
+ btn.innerHTML = originalHTML;
+ btn.classList.remove('success');
+ }, 2000);
+ } catch (err) {
+ console.error('Failed to copy code: ', err);
+ }
+ }
+ </script>
+
+</body>
+</html>+
\ No newline at end of file
diff --git a/codenct.html b/codenct.html
@@ -0,0 +1,1099 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+ <meta charset="UTF-8">
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
+ <title>NCT Code</title>
+ <!-- Link to Cascadia Code font from CDN -->
+ <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@xz/fonts@1/serve/cascadia-code.min.css">
+ <style>
+ /* Base styling for the entire page */
+ body {
+ background-color: black; /* Black background for the entire page */
+ margin: 0;
+ padding: 20px;
+ /* Prioritize Cascadia Code, fall back to other monospaced fonts */
+ font-family: 'Cascadia Code', 'Fira Code', 'Roboto Mono', monospace;
+ color: white; /* Default text color for the page */
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ min-height: 100vh;
+ }
+
+ h1 {
+ color: #f0f0f0; /* White for the main heading */
+ text-align: center;
+ margin-bottom: 20px;
+ }
+
+ /* Container for the tabs */
+ .tab-container {
+ background-color: #1a1a1a;
+ color: #f0f0f0;
+ padding: 15px;
+ border-radius: 8px;
+ width: 100%;
+ max-width: 900px;
+ box-shadow: 0 4px 12px rgba(0, 0, 0, 0.2);
+ position: relative;
+ overflow: hidden;
+ }
+
+ /* Tab buttons styling */
+ .tabs {
+ display: flex;
+ justify-content: center;
+ margin-bottom: 20px;
+ border-bottom: 1px solid #333;
+ }
+
+ .tab-button {
+ background-color: transparent;
+ border: none;
+ padding: 10px 20px;
+ font-size: 1.2em;
+ color: #f0f0f0;
+ cursor: pointer;
+ transition: color 0.3s ease, background-color 0.3s ease, transform 0.3s ease;
+ position: relative;
+ overflow: hidden;
+ border-radius: 5px 5px 0 0;
+ margin: 0 5px;
+ }
+
+ .tab-button:hover {
+ color: #1abc9c;
+ transform: translateY(-2px);
+ }
+
+ .tab-button.active {
+ color: #1abc9c;
+ font-weight: bold;
+ background-color: #333;
+ transform: translateY(-2px);
+ }
+
+ .tab-button.active::after {
+ content: '';
+ position: absolute;
+ bottom: 0;
+ left: 0;
+ width: 100%;
+ height: 3px;
+ background-color: #1abc9c;
+ animation: tabUnderline 0.3s forwards;
+ }
+
+ @keyframes tabUnderline {
+ from { width: 0; }
+ to { width: 100%; }
+ }
+
+ /* Tab content styling */
+ .tab-content {
+ display: none;
+ padding: 15px 0;
+ animation: fadeIn 0.5s ease-in-out;
+ }
+
+ .tab-content.active {
+ display: block;
+ }
+
+ @keyframes fadeIn {
+ from { opacity: 0; transform: translateY(10px); }
+ to { opacity: 1; transform: translateY(0); }
+ }
+
+ /* Styling for the code block container */
+ .code-block {
+ background-color: #1a1a1a; /* Slightly lighter black for the code block background */
+ color: #f0f0f0; /* Off-white for general code text */
+ padding: 15px;
+ border-radius: 8px;
+ overflow-x: auto; /* Enable horizontal scrolling for long lines */
+ font-size: 0.95em;
+ line-height: 1.5;
+ tab-size: 4; /* Adjust tab width */
+ -moz-tab-size: 4; /* Firefox specific */
+ white-space: pre-wrap; /* Preserve whitespace and wrap long lines */
+ word-wrap: break-word; /* Break long words */
+ border: 1px solid #333; /* Subtle border */
+ }
+
+ /* Download section styling */
+ .download-item {
+ background-color: #2a2a2a;
+ padding: 15px;
+ border-radius: 8px;
+ margin-bottom: 15px;
+ display: flex;
+ flex-direction: column;
+ align-items: flex-start;
+ border: 1px solid #333;
+ }
+
+ .download-item h3 {
+ color: #1abc9c;
+ margin-bottom: 5px;
+ }
+
+ .download-item p {
+ font-size: 0.9em;
+ color: #ccc;
+ margin-bottom: 10px;
+ }
+
+ .download-button {
+ background-color: #007bff;
+ color: white;
+ border: none;
+ padding: 10px 15px;
+ border-radius: 5px;
+ cursor: pointer;
+ transition: background-color 0.3s ease, transform 0.2s ease;
+ }
+
+ .download-button:hover {
+ background-color: #0056b3;
+ transform: translateY(-1px);
+ }
+
+ .download-button.thank-you {
+ background-color: #28a745;
+ cursor: default;
+ }
+
+ /* Syntax Highlighting Styles (from original code) */
+ .tag { color: #569cd6; }
+ .attribute { color: #9cd656; }
+ .string { color: #d69d66; }
+ .selector { color: #dcdcaa; }
+ .property { color: #4ec9b0; }
+ .value { color: #ce9178; }
+ .comment { color: #6a9955; font-style: italic; }
+ .keyword { color: #c586c0; }
+ .js-identifier { color: #9cdcfe; }
+ .number { color: #b5cea8; }
+ .operator { color: #ff80ed; }
+ .punctuation { color: #f0f0f0; }
+ .highlight-h1 { color: #e65555; font-weight: bold; }
+ .highlight-u { color: #e6e655; font-weight: bold; text-decoration: underline; }
+
+ /* Original NCT app styles (modified to fit new structure) */
+ .logo {
+ font-size: 3em;
+ font-family: 'Roboto';
+ color: #fff;
+ text-shadow: 1px 1px 5px rgba(0, 0, 0, 0.4);
+ margin-bottom: 10px;
+ animation: logoColorChange 3s ease infinite;
+ }
+
+ @keyframes logoColorChange {
+ 0% { color: #FF416C; }
+ 33% { color: #4285F4; }
+ 66% { color: #34A853; }
+ 100% { color: #FF416C; }
+ }
+
+ .container { /* This will now be the main content area for the NCT app, not the whole page */
+ background: #fff;
+ width: 100%;
+ max-width: 900px;
+ padding: 20px;
+ border-radius: 12px;
+ box-shadow: 0 4px 12px rgba(0, 0, 0, 0.2);
+ text-align: center;
+ position: relative;
+ overflow: hidden;
+ margin-top: 20px; /* Added margin to separate from tabs */
+ }
+
+ /* Other NCT app styles from original code */
+ .popup-overlay {
+ position: fixed;
+ top: 0;
+ left: 0;
+ width: 100%;
+ height: 100%;
+ background: rgba(0, 0, 0, 0.8);
+ color: white;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ z-index: 1000;
+ }
+
+ .popup-box {
+ text-align: center;
+ background: #333;
+ padding: 20px;
+ border-radius: 10px;
+ max-width: 90%;
+ width: 400px;
+ box-shadow: 0 4px 8px rgba(0, 0, 0, 0.3);
+ }
+
+ .popup-box h2 { margin-bottom: 15px; }
+ .popup-box button {
+ margin-top: 15px;
+ padding: 10px 20px;
+ border: none;
+ background-color: #007bff;
+ color: white;
+ border-radius: 5px;
+ cursor: pointer;
+ }
+ .popup-box button:hover { background-color: #0056b3; }
+
+ .menu {
+ margin-bottom: 20px;
+ overflow-x: auto;
+ white-space: nowrap;
+ -webkit-overflow-scrolling: touch;
+ }
+
+ .menu ul {
+ list-style: none;
+ display: flex;
+ padding: 0;
+ gap: 10px;
+ }
+
+ .menu li {
+ font-size: 1.2em;
+ padding: 10px 15px;
+ background: linear-gradient(135deg, #34495e, #2c3e50);
+ color: #fff;
+ cursor: pointer;
+ border-radius: 8px;
+ transition: transform 0.3s ease, background 0.3s ease;
+ }
+
+ .menu li:hover {
+ background: linear-gradient(135deg, #1abc9c, #16a085);
+ transform: scale(1.05);
+ }
+
+ .input-section { margin-bottom: 20px; }
+ .input-section label {
+ font-size: 1.1em;
+ margin-bottom: 10px;
+ display: block;
+ color: #34495e;
+ }
+
+ .input-section input {
+ width: 100%;
+ padding: 12px;
+ font-size: 1.2em;
+ border: 1px solid #ccc;
+ border-radius: 8px;
+ margin-bottom: 20px;
+ box-sizing: border-box;
+ transition: box-shadow 0.3s ease;
+ }
+
+ .input-section input:focus {
+ outline: none;
+ border-color: #1abc9c;
+ box-shadow: 0 4px 8px rgba(26, 188, 156, 0.4);
+ }
+
+ .actions button {
+ padding: 12px 20px;
+ font-size: 1.2em;
+ background: linear-gradient(135deg, #1abc9c, #16a085);
+ color: white;
+ border: none;
+ border-radius: 8px;
+ cursor: pointer;
+ transition: background 0.3s ease, transform 0.2s ease;
+ }
+
+ .actions button:hover {
+ background: linear-gradient(135deg, #16a085, #1abc9c);
+ transform: scale(1.05);
+ }
+ .support button {
+ padding: 12px 20px;
+ font-size: 1.2em;
+ background: linear-gradient(135deg, #1abc9c, #16a100);
+ color: white;
+ border: none;
+ border-radius: 8px;
+ cursor: pointer;
+ transition: background 0.3s ease, transform 0.2s ease;
+ }
+
+ .support button:hover {
+ background: linear-gradient(135deg, #16a100, #1abc9c);
+ transform: scale(1.05);
+ }
+
+ .output {
+ margin-top: 30px;
+ text-align: left;
+ }
+
+ .output h3 {
+ font-size: 1.3em;
+ color: #34495e;
+ margin-bottom: 10px;
+ }
+
+ #stepsOutput {
+ background: #ecf0f1;
+ padding: 15px;
+ border-radius: 8px;
+ font-family: 'Courier New', Courier, monospace;
+ color: #7f8c8d;
+ white-space: pre-wrap;
+ word-wrap: break-word;
+ overflow-x: auto;
+ box-shadow: inset 0 2px 4px rgba(0, 0, 0, 0.1);
+ margin-bottom: 20px;
+ }
+
+ #resultOutput {
+ font-size: 1.4em;
+ font-weight: bold;
+ color: #34495e;
+ background: #f9fafb;
+ padding: 15px;
+ border-radius: 8px;
+ border: 1px solid #ddd;
+ box-shadow: 0 2px 6px rgba(0, 0, 0, 0.1);
+ }
+ #otp-modal {
+ display: none;
+ position: fixed;
+ top: 0;
+ left: 0;
+ width: 100%;
+ height: 100%;
+ background: rgba(0, 0, 0, 0.7);
+ z-index: 1000;
+ }
+ #otp-modal div {
+ margin: 10% auto;
+ padding: 20px;
+ background: white;
+ width: 80%;
+ max-width: 400px;
+ text-align: center;
+ border-radius: 8px;
+ }
+
+ </style>
+</head>
+<body>
+
+ <h1>--- Code NCT ---</h1>
+
+ <div class="tab-container">
+ <div class="tabs">
+ <button class="tab-button active" data-tab="code">Code</button>
+ <button class="tab-button" data-tab="download">Download</button>
+ </div>
+
+ <div id="code-tab-content" class="tab-content active">
+
+ <br><center>This code might have error. Please download the perfect code from the download tab</center><br>
+
+ <pre class="code-block">
+<!DOCTYPE html>
+<html lang="en">
+<head>
+ <meta charset="UTF-8">
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
+ <title>Conversion Tool</title>
+ <style>
+ /* Full-screen popup overlay styling */
+ .popup-overlay {
+ position: fixed;
+ top: 0;
+ left: 0;
+ width: 100%;
+ height: 100%;
+ background: rgba(0, 0, 0, 0.8);
+ color: white;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ z-index: 1000;
+ }
+
+ .popup-box {
+ text-align: center;
+ background: #333;
+ padding: 20px;
+ border-radius: 10px;
+ max-width: 90%;
+ width: 400px;
+ box-shadow: 0 4px 8px rgba(0, 0, 0, 0.3);
+ }
+
+ .popup-box h2 {
+ margin-bottom: 15px;
+ }
+
+ .popup-box button {
+ margin-top: 15px;
+ padding: 10px 20px;
+ border: none;
+ background-color: #007bff;
+ color: white;
+ border-radius: 5px;
+ cursor: pointer;
+ }
+
+ .popup-box button:hover {
+ background-color: #0056b3;
+ }
+ /* Global reset for all elements */
+ * {
+ margin: 0;
+ padding: 0;
+ box-sizing: border-box;
+ }
+
+ /* Body styling */
+ body {
+ font-family: 'Arial', sans-serif;
+ background: linear-gradient(45deg, #FF416C, #FF4B2B, #4285F4, #34A853, #FBBC05, #FF416C, #FF416C, #FF4B2B, #4285F4, #34A853, #FBBC05, #FF416C);
+ background-size: 300% 300%;
+ /* Larger background to enable smooth transition */
+ animation: gradientAnimation 15s infinite;
+ color: #333;
+ display: flex;
+ justify-content: center;
+ align-items: flex-start;
+ min-height: 100vh;
+ flex-direction: column;
+ padding: 10px;
+ }
+ @keyframes gradientAnimation {
+ 0% {
+ background-position: 0% 50%;
+ }
+ 50% {
+ background-position: 100% 50%;
+ }
+ 100% {
+ background-position: 0% 50%;
+ }
+ }
+ /* Main container */
+ .container {
+ background: #fff;
+ width: 100%;
+ max-width: 900px;
+ padding: 20px;
+ border-radius: 12px;
+ box-shadow: 0 4px 12px rgba(0, 0, 0, 0.2);
+ text-align: center;
+ position: relative;
+ overflow: hidden;
+ }
+
+ /* Header styling */
+ h1 {
+ font-size: 2.5em;
+ color: #34495e;
+ margin-bottom: 20px;
+ font-weight: bold;
+ text-shadow: 2px 2px 4px rgba(0, 0, 0, 0.1);
+ }
+
+ /* Swiping menu */
+ .menu {
+ margin-bottom: 20px;
+ overflow-x: auto;
+ white-space: nowrap;
+ -webkit-overflow-scrolling: touch;
+ }
+
+ .menu ul {
+ list-style: none;
+ display: flex;
+ padding: 0;
+ gap: 10px;
+ }
+
+ .menu li {
+ font-size: 1.2em;
+ padding: 10px 15px;
+ background: linear-gradient(135deg, #34495e, #2c3e50);
+ color: #fff;
+ cursor: pointer;
+ border-radius: 8px;
+ transition: transform 0.3s ease, background 0.3s ease;
+ }
+
+ .menu li:hover {
+ background: linear-gradient(135deg, #1abc9c, #16a085);
+ transform: scale(1.05);
+ }
+
+ /* Input section */
+ .input-section {
+ margin-bottom: 20px;
+ }
+
+ .input-section label {
+ font-size: 1.1em;
+ margin-bottom: 10px;
+ display: block;
+ color: #34495e;
+ }
+
+ .input-section input {
+ width: 100%;
+ padding: 12px;
+ font-size: 1.2em;
+ border: 1px solid #ccc;
+ border-radius: 8px;
+ margin-bottom: 20px;
+ box-sizing: border-box;
+ transition: box-shadow 0.3s ease;
+ }
+
+ .input-section input:focus {
+ outline: none;
+ border-color: #1abc9c;
+ box-shadow: 0 4px 8px rgba(26, 188, 156, 0.4);
+ }
+
+ /* Buttons */
+ .actions button {
+ padding: 12px 20px;
+ font-size: 1.2em;
+ background: linear-gradient(135deg, #1abc9c, #16a085);
+ color: white;
+ border: none;
+ border-radius: 8px;
+ cursor: pointer;
+ transition: background 0.3s ease, transform 0.2s ease;
+ }
+
+ .actions button:hover {
+ background: linear-gradient(135deg, #16a085, #1abc9c);
+ transform: scale(1.05);
+ }
+ .support button {
+ padding: 12px 20px;
+ font-size: 1.2em;
+ background: linear-gradient(135deg, #1abc9c, #16a100);
+ color: white;
+ border: none;
+ border-radius: 8px;
+ cursor: pointer;
+ transition: background 0.3s ease, transform 0.2s ease;
+ }
+
+ .support button:hover {
+ background: linear-gradient(135deg, #16a100, #1abc9c);
+ transform: scale(1.05);
+ }
+
+ /* Output sections */
+ .output {
+ margin-top: 30px;
+ text-align: left;
+ }
+
+ .output h3 {
+ font-size: 1.3em;
+ color: #34495e;
+ margin-bottom: 10px;
+ }
+
+ #stepsOutput {
+ background: #ecf0f1;
+ padding: 15px;
+ border-radius: 8px;
+ font-family: 'Courier New', Courier, monospace;
+ color: #7f8c8d;
+ white-space: pre-wrap;
+ word-wrap: break-word;
+ overflow-x: auto;
+ box-shadow: inset 0 2px 4px rgba(0, 0, 0, 0.1);
+ margin-bottom: 20px;
+ }
+
+ #resultOutput {
+ font-size: 1.4em;
+ font-weight: bold;
+ color: #34495e;
+ background: #f9fafb;
+ padding: 15px;
+ border-radius: 8px;
+ border: 1px solid #ddd;
+ box-shadow: 0 2px 6px rgba(0, 0, 0, 0.1);
+ }
+ #otp-modal {
+ display: none;
+ position: fixed;
+ top: 0;
+ left: 0;
+ width: 100%;
+ height: 100%;
+ background: rgba(0, 0, 0, 0.7);
+ z-index: 1000;
+ }
+ #otp-modal div {
+ margin: 10% auto;
+ padding: 20px;
+ background: white;
+ width: 80%;
+ max-width: 400px;
+ text-align: center;
+ border-radius: 8px;
+ }
+
+ </style>
+</head>
+<body>
+ <!-- Popup -->
+ <div class="popup-overlay" id="paymentPopup">
+ <div class="popup-box">
+ <h2>Support Us</h2>
+ <p>This app is free to use.
+ Consider supporting us to help improve and maintain it.</p>
+ <button id="payNowButton">Pay Now</button>
+ <button id="loginButton">Continue Without Paying</button>
+ <button id="closePopup">Logged in</button>
+ </div>
+ </div>
+
+ <div class="container">
+ <h1 class="logo"><u>Number Conversion Tool</u></h1>
+
+ <div class="menu">
+ <ul id="conversionList">
+ <li data-type="binary-to-decimal">Binary to Decimal</li>
+ <li data-type="decimal-to-binary">Decimal to Binary</li>
+ <li data-type="binary-to-octal">Binary to Octal</li>
+ <li data-type="octal-to-binary">Octal to Binary</li>
+ <li data-type="binary-to-hex">Binary to Hex</li>
+ <li data-type="hex-to-binary">Hex to Binary</li>
+ <li data-type="decimal-to-octal">Decimal to Octal</li>
+ <li data-type="octal-to-decimal">Octal to Decimal</li>
+ <li data-type="decimal-to-hex">Decimal to Hex</li>
+ <li data-type="hex-to-decimal">Hex to Decimal</li>
+ <li data-type="octal-to-hex">Octal to Hex</li>
+ <li data-type="hex-to-octal">Hex to Octal</li>
+ </ul>
+ </div>
+
+ <div class="input-section">
+ <label for="inputNumber">Enter Number:</label>
+ <input type="text" id="inputNumber" placeholder="Enter number">
+ </div>
+
+ <div class="actions">
+ <button id="convertButton">Convert</button>
+ </div>
+
+ <div class="output">
+ <h3>Steps:</h3>
+ <pre id="stepsOutput"></pre>
+ <h3>Result:</h3>
+ <p id="resultOutput"></p>
+ </div>
+ </div>
+<br><div class="container" align="center">
+ <p><u>Made by Amit Dutta, NBCBHS (HS : 2024-25)</u></p><br>
+ <div class="support">
+ <button id="payNowButton">Support</button>
+ <span style="margin: 0 5px;"> </span>
+ <a href="https://forms.gle/Y2AWZhQFNVvApxq67"><button>Give Feedback</button></a>
+ </div>
+</div>
+
+ <script>
+ // Razorpay payment link
+ const razorpayLink = "https://rzp.io/rzp/MffwgmY";
+ const loginurl="https://script.google.com/macros/s/--- secret code is hidden ---/exec";
+
+ // Get popup elements
+ const paymentPopup = document.getElementById("paymentPopup");
+ const payNowButton = document.getElementById("payNowButton");
+ const closePopup = document.getElementById("closePopup");
+ const loginButton = document.getElementById("loginButton");
+
+ // Redirect to Razorpay payment link
+ payNowButton.addEventListener("click", () => {
+ window.open(razorpayLink, "_blank");
+ });
+
+ // Close the popup
+ closePopup.addEventListener("click", () => {
+ paymentPopup.style.display = "none";
+ });
+ loginButton.addEventListener("click", () => {
+ window.open(loginurl, "_blank");
+ });
+
+ // Show popup on page load
+ window.onload = () => {
+ paymentPopup.style.display = "flex";
+ };
+ </script>
+ <script>
+
+ document.addEventListener('DOMContentLoaded', () => {
+ const conversionList = document.getElementById('conversionList');
+ const convertButton = document.getElementById('convertButton');
+ const inputNumber = document.getElementById('inputNumber');
+ const stepsOutput = document.getElementById('stepsOutput');
+ const resultOutput = document.getElementById('resultOutput');
+
+ let selectedConversion = 'binary-to-decimal';
+
+ conversionList.addEventListener('click', (event) => {
+ if (event.target.tagName === 'LI') {
+ selectedConversion = event.target.dataset.type;
+ clearOutput();
+ }
+ });
+
+ convertButton.addEventListener('click', () => {
+ const number = inputNumber.value.trim();
+ if (!number) {
+ alert('Please enter a number!');
+ return;
+ }
+ performConversion(number, selectedConversion);
+ });
+
+ function performConversion(number, conversionType) {
+ stepsOutput.textContent = '';
+ resultOutput.textContent = '';
+
+ try {
+ let result, steps;
+ switch (conversionType) {
+ case 'binary-to-decimal':
+ ({ result, steps } = binaryToDecimal(number));
+ break;
+ case 'decimal-to-binary':
+ ({ result, steps } = decimalToBinary(number));
+ break;
+ case 'binary-to-octal':
+ ({ result, steps } = binaryToOctal(number));
+ break;
+ case 'octal-to-binary':
+ ({ result, steps } = octalToBinary(number));
+ break;
+ case 'binary-to-hex':
+ ({ result, steps } = binaryToHex(number));
+ break;
+ case 'hex-to-binary':
+ ({ result, steps } = hexToBinary(number));
+ break;
+ case 'decimal-to-octal':
+ ({ result, steps } = decimalToOctal(number));
+ break;
+ case 'octal-to-decimal':
+ ({ result, steps } = octalToDecimal(number));
+ break;
+ case 'decimal-to-hex':
+ ({ result, steps } = decimalToHex(number));
+ break;
+ case 'hex-to-decimal':
+ ({ result, steps } = hexToDecimal(number));
+ break;
+ case 'octal-to-hex':
+ ({ result, steps } = octalToHex(number));
+ break;
+ case 'hex-to-octal':
+ ({ result, steps } = hexToOctal(number));
+ break;
+ default:
+ throw new Error('Conversion type not supported.');
+ }
+
+ stepsOutput.textContent = steps;
+ resultOutput.textContent = `Result: ${result}`;
+ } catch (error) {
+ alert(error.message);
+ }
+ });
+
+ // Utility Functions
+ function binaryToDecimal(binary) {
+ const [integerPart, fractionalPart] = binary.split('.');
+ let result = 0;
+ let steps = [];
+
+ if (integerPart) {
+ integerPart.split('').reverse().forEach((digit, index) => {
+ const value = parseInt(digit, 10) * Math.pow(2, index);
+ steps.push(`${digit} * 2^${index} = ${value}`);
+ result += value;
+ });
+ }
+
+ if (fractionalPart) {
+ fractionalPart.split('').forEach((digit, index) => {
+ const value = parseInt(digit, 10) * Math.pow(2, -(index + 1));
+ steps.push(`${digit} * 2^-${index + 1} = ${value}`);
+ result += value;
+ });
+ }
+
+ return { result: result.toString(), steps: steps.join('\n') };
+ }
+
+ function decimalToBinary(decimal) {
+ const [integerPart, fractionalPart] = decimal.split('.');
+ let integerResult = '';
+ let steps = [];
+ let num = parseInt(integerPart, 10);
+
+ while (num > 0) {
+ const remainder = num % 2;
+ integerResult = remainder + integerResult;
+ steps.push(`${num} / 2 = ${Math.floor(num / 2)}, remainder ${remainder}`);
+ num = Math.floor(num / 2);
+ }
+
+ let fractionalResult = '';
+ if (fractionalPart) {
+ let fracPart = parseFloat('0.' + fractionalPart);
+ for (let i = 0; i < 4 && fracPart !== 0; i++) {
+ fracPart *= 2;
+ const bit = Math.floor(fracPart);
+ fractionalResult += bit.toString();
+ fracPart -= bit;
+ steps.push(`0.${fracPart.toFixed(4)} * 2 -> ${bit}`);
+ }
+ }
+
+ return { result: integerResult + (fractionalResult ? '.' + fractionalResult : ''), steps: steps.join('\n') };
+ }
+
+ function binaryToOctal(binary) {
+ const decimal = binaryToDecimal(binary).result;
+ return decimalToOctal(decimal);
+ }
+
+ function octalToBinary(octal) {
+ const decimal = octalToDecimal(octal).result;
+ return decimalToBinary(decimal);
+ }
+
+ function binaryToHex(binary) {
+ const decimal = binaryToDecimal(binary).result;
+ return decimalToHex(decimal);
+ }
+
+ function hexToBinary(hex) {
+ const decimal = hexToDecimal(hex).result;
+ return decimalToBinary(decimal);
+ }
+
+ function decimalToOctal(decimal) {
+ const [integerPart, fractionalPart] = decimal.split('.');
+ let integerResult = '';
+ let steps = [];
+ let num = parseInt(integerPart, 10);
+
+ while (num > 0) {
+ const remainder = num % 8;
+ integerResult = remainder + integerResult;
+ steps.push(`${num} / 8 = ${Math.floor(num / 8)}, remainder ${remainder}`);
+ num = Math.floor(num / 8);
+ }
+
+ let fractionalResult = '';
+ if (fractionalPart) {
+ let fracPart = parseFloat('0.' + fractionalPart);
+ for (let i = 0; i < 4 && fracPart !== 0; i++) {
+ fracPart *= 8;
+ const digit = Math.floor(fracPart);
+ fractionalResult += digit.toString();
+ fracPart -= digit;
+ steps.push(`0.${fracPart.toFixed(4)} * 8 -> ${digit}`);
+ }
+ }
+
+ return { result: integerResult + (fractionalResult ? '.' + fractionalResult : ''), steps: steps.join('\n') };
+ }
+
+ function octalToDecimal(octal) {
+ const [integerPart, fractionalPart] = octal.split('.');
+ let result = 0;
+ let steps = [];
+
+ if (integerPart) {
+ integerPart.split('').reverse().forEach((digit, index) => {
+ const value = parseInt(digit, 10) * Math.pow(8, index);
+ steps.push(`${digit} * 8^${index} = ${value}`);
+ result += value;
+ });
+ }
+
+ if (fractionalPart) {
+ fractionalPart.split('').forEach((digit, index) => {
+ const value = parseInt(digit, 10) * Math.pow(8, -(index + 1));
+ steps.push(`${digit} * 8^-${index + 1} = ${value}`);
+ result += value;
+ });
+ }
+
+ return { result: result.toString(), steps: steps.join('\n') };
+ }
+
+ function decimalToHex(decimal) {
+ const [integerPart, fractionalPart] = decimal.split('.');
+ let integerResult = '';
+ let steps = [];
+ let num = parseInt(integerPart, 10);
+
+ while (num > 0) {
+ const remainder = num % 16;
+ const hexChar = remainder < 10 ? remainder : String.fromCharCode(remainder + 87);
+ integerResult = hexChar + integerResult;
+ steps.push(`${num} / 16 = ${Math.floor(num / 16)}, remainder ${hexChar}`);
+ num = Math.floor(num / 16);
+ }
+
+ let fractionalResult = '';
+ if (fractionalPart) {
+ let fracPart = parseFloat('0.' + fractionalPart);
+ for (let i = 0; i < 4 && fracPart !== 0; i++) {
+ fracPart *= 16;
+ const digit = Math.floor(fracPart);
+ const hexChar = digit < 10 ? digit : String.fromCharCode(digit + 87);
+ fractionalResult += hexChar;
+ fracPart -= digit;
+ steps.push(`0.${fracPart.toFixed(4)} * 16 -> ${hexChar}`);
+ }
+ }
+
+ return { result: integerResult + (fractionalResult ? '.' + fractionalResult : ''), steps: steps.join('\n') };
+ }
+
+ function hexToDecimal(hex) {
+ const [integerPart, fractionalPart] = hex.split('.');
+ let result = 0;
+ let steps = [];
+
+ if (integerPart) {
+ integerPart.split('').reverse().forEach((digit, index) => {
+ const value = parseInt(digit, 16) * Math.pow(16, index);
+ steps.push(`${digit} * 16^${index} = ${value}`);
+ result += value;
+ });
+ }
+
+ if (fractionalPart) {
+ fractionalPart.split('').forEach((digit, index) => {
+ const value = parseInt(digit, 16) * Math.pow(16, -(index + 1));
+ steps.push(`${digit} * 16^-${index + 1} = ${value}`);
+ result += value;
+ });
+ }
+
+ return { result: result.toString(), steps: steps.join('\n') };
+ }
+
+ function octalToHex(octal) {
+ const decimal = octalToDecimal(octal).result;
+ return decimalToHex(decimal);
+ }
+
+ function hexToOctal(hex) {
+ const decimal = hexToDecimal(hex).result;
+ return decimalToOctal(decimal);
+ }
+
+ function clearOutput() {
+ stepsOutput.textContent = '';
+ resultOutput.textContent = '';
+ inputNumber.value = '';
+ }
+ });
+
+ </script>
+</body>
+</html>
+</pre>
+ </div>
+
+ <div id="download-tab-content" class="tab-content">
+ <div class="download-item">
+ <h3>NCT_App_Source_Code.txt</h3>
+ <p>Full source code of the Number Conversion Tool web application.</p>
+ <button class="download-button" data-file="/download/nct_app_source_code.txt">Download</button>
+ </div>
+ <div class="download-item">
+ <h3>NCT App</h3>
+ <p>Download the app here.</p>
+ <button class="download-button" data-file="https://github.com/notamitgamer/app/releases/download/app/nct.apk">Download</button>
+ </div>
+ </div>
+ </div>
+<script>
+ document.addEventListener('DOMContentLoaded', () => {
+ const tabButtons = document.querySelectorAll('.tab-button');
+ const tabContents = document.querySelectorAll('.tab-content');
+ const downloadButtons = document.querySelectorAll('.download-button');
+
+ // Function to switch tabs
+ function switchTab(selectedTab) {
+ tabButtons.forEach(button => {
+ button.classList.remove('active');
+ });
+ tabContents.forEach(content => {
+ content.classList.remove('active');
+ });
+
+ document.querySelector(`.tab-button[data-tab="${selectedTab}"]`).classList.add('active');
+ document.getElementById(`${selectedTab}-tab-content`).classList.add('active');
+ }
+
+ // Event listeners for tab buttons
+ tabButtons.forEach(button => {
+ button.addEventListener('click', () => {
+ const tab = button.dataset.tab;
+ switchTab(tab);
+ });
+ });
+
+ // Event listeners for download buttons
+ downloadButtons.forEach(button => {
+ button.addEventListener('click', () => {
+ const fileUrl = button.dataset.file;
+
+ if (fileUrl) {
+ const a = document.createElement('a');
+ a.href = fileUrl;
+ a.download = fileUrl.split('/').pop(); // Optional: force filename
+ document.body.appendChild(a);
+ a.click();
+ document.body.removeChild(a);
+ }
+
+ // UI feedback
+ button.textContent = 'Thank You!';
+ button.classList.add('thank-you');
+ button.disabled = true;
+
+ setTimeout(() => {
+ button.textContent = 'Download';
+ button.classList.remove('thank-you');
+ button.disabled = false;
+ }, 3000);
+ });
+ });
+ });
+</script>
+
+</body>
+</html>
diff --git a/nct-legacy.html b/nct-legacy.html
@@ -0,0 +1,1661 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+ <meta charset="UTF-8">
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
+ <title>NCT v2.11 | Legacy Edition</title>
+ <link rel="icon" href="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIzMiIgaGVpZ2h0PSIzMiIgdmlld0JveD0iMCAwIDI0IDI0Ij48c3R5bGU+cGF0aHtmaWxsOiNEMEJDRkZ9QG1lZGlhIChwcmVmZXJzLWNvbG9yLXNjaGVtZTpkYXJrKXtwYXRoe2ZpbGw6I2ZmZnx9fTwvc3R5bGU+PHBhdGggZD0iTTcgMnYxMWgzdjlsNy0xMmgtNGw0LTh6Ii8+PC9zdmc+">
+ <link rel="preconnect" href="https://fonts.googleapis.com">
+ <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
+ <link href="https://fonts.googleapis.com/css2?family=Roboto:wght@400;500;700&family=Google+Sans:wght@400;500;700&family=Roboto+Mono:wght@400;500&display=swap" rel="stylesheet">
+ <script type="application/ld+json">
+ {
+ "@context": "https://schema.org",
+ "@type": "WebApplication",
+ "name": "NCT v2.11 | Legacy Edition",
+ "url": "https://amit.is-a.dev/nct-legacy",
+ "image": "https://amit.is-a.dev/nctlogo.png",
+ "description": "Number conversion tool",
+ "applicationCategory": "EducationalApplication",
+ "operatingSystem": "Any"
+ }
+</script>
+ <style>
+ /* --- MATERIAL DESIGN 3 TOKENS --- */
+ :root {
+ --md-sys-color-background: #141218;
+ --md-sys-color-surface: #1D1B20;
+ --md-sys-color-surface-container: #211F26;
+ --md-sys-color-surface-container-high: #2B2930;
+
+ /* Primary */
+ --md-sys-color-primary: #D0BCFF;
+ --md-sys-color-on-primary: #381E72;
+ --md-sys-color-primary-container: #4F378B;
+ --md-sys-color-on-primary-container: #EADDFF;
+
+ /* Text */
+ --md-sys-color-on-surface: #E6E1E5;
+ --md-sys-color-on-surface-variant: #CAC4D0;
+ --md-sys-color-outline: #938F99;
+
+ /* Error */
+ --md-sys-color-error: #F2B8B5;
+ --md-sys-color-error-container: #8C1D18;
+
+ /* Success/Accent */
+ --md-sys-color-tertiary: #B6C4FF;
+ --md-sys-color-success: #4ade80;
+
+ /* Dimensions */
+ --radius-xl: 28px;
+ --radius-l: 16px;
+ --radius-m: 12px;
+ --radius-s: 8px;
+
+ --font-main: 'Google Sans', 'Roboto', sans-serif;
+ --font-code: 'Roboto Mono', monospace;
+
+ /* Animation */
+ --ease-elastic: cubic-bezier(0.4, 0.0, 0.2, 1);
+ }
+
+ /* --- CUSTOM SCROLLBAR --- */
+ ::-webkit-scrollbar { width: 6px; height: 6px; }
+ ::-webkit-scrollbar-track { background: transparent; }
+ ::-webkit-scrollbar-thumb { background: rgba(255,255,255,0.2); border-radius: 3px; }
+ ::-webkit-scrollbar-thumb:hover { background: rgba(255,255,255,0.4); }
+
+ * { margin: 0; padding: 0; box-sizing: border-box; -webkit-tap-highlight-color: transparent; }
+
+ body {
+ font-family: var(--font-main);
+ background-color: var(--md-sys-color-background);
+ color: var(--md-sys-color-on-surface);
+ min-height: 100vh;
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ overflow-x: hidden;
+ background-image: radial-gradient(circle at 50% 0%, rgba(208, 188, 255, 0.05) 0%, transparent 60%);
+ }
+
+ /* --- LAYOUT --- */
+ .app-container {
+ width: 100%;
+ max-width: 1000px;
+ padding: 24px;
+ display: flex;
+ flex-direction: column;
+ gap: 24px;
+ flex-grow: 1;
+ }
+
+ /* --- HEADER & NAV --- */
+ header {
+ display: flex;
+ flex-direction: column;
+ gap: 20px;
+ padding: 16px 0;
+ }
+
+ .header-top {
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ }
+
+ .logo {
+ font-size: 1.75rem;
+ font-weight: 500;
+ color: var(--md-sys-color-on-surface);
+ display: flex;
+ align-items: center;
+ gap: 12px;
+ }
+
+ .logo span { color: var(--md-sys-color-primary); }
+
+ /* Navigation Tabs with Animation */
+ .nav-tabs {
+ position: relative;
+ display: flex;
+ background: var(--md-sys-color-surface-container);
+ padding: 4px;
+ border-radius: 32px;
+ width: fit-content;
+ margin: 0 auto;
+ border: 1px solid rgba(255,255,255,0.05);
+ }
+
+ .nav-glider {
+ position: absolute;
+ top: 4px;
+ left: 4px;
+ height: calc(100% - 8px);
+ width: 110px;
+ background: var(--md-sys-color-primary-container);
+ border-radius: 28px;
+ transition: transform 0.3s var(--ease-elastic);
+ z-index: 1;
+ }
+
+ .nav-tab {
+ position: relative;
+ padding: 10px 24px;
+ border-radius: 24px;
+ color: var(--md-sys-color-on-surface-variant);
+ font-weight: 500;
+ cursor: pointer;
+ transition: color 0.2s;
+ border: none;
+ background: none;
+ z-index: 2;
+ width: 110px;
+ }
+
+ .nav-tab.active { color: var(--md-sys-color-on-primary-container); }
+
+ .header-actions {
+ display: flex;
+ gap: 10px;
+ }
+
+ .tools-btn {
+ background: transparent;
+ border: 1px solid var(--md-sys-color-outline);
+ color: var(--md-sys-color-on-surface);
+ padding: 8px 16px;
+ border-radius: var(--radius-l);
+ cursor: pointer;
+ font-weight: 500;
+ display: flex;
+ align-items: center;
+ gap: 8px;
+ transition: transform 0.1s;
+ }
+ .tools-btn:active { transform: scale(0.95); }
+ .tools-btn:hover { background: rgba(255,255,255,0.05); }
+
+ /* --- SECTIONS --- */
+ .app-section {
+ display: none;
+ flex-direction: column;
+ gap: 24px;
+ animation: fadeIn 0.3s ease-out;
+ }
+ .app-section.active { display: flex; }
+
+ @keyframes fadeIn { from { opacity: 0; transform: translateY(10px); } to { opacity: 1; transform: translateY(0); } }
+
+ /* --- CONVERTER STAGE --- */
+ .converter-stage {
+ display: grid;
+ grid-template-columns: 1fr 64px 1fr;
+ gap: 16px;
+ align-items: stretch;
+ position: relative;
+ }
+
+ .material-card {
+ background-color: var(--md-sys-color-surface-container);
+ border-radius: var(--radius-xl);
+ padding: 24px;
+ display: flex;
+ flex-direction: column;
+ height: 380px;
+ position: relative;
+ transition: transform 0.4s var(--ease-elastic), box-shadow 0.2s;
+ border: 1px solid transparent;
+ z-index: 1;
+ }
+
+ .material-card:focus-within {
+ background-color: var(--md-sys-color-surface-container-high);
+ border-color: var(--md-sys-color-outline);
+ }
+
+ /* CARD HEADER & CUSTOM DROPDOWN */
+ .card-header {
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ margin-bottom: 20px;
+ border-bottom: 1px solid var(--md-sys-color-outline);
+ padding-bottom: 12px;
+ z-index: 10;
+ }
+
+ .input-label {
+ font-size: 0.875rem;
+ font-weight: 500;
+ color: var(--md-sys-color-primary);
+ text-transform: uppercase;
+ letter-spacing: 1px;
+ }
+
+ /* Custom Dropdown Styles */
+ .custom-select-wrapper {
+ position: relative;
+ min-width: 150px;
+ user-select: none;
+ }
+ .custom-select-trigger {
+ display: flex;
+ align-items: center;
+ justify-content: flex-end;
+ gap: 8px;
+ cursor: pointer;
+ color: var(--md-sys-color-on-surface);
+ font-weight: 500;
+ padding: 4px 0;
+ transition: color 0.2s;
+ }
+ .custom-select-trigger:hover { color: var(--md-sys-color-primary); }
+ .arrow { font-size: 0.7rem; transition: transform 0.3s; }
+
+ .custom-options {
+ position: absolute;
+ top: 120%;
+ right: 0;
+ background: var(--md-sys-color-surface-container-high);
+ border-radius: var(--radius-m);
+ box-shadow: 0 8px 16px rgba(0,0,0,0.4);
+ opacity: 0;
+ visibility: hidden;
+ pointer-events: none;
+ transform: translateY(-10px);
+ transition: all 0.2s var(--ease-elastic);
+ min-width: 180px;
+ overflow: hidden;
+ border: 1px solid rgba(255,255,255,0.1);
+ z-index: 1000;
+ }
+ .custom-select-wrapper.open .custom-options {
+ opacity: 1; visibility: visible; pointer-events: all; transform: translateY(0);
+ }
+ .custom-select-wrapper.open .arrow { transform: rotate(180deg); }
+
+ .custom-option {
+ padding: 12px 16px;
+ cursor: pointer;
+ color: var(--md-sys-color-on-surface-variant);
+ transition: 0.2s;
+ }
+ .custom-option:hover { background: rgba(255,255,255,0.05); color: var(--md-sys-color-on-surface); }
+ .custom-option.selected { color: var(--md-sys-color-primary); background: rgba(208, 188, 255, 0.1); }
+
+ /* INPUT AREA */
+ .input-area {
+ flex-grow: 1;
+ background: transparent;
+ border: none;
+ color: var(--md-sys-color-on-surface);
+ font-size: 2.2rem;
+ font-family: var(--font-main);
+ resize: none;
+ outline: none;
+ line-height: 1.2;
+ z-index: 1;
+ }
+ .input-area::placeholder { color: var(--md-sys-color-on-surface-variant); opacity: 0.3; }
+
+ /* Error Chip */
+ .error-chip {
+ position: absolute;
+ bottom: 80px;
+ left: 24px;
+ background: var(--md-sys-color-error-container);
+ color: var(--md-sys-color-error);
+ padding: 8px 16px;
+ border-radius: 8px;
+ font-size: 0.875rem;
+ display: flex;
+ align-items: center;
+ gap: 8px;
+ opacity: 0;
+ transform: translateY(10px);
+ transition: all 0.3s;
+ pointer-events: none;
+ }
+ .error-chip.visible { opacity: 1; transform: translateY(0); }
+
+ /* Card Footer */
+ .card-footer {
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ margin-top: auto;
+ }
+ .char-count { font-size: 0.75rem; color: var(--md-sys-color-on-surface-variant); }
+
+ /* Icons & Buttons */
+ .icon-btn {
+ background: transparent;
+ border: none;
+ color: var(--md-sys-color-on-surface-variant);
+ width: 40px; height: 40px;
+ border-radius: 50%;
+ cursor: pointer;
+ display: flex; align-items: center; justify-content: center;
+ transition: 0.2s;
+ }
+ .icon-btn:hover { background: rgba(255,255,255,0.1); color: var(--md-sys-color-on-surface); transform: scale(1.1); }
+ .icon-btn:active { transform: scale(0.9); }
+
+ /* Copy Button Animation */
+ .icon-btn.success svg {
+ animation: check-pop 0.4s cubic-bezier(0.175, 0.885, 0.32, 1.275);
+ color: var(--md-sys-color-success);
+ }
+ @keyframes check-pop {
+ 0% { transform: scale(0); }
+ 50% { transform: scale(1.3); }
+ 100% { transform: scale(1); }
+ }
+
+ /* Trash Shake Animation */
+ @keyframes trash-shake {
+ 0% { transform: rotate(0deg); }
+ 25% { transform: rotate(-15deg); }
+ 50% { transform: rotate(15deg); }
+ 75% { transform: rotate(-15deg); }
+ 100% { transform: rotate(0deg); }
+ }
+ .icon-btn.shaking {
+ animation: trash-shake 0.4s ease-in-out;
+ color: var(--md-sys-color-error);
+ }
+
+ /* Swap Button */
+ .swap-container { display: flex; justify-content: center; align-items: center; z-index: 2; }
+ .swap-fab {
+ width: 56px; height: 56px;
+ border-radius: 16px;
+ background-color: var(--md-sys-color-surface-container-high);
+ border: 1px solid var(--md-sys-color-outline);
+ color: var(--md-sys-color-on-surface);
+ cursor: pointer;
+ display: flex; align-items: center; justify-content: center;
+ transition: all 0.3s var(--ease-elastic);
+ box-shadow: 0 4px 8px rgba(0,0,0,0.2);
+ }
+ .swap-fab:hover {
+ background-color: var(--md-sys-color-primary);
+ color: var(--md-sys-color-on-primary);
+ transform: rotate(180deg) scale(1.1);
+ border-color: transparent;
+ }
+
+ /* --- SWAP ANIMATION CLASSES --- */
+ .anim-move-right { transform: translateX(calc(100% + 16px)); }
+ .anim-move-left { transform: translateX(calc(-100% - 16px)); }
+ .anim-move-down { transform: translateY(calc(100% + 16px)); }
+ .anim-move-up { transform: translateY(calc(-100% - 16px)); }
+
+ /* --- DYNAMIC CALCULATOR --- */
+ .calc-wrapper {
+ background: var(--md-sys-color-surface-container);
+ padding: 24px;
+ border-radius: var(--radius-xl);
+ max-width: 800px;
+ margin: 0 auto;
+ width: 100%;
+ }
+
+ .calc-header {
+ display: flex;
+ gap: 24px;
+ margin-bottom: 24px;
+ flex-wrap: wrap;
+ }
+
+ .calc-ctrl-group {
+ flex: 1;
+ min-width: 200px;
+ display: flex;
+ align-items: center;
+ gap: 12px;
+ background: rgba(0,0,0,0.2);
+ padding: 8px 16px;
+ border-radius: 12px;
+ }
+ .calc-ctrl-group label {
+ font-size: 0.8rem;
+ color: var(--md-sys-color-primary);
+ text-transform: uppercase;
+ white-space: nowrap;
+ margin: 0;
+ }
+
+ .calc-rows {
+ display: flex;
+ flex-direction: column;
+ gap: 12px;
+ margin-bottom: 24px;
+ }
+
+ .calc-row {
+ display: flex;
+ gap: 12px;
+ align-items: center;
+ animation: fadeIn 0.3s ease-out;
+ }
+
+ /* Simple Fade Out */
+ .calc-row.removing {
+ opacity: 0;
+ transition: opacity 0.3s ease;
+ }
+
+ .calc-input-wrapper {
+ flex-grow: 1;
+ position: relative;
+ }
+ .calc-input {
+ width: 100%;
+ background: rgba(0,0,0,0.2);
+ border: 1px solid transparent;
+ color: var(--md-sys-color-on-surface);
+ padding: 16px;
+ border-radius: 12px;
+ font-family: var(--font-code);
+ font-size: 1.1rem;
+ outline: none;
+ transition: 0.2s;
+ }
+ .calc-input:focus { border-color: var(--md-sys-color-primary); background: rgba(0,0,0,0.4); }
+ .calc-input.invalid { border-color: var(--md-sys-color-error); }
+
+ .calc-remove-btn {
+ background: var(--md-sys-color-surface-container-high);
+ border: none;
+ color: var(--md-sys-color-error);
+ width: 40px; height: 40px;
+ border-radius: 8px;
+ cursor: pointer;
+ display: flex; align-items: center; justify-content: center;
+ transition: 0.2s;
+ }
+ .calc-remove-btn:hover { background: var(--md-sys-color-error-container); color: #fff; }
+
+ /* The Divider Area */
+ .calc-actions {
+ display: flex;
+ align-items: center;
+ border-top: 1px solid var(--md-sys-color-outline);
+ padding-top: 20px;
+ margin-top: 20px;
+ }
+
+ .add-row-btn {
+ background: rgba(255,255,255,0.05);
+ border: 1px dashed var(--md-sys-color-outline);
+ color: var(--md-sys-color-on-surface-variant);
+ padding: 10px 20px;
+ border-radius: 12px;
+ cursor: pointer;
+ font-weight: 500;
+ width: fit-content;
+ text-align: left;
+ transition: 0.2s;
+ margin-bottom: 20px; /* Space before divider */
+ }
+ .add-row-btn:hover { border-color: var(--md-sys-color-primary); color: var(--md-sys-color-primary); background: rgba(208, 188, 255, 0.05); }
+
+ .calc-result-area {
+ display: flex;
+ align-items: center;
+ gap: 20px;
+ margin-left: auto; /* Forces result to the right */
+ }
+
+ .visualize-btn {
+ background: var(--md-sys-color-primary-container);
+ color: var(--md-sys-color-on-primary-container);
+ border: none;
+ padding: 8px 16px;
+ border-radius: 20px;
+ font-size: 0.9rem;
+ font-weight: 500;
+ cursor: pointer;
+ display: none; /* Hidden by default */
+ align-items: center;
+ gap: 6px;
+ transition: 0.2s;
+ animation: popIn 0.3s cubic-bezier(0.175, 0.885, 0.32, 1.275);
+ margin-right: auto; /* Keeps it left-aligned in actions */
+ }
+ .visualize-btn:hover { filter: brightness(1.2); }
+ .visualize-btn:active { transform: scale(0.95); }
+ @keyframes popIn { from{transform: scale(0.8); opacity:0} to{transform: scale(1); opacity:1} }
+
+ .calc-result-display { text-align: right; }
+ .res-label { font-size: 0.9rem; color: var(--md-sys-color-on-surface-variant); }
+ .res-value { font-size: 2.2rem; color: var(--md-sys-color-primary); font-family: var(--font-code); margin-top: 5px; font-weight: 700; }
+
+
+ /* --- VISUALIZER PANEL --- */
+ .calc-visualizer {
+ background: #111;
+ border-radius: var(--radius-m);
+ padding: 20px;
+ margin-top: 20px;
+ display: none;
+ border: 1px solid var(--md-sys-color-outline);
+ overflow-x: auto;
+ }
+ .calc-visualizer.active { display: block; animation: slideIn 0.4s var(--ease-elastic); }
+
+ .viz-grid {
+ display: grid;
+ font-family: var(--font-code);
+ font-size: 1.2rem;
+ color: var(--md-sys-color-on-surface);
+ gap: 4px;
+ }
+ .viz-row { display: contents; }
+ .viz-cell {
+ padding: 4px 8px;
+ text-align: center;
+ position: relative;
+ }
+ .viz-cell.carry { color: var(--md-sys-color-tertiary); font-size: 0.8rem; height: 20px; }
+ .viz-cell.operator { color: var(--md-sys-color-primary); }
+ .viz-cell.border-bottom { border-bottom: 2px solid var(--md-sys-color-outline); }
+ .viz-cell.active-col { background: rgba(208, 188, 255, 0.15); border-radius: 4px; }
+ .viz-cell.result { color: var(--md-sys-color-primary); font-weight: bold; }
+
+ .viz-info {
+ margin-top: 12px;
+ font-size: 0.9rem;
+ color: var(--md-sys-color-on-surface-variant);
+ font-family: var(--font-code);
+ min-height: 20px;
+ }
+
+ /* --- CONVERTER STEPS PANEL --- */
+ .steps-container {
+ margin-top: 20px;
+ background: rgba(0,0,0,0.2);
+ border-radius: var(--radius-l);
+ padding: 20px;
+ border: 1px solid rgba(255,255,255,0.05);
+ display: none;
+ }
+ .steps-container.visible { display: block; animation: fadeIn 0.5s; }
+
+ .steps-header {
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ margin-bottom: 12px;
+ border-bottom: 1px solid rgba(255,255,255,0.1);
+ padding-bottom: 8px;
+ }
+
+ .steps-content {
+ font-family: var(--font-code);
+ color: var(--md-sys-color-tertiary);
+ font-size: 0.9rem;
+ white-space: pre-wrap;
+ line-height: 1.5;
+ }
+
+ /* --- HISTORY TAPE --- */
+ .history-section {
+ margin-top: 20px;
+ border-top: 1px solid var(--md-sys-color-outline);
+ padding-top: 20px;
+ }
+ .history-header {
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ margin-bottom: 10px;
+ }
+ .history-list {
+ display: flex; flex-direction: column; gap: 8px;
+ max-height: 200px; overflow-y: auto;
+ transition: opacity 0.3s;
+ }
+
+ /* History Item Animation: Slide Out to Right */
+ .history-list.clearing .history-item {
+ animation: slide-out-right 0.3s forwards;
+ }
+ @keyframes slide-out-right {
+ to { transform: translateX(50px); opacity: 0; }
+ }
+
+ .history-item {
+ display: flex; justify-content: space-between;
+ padding: 12px;
+ background: rgba(255,255,255,0.03);
+ border-radius: 8px;
+ font-family: var(--font-code);
+ font-size: 0.9rem;
+ cursor: pointer;
+ transition: 0.2s;
+ align-items: center;
+ }
+ .history-item:hover { background: rgba(255,255,255,0.08); transform: translateX(4px); }
+ .hist-val { color: var(--md-sys-color-on-surface); }
+ .hist-meta { font-size: 0.75rem; color: var(--md-sys-color-outline); margin-right: 10px; }
+
+ /* --- FOOTER --- */
+ footer {
+ margin-top: 40px;
+ text-align: center;
+ padding: 20px;
+ color: var(--md-sys-color-on-surface-variant);
+ font-size: 0.85rem;
+ border-top: 1px solid var(--md-sys-color-outline);
+ width: 100%;
+ background: rgba(0,0,0,0.2);
+ }
+ .footer-content {
+ display: flex;
+ flex-direction: column;
+ gap: 8px;
+ }
+ .footer-links {
+ display: flex;
+ justify-content: center;
+ gap: 15px;
+ margin-top: 5px;
+ }
+ .footer-link {
+ color: var(--md-sys-color-primary);
+ text-decoration: none;
+ transition: 0.2s;
+ }
+ .footer-link:hover { text-decoration: underline; color: var(--md-sys-color-tertiary); }
+
+ /* --- MODAL (LEGACY & CHEATSHEET) --- */
+ .modal-overlay {
+ position: fixed;
+ top: 0; left: 0; width: 100%; height: 100%;
+ background: rgba(0,0,0,0.7);
+ backdrop-filter: blur(4px);
+ z-index: 2000;
+ display: none;
+ justify-content: center;
+ align-items: center;
+ opacity: 0;
+ transition: opacity 0.3s;
+ }
+ .modal-overlay.open { display: flex; opacity: 1; }
+
+ .modal-content {
+ background: var(--md-sys-color-surface-container);
+ width: 90%; max-width: 500px;
+ border-radius: var(--radius-xl);
+ padding: 24px;
+ max-height: 80vh;
+ display: flex;
+ flex-direction: column;
+ box-shadow: 0 10px 30px rgba(0,0,0,0.5);
+ border: 1px solid var(--md-sys-color-outline);
+ transform: scale(0.9);
+ transition: transform 0.3s var(--ease-elastic);
+ }
+ .modal-overlay.open .modal-content { transform: scale(1); }
+
+ .modal-header {
+ display: flex; justify-content: space-between; align-items: center;
+ margin-bottom: 20px;
+ border-bottom: 1px solid var(--md-sys-color-outline);
+ padding-bottom: 12px;
+ flex-shrink: 0;
+ }
+ .modal-header h3 { margin: 0; color: var(--md-sys-color-primary); }
+ .close-modal {
+ background: none; border: none; color: var(--md-sys-color-on-surface);
+ font-size: 1.5rem; cursor: pointer;
+ }
+
+ .modal-body {
+ overflow-y: auto;
+ padding-right: 5px; /* Space for scrollbar */
+ }
+
+ /* History Timeline */
+ .timeline-item {
+ position: relative;
+ padding-left: 20px;
+ border-left: 2px solid var(--md-sys-color-outline);
+ margin-bottom: 20px;
+ }
+ .timeline-item::before {
+ content: ''; position: absolute; left: -6px; top: 0;
+ width: 10px; height: 10px; border-radius: 50%;
+ background: var(--md-sys-color-primary);
+ }
+ .version-tag {
+ font-size: 0.8rem; font-weight: bold; color: var(--md-sys-color-tertiary);
+ margin-bottom: 4px; display: inline-block; background: rgba(255,255,255,0.1);
+ padding: 2px 8px; border-radius: 4px;
+ }
+ .version-tag.current { background: var(--md-sys-color-primary); color: var(--md-sys-color-on-primary); }
+ .timeline-desc { font-size: 0.9rem; color: var(--md-sys-color-on-surface-variant); margin: 0; }
+
+ /* Sidebar (Cheat Sheet) */
+ .sidebar {
+ position: fixed; top: 0; right: -320px;
+ width: 300px; height: 100%;
+ background: var(--md-sys-color-surface-container-high);
+ box-shadow: -5px 0 15px rgba(0,0,0,0.5);
+ padding: 24px;
+ transition: 0.3s ease;
+ z-index: 1000;
+ overflow-y: auto;
+ }
+ .sidebar.open { right: 0; }
+ .sidebar h3 { color: var(--md-sys-color-primary); margin-bottom: 16px; border-bottom: 1px solid rgba(255,255,255,0.1); padding-bottom: 8px; }
+ .cheat-table { width: 100%; border-collapse: collapse; font-family: var(--font-code); font-size: 0.8rem; }
+ .cheat-table th, .cheat-table td { text-align: left; padding: 6px; border-bottom: 1px solid rgba(255,255,255,0.05); }
+ .close-sidebar {
+ position: absolute; top: 10px; right: 10px;
+ background: none; border: none; color: var(--md-sys-color-on-surface);
+ font-size: 1.5rem; cursor: pointer;
+ }
+
+ /* --- RESPONSIVE --- */
+ @media (max-width: 768px) {
+ .converter-stage { grid-template-columns: 1fr; grid-template-rows: 1fr 64px 1fr; gap: 8px; }
+ .material-card { height: 260px; }
+ .swap-container { transform: rotate(90deg); }
+ .swap-fab:hover { transform: rotate(180deg); }
+ .header-top { flex-direction: column; gap: 10px; }
+ .calc-header { flex-direction: column; gap: 10px; }
+ .calc-result-area { margin-left: 0; justify-content: flex-end; width: 100%; }
+ .visualize-btn { margin-right: 0; margin-bottom: 10px; }
+ .calc-actions { flex-direction: column-reverse; align-items: stretch; }
+ }
+ </style>
+</head>
+<body>
+
+ <header class="app-container">
+ <div class="header-top">
+ <div class="logo">
+ <svg width="32" height="32" viewBox="0 0 24 24" fill="var(--md-sys-color-primary)"><path d="M7 2v11h3v9l7-12h-4l4-8z"/></svg>
+ NCT <span>v2.11</span>
+ </div>
+
+ <!-- Animated Navigation -->
+ <div class="nav-tabs">
+ <div class="nav-glider" id="navGlider"></div>
+ <button class="nav-tab active" onclick="switchTab('converter', 0)">Converter</button>
+ <button class="nav-tab" onclick="switchTab('calculator', 1)">Calculator</button>
+ </div>
+
+ <div class="header-actions">
+ <button class="tools-btn" onclick="toggleHistory()">
+ <svg width="18" height="18" viewBox="0 0 24 24" fill="currentColor"><path d="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm1 15h-2v-6h2v6zm0-8h-2V7h2v2z"/></svg>
+ Versions
+ </button>
+ <button class="tools-btn" onclick="toggleSidebar()">
+ <svg width="18" height="18" viewBox="0 0 24 24" fill="currentColor"><path d="M11 18h2v-2h-2v2zm1-16C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm0 18c-4.41 0-8-3.59-8-8s3.59-8 8-8 8 3.59 8 8-3.59 8-8 8zm0-14c-2.21 0-4 1.79-4 4h2c0-1.1.9-2 2-2s2 .9 2 2c0 2-3 1.75-3 5h2c0-2.25 3-2.5 3-5 0-2.21-1.79-4-4-4z"/></svg>
+ Cheat Sheet
+ </button>
+ </div>
+ </div>
+ </header>
+
+ <main class="app-container">
+
+ <!-- SECTION 1: CONVERTER -->
+ <section id="converter-section" class="app-section active">
+
+ <div class="converter-stage">
+ <!-- SOURCE CARD -->
+ <div class="material-card" id="cardSource">
+ <div class="card-header">
+ <span class="input-label">Input</span>
+ <div class="custom-select-wrapper" id="sourceSelectWrapper">
+ <div class="custom-select-trigger">
+ <span class="selection-text">Decimal</span>
+ <span class="arrow">▼</span>
+ </div>
+ <div class="custom-options">
+ <div class="custom-option" data-value="2">Binary</div>
+ <div class="custom-option" data-value="8">Octal</div>
+ <div class="custom-option selected" data-value="10">Decimal</div>
+ <div class="custom-option" data-value="16">Hexadecimal</div>
+ </div>
+ <select id="fromBase" style="display:none">
+ <option value="2">Binary</option>
+ <option value="8">Octal</option>
+ <option value="10" selected>Decimal</option>
+ <option value="16">Hexadecimal</option>
+ </select>
+ </div>
+ </div>
+
+ <textarea id="inputVal" class="input-area" placeholder="0" spellcheck="false"></textarea>
+ <div id="errorChip" class="error-chip">⚠️ Invalid character for this base</div>
+
+ <div class="card-footer">
+ <div class="char-count" id="sourceCount">0 chars</div>
+ <div class="icon-actions">
+ <button class="icon-btn" onclick="clearAll()" title="Clear">
+ <svg width="20" height="20" viewBox="0 0 24 24" fill="currentColor"><path d="M19 6.41L17.59 5 12 10.59 6.41 5 5 6.41 10.59 12 5 17.59 6.41 19 12 13.41 17.59 19 19 17.59 13.41 12z"/></svg>
+ </button>
+ </div>
+ </div>
+ </div>
+
+ <!-- SWAP -->
+ <div class="swap-container">
+ <button class="swap-fab" id="swapBtn" title="Swap (Alt+S)">
+ <svg width="24" height="24" viewBox="0 0 24 24" fill="currentColor"><path d="M6.99 11L3 15l3.99 4v-3H14v-2H6.99v-3zM21 9l-3.99-4v3H10v2h7.01v3L21 9z"/></svg>
+ </button>
+ </div>
+
+ <!-- TARGET CARD -->
+ <div class="material-card" id="cardTarget">
+ <div class="card-header">
+ <span class="input-label">Result</span>
+ <div class="custom-select-wrapper" id="targetSelectWrapper">
+ <div class="custom-select-trigger">
+ <span class="selection-text">Binary</span>
+ <span class="arrow">▼</span>
+ </div>
+ <div class="custom-options">
+ <div class="custom-option selected" data-value="2">Binary</div>
+ <div class="custom-option" data-value="8">Octal</div>
+ <div class="custom-option" data-value="10">Decimal</div>
+ <div class="custom-option" data-value="16">Hexadecimal</div>
+ </div>
+ <select id="toBase" style="display:none">
+ <option value="2" selected>Binary</option>
+ <option value="8">Octal</option>
+ <option value="10">Decimal</option>
+ <option value="16">Hexadecimal</option>
+ </select>
+ </div>
+ </div>
+
+ <textarea id="outputVal" class="input-area" readonly placeholder="..." spellcheck="false"></textarea>
+
+ <div class="card-footer">
+ <div class="char-count">Result</div>
+ <div class="icon-actions">
+ <button class="icon-btn" onclick="speakResult()" title="Speak">
+ <svg width="20" height="20" viewBox="0 0 24 24" fill="currentColor"><path d="M3 9v6h4l5 5V4L7 9H3zm13.5 3c0-1.77-1.02-3.29-2.5-4.03v8.05c1.48-.73 2.5-2.25 2.5-4.02zM14 3.23v2.06c2.89.86 5 3.54 5 6.71s-2.11 5.85-5 6.71v2.06c4.01-.91 7-4.49 7-8.77s-2.99-7.86-7-8.77z"/></svg>
+ </button>
+ <button class="icon-btn" id="copyBtn" onclick="copyToClipboard('outputVal', 'copyBtn')" title="Copy (Ctrl+Enter)">
+ <svg width="20" height="20" viewBox="0 0 24 24" fill="currentColor"><path d="M16 1H4c-1.1 0-2 .9-2 2v14h2V3h12V1zm3 4H8c-1.1 0-2 .9-2 2v14c0 1.1.9 2 2 2h11c1.1 0 2-.9 2-2V7c0-1.1-.9-2-2-2zm0 16H8V7h11v14z"/></svg>
+ </button>
+ </div>
+ </div>
+ </div>
+ </div>
+
+ <!-- STEPS PANEL WITH COPY BUTTON -->
+ <div class="steps-container" id="stepsContainer">
+ <div class="steps-header">
+ <h4 style="color:var(--md-sys-color-primary); margin:0;">Calculation Steps</h4>
+ <button class="icon-btn" id="copyStepsBtn" onclick="copyStepsText()" title="Copy Steps">
+ <svg width="18" height="18" viewBox="0 0 24 24" fill="currentColor"><path d="M16 1H4c-1.1 0-2 .9-2 2v14h2V3h12V1zm3 4H8c-1.1 0-2 .9-2 2v14c0 1.1.9 2 2 2h11c1.1 0 2-.9 2-2V7c0-1.1-.9-2-2-2zm0 16H8V7h11v14z"/></svg>
+ </button>
+ </div>
+ <div class="steps-content" id="stepsOutput">Waiting for input...</div>
+ </div>
+
+ <div class="history-section">
+ <div class="history-header" style="display:flex; justify-content:space-between; align-items:center; margin-bottom:10px;">
+ <h4 style="color:var(--md-sys-color-on-surface-variant); margin:0;">Recent History</h4>
+ <button class="icon-btn" id="clearHistBtn" onclick="clearHistory()" title="Clear History">
+ <svg width="18" height="18" viewBox="0 0 24 24" fill="currentColor"><path d="M6 19c0 1.1.9 2 2 2h8c1.1 0 2-.9 2-2V7H6v12zM19 4h-3.5l-1-1h-5l-1 1H5v2h14V4z"/></svg>
+ </button>
+ </div>
+ <div class="history-list" id="historyList">
+ <div style="padding:10px; color:#555; text-align:center;">No history yet</div>
+ </div>
+ </div>
+
+ </section>
+
+ <!-- SECTION 2: DYNAMIC CALCULATOR -->
+ <section id="calculator-section" class="app-section">
+ <div class="calc-wrapper">
+
+ <div class="calc-header">
+ <div class="calc-ctrl-group">
+ <label>Base Mode</label>
+ <div class="custom-select-wrapper" id="calcBaseWrapper" style="flex:1">
+ <div class="custom-select-trigger">
+ <span class="selection-text">Decimal (Base 10)</span>
+ <span class="arrow">▼</span>
+ </div>
+ <div class="custom-options">
+ <div class="custom-option" data-value="2">Binary (Base 2)</div>
+ <div class="custom-option" data-value="8">Octal (Base 8)</div>
+ <div class="custom-option selected" data-value="10">Decimal (Base 10)</div>
+ <div class="custom-option" data-value="16">Hex (Base 16)</div>
+ </div>
+ <select id="calcBase" style="display:none" onchange="validateAllRows()">
+ <option value="2">Binary</option>
+ <option value="8">Octal</option>
+ <option value="10" selected>Decimal</option>
+ <option value="16">Hex</option>
+ </select>
+ </div>
+ </div>
+ <div class="calc-ctrl-group">
+ <label>Operation</label>
+ <div class="custom-select-wrapper" id="calcOpWrapper" style="flex:1">
+ <div class="custom-select-trigger">
+ <span class="selection-text">Addition (+)</span>
+ <span class="arrow">▼</span>
+ </div>
+ <div class="custom-options">
+ <div class="custom-option selected" data-value="add">Addition (+)</div>
+ <div class="custom-option" data-value="sub">Subtraction (-)</div>
+ <div class="custom-option" data-value="mul">Multiplication (×)</div>
+ <div class="custom-option" data-value="div">Division (÷)</div>
+ </div>
+ <select id="calcOp" style="display:none" onchange="runCalculation()">
+ <option value="add" selected>Addition</option>
+ <option value="sub">Subtraction</option>
+ <option value="mul">Multiplication</option>
+ <option value="div">Division</option>
+ </select>
+ </div>
+ </div>
+ </div>
+
+ <div id="calcRows" class="calc-rows">
+ <!-- Dynamic Rows Injected Here -->
+ </div>
+
+ <!-- ADD ROW BUTTON (Left Aligned) -->
+ <button class="add-row-btn" onclick="addCalcRow()">+ Add Number</button>
+
+ <div class="calc-actions">
+ <button class="visualize-btn" id="vizBtn" onclick="visualizeCalculation()">
+ <svg width="18" height="18" viewBox="0 0 24 24" fill="currentColor"><path d="M12 4.5C7 4.5 2.73 7.61 1 12c1.73 4.39 6 7.5 11 7.5s9.27-3.11 11-7.5c-1.73-4.39-6-7.5-11-7.5zM12 17c-2.76 0-5-2.24-5-5s2.24-5 5-5 5 2.24 5 5-2.24 5-5 5zm0-8c-1.66 0-3 1.34-3 3s1.34 3 3 3 3-1.34 3-3-1.34-3-3-3z"/></svg>
+ Visualize
+ </button>
+
+ <div class="calc-result-area">
+ <div class="calc-result-display">
+ <div class="res-label">Result</div>
+ <div class="res-value" id="calcResult">0</div>
+ </div>
+ </div>
+ </div>
+
+ <!-- BIT-BY-BIT VISUALIZER PANEL -->
+ <div class="calc-visualizer" id="visualizerPanel">
+ <h4 style="color:var(--md-sys-color-primary); margin-bottom:15px; border-bottom:1px solid #333; padding-bottom:10px;">Calculation Steps</h4>
+ <div class="viz-grid" id="vizGrid">
+ <!-- Grid injected here -->
+ </div>
+ <div class="viz-info" id="vizInfo">Click visualize to start...</div>
+ </div>
+
+ </div>
+ </section>
+
+ </main>
+
+ <!-- PROJECT LEGACY MODAL -->
+ <div class="modal-overlay" id="historyModal">
+ <div class="modal-content">
+ <div class="modal-header">
+ <h3>Project Legacy</h3>
+ <button class="close-modal" onclick="toggleHistory()">×</button>
+ </div>
+ <div class="modal-body">
+ <div class="history-timeline">
+ <div class="timeline-item">
+ <div class="version-tag">v1.0 (2024)</div>
+ <p class="timeline-desc">Initial High School Project. Single file, manual conversion logic, animated background.</p>
+ </div>
+ <div class="timeline-item">
+ <div class="version-tag">v2.0</div>
+ <p class="timeline-desc">Modularized Logic & URL State. Moved away from switch cases to a Base-N engine.</p>
+ </div>
+ <div class="timeline-item">
+ <div class="version-tag">v2.1</div>
+ <p class="timeline-desc">Material You UI. Glassmorphism, animated cards, and custom dropdowns.</p>
+ </div>
+ <div class="timeline-item">
+ <div class="version-tag">v2.2</div>
+ <p class="timeline-desc">Dynamic Calculator. Added list-based arithmetic with strict validation.</p>
+ </div>
+ <div class="timeline-item">
+ <div class="version-tag">v2.3 - v2.8</div>
+ <p class="timeline-desc">Visualizer Engine. Bit-by-bit animation, step explanations, and history tape.</p>
+ </div>
+ <div class="timeline-item">
+ <div class="version-tag">v2.10</div>
+ <p class="timeline-desc">Professional Polish. Optimized UX layout, legacy tracking, and performance tuning.</p>
+ </div>
+ <div class="timeline-item">
+ <div class="version-tag current">v2.11 (2026)</div>
+ <p class="timeline-desc">Ultimate Refinement. Enhanced animations, layout stability, and smart controls.</p>
+ </div>
+ </div>
+ </div>
+ </div>
+ </div>
+
+ <!-- SIDEBAR CHEATSHEET -->
+ <aside class="sidebar" id="sidebar">
+ <button class="close-sidebar" onclick="toggleSidebar()">×</button>
+ <h3>Cheat Sheet</h3>
+
+ <h4>Powers of 2</h4>
+ <table class="cheat-table" style="margin-bottom:20px;">
+ <tr><td>2⁰</td><td>1</td></tr>
+ <tr><td>2¹</td><td>2</td></tr>
+ <tr><td>2²</td><td>4</td></tr>
+ <tr><td>2³</td><td>8</td></tr>
+ <tr><td>2⁴</td><td>16</td></tr>
+ <tr><td>2⁵</td><td>32</td></tr>
+ <tr><td>2⁶</td><td>64</td></tr>
+ <tr><td>2⁷</td><td>128</td></tr>
+ <tr><td>2⁸</td><td>256</td></tr>
+ <tr><td>2¹⁰</td><td>1024</td></tr>
+ </table>
+
+ <h4>Hex Lookup</h4>
+ <table class="cheat-table">
+ <tr><th>Dec</th><th>Hex</th><th>Bin</th></tr>
+ <tr><td>0</td><td>0</td><td>0000</td></tr>
+ <tr><td>1</td><td>1</td><td>0001</td></tr>
+ <tr><td>10</td><td>A</td><td>1010</td></tr>
+ <tr><td>11</td><td>B</td><td>1011</td></tr>
+ <tr><td>12</td><td>C</td><td>1100</td></tr>
+ <tr><td>13</td><td>D</td><td>1101</td></tr>
+ <tr><td>14</td><td>E</td><td>1110</td></tr>
+ <tr><td>15</td><td>F</td><td>1111</td></tr>
+ </table>
+ </aside>
+
+ <footer>
+ <div class="footer-content">
+ <p>© 2026 <strong>Amit Dutta</strong>. All rights reserved.</p>
+ <div class="footer-links">
+ <a href="https://amit.is-a.dev" target="_blank" class="footer-link">amit.is-a.dev</a>
+ <span>•</span>
+ <a href="https://github.com/notamitgamer" target="_blank" class="footer-link">GitHub</a>
+ </div>
+ </div>
+ </footer>
+
+ <script>
+ document.addEventListener('DOMContentLoaded', () => {
+ setupCustomDropdown('sourceSelectWrapper', 'fromBase');
+ setupCustomDropdown('targetSelectWrapper', 'toBase');
+ setupCustomDropdown('calcBaseWrapper', 'calcBase');
+ setupCustomDropdown('calcOpWrapper', 'calcOp');
+
+ setupEventListeners();
+
+ // Initialize Calculator
+ addCalcRow();
+ addCalcRow();
+
+ renderHistory();
+
+ try {
+ const params = new URLSearchParams(window.location.search);
+ if(params.has('val')) {
+ document.getElementById('inputVal').value = params.get('val');
+ if(params.has('from')) {
+ const val = params.get('from');
+ document.getElementById('fromBase').value = val;
+ updateDropdownUI('sourceSelectWrapper', val);
+ }
+ if(params.has('to')) {
+ const val = params.get('to');
+ document.getElementById('toBase').value = val;
+ updateDropdownUI('targetSelectWrapper', val);
+ }
+ convert();
+ }
+ } catch(e) {}
+
+ document.addEventListener('click', (e) => {
+ const sidebar = document.getElementById('sidebar');
+ const historyModal = document.getElementById('historyModal');
+ const toolsBtns = document.querySelectorAll('.tools-btn');
+ let clickedBtn = false;
+ toolsBtns.forEach(btn => { if(btn.contains(e.target)) clickedBtn = true; });
+
+ if (!sidebar.contains(e.target) && !clickedBtn && sidebar.classList.contains('open')) {
+ toggleSidebar();
+ }
+ if (e.target === historyModal) {
+ toggleHistory();
+ }
+ });
+ });
+
+ /* --- NAVIGATION --- */
+ function switchTab(tabName, index) {
+ document.querySelectorAll('.app-section').forEach(el => el.classList.remove('active'));
+ document.getElementById(tabName + '-section').classList.add('active');
+
+ document.querySelectorAll('.nav-tab').forEach(el => el.classList.remove('active'));
+ const tabs = document.querySelectorAll('.nav-tab');
+ tabs[index].classList.add('active');
+
+ const glider = document.getElementById('navGlider');
+ glider.style.transform = `translateX(${index * 100}%)`;
+ }
+
+ function toggleSidebar() {
+ document.getElementById('sidebar').classList.toggle('open');
+ }
+
+ function toggleHistory() {
+ const modal = document.getElementById('historyModal');
+ modal.classList.toggle('open');
+ }
+
+ /* --- DROPDOWN LOGIC --- */
+ function setupCustomDropdown(wrapperId, nativeSelectId) {
+ const wrapper = document.getElementById(wrapperId);
+ const nativeSelect = document.getElementById(nativeSelectId);
+ const trigger = wrapper.querySelector('.custom-select-trigger');
+ const triggerText = trigger.querySelector('.selection-text');
+ const options = wrapper.querySelectorAll('.custom-option');
+
+ trigger.addEventListener('click', (e) => {
+ e.stopPropagation();
+ document.querySelectorAll('.custom-select-wrapper').forEach(w => {
+ if(w !== wrapper) w.classList.remove('open');
+ });
+ wrapper.classList.toggle('open');
+ });
+
+ options.forEach(opt => {
+ opt.addEventListener('click', (e) => {
+ e.stopPropagation();
+ wrapper.querySelectorAll('.custom-option').forEach(o => o.classList.remove('selected'));
+ opt.classList.add('selected');
+ triggerText.textContent = opt.textContent;
+ wrapper.classList.remove('open');
+
+ nativeSelect.value = opt.getAttribute('data-value');
+ nativeSelect.dispatchEvent(new Event('change'));
+ });
+ });
+
+ document.addEventListener('click', () => {
+ wrapper.classList.remove('open');
+ });
+ }
+
+ function updateDropdownUI(wrapperId, value) {
+ const wrapper = document.getElementById(wrapperId);
+ const triggerText = wrapper.querySelector('.selection-text');
+ const options = wrapper.querySelectorAll('.custom-option');
+
+ options.forEach(opt => {
+ if(opt.getAttribute('data-value') === value) {
+ opt.classList.add('selected');
+ triggerText.textContent = opt.textContent;
+ } else {
+ opt.classList.remove('selected');
+ }
+ });
+ }
+
+ /* --- CONVERTER LOGIC --- */
+ function convert() {
+ const inputVal = document.getElementById('inputVal').value;
+ const outputEl = document.getElementById('outputVal');
+ const errorChip = document.getElementById('errorChip');
+ const fromBase = parseInt(document.getElementById('fromBase').value);
+ const toBase = parseInt(document.getElementById('toBase').value);
+
+ document.getElementById('sourceCount').textContent = `${inputVal.length} chars`;
+ errorChip.classList.remove('visible');
+
+ if(!inputVal) {
+ outputEl.value = '';
+ document.getElementById('stepsContainer').classList.remove('visible');
+ updateURL('');
+ return;
+ }
+
+ const normalized = inputVal.trim().toUpperCase();
+ const validChars = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ.".substring(0, fromBase) + ".";
+ const regex = new RegExp(`^[${validChars}]+$`);
+
+ if(!regex.test(normalized)) {
+ errorChip.classList.add('visible');
+ outputEl.value = "Error";
+ document.getElementById('stepsContainer').classList.remove('visible');
+ return;
+ }
+
+ try {
+ updateURL(normalized, fromBase, toBase);
+
+ const parts = normalized.split('.');
+ const decInt = parseInt(parts[0], fromBase);
+ let decTotal = decInt;
+
+ const result = decTotal.toString(toBase).toUpperCase();
+ outputEl.value = result;
+
+ generateSteps(normalized, fromBase, toBase, decTotal, result);
+ addToHistory(normalized, result, fromBase, toBase);
+
+ } catch(e) {
+ outputEl.value = "Error";
+ }
+ }
+
+ function generateSteps(input, from, to, decimal, result) {
+ const container = document.getElementById('stepsContainer');
+ const output = document.getElementById('stepsOutput');
+ container.classList.add('visible');
+
+ let log = `Converting ${input} (Base ${from}) to Base ${to}\n`;
+ log += `------------------------------------------\n`;
+
+ if(from !== 10) {
+ log += `1. Convert to Decimal:\n `;
+ const digits = input.split('.')[0].split('').reverse();
+ digits.forEach((d, i) => {
+ log += `${d}×${from}^${i} + `;
+ });
+ log = log.slice(0, -3); // Remove last +
+ log += ` = ${decimal}\n\n`;
+ } else {
+ log += `1. Already Decimal: ${decimal}\n\n`;
+ }
+
+ if(to !== 10) {
+ log += `2. Convert Decimal ${decimal} to Base ${to}:\n`;
+ let temp = decimal;
+ while(temp > 0) {
+ let rem = temp % to;
+ let quot = Math.floor(temp / to);
+ log += ` ${temp} ÷ ${to} = ${quot} [Rem: ${rem.toString(to).toUpperCase()}]\n`;
+ temp = quot;
+ }
+ log += ` Read remainders bottom-up: ${result}`;
+ } else {
+ log += `2. Result is Decimal: ${result}`;
+ }
+ output.textContent = log;
+ }
+
+ function updateURL(val, from, to) {
+ try {
+ const url = new URL(window.location);
+ if(val) {
+ url.searchParams.set('val', val);
+ url.searchParams.set('from', from);
+ url.searchParams.set('to', to);
+ window.history.replaceState({}, '', url);
+ } else {
+ const cleanUrl = window.location.pathname;
+ window.history.replaceState({}, '', cleanUrl);
+ }
+ } catch(e) {}
+ }
+
+ /* --- HISTORY LOGIC --- */
+ function addToHistory(inVal, outVal, from, to) {
+ const key = "nct_history";
+ let history = JSON.parse(localStorage.getItem(key) || "[]");
+ if(history.length > 0 && history[0].inVal === inVal && history[0].to === to) return;
+ history.unshift({ inVal, outVal, from, to });
+ if(history.length > 10) history.pop();
+ localStorage.setItem(key, JSON.stringify(history));
+ renderHistory();
+ }
+
+ function renderHistory() {
+ const list = document.getElementById('historyList');
+ const history = JSON.parse(localStorage.getItem("nct_history") || "[]");
+ if(history.length === 0) {
+ list.innerHTML = '<div style="padding:10px; color:#555; text-align:center;">No history yet</div>';
+ return;
+ }
+ list.innerHTML = '';
+ history.forEach(item => {
+ const div = document.createElement('div');
+ div.className = 'history-item';
+ div.innerHTML = `<div><span class="hist-meta">Base ${item.from} → ${item.to}</span><span class="hist-val">${item.inVal} = ${item.outVal}</span></div>`;
+ div.onclick = () => {
+ document.getElementById('inputVal').value = item.inVal;
+ document.getElementById('fromBase').value = item.from;
+ document.getElementById('toBase').value = item.to;
+ updateDropdownUI('sourceSelectWrapper', item.from.toString());
+ updateDropdownUI('targetSelectWrapper', item.to.toString());
+ convert();
+ };
+ list.appendChild(div);
+ });
+ }
+
+ function clearHistory() {
+ const list = document.getElementById('historyList');
+ const btn = document.getElementById('clearHistBtn');
+
+ if(list.children.length === 0 || list.innerText.includes("No history")) return;
+
+ // Trigger Shake Animation on Button
+ btn.classList.add('shaking');
+
+ // Trigger Slide Out Animation on List
+ list.classList.add('clearing');
+
+ setTimeout(() => {
+ localStorage.removeItem("nct_history");
+ renderHistory();
+ list.classList.remove('clearing');
+ btn.classList.remove('shaking');
+ }, 400); // Duration matches CSS animation
+ }
+
+ /* --- DYNAMIC CALCULATOR LOGIC --- */
+ function addCalcRow() {
+ const container = document.getElementById('calcRows');
+ const div = document.createElement('div');
+ div.className = 'calc-row';
+ div.innerHTML = `
+ <div class="calc-input-wrapper">
+ <input type="text" class="calc-input" placeholder="Enter number..." oninput="validateRow(this); runCalculation()">
+ </div>
+ <button class="calc-remove-btn" onclick="removeRow(this)" title="Remove">×</button>
+ `;
+ container.appendChild(div);
+ div.querySelector('input').focus();
+ }
+
+ function removeRow(btn) {
+ const row = btn.parentElement;
+ if(document.querySelectorAll('.calc-row').length > 1) {
+ row.classList.add('removing');
+ setTimeout(() => {
+ row.remove();
+ runCalculation();
+ }, 300);
+ }
+ }
+
+ function validateRow(input) {
+ const base = parseInt(document.getElementById('calcBase').value);
+ const val = input.value.trim().toUpperCase();
+ if(!val) {
+ input.classList.remove('invalid');
+ return true;
+ }
+ const validChars = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ.".substring(0, base) + ".";
+ const regex = new RegExp(`^[${validChars}]+$`);
+ if(!regex.test(val)) {
+ input.classList.add('invalid');
+ return false;
+ } else {
+ input.classList.remove('invalid');
+ return true;
+ }
+ }
+
+ function validateAllRows() {
+ document.querySelectorAll('.calc-input').forEach(input => validateRow(input));
+ runCalculation();
+ }
+
+ function runCalculation() {
+ const base = parseInt(document.getElementById('calcBase').value);
+ const op = document.getElementById('calcOp').value;
+ const inputs = document.querySelectorAll('.calc-input');
+ const vizBtn = document.getElementById('vizBtn');
+ let total = null;
+ let error = false;
+
+ inputs.forEach(input => {
+ if(input.classList.contains('invalid')) { error = true; return; }
+ const valStr = input.value.trim().toUpperCase();
+ if(!valStr) return;
+ const val = parseInt(valStr, base);
+ if(isNaN(val)) return;
+ if(total === null) { total = val; } else {
+ if(op === 'add') total += val;
+ if(op === 'sub') total -= val;
+ if(op === 'mul') total *= val;
+ if(op === 'div') total = Math.floor(total / val);
+ }
+ });
+
+ const resEl = document.getElementById('calcResult');
+ if(error) {
+ resEl.textContent = "Invalid Input";
+ resEl.style.color = "var(--md-sys-color-error)";
+ vizBtn.style.display = 'none';
+ } else if(total === null) {
+ resEl.textContent = "0";
+ resEl.style.color = "var(--md-sys-color-primary)";
+ vizBtn.style.display = 'none';
+ } else {
+ resEl.textContent = total.toString(base).toUpperCase();
+ resEl.style.color = "var(--md-sys-color-primary)";
+ vizBtn.style.display = (op === 'add') ? 'flex' : 'none';
+ }
+ document.getElementById('visualizerPanel').classList.remove('active');
+ }
+
+ /* --- VISUALIZER LOGIC (Multi-Base) --- */
+ function visualizeCalculation() {
+ const base = parseInt(document.getElementById('calcBase').value);
+ const op = document.getElementById('calcOp').value;
+ const panel = document.getElementById('visualizerPanel');
+ const grid = document.getElementById('vizGrid');
+ const info = document.getElementById('vizInfo');
+
+ if(op !== 'add') return;
+
+ const inputs = [];
+ let isValid = true;
+ document.querySelectorAll('.calc-input').forEach(inp => {
+ if(inp.value.trim()) {
+ const val = parseInt(inp.value.trim(), base);
+ if(isNaN(val)) isValid = false;
+ inputs.push(val);
+ }
+ });
+
+ if(!isValid || inputs.length < 2) {
+ alert("Enter at least two numbers.");
+ return;
+ }
+
+ panel.classList.add('active');
+ grid.innerHTML = '';
+
+ const digitArrays = inputs.map(n => n.toString(base).toUpperCase().split(''));
+ const maxLen = Math.max(...digitArrays.map(arr => arr.length)) + 1;
+
+ const paddedArrays = digitArrays.map(arr => {
+ const padding = new Array(maxLen - arr.length).fill('0');
+ return padding.concat(arr);
+ });
+
+ grid.style.gridTemplateColumns = `repeat(${maxLen}, 1fr)`;
+
+ for(let i=0; i<maxLen; i++) {
+ const cell = document.createElement('div');
+ cell.className = 'viz-cell carry';
+ cell.id = `carry-${i}`;
+ grid.appendChild(cell);
+ }
+
+ paddedArrays.forEach((rowDigits, rIdx) => {
+ for(let i=0; i<maxLen; i++) {
+ const cell = document.createElement('div');
+ cell.className = 'viz-cell';
+ if(rIdx === paddedArrays.length -1) cell.classList.add('border-bottom');
+ const val = rowDigits[i];
+ cell.textContent = val;
+ if(i === 0 && rIdx > 0) {
+ cell.classList.add('operator');
+ cell.innerHTML = `+ ${val}`;
+ }
+ grid.appendChild(cell);
+ }
+ });
+
+ for(let i=0; i<maxLen; i++) {
+ const cell = document.createElement('div');
+ cell.className = 'viz-cell result';
+ cell.id = `res-${i}`;
+ cell.textContent = '?';
+ grid.appendChild(cell);
+ }
+
+ let carry = 0;
+ let colIdx = maxLen - 1;
+
+ function step() {
+ if(colIdx < 0) {
+ info.textContent = "Complete.";
+ return;
+ }
+ document.querySelectorAll('.active-col').forEach(el => el.classList.remove('active-col'));
+ const resCell = document.getElementById(`res-${colIdx}`);
+ resCell.classList.add('active-col');
+
+ let sum = carry;
+ const digitVals = [];
+ paddedArrays.forEach(row => {
+ const char = row[colIdx];
+ const val = parseInt(char, base);
+ sum += val;
+ digitVals.push(val.toString(base).toUpperCase());
+ });
+
+ const writeVal = sum % base;
+ const newCarry = Math.floor(sum / base);
+
+ resCell.textContent = writeVal.toString(base).toUpperCase();
+ if(newCarry > 0 && colIdx > 0) {
+ document.getElementById(`carry-${colIdx-1}`).textContent = newCarry.toString(base).toUpperCase();
+ }
+
+ const eqStr = `${digitVals.join('+')} + ${carry}(c) = ${sum}`;
+ const writeStr = writeVal.toString(base).toUpperCase();
+ const carryStr = newCarry.toString(base).toUpperCase();
+ info.textContent = `Col ${maxLen - colIdx}: ${eqStr} (Base ${base}). Write ${writeStr}, Carry ${carryStr}`;
+
+ carry = newCarry;
+ colIdx--;
+ setTimeout(step, 800);
+ }
+ step();
+ }
+
+ /* --- UTILS & SWAP --- */
+ function setupEventListeners() {
+ document.addEventListener('keydown', (e) => {
+ if(e.ctrlKey && e.key === 'Enter') copyToClipboard('outputVal', 'copyBtn');
+ if(e.altKey && e.key === 's') document.getElementById('swapBtn').click();
+ });
+
+ document.getElementById('inputVal').addEventListener('input', convert);
+ document.getElementById('fromBase').addEventListener('change', convert);
+ document.getElementById('toBase').addEventListener('change', convert);
+
+ document.getElementById('swapBtn').addEventListener('click', () => {
+ const cardSource = document.getElementById('cardSource');
+ const cardTarget = document.getElementById('cardTarget');
+ const isMobile = window.innerWidth <= 768;
+
+ if(isMobile) {
+ cardSource.classList.add('anim-move-down');
+ cardTarget.classList.add('anim-move-up');
+ } else {
+ cardSource.classList.add('anim-move-right');
+ cardTarget.classList.add('anim-move-left');
+ }
+
+ setTimeout(() => {
+ const inEl = document.getElementById('inputVal');
+ const outEl = document.getElementById('outputVal');
+ const from = document.getElementById('fromBase');
+ const to = document.getElementById('toBase');
+
+ const tempVal = inEl.value;
+ inEl.value = outEl.value;
+ const tempBase = from.value;
+ from.value = to.value;
+ to.value = tempBase;
+
+ updateDropdownUI('sourceSelectWrapper', from.value);
+ updateDropdownUI('targetSelectWrapper', to.value);
+
+ cardSource.classList.remove('anim-move-down', 'anim-move-right');
+ cardTarget.classList.remove('anim-move-up', 'anim-move-left');
+ convert();
+ }, 400);
+ });
+ }
+
+ function speakResult() {
+ const txt = document.getElementById('outputVal').value;
+ if(!txt) return;
+ let spoken = txt;
+ if(document.getElementById('toBase').value == 2) {
+ spoken = txt.split('').join(' ');
+ }
+ const utt = new SpeechSynthesisUtterance(spoken);
+ window.speechSynthesis.speak(utt);
+ }
+
+ function copyToClipboard(id, btnId) {
+ const el = document.getElementById(id);
+ if(el) {
+ el.select();
+ document.execCommand('copy');
+ }
+ const btn = document.getElementById(btnId);
+ if(btn) {
+ const originalHTML = btn.innerHTML;
+ btn.innerHTML = `<svg width="20" height="20" viewBox="0 0 24 24" fill="var(--md-sys-color-success)"><path d="M9 16.17L4.83 12l-1.42 1.41L9 19 21 7l-1.41-1.41z"/></svg>`;
+ btn.classList.add('success');
+ setTimeout(() => {
+ btn.innerHTML = originalHTML;
+ btn.classList.remove('success');
+ }, 2000);
+ }
+ }
+
+ function copyStepsText() {
+ const stepsText = document.getElementById('stepsOutput').innerText;
+ navigator.clipboard.writeText(stepsText).then(() => {
+ const btn = document.getElementById('copyStepsBtn');
+ const originalHTML = btn.innerHTML;
+ btn.innerHTML = `<svg width="18" height="18" viewBox="0 0 24 24" fill="var(--md-sys-color-success)"><path d="M9 16.17L4.83 12l-1.42 1.41L9 19 21 7l-1.41-1.41z"/></svg>`;
+ btn.classList.add('success');
+ setTimeout(() => {
+ btn.innerHTML = originalHTML;
+ btn.classList.remove('success');
+ }, 2000);
+ });
+ }
+
+ function clearAll() {
+ document.getElementById('inputVal').value = '';
+ document.getElementById('outputVal').value = '';
+ document.getElementById('sourceCount').textContent = '0 chars';
+ document.getElementById('errorChip').classList.remove('visible');
+ document.getElementById('stepsContainer').classList.remove('visible');
+ updateURL('');
+ }
+ </script>
+</body>
+</html>+
\ No newline at end of file
diff --git a/nctlogo.png b/nctlogo.png
Binary files differ.