NCT

A modern, Material You-inspire...
Log | Files | Refs | Activity | README | LICENSE

root / nct-legacy.html

nct-legacy.html (79899B)


      1 <!DOCTYPE html>
      2 <html lang="en">
      3 <head>
      4     <link rel="canonical" href="https://amit.is-a.dev/nct-legacy.html" />
      5     <meta charset="UTF-8">
      6     <meta name="viewport" content="width=device-width, initial-scale=1.0">
      7     <title>NCT v2.11 | Legacy Edition</title>
      8     <link rel="icon" href="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIzMiIgaGVpZ2h0PSIzMiIgdmlld0JveD0iMCAwIDI0IDI0Ij48c3R5bGU+cGF0aHtmaWxsOiNEMEJDRkZ9QG1lZGlhIChwcmVmZXJzLWNvbG9yLXNjaGVtZTpkYXJrKXtwYXRoe2ZpbGw6I2ZmZnx9fTwvc3R5bGU+PHBhdGggZD0iTTcgMnYxMWgzdjlsNy0xMmgtNGw0LTh6Ii8+PC9zdmc+">
      9     <link rel="preconnect" href="https://fonts.googleapis.com">
     10     <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
     11     <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">
     12     <script type="application/ld+json">
     13   {
     14     "@context": "https://schema.org",
     15     "@type": "WebApplication",
     16     "name": "NCT v2.11 | Legacy Edition",
     17     "url": "https://amit.is-a.dev/nct-legacy",
     18     "image": "https://amit.is-a.dev/nctlogo.png",
     19     "description": "Number conversion tool",
     20     "applicationCategory": "EducationalApplication",
     21     "operatingSystem": "Any"
     22   }
     23 </script>
     24     <style>
     25         /* --- MATERIAL DESIGN 3 TOKENS --- */
     26         :root {
     27             --md-sys-color-background: #141218;
     28             --md-sys-color-surface: #1D1B20;
     29             --md-sys-color-surface-container: #211F26;
     30             --md-sys-color-surface-container-high: #2B2930;
     31             
     32             /* Primary */
     33             --md-sys-color-primary: #D0BCFF;
     34             --md-sys-color-on-primary: #381E72;
     35             --md-sys-color-primary-container: #4F378B;
     36             --md-sys-color-on-primary-container: #EADDFF;
     37 
     38             /* Text */
     39             --md-sys-color-on-surface: #E6E1E5;
     40             --md-sys-color-on-surface-variant: #CAC4D0;
     41             --md-sys-color-outline: #938F99;
     42 
     43             /* Error */
     44             --md-sys-color-error: #F2B8B5;
     45             --md-sys-color-error-container: #8C1D18;
     46 
     47             /* Success/Accent */
     48             --md-sys-color-tertiary: #B6C4FF;
     49             --md-sys-color-success: #4ade80;
     50             
     51             /* Dimensions */
     52             --radius-xl: 28px;
     53             --radius-l: 16px;
     54             --radius-m: 12px;
     55             --radius-s: 8px;
     56 
     57             --font-main: 'Google Sans', 'Roboto', sans-serif;
     58             --font-code: 'Roboto Mono', monospace;
     59             
     60             /* Animation */
     61             --ease-elastic: cubic-bezier(0.4, 0.0, 0.2, 1);
     62         }
     63 
     64         /* --- CUSTOM SCROLLBAR --- */
     65         ::-webkit-scrollbar { width: 6px; height: 6px; }
     66         ::-webkit-scrollbar-track { background: transparent; }
     67         ::-webkit-scrollbar-thumb { background: rgba(255,255,255,0.2); border-radius: 3px; }
     68         ::-webkit-scrollbar-thumb:hover { background: rgba(255,255,255,0.4); }
     69 
     70         * { margin: 0; padding: 0; box-sizing: border-box; -webkit-tap-highlight-color: transparent; }
     71 
     72         html, body {
     73             font-family: var(--font-main);
     74             background-color: var(--md-sys-color-background);
     75             color: var(--md-sys-color-on-surface);
     76             min-height: 100vh;
     77             display: flex;
     78             flex-direction: column;
     79             align-items: center;
     80             overflow-x: hidden;
     81             width: 100%;
     82             background-image: radial-gradient(circle at 50% 0%, rgba(208, 188, 255, 0.05) 0%, transparent 60%);
     83         }
     84 
     85         /* --- LAYOUT --- */
     86         .app-container {
     87             width: 100%;
     88             max-width: 1000px;
     89             padding: 24px;
     90             display: flex;
     91             flex-direction: column;
     92             gap: 24px;
     93             flex-grow: 1;
     94         }
     95 
     96         /* --- HEADER & NAV --- */
     97         header {
     98             display: flex;
     99             flex-direction: column;
    100             gap: 20px;
    101             padding: 16px 0;
    102         }
    103 
    104         .header-top {
    105             display: flex;
    106             justify-content: space-between;
    107             align-items: center;
    108         }
    109 
    110         .logo {
    111             font-size: 1.75rem;
    112             font-weight: 500;
    113             color: var(--md-sys-color-on-surface);
    114             display: flex;
    115             align-items: center;
    116             gap: 12px;
    117         }
    118 
    119         .logo span { color: var(--md-sys-color-primary); }
    120 
    121         /* Navigation Tabs with Animation */
    122         .nav-tabs {
    123             position: relative;
    124             display: flex;
    125             background: var(--md-sys-color-surface-container);
    126             padding: 4px;
    127             border-radius: 32px;
    128             width: fit-content;
    129             margin: 0 auto;
    130             border: 1px solid rgba(255,255,255,0.05);
    131         }
    132 
    133         .nav-glider {
    134             position: absolute;
    135             top: 4px;
    136             left: 4px;
    137             height: calc(100% - 8px);
    138             width: 110px; 
    139             background: var(--md-sys-color-primary-container);
    140             border-radius: 28px;
    141             transition: transform 0.3s var(--ease-elastic);
    142             z-index: 1;
    143         }
    144 
    145         .nav-tab {
    146             position: relative;
    147             padding: 10px 24px;
    148             border-radius: 24px;
    149             color: var(--md-sys-color-on-surface-variant);
    150             font-weight: 500;
    151             cursor: pointer;
    152             transition: color 0.2s;
    153             border: none;
    154             background: none;
    155             z-index: 2;
    156             width: 110px; 
    157         }
    158 
    159         .nav-tab.active { color: var(--md-sys-color-on-primary-container); }
    160 
    161         .header-actions {
    162             display: flex;
    163             gap: 10px;
    164         }
    165 
    166         .tools-btn {
    167             background: transparent;
    168             border: 1px solid var(--md-sys-color-outline);
    169             color: var(--md-sys-color-on-surface);
    170             padding: 8px 16px;
    171             border-radius: var(--radius-l);
    172             cursor: pointer;
    173             font-weight: 500;
    174             display: flex;
    175             align-items: center;
    176             gap: 8px;
    177             transition: transform 0.1s;
    178             white-space: nowrap; /* Prevent internal wrapping */
    179         }
    180         .tools-btn:active { transform: scale(0.95); }
    181         .tools-btn:hover { background: rgba(255,255,255,0.05); }
    182 
    183         /* --- SECTIONS --- */
    184         .app-section {
    185             display: none;
    186             flex-direction: column;
    187             gap: 24px;
    188             animation: fadeIn 0.3s ease-out;
    189         }
    190         .app-section.active { display: flex; }
    191         
    192         @keyframes fadeIn { from { opacity: 0; transform: translateY(10px); } to { opacity: 1; transform: translateY(0); } }
    193 
    194         /* --- CONVERTER STAGE --- */
    195         .converter-stage {
    196             display: grid;
    197             grid-template-columns: 1fr 64px 1fr;
    198             gap: 16px;
    199             align-items: stretch;
    200             position: relative;
    201         }
    202 
    203         .material-card {
    204             background-color: var(--md-sys-color-surface-container);
    205             border-radius: var(--radius-xl);
    206             padding: 24px;
    207             display: flex;
    208             flex-direction: column;
    209             height: 380px;
    210             position: relative;
    211             transition: transform 0.4s var(--ease-elastic), box-shadow 0.2s;
    212             border: 1px solid transparent;
    213             z-index: 1;
    214             min-width: 0; /* Prevents Grid Blowout */
    215         }
    216 
    217         .material-card:focus-within {
    218             background-color: var(--md-sys-color-surface-container-high);
    219             border-color: var(--md-sys-color-outline);
    220         }
    221 
    222         /* CARD HEADER & CUSTOM DROPDOWN */
    223         .card-header {
    224             display: flex;
    225             justify-content: space-between;
    226             align-items: center;
    227             margin-bottom: 20px;
    228             border-bottom: 1px solid var(--md-sys-color-outline);
    229             padding-bottom: 12px;
    230             z-index: 10;
    231         }
    232 
    233         .input-label {
    234             font-size: 0.875rem;
    235             font-weight: 500;
    236             color: var(--md-sys-color-primary);
    237             text-transform: uppercase;
    238             letter-spacing: 1px;
    239         }
    240 
    241         /* Custom Dropdown Styles */
    242         .custom-select-wrapper {
    243             position: relative;
    244             min-width: 150px;
    245             user-select: none;
    246         }
    247         .custom-select-trigger {
    248             display: flex;
    249             align-items: center;
    250             justify-content: flex-end;
    251             gap: 8px;
    252             cursor: pointer;
    253             color: var(--md-sys-color-on-surface);
    254             font-weight: 500;
    255             padding: 4px 0;
    256             transition: color 0.2s;
    257         }
    258         .custom-select-trigger:hover { color: var(--md-sys-color-primary); }
    259         .arrow { font-size: 0.7rem; transition: transform 0.3s; }
    260         
    261         .custom-options {
    262             position: absolute;
    263             top: 120%;
    264             right: 0;
    265             background: var(--md-sys-color-surface-container-high);
    266             border-radius: var(--radius-m);
    267             box-shadow: 0 8px 16px rgba(0,0,0,0.4);
    268             opacity: 0;
    269             visibility: hidden;
    270             pointer-events: none;
    271             transform: translateY(-10px);
    272             transition: all 0.2s var(--ease-elastic);
    273             min-width: 180px;
    274             overflow: hidden;
    275             border: 1px solid rgba(255,255,255,0.1);
    276             z-index: 1000;
    277         }
    278         .custom-select-wrapper.open .custom-options {
    279             opacity: 1; visibility: visible; pointer-events: all; transform: translateY(0);
    280         }
    281         .custom-select-wrapper.open .arrow { transform: rotate(180deg); }
    282         
    283         .custom-option {
    284             padding: 12px 16px;
    285             cursor: pointer;
    286             color: var(--md-sys-color-on-surface-variant);
    287             transition: 0.2s;
    288         }
    289         .custom-option:hover { background: rgba(255,255,255,0.05); color: var(--md-sys-color-on-surface); }
    290         .custom-option.selected { color: var(--md-sys-color-primary); background: rgba(208, 188, 255, 0.1); }
    291 
    292         /* INPUT AREA */
    293         .input-area {
    294             flex-grow: 1;
    295             width: 100%;
    296             max-width: 100%;
    297             background: transparent;
    298             border: none;
    299             color: var(--md-sys-color-on-surface);
    300             font-size: 2.2rem;
    301             font-family: var(--font-main);
    302             resize: none;
    303             outline: none;
    304             line-height: 1.2;
    305             z-index: 1;
    306             word-break: break-all; /* Ensure continuous numbers wrap */
    307         }
    308         .input-area::placeholder { color: var(--md-sys-color-on-surface-variant); opacity: 0.3; }
    309 
    310         /* Error Chip */
    311         .error-chip {
    312             position: absolute;
    313             bottom: 80px;
    314             left: 24px;
    315             background: var(--md-sys-color-error-container);
    316             color: var(--md-sys-color-error);
    317             padding: 8px 16px;
    318             border-radius: 8px;
    319             font-size: 0.875rem;
    320             display: flex;
    321             align-items: center;
    322             gap: 8px;
    323             opacity: 0;
    324             transform: translateY(10px);
    325             transition: all 0.3s;
    326             pointer-events: none;
    327         }
    328         .error-chip.visible { opacity: 1; transform: translateY(0); }
    329 
    330         /* Card Footer */
    331         .card-footer {
    332             display: flex;
    333             justify-content: space-between;
    334             align-items: center;
    335             margin-top: auto;
    336         }
    337         .char-count { font-size: 0.75rem; color: var(--md-sys-color-on-surface-variant); }
    338 
    339         /* Icons & Buttons */
    340         .icon-btn {
    341             background: transparent;
    342             border: none;
    343             color: var(--md-sys-color-on-surface-variant);
    344             width: 40px; height: 40px;
    345             border-radius: 50%;
    346             cursor: pointer;
    347             display: flex; align-items: center; justify-content: center;
    348             transition: 0.2s;
    349         }
    350         .icon-btn:hover { background: rgba(255,255,255,0.1); color: var(--md-sys-color-on-surface); transform: scale(1.1); }
    351         .icon-btn:active { transform: scale(0.9); }
    352         
    353         /* Copy Button Animation */
    354         .icon-btn.success svg {
    355             animation: check-pop 0.4s cubic-bezier(0.175, 0.885, 0.32, 1.275);
    356             color: var(--md-sys-color-success);
    357         }
    358         @keyframes check-pop {
    359             0% { transform: scale(0); }
    360             50% { transform: scale(1.3); }
    361             100% { transform: scale(1); }
    362         }
    363 
    364         /* Trash Shake Animation */
    365         @keyframes trash-shake {
    366             0% { transform: rotate(0deg); }
    367             25% { transform: rotate(-15deg); }
    368             50% { transform: rotate(15deg); }
    369             75% { transform: rotate(-15deg); }
    370             100% { transform: rotate(0deg); }
    371         }
    372         .icon-btn.shaking {
    373             animation: trash-shake 0.4s ease-in-out;
    374             color: var(--md-sys-color-error);
    375         }
    376 
    377         /* Swap Button */
    378         .swap-container { display: flex; justify-content: center; align-items: center; z-index: 2; }
    379         .swap-fab {
    380             width: 56px; height: 56px;
    381             border-radius: 16px;
    382             background-color: var(--md-sys-color-surface-container-high);
    383             border: 1px solid var(--md-sys-color-outline);
    384             color: var(--md-sys-color-on-surface);
    385             cursor: pointer;
    386             display: flex; align-items: center; justify-content: center;
    387             transition: all 0.3s var(--ease-elastic);
    388             box-shadow: 0 4px 8px rgba(0,0,0,0.2);
    389         }
    390         .swap-fab:hover {
    391             background-color: var(--md-sys-color-primary);
    392             color: var(--md-sys-color-on-primary);
    393             transform: rotate(180deg) scale(1.1);
    394             border-color: transparent;
    395         }
    396 
    397         /* --- SWAP ANIMATION CLASSES --- */
    398         .anim-move-right { transform: translateX(calc(100% + 16px)); }
    399         .anim-move-left { transform: translateX(calc(-100% - 16px)); }
    400         .anim-move-down { transform: translateY(calc(100% + 16px)); }
    401         .anim-move-up { transform: translateY(calc(-100% - 16px)); }
    402 
    403         /* --- DYNAMIC CALCULATOR --- */
    404         .calc-wrapper {
    405             background: var(--md-sys-color-surface-container);
    406             padding: 24px;
    407             border-radius: var(--radius-xl);
    408             max-width: 800px;
    409             margin: 0 auto;
    410             width: 100%;
    411         }
    412 
    413         .calc-header {
    414             display: flex;
    415             gap: 24px;
    416             margin-bottom: 24px;
    417             flex-wrap: wrap;
    418         }
    419 
    420         .calc-ctrl-group {
    421             flex: 1;
    422             min-width: 200px;
    423             display: flex;
    424             align-items: center;
    425             gap: 12px;
    426             background: rgba(0,0,0,0.2);
    427             padding: 8px 16px;
    428             border-radius: 12px;
    429         }
    430         .calc-ctrl-group label {
    431             font-size: 0.8rem;
    432             color: var(--md-sys-color-primary);
    433             text-transform: uppercase;
    434             white-space: nowrap;
    435             margin: 0;
    436         }
    437         
    438         .calc-rows {
    439             display: flex;
    440             flex-direction: column;
    441             gap: 12px;
    442             margin-bottom: 24px;
    443         }
    444 
    445         .calc-row {
    446             display: flex;
    447             gap: 12px;
    448             align-items: center;
    449             animation: fadeIn 0.3s ease-out;
    450         }
    451         
    452         /* Simple Fade Out */
    453         .calc-row.removing {
    454             opacity: 0;
    455             transition: opacity 0.3s ease;
    456         }
    457         
    458         .calc-input-wrapper {
    459             flex-grow: 1;
    460             position: relative;
    461         }
    462         .calc-input {
    463             width: 100%;
    464             background: rgba(0,0,0,0.2);
    465             border: 1px solid transparent;
    466             color: var(--md-sys-color-on-surface);
    467             padding: 16px;
    468             border-radius: 12px;
    469             font-family: var(--font-code);
    470             font-size: 1.1rem;
    471             outline: none;
    472             transition: 0.2s;
    473         }
    474         .calc-input:focus { border-color: var(--md-sys-color-primary); background: rgba(0,0,0,0.4); }
    475         .calc-input.invalid { border-color: var(--md-sys-color-error); }
    476         
    477         .calc-remove-btn {
    478             background: var(--md-sys-color-surface-container-high);
    479             border: none;
    480             color: var(--md-sys-color-error);
    481             width: 40px; height: 40px;
    482             border-radius: 8px;
    483             cursor: pointer;
    484             display: flex; align-items: center; justify-content: center;
    485             transition: 0.2s;
    486         }
    487         .calc-remove-btn:hover { background: var(--md-sys-color-error-container); color: #fff; }
    488 
    489         /* The Divider Area */
    490         .calc-actions {
    491             display: flex;
    492             align-items: center;
    493             border-top: 1px solid var(--md-sys-color-outline);
    494             padding-top: 20px;
    495             margin-top: 20px;
    496         }
    497 
    498         .add-row-btn {
    499             background: rgba(255,255,255,0.05);
    500             border: 1px dashed var(--md-sys-color-outline);
    501             color: var(--md-sys-color-on-surface-variant);
    502             padding: 10px 20px;
    503             border-radius: 12px;
    504             cursor: pointer;
    505             font-weight: 500;
    506             width: fit-content;
    507             text-align: left;
    508             transition: 0.2s;
    509             margin-bottom: 20px; /* Space before divider */
    510         }
    511         .add-row-btn:hover { border-color: var(--md-sys-color-primary); color: var(--md-sys-color-primary); background: rgba(208, 188, 255, 0.05); }
    512 
    513         .calc-result-area {
    514             display: flex;
    515             align-items: center;
    516             gap: 20px;
    517             margin-left: auto; /* Forces result to the right */
    518         }
    519 
    520         .visualize-btn {
    521             background: var(--md-sys-color-primary-container);
    522             color: var(--md-sys-color-on-primary-container);
    523             border: none;
    524             padding: 8px 16px;
    525             border-radius: 20px;
    526             font-size: 0.9rem;
    527             font-weight: 500;
    528             cursor: pointer;
    529             display: flex; /* Always visible if result exists */
    530             align-items: center;
    531             gap: 6px;
    532             transition: 0.2s;
    533             animation: popIn 0.3s cubic-bezier(0.175, 0.885, 0.32, 1.275);
    534             margin-right: auto; /* Keeps it left-aligned in actions */
    535         }
    536         .visualize-btn:hover { filter: brightness(1.2); }
    537         .visualize-btn:active { transform: scale(0.95); }
    538         @keyframes popIn { from{transform: scale(0.8); opacity:0} to{transform: scale(1); opacity:1} }
    539 
    540         .calc-result-display { text-align: right; }
    541         .res-label { font-size: 0.9rem; color: var(--md-sys-color-on-surface-variant); }
    542         .res-value { font-size: 2.2rem; color: var(--md-sys-color-primary); font-family: var(--font-code); margin-top: 5px; font-weight: 700; }
    543 
    544 
    545         /* --- VISUALIZER PANEL --- */
    546         .calc-visualizer {
    547             background: #111;
    548             border-radius: var(--radius-m);
    549             padding: 20px;
    550             margin-top: 20px;
    551             display: none; 
    552             border: 1px solid var(--md-sys-color-outline);
    553             overflow-x: auto;
    554         }
    555         .calc-visualizer.active { display: block; animation: slideIn 0.4s var(--ease-elastic); }
    556         
    557         .viz-grid {
    558             display: grid;
    559             font-family: var(--font-code);
    560             font-size: 1.2rem;
    561             color: var(--md-sys-color-on-surface);
    562             gap: 4px;
    563         }
    564         .viz-row { display: contents; }
    565         .viz-cell {
    566             padding: 4px 8px;
    567             text-align: center;
    568             position: relative;
    569         }
    570         .viz-cell.carry { color: var(--md-sys-color-tertiary); font-size: 0.8rem; height: 20px; }
    571         .viz-cell.borrow { color: var(--md-sys-color-error); font-size: 0.8rem; height: 20px; } /* NEW */
    572         .viz-cell.operator { color: var(--md-sys-color-primary); }
    573         .viz-cell.border-bottom { border-bottom: 2px solid var(--md-sys-color-outline); }
    574         .viz-cell.active-col { background: rgba(208, 188, 255, 0.15); border-radius: 4px; }
    575         .viz-cell.result { color: var(--md-sys-color-primary); font-weight: bold; }
    576 
    577         .viz-info {
    578             margin-top: 12px;
    579             font-size: 0.9rem;
    580             color: var(--md-sys-color-on-surface-variant);
    581             font-family: var(--font-code);
    582             min-height: 20px;
    583             white-space: pre-wrap; /* For multiline text */
    584         }
    585 
    586         /* --- CONVERTER STEPS PANEL --- */
    587         .steps-container {
    588             margin-top: 20px;
    589             background: rgba(0,0,0,0.2);
    590             border-radius: var(--radius-l);
    591             padding: 20px;
    592             border: 1px solid rgba(255,255,255,0.05);
    593             display: none;
    594         }
    595         .steps-container.visible { display: block; animation: fadeIn 0.5s; }
    596         
    597         .steps-header {
    598             display: flex;
    599             justify-content: space-between;
    600             align-items: center;
    601             margin-bottom: 12px;
    602             border-bottom: 1px solid rgba(255,255,255,0.1);
    603             padding-bottom: 8px;
    604         }
    605         
    606         .steps-content {
    607             font-family: var(--font-code);
    608             color: var(--md-sys-color-tertiary);
    609             font-size: 0.9rem;
    610             white-space: pre-wrap;
    611             line-height: 1.5;
    612         }
    613 
    614         /* --- HISTORY TAPE --- */
    615         .history-section {
    616             margin-top: 20px;
    617             border-top: 1px solid var(--md-sys-color-outline);
    618             padding-top: 20px;
    619         }
    620         .history-header {
    621             display: flex;
    622             justify-content: space-between;
    623             align-items: center;
    624             margin-bottom: 10px;
    625         }
    626         .history-list {
    627             display: flex; flex-direction: column; gap: 8px;
    628             max-height: 200px; overflow-y: auto;
    629             transition: opacity 0.3s;
    630         }
    631         
    632         /* History Item Animation: Slide Out to Right */
    633         .history-list.clearing .history-item {
    634             animation: slide-out-right 0.3s forwards;
    635         }
    636         @keyframes slide-out-right {
    637             to { transform: translateX(50px); opacity: 0; }
    638         }
    639 
    640         .history-item {
    641             display: flex; justify-content: space-between;
    642             padding: 12px;
    643             background: rgba(255,255,255,0.03);
    644             border-radius: 8px;
    645             font-family: var(--font-code);
    646             font-size: 0.9rem;
    647             cursor: pointer;
    648             transition: 0.2s;
    649             align-items: center;
    650         }
    651         .history-item:hover { background: rgba(255,255,255,0.08); transform: translateX(4px); }
    652         .hist-val { color: var(--md-sys-color-on-surface); }
    653         .hist-meta { font-size: 0.75rem; color: var(--md-sys-color-outline); margin-right: 10px; }
    654 
    655         /* --- FOOTER --- */
    656         footer {
    657             margin-top: 40px;
    658             text-align: center;
    659             padding: 20px;
    660             color: var(--md-sys-color-on-surface-variant);
    661             font-size: 0.85rem;
    662             border-top: 1px solid var(--md-sys-color-outline);
    663             width: 100%;
    664             background: rgba(0,0,0,0.2);
    665         }
    666         .footer-content {
    667             display: flex;
    668             flex-direction: column;
    669             gap: 8px;
    670         }
    671         .footer-links {
    672             display: flex;
    673             justify-content: center;
    674             gap: 15px;
    675             margin-top: 5px;
    676         }
    677         .footer-link {
    678             color: var(--md-sys-color-primary);
    679             text-decoration: none;
    680             transition: 0.2s;
    681         }
    682         .footer-link:hover { text-decoration: underline; color: var(--md-sys-color-tertiary); }
    683 
    684         /* --- MODAL (LEGACY & CHEATSHEET) --- */
    685         .modal-overlay {
    686             position: fixed;
    687             top: 0; left: 0; width: 100%; height: 100%;
    688             background: rgba(0,0,0,0.7);
    689             backdrop-filter: blur(4px);
    690             z-index: 2000;
    691             display: none;
    692             justify-content: center;
    693             align-items: center;
    694             opacity: 0;
    695             transition: opacity 0.3s;
    696         }
    697         .modal-overlay.open { display: flex; opacity: 1; }
    698         
    699         .modal-content {
    700             background: var(--md-sys-color-surface-container);
    701             width: 90%; max-width: 500px;
    702             border-radius: var(--radius-xl);
    703             padding: 24px;
    704             max-height: 80vh;
    705             display: flex;
    706             flex-direction: column;
    707             box-shadow: 0 10px 30px rgba(0,0,0,0.5);
    708             border: 1px solid var(--md-sys-color-outline);
    709             transform: scale(0.9);
    710             transition: transform 0.3s var(--ease-elastic);
    711         }
    712         .modal-overlay.open .modal-content { transform: scale(1); }
    713 
    714         .modal-header {
    715             display: flex; justify-content: space-between; align-items: center;
    716             margin-bottom: 20px;
    717             border-bottom: 1px solid var(--md-sys-color-outline);
    718             padding-bottom: 12px;
    719             flex-shrink: 0;
    720         }
    721         .modal-header h3 { margin: 0; color: var(--md-sys-color-primary); }
    722         .close-modal {
    723             background: none; border: none; color: var(--md-sys-color-on-surface);
    724             font-size: 1.5rem; cursor: pointer;
    725         }
    726         
    727         .modal-body {
    728             overflow-y: auto;
    729             padding-right: 5px; /* Space for scrollbar */
    730         }
    731 
    732         /* History Timeline */
    733         .timeline-item {
    734             position: relative;
    735             padding-left: 20px;
    736             border-left: 2px solid var(--md-sys-color-outline);
    737             margin-bottom: 20px;
    738         }
    739         .timeline-item::before {
    740             content: ''; position: absolute; left: -6px; top: 0;
    741             width: 10px; height: 10px; border-radius: 50%;
    742             background: var(--md-sys-color-primary);
    743         }
    744         .version-tag {
    745             font-size: 0.8rem; font-weight: bold; color: var(--md-sys-color-tertiary);
    746             margin-bottom: 4px; display: inline-block; background: rgba(255,255,255,0.1);
    747             padding: 2px 8px; border-radius: 4px;
    748         }
    749         .version-tag.current { background: var(--md-sys-color-primary); color: var(--md-sys-color-on-primary); }
    750         .timeline-desc { font-size: 0.9rem; color: var(--md-sys-color-on-surface-variant); margin: 0; }
    751 
    752         /* Sidebar (Cheat Sheet) */
    753         .sidebar {
    754             position: fixed; top: 0; right: -320px;
    755             width: 300px; height: 100%;
    756             background: var(--md-sys-color-surface-container-high);
    757             box-shadow: -5px 0 15px rgba(0,0,0,0.5);
    758             padding: 24px;
    759             transition: 0.3s ease;
    760             z-index: 1000;
    761             overflow-y: auto;
    762         }
    763         .sidebar.open { right: 0; }
    764         .sidebar h3 { color: var(--md-sys-color-primary); margin-bottom: 16px; border-bottom: 1px solid rgba(255,255,255,0.1); padding-bottom: 8px; }
    765         .cheat-table { width: 100%; border-collapse: collapse; font-family: var(--font-code); font-size: 0.8rem; }
    766         .cheat-table th, .cheat-table td { text-align: left; padding: 6px; border-bottom: 1px solid rgba(255,255,255,0.05); }
    767         .close-sidebar {
    768             position: absolute; top: 10px; right: 10px;
    769             background: none; border: none; color: var(--md-sys-color-on-surface);
    770             font-size: 1.5rem; cursor: pointer;
    771         }
    772 
    773         /* --- RESPONSIVE --- */
    774         @media (max-width: 768px) {
    775             .app-container { padding: 16px; }
    776             .converter-stage { grid-template-columns: 1fr; grid-template-rows: 1fr 64px 1fr; gap: 8px; }
    777             .material-card { height: 260px; padding: 16px; /* Tighter padding for mobile */ }
    778             .input-area { font-size: 1.8rem; /* Smaller text for mobile */ }
    779             .swap-container { transform: rotate(90deg); }
    780             .swap-fab:hover { transform: rotate(180deg); }
    781             .header-top { flex-direction: column; gap: 16px; }
    782             .header-actions { flex-wrap: wrap; justify-content: center; }
    783             .calc-header { flex-direction: column; gap: 10px; }
    784             
    785             /* --- UPDATED MOBILE LAYOUT FOR VISUALIZER BTN --- */
    786             .calc-actions { 
    787                 flex-direction: row; 
    788                 align-items: center; 
    789                 justify-content: space-between;
    790                 gap: 10px;
    791             }
    792             .visualize-btn { 
    793                 margin-right: 0; 
    794                 margin-bottom: 0;
    795                 flex-shrink: 0; 
    796             }
    797             .calc-result-area { 
    798                 width: auto; 
    799                 margin-left: auto;
    800                 justify-content: flex-end;
    801             }
    802         }
    803     </style>
    804 </head>
    805 <body>
    806 
    807     <header class="app-container">
    808         <div class="header-top">
    809             <div class="logo">
    810                 <svg width="32" height="32" viewBox="0 0 24 24" fill="var(--md-sys-color-primary)"><path d="M7 2v11h3v9l7-12h-4l4-8z"/></svg>
    811                 NCT <span>v2.11</span>
    812             </div>
    813             
    814             <!-- Animated Navigation -->
    815             <div class="nav-tabs">
    816                 <div class="nav-glider" id="navGlider"></div>
    817                 <button class="nav-tab active" onclick="switchTab('converter', 0)">Converter</button>
    818                 <button class="nav-tab" onclick="switchTab('calculator', 1)">Calculator</button>
    819             </div>
    820 
    821             <div class="header-actions">
    822                 <button class="tools-btn" onclick="window.open('https://amit.is-a.dev/code-nct-legacy', '_blank')">
    823     <svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
    824         <polyline points="16 18 22 12 16 6"></polyline>
    825         <polyline points="8 6 2 12 8 18"></polyline>
    826     </svg>
    827     Source Code
    828 </button>
    829 
    830                 <button class="tools-btn" onclick="toggleHistory()">
    831                     <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>
    832                     Versions
    833                 </button>
    834                 <button class="tools-btn" onclick="toggleSidebar()">
    835                     <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>
    836                     Cheat Sheet
    837                 </button>
    838             </div>
    839         </div>
    840     </header>
    841 
    842     <main class="app-container">
    843         
    844         <!-- SECTION 1: CONVERTER -->
    845         <section id="converter-section" class="app-section active">
    846             
    847             <div class="converter-stage">
    848                 <!-- SOURCE CARD -->
    849                 <div class="material-card" id="cardSource">
    850                     <div class="card-header">
    851                         <span class="input-label">Input</span>
    852                         <div class="custom-select-wrapper" id="sourceSelectWrapper">
    853                             <div class="custom-select-trigger">
    854                                 <span class="selection-text">Decimal</span>
    855                                 <span class="arrow">▼</span>
    856                             </div>
    857                             <div class="custom-options">
    858                                 <div class="custom-option" data-value="2">Binary</div>
    859                                 <div class="custom-option" data-value="8">Octal</div>
    860                                 <div class="custom-option selected" data-value="10">Decimal</div>
    861                                 <div class="custom-option" data-value="16">Hexadecimal</div>
    862                             </div>
    863                             <select id="fromBase" style="display:none">
    864                                 <option value="2">Binary</option>
    865                                 <option value="8">Octal</option>
    866                                 <option value="10" selected>Decimal</option>
    867                                 <option value="16">Hexadecimal</option>
    868                             </select>
    869                         </div>
    870                     </div>
    871                     
    872                     <textarea id="inputVal" class="input-area" placeholder="0" spellcheck="false"></textarea>
    873                     <div id="errorChip" class="error-chip">⚠️ Invalid character for this base</div>
    874                     
    875                     <div class="card-footer">
    876                         <div class="char-count" id="sourceCount">0 chars</div>
    877                         <div class="icon-actions">
    878                             <button class="icon-btn" onclick="clearAll()" title="Clear">
    879                                 <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>
    880                             </button>
    881                         </div>
    882                     </div>
    883                 </div>
    884 
    885                 <!-- SWAP -->
    886                 <div class="swap-container">
    887                     <button class="swap-fab" id="swapBtn" title="Swap (Alt+S)">
    888                         <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>
    889                     </button>
    890                 </div>
    891 
    892                 <!-- TARGET CARD -->
    893                 <div class="material-card" id="cardTarget">
    894                     <div class="card-header">
    895                         <span class="input-label">Result</span>
    896                         <div class="custom-select-wrapper" id="targetSelectWrapper">
    897                             <div class="custom-select-trigger">
    898                                 <span class="selection-text">Binary</span>
    899                                 <span class="arrow">▼</span>
    900                             </div>
    901                             <div class="custom-options">
    902                                 <div class="custom-option selected" data-value="2">Binary</div>
    903                                 <div class="custom-option" data-value="8">Octal</div>
    904                                 <div class="custom-option" data-value="10">Decimal</div>
    905                                 <div class="custom-option" data-value="16">Hexadecimal</div>
    906                             </div>
    907                             <select id="toBase" style="display:none">
    908                                 <option value="2" selected>Binary</option>
    909                                 <option value="8">Octal</option>
    910                                 <option value="10">Decimal</option>
    911                                 <option value="16">Hexadecimal</option>
    912                             </select>
    913                         </div>
    914                     </div>
    915                     
    916                     <textarea id="outputVal" class="input-area" readonly placeholder="..." spellcheck="false"></textarea>
    917                     
    918                     <div class="card-footer">
    919                         <div class="char-count">Result</div>
    920                         <div class="icon-actions">
    921                             <button class="icon-btn" onclick="speakResult()" title="Speak">
    922                                 <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>
    923                             </button>
    924                             <button class="icon-btn" id="copyBtn" onclick="copyToClipboard('outputVal', 'copyBtn')" title="Copy (Ctrl+Enter)">
    925                                 <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>
    926                             </button>
    927                         </div>
    928                     </div>
    929                 </div>
    930             </div>
    931 
    932             <!-- STEPS PANEL WITH COPY BUTTON -->
    933             <div class="steps-container" id="stepsContainer">
    934                 <div class="steps-header">
    935                     <h4 style="color:var(--md-sys-color-primary); margin:0;">Calculation Steps</h4>
    936                     <button class="icon-btn" id="copyStepsBtn" onclick="copyStepsText()" title="Copy Steps">
    937                         <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>
    938                     </button>
    939                 </div>
    940                 <div class="steps-content" id="stepsOutput">Waiting for input...</div>
    941             </div>
    942 
    943             <div class="history-section">
    944                 <div class="history-header" style="display:flex; justify-content:space-between; align-items:center; margin-bottom:10px;">
    945                     <h4 style="color:var(--md-sys-color-on-surface-variant); margin:0;">Recent History</h4>
    946                     <button class="icon-btn" id="clearHistBtn" onclick="clearHistory()" title="Clear History">
    947                         <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>
    948                     </button>
    949                 </div>
    950                 <div class="history-list" id="historyList">
    951                     <div style="padding:10px; color:#555; text-align:center;">No history yet</div>
    952                 </div>
    953             </div>
    954 
    955         </section>
    956 
    957         <!-- SECTION 2: DYNAMIC CALCULATOR -->
    958         <section id="calculator-section" class="app-section">
    959             <div class="calc-wrapper">
    960                 
    961                 <div class="calc-header">
    962                     <div class="calc-ctrl-group">
    963                         <label>Base Mode</label>
    964                         <div class="custom-select-wrapper" id="calcBaseWrapper" style="flex:1">
    965                             <div class="custom-select-trigger">
    966                                 <span class="selection-text">Decimal (Base 10)</span>
    967                                 <span class="arrow">▼</span>
    968                             </div>
    969                             <div class="custom-options">
    970                                 <div class="custom-option" data-value="2">Binary (Base 2)</div>
    971                                 <div class="custom-option" data-value="8">Octal (Base 8)</div>
    972                                 <div class="custom-option selected" data-value="10">Decimal (Base 10)</div>
    973                                 <div class="custom-option" data-value="16">Hex (Base 16)</div>
    974                             </div>
    975                             <select id="calcBase" style="display:none" onchange="validateAllRows()">
    976                                 <option value="2">Binary</option>
    977                                 <option value="8">Octal</option>
    978                                 <option value="10" selected>Decimal</option>
    979                                 <option value="16">Hex</option>
    980                             </select>
    981                         </div>
    982                     </div>
    983                     <div class="calc-ctrl-group">
    984                         <label>Operation</label>
    985                         <div class="custom-select-wrapper" id="calcOpWrapper" style="flex:1">
    986                             <div class="custom-select-trigger">
    987                                 <span class="selection-text">Addition (+)</span>
    988                                 <span class="arrow">▼</span>
    989                             </div>
    990                             <div class="custom-options">
    991                                 <div class="custom-option selected" data-value="add">Addition (+)</div>
    992                                 <div class="custom-option" data-value="sub">Subtraction (-)</div>
    993                                 <div class="custom-option" data-value="mul">Multiplication (×)</div>
    994                                 <div class="custom-option" data-value="div">Division (÷)</div>
    995                             </div>
    996                             <select id="calcOp" style="display:none" onchange="runCalculation()">
    997                                 <option value="add" selected>Addition</option>
    998                                 <option value="sub">Subtraction</option>
    999                                 <option value="mul">Multiplication</option>
   1000                                 <option value="div">Division</option>
   1001                             </select>
   1002                         </div>
   1003                     </div>
   1004                 </div>
   1005 
   1006                 <div id="calcRows" class="calc-rows">
   1007                     <!-- Dynamic Rows Injected Here -->
   1008                 </div>
   1009 
   1010                 <!-- ADD ROW BUTTON (Left Aligned) -->
   1011                 <button class="add-row-btn" onclick="addCalcRow()">+ Add Number</button>
   1012 
   1013                 <div class="calc-actions">
   1014                     <button class="visualize-btn" id="vizBtn" onclick="visualizeCalculation()">
   1015                         <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>
   1016                         Visualize
   1017                     </button>
   1018                     
   1019                     <div class="calc-result-area">
   1020                         <div class="calc-result-display">
   1021                             <div class="res-label">Result</div>
   1022                             <div class="res-value" id="calcResult">0</div>
   1023                         </div>
   1024                     </div>
   1025                 </div>
   1026 
   1027                 <!-- BIT-BY-BIT VISUALIZER PANEL -->
   1028                 <div class="calc-visualizer" id="visualizerPanel">
   1029                     <h4 style="color:var(--md-sys-color-primary); margin-bottom:15px; border-bottom:1px solid #333; padding-bottom:10px;">Calculation Steps</h4>
   1030                     <div class="viz-grid" id="vizGrid">
   1031                         <!-- Grid injected here -->
   1032                     </div>
   1033                     <div class="viz-info" id="vizInfo">Click visualize to start...</div>
   1034                 </div>
   1035                 
   1036             </div>
   1037         </section>
   1038 
   1039     </main>
   1040 
   1041     <!-- PROJECT LEGACY MODAL -->
   1042     <div class="modal-overlay" id="historyModal">
   1043         <div class="modal-content">
   1044             <div class="modal-header">
   1045                 <h3>Project Legacy</h3>
   1046                 <button class="close-modal" onclick="toggleHistory()">×</button>
   1047             </div>
   1048             <div class="modal-body">
   1049                 <div class="history-timeline">
   1050                     <div class="timeline-item">
   1051                         <div class="version-tag">v1.0 (2024)</div>
   1052                         <p class="timeline-desc">Initial High School Project. Single file, manual conversion logic, animated background.</p>
   1053                     </div>
   1054                     <div class="timeline-item">
   1055                         <div class="version-tag">v2.0</div>
   1056                         <p class="timeline-desc">Modularized Logic & URL State. Moved away from switch cases to a Base-N engine.</p>
   1057                     </div>
   1058                     <div class="timeline-item">
   1059                         <div class="version-tag">v2.1</div>
   1060                         <p class="timeline-desc">Material You UI. Glassmorphism, animated cards, and custom dropdowns.</p>
   1061                     </div>
   1062                     <div class="timeline-item">
   1063                         <div class="version-tag">v2.2</div>
   1064                         <p class="timeline-desc">Dynamic Calculator. Added list-based arithmetic with strict validation.</p>
   1065                     </div>
   1066                     <div class="timeline-item">
   1067                         <div class="version-tag">v2.3 - v2.8</div>
   1068                         <p class="timeline-desc">Visualizer Engine. Bit-by-bit animation, step explanations, and history tape.</p>
   1069                     </div>
   1070                     <div class="timeline-item">
   1071                         <div class="version-tag">v2.10</div>
   1072                         <p class="timeline-desc">Professional Polish. Optimized UX layout, legacy tracking, and performance tuning.</p>
   1073                     </div>
   1074                     <div class="timeline-item">
   1075                         <div class="version-tag current">v2.11 (2026)</div>
   1076                         <p class="timeline-desc">Ultimate Refinement. Enhanced animations, layout stability, and smart controls.</p>
   1077                     </div>
   1078                 </div>
   1079             </div>
   1080         </div>
   1081     </div>
   1082 
   1083     <!-- SIDEBAR CHEATSHEET -->
   1084     <aside class="sidebar" id="sidebar">
   1085         <button class="close-sidebar" onclick="toggleSidebar()">×</button>
   1086         <h3>Cheat Sheet</h3>
   1087         
   1088         <h4>Powers of 2</h4>
   1089         <table class="cheat-table" style="margin-bottom:20px;">
   1090             <tr><td>2⁰</td><td>1</td></tr>
   1091             <tr><td>2¹</td><td>2</td></tr>
   1092             <tr><td>2²</td><td>4</td></tr>
   1093             <tr><td>2³</td><td>8</td></tr>
   1094             <tr><td>2⁴</td><td>16</td></tr>
   1095             <tr><td>2⁵</td><td>32</td></tr>
   1096             <tr><td>2⁶</td><td>64</td></tr>
   1097             <tr><td>2⁷</td><td>128</td></tr>
   1098             <tr><td>2⁸</td><td>256</td></tr>
   1099             <tr><td>2¹⁰</td><td>1024</td></tr>
   1100         </table>
   1101 
   1102         <h4>Hex Lookup</h4>
   1103         <table class="cheat-table">
   1104             <tr><th>Dec</th><th>Hex</th><th>Bin</th></tr>
   1105             <tr><td>0</td><td>0</td><td>0000</td></tr>
   1106             <tr><td>1</td><td>1</td><td>0001</td></tr>
   1107             <tr><td>10</td><td>A</td><td>1010</td></tr>
   1108             <tr><td>11</td><td>B</td><td>1011</td></tr>
   1109             <tr><td>12</td><td>C</td><td>1100</td></tr>
   1110             <tr><td>13</td><td>D</td><td>1101</td></tr>
   1111             <tr><td>14</td><td>E</td><td>1110</td></tr>
   1112             <tr><td>15</td><td>F</td><td>1111</td></tr>
   1113         </table>
   1114     </aside>
   1115 
   1116     <footer>
   1117         <div class="footer-content">
   1118             <p>&copy; 2026 <strong>Amit Dutta</strong>. All rights reserved.</p>
   1119             <div class="footer-links">
   1120                 <a href="https://amit.is-a.dev" target="_blank" class="footer-link">amit.is-a.dev</a>
   1121                 <span>•</span>
   1122                 <a href="https://github.com/notamitgamer" target="_blank" class="footer-link">GitHub</a>
   1123             </div>
   1124         </div>
   1125     </footer>
   1126 
   1127     <script>
   1128         document.addEventListener('DOMContentLoaded', () => {
   1129             setupCustomDropdown('sourceSelectWrapper', 'fromBase');
   1130             setupCustomDropdown('targetSelectWrapper', 'toBase');
   1131             setupCustomDropdown('calcBaseWrapper', 'calcBase');
   1132             setupCustomDropdown('calcOpWrapper', 'calcOp');
   1133             
   1134             setupEventListeners();
   1135             
   1136             // Initialize Calculator
   1137             addCalcRow();
   1138             addCalcRow();
   1139             
   1140             renderHistory();
   1141 
   1142             try {
   1143                 const params = new URLSearchParams(window.location.search);
   1144                 if(params.has('val')) {
   1145                     document.getElementById('inputVal').value = params.get('val');
   1146                     if(params.has('from')) {
   1147                         const val = params.get('from');
   1148                         document.getElementById('fromBase').value = val;
   1149                         updateDropdownUI('sourceSelectWrapper', val);
   1150                     }
   1151                     if(params.has('to')) {
   1152                         const val = params.get('to');
   1153                         document.getElementById('toBase').value = val;
   1154                         updateDropdownUI('targetSelectWrapper', val);
   1155                     }
   1156                     convert();
   1157                 }
   1158             } catch(e) {}
   1159             
   1160             document.addEventListener('click', (e) => {
   1161                 const sidebar = document.getElementById('sidebar');
   1162                 const historyModal = document.getElementById('historyModal');
   1163                 const toolsBtns = document.querySelectorAll('.tools-btn');
   1164                 let clickedBtn = false;
   1165                 toolsBtns.forEach(btn => { if(btn.contains(e.target)) clickedBtn = true; });
   1166 
   1167                 if (!sidebar.contains(e.target) && !clickedBtn && sidebar.classList.contains('open')) {
   1168                     toggleSidebar();
   1169                 }
   1170                 if (e.target === historyModal) {
   1171                     toggleHistory();
   1172                 }
   1173             });
   1174         });
   1175 
   1176         /* --- NAVIGATION --- */
   1177         function switchTab(tabName, index) {
   1178             document.querySelectorAll('.app-section').forEach(el => el.classList.remove('active'));
   1179             document.getElementById(tabName + '-section').classList.add('active');
   1180             
   1181             document.querySelectorAll('.nav-tab').forEach(el => el.classList.remove('active'));
   1182             const tabs = document.querySelectorAll('.nav-tab');
   1183             tabs[index].classList.add('active');
   1184             
   1185             const glider = document.getElementById('navGlider');
   1186             glider.style.transform = `translateX(${index * 100}%)`;
   1187         }
   1188 
   1189         function toggleSidebar() {
   1190             document.getElementById('sidebar').classList.toggle('open');
   1191         }
   1192 
   1193         function toggleHistory() {
   1194             const modal = document.getElementById('historyModal');
   1195             modal.classList.toggle('open');
   1196         }
   1197 
   1198         /* --- DROPDOWN LOGIC --- */
   1199         function setupCustomDropdown(wrapperId, nativeSelectId) {
   1200             const wrapper = document.getElementById(wrapperId);
   1201             const nativeSelect = document.getElementById(nativeSelectId);
   1202             const trigger = wrapper.querySelector('.custom-select-trigger');
   1203             const triggerText = trigger.querySelector('.selection-text');
   1204             const options = wrapper.querySelectorAll('.custom-option');
   1205 
   1206             trigger.addEventListener('click', (e) => {
   1207                 e.stopPropagation();
   1208                 document.querySelectorAll('.custom-select-wrapper').forEach(w => {
   1209                     if(w !== wrapper) w.classList.remove('open');
   1210                 });
   1211                 wrapper.classList.toggle('open');
   1212             });
   1213 
   1214             options.forEach(opt => {
   1215                 opt.addEventListener('click', (e) => {
   1216                     e.stopPropagation();
   1217                     wrapper.querySelectorAll('.custom-option').forEach(o => o.classList.remove('selected'));
   1218                     opt.classList.add('selected');
   1219                     triggerText.textContent = opt.textContent;
   1220                     wrapper.classList.remove('open');
   1221                     
   1222                     nativeSelect.value = opt.getAttribute('data-value');
   1223                     nativeSelect.dispatchEvent(new Event('change'));
   1224                 });
   1225             });
   1226 
   1227             document.addEventListener('click', () => {
   1228                 wrapper.classList.remove('open');
   1229             });
   1230         }
   1231         
   1232         function updateDropdownUI(wrapperId, value) {
   1233             const wrapper = document.getElementById(wrapperId);
   1234             const triggerText = wrapper.querySelector('.selection-text');
   1235             const options = wrapper.querySelectorAll('.custom-option');
   1236             
   1237             options.forEach(opt => {
   1238                 if(opt.getAttribute('data-value') === value) {
   1239                     opt.classList.add('selected');
   1240                     triggerText.textContent = opt.textContent;
   1241                 } else {
   1242                     opt.classList.remove('selected');
   1243                 }
   1244             });
   1245         }
   1246 
   1247         /* --- CONVERTER LOGIC --- */
   1248         function convert() {
   1249             const inputVal = document.getElementById('inputVal').value;
   1250             const outputEl = document.getElementById('outputVal');
   1251             const errorChip = document.getElementById('errorChip');
   1252             const fromBase = parseInt(document.getElementById('fromBase').value);
   1253             const toBase = parseInt(document.getElementById('toBase').value);
   1254 
   1255             document.getElementById('sourceCount').textContent = `${inputVal.length} chars`;
   1256             errorChip.classList.remove('visible');
   1257 
   1258             if(!inputVal) { 
   1259                 outputEl.value = ''; 
   1260                 document.getElementById('stepsContainer').classList.remove('visible');
   1261                 updateURL('');
   1262                 return; 
   1263             }
   1264 
   1265             const normalized = inputVal.trim().toUpperCase();
   1266             const validChars = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ.".substring(0, fromBase) + ".";
   1267             const regex = new RegExp(`^[${validChars}]+$`);
   1268             
   1269             if(!regex.test(normalized)) {
   1270                 errorChip.classList.add('visible');
   1271                 outputEl.value = "Error";
   1272                 document.getElementById('stepsContainer').classList.remove('visible');
   1273                 return;
   1274             }
   1275 
   1276             try {
   1277                 updateURL(normalized, fromBase, toBase);
   1278 
   1279                 const parts = normalized.split('.');
   1280                 const decInt = parseInt(parts[0], fromBase);
   1281                 let decTotal = decInt;
   1282                 
   1283                 const result = decTotal.toString(toBase).toUpperCase();
   1284                 outputEl.value = result;
   1285 
   1286                 generateSteps(normalized, fromBase, toBase, decTotal, result);
   1287                 addToHistory(normalized, result, fromBase, toBase);
   1288 
   1289             } catch(e) {
   1290                 outputEl.value = "Error";
   1291             }
   1292         }
   1293 
   1294         function generateSteps(input, from, to, decimal, result) {
   1295             const container = document.getElementById('stepsContainer');
   1296             const output = document.getElementById('stepsOutput');
   1297             container.classList.add('visible');
   1298             
   1299             let log = `Converting ${input} (Base ${from}) to Base ${to}\n`;
   1300             log += `------------------------------------------\n`;
   1301             
   1302             if(from !== 10) {
   1303                 log += `1. Convert to Decimal:\n   `;
   1304                 const digits = input.split('.')[0].split('').reverse();
   1305                 digits.forEach((d, i) => {
   1306                     log += `${d}×${from}^${i} + `;
   1307                 });
   1308                 log = log.slice(0, -3); // Remove last +
   1309                 log += ` = ${decimal}\n\n`;
   1310             } else {
   1311                 log += `1. Already Decimal: ${decimal}\n\n`;
   1312             }
   1313 
   1314             if(to !== 10) {
   1315                 log += `2. Convert Decimal ${decimal} to Base ${to}:\n`;
   1316                 let temp = decimal;
   1317                 while(temp > 0) {
   1318                     let rem = temp % to;
   1319                     let quot = Math.floor(temp / to);
   1320                     log += `   ${temp} ÷ ${to} = ${quot} [Rem: ${rem.toString(to).toUpperCase()}]\n`;
   1321                     temp = quot;
   1322                 }
   1323                 log += `   Read remainders bottom-up: ${result}`;
   1324             } else {
   1325                 log += `2. Result is Decimal: ${result}`;
   1326             }
   1327             output.textContent = log;
   1328         }
   1329 
   1330         function updateURL(val, from, to) {
   1331             try {
   1332                 const url = new URL(window.location);
   1333                 if(val) {
   1334                     url.searchParams.set('val', val);
   1335                     url.searchParams.set('from', from);
   1336                     url.searchParams.set('to', to);
   1337                     window.history.replaceState({}, '', url);
   1338                 } else {
   1339                     const cleanUrl = window.location.pathname;
   1340                     window.history.replaceState({}, '', cleanUrl);
   1341                 }
   1342             } catch(e) {}
   1343         }
   1344 
   1345         /* --- HISTORY LOGIC --- */
   1346         function addToHistory(inVal, outVal, from, to) {
   1347             const key = "nct_history";
   1348             let history = JSON.parse(localStorage.getItem(key) || "[]");
   1349             if(history.length > 0 && history[0].inVal === inVal && history[0].to === to) return;
   1350             history.unshift({ inVal, outVal, from, to });
   1351             if(history.length > 10) history.pop();
   1352             localStorage.setItem(key, JSON.stringify(history));
   1353             renderHistory();
   1354         }
   1355 
   1356         function renderHistory() {
   1357             const list = document.getElementById('historyList');
   1358             const history = JSON.parse(localStorage.getItem("nct_history") || "[]");
   1359             if(history.length === 0) {
   1360                 list.innerHTML = '<div style="padding:10px; color:#555; text-align:center;">No history yet</div>';
   1361                 return;
   1362             }
   1363             list.innerHTML = '';
   1364             history.forEach(item => {
   1365                 const div = document.createElement('div');
   1366                 div.className = 'history-item';
   1367                 div.innerHTML = `<div><span class="hist-meta">Base ${item.from} → ${item.to}</span><span class="hist-val">${item.inVal} = ${item.outVal}</span></div>`;
   1368                 div.onclick = () => {
   1369                     document.getElementById('inputVal').value = item.inVal;
   1370                     document.getElementById('fromBase').value = item.from;
   1371                     document.getElementById('toBase').value = item.to;
   1372                     updateDropdownUI('sourceSelectWrapper', item.from.toString());
   1373                     updateDropdownUI('targetSelectWrapper', item.to.toString());
   1374                     convert();
   1375                 };
   1376                 list.appendChild(div);
   1377             });
   1378         }
   1379         
   1380         function clearHistory() {
   1381             const list = document.getElementById('historyList');
   1382             const btn = document.getElementById('clearHistBtn');
   1383             
   1384             if(list.children.length === 0 || list.innerText.includes("No history")) return;
   1385             
   1386             // Trigger Shake Animation on Button
   1387             btn.classList.add('shaking');
   1388             
   1389             // Trigger Slide Out Animation on List
   1390             list.classList.add('clearing');
   1391             
   1392             setTimeout(() => {
   1393                 localStorage.removeItem("nct_history");
   1394                 renderHistory();
   1395                 list.classList.remove('clearing');
   1396                 btn.classList.remove('shaking');
   1397             }, 400); // Duration matches CSS animation
   1398         }
   1399 
   1400         /* --- DYNAMIC CALCULATOR LOGIC --- */
   1401         function addCalcRow() {
   1402             const container = document.getElementById('calcRows');
   1403             const div = document.createElement('div');
   1404             div.className = 'calc-row';
   1405             div.innerHTML = `
   1406                 <div class="calc-input-wrapper">
   1407                     <input type="text" class="calc-input" placeholder="Enter number..." oninput="validateRow(this); runCalculation()">
   1408                 </div>
   1409                 <button class="calc-remove-btn" onclick="removeRow(this)" title="Remove">×</button>
   1410             `;
   1411             container.appendChild(div);
   1412             div.querySelector('input').focus();
   1413         }
   1414 
   1415         function removeRow(btn) {
   1416             const row = btn.parentElement;
   1417             if(document.querySelectorAll('.calc-row').length > 1) {
   1418                 row.classList.add('removing');
   1419                 setTimeout(() => {
   1420                     row.remove();
   1421                     runCalculation();
   1422                 }, 300);
   1423             }
   1424         }
   1425 
   1426         function validateRow(input) {
   1427             const base = parseInt(document.getElementById('calcBase').value);
   1428             const val = input.value.trim().toUpperCase();
   1429             if(!val) {
   1430                 input.classList.remove('invalid');
   1431                 return true;
   1432             }
   1433             const validChars = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ.".substring(0, base) + ".";
   1434             const regex = new RegExp(`^[${validChars}]+$`);
   1435             if(!regex.test(val)) {
   1436                 input.classList.add('invalid');
   1437                 return false;
   1438             } else {
   1439                 input.classList.remove('invalid');
   1440                 return true;
   1441             }
   1442         }
   1443 
   1444         function validateAllRows() {
   1445             document.querySelectorAll('.calc-input').forEach(input => validateRow(input));
   1446             runCalculation();
   1447         }
   1448 
   1449         function runCalculation() {
   1450             const base = parseInt(document.getElementById('calcBase').value);
   1451             const op = document.getElementById('calcOp').value;
   1452             const inputs = document.querySelectorAll('.calc-input');
   1453             const vizBtn = document.getElementById('vizBtn');
   1454             let total = null;
   1455             let error = false;
   1456 
   1457             inputs.forEach(input => {
   1458                 if(input.classList.contains('invalid')) { error = true; return; }
   1459                 const valStr = input.value.trim().toUpperCase();
   1460                 if(!valStr) return;
   1461                 const val = parseInt(valStr, base);
   1462                 if(isNaN(val)) return;
   1463                 if(total === null) { total = val; } else {
   1464                     if(op === 'add') total += val;
   1465                     if(op === 'sub') total -= val;
   1466                     if(op === 'mul') total *= val;
   1467                     if(op === 'div') total = Math.floor(total / val);
   1468                 }
   1469             });
   1470 
   1471             const resEl = document.getElementById('calcResult');
   1472             if(error) {
   1473                 resEl.textContent = "Invalid Input";
   1474                 resEl.style.color = "var(--md-sys-color-error)";
   1475                 // vizBtn.style.display = 'none'; // Replaced to always show if result valid
   1476             } else if(total === null) {
   1477                 resEl.textContent = "0";
   1478                 resEl.style.color = "var(--md-sys-color-primary)";
   1479                 // vizBtn.style.display = 'none'; // Replaced
   1480             } else {
   1481                 resEl.textContent = total.toString(base).toUpperCase();
   1482                 resEl.style.color = "var(--md-sys-color-primary)";
   1483             }
   1484             // Always show visualize button if we have result
   1485             if(!error && total !== null) {
   1486                 vizBtn.style.display = 'flex';
   1487             } else {
   1488                 vizBtn.style.display = 'none';
   1489             }
   1490             document.getElementById('visualizerPanel').classList.remove('active');
   1491         }
   1492 
   1493         /* --- VISUALIZER LOGIC (Multi-Base) --- */
   1494         function visualizeCalculation() {
   1495             const base = parseInt(document.getElementById('calcBase').value);
   1496             const op = document.getElementById('calcOp').value;
   1497             const panel = document.getElementById('visualizerPanel');
   1498             const grid = document.getElementById('vizGrid');
   1499             const info = document.getElementById('vizInfo');
   1500             
   1501             const inputs = [];
   1502             let isValid = true;
   1503             document.querySelectorAll('.calc-input').forEach(inp => {
   1504                 if(inp.value.trim()) {
   1505                      const val = parseInt(inp.value.trim(), base);
   1506                      if(isNaN(val)) isValid = false;
   1507                      inputs.push(val);
   1508                 }
   1509             });
   1510 
   1511             if(!isValid || inputs.length < 2) {
   1512                 alert("Enter at least two numbers.");
   1513                 return;
   1514             }
   1515 
   1516             panel.classList.add('active');
   1517             grid.innerHTML = '';
   1518             info.textContent = ''; // Clear previous
   1519 
   1520             // === ADDITION (Existing Grid) ===
   1521             if (op === 'add') {
   1522                 const digitArrays = inputs.map(n => n.toString(base).toUpperCase().split(''));
   1523                 const maxLen = Math.max(...digitArrays.map(arr => arr.length)) + 1;
   1524                 
   1525                 const paddedArrays = digitArrays.map(arr => {
   1526                     const padding = new Array(maxLen - arr.length).fill('0');
   1527                     return padding.concat(arr);
   1528                 });
   1529                 
   1530                 grid.style.gridTemplateColumns = `repeat(${maxLen}, 1fr)`;
   1531                 
   1532                 for(let i=0; i<maxLen; i++) {
   1533                     const cell = document.createElement('div');
   1534                     cell.className = 'viz-cell carry';
   1535                     cell.id = `carry-${i}`;
   1536                     grid.appendChild(cell);
   1537                 }
   1538                 
   1539                 paddedArrays.forEach((rowDigits, rIdx) => {
   1540                     for(let i=0; i<maxLen; i++) {
   1541                         const cell = document.createElement('div');
   1542                         cell.className = 'viz-cell';
   1543                         if(rIdx === paddedArrays.length -1) cell.classList.add('border-bottom');
   1544                         const val = rowDigits[i];
   1545                         cell.textContent = val;
   1546                         if(i === 0 && rIdx > 0) {
   1547                             cell.classList.add('operator');
   1548                             cell.innerHTML = `+ ${val}`;
   1549                         }
   1550                         grid.appendChild(cell);
   1551                     }
   1552                 });
   1553 
   1554                 for(let i=0; i<maxLen; i++) {
   1555                     const cell = document.createElement('div');
   1556                     cell.className = 'viz-cell result';
   1557                     cell.id = `res-${i}`;
   1558                     cell.textContent = '?';
   1559                     grid.appendChild(cell);
   1560                 }
   1561 
   1562                 let carry = 0;
   1563                 let colIdx = maxLen - 1;
   1564 
   1565                 function stepAdd() {
   1566                     if(colIdx < 0) {
   1567                         info.textContent = "Addition Complete.";
   1568                         return;
   1569                     }
   1570                     document.querySelectorAll('.active-col').forEach(el => el.classList.remove('active-col'));
   1571                     const resCell = document.getElementById(`res-${colIdx}`);
   1572                     resCell.classList.add('active-col');
   1573                     
   1574                     let sum = carry;
   1575                     const digitVals = [];
   1576                     paddedArrays.forEach(row => {
   1577                         const char = row[colIdx];
   1578                         const val = parseInt(char, base);
   1579                         sum += val;
   1580                         digitVals.push(val.toString(base).toUpperCase());
   1581                     });
   1582 
   1583                     const writeVal = sum % base;
   1584                     const newCarry = Math.floor(sum / base);
   1585 
   1586                     resCell.textContent = writeVal.toString(base).toUpperCase();
   1587                     if(newCarry > 0 && colIdx > 0) {
   1588                         document.getElementById(`carry-${colIdx-1}`).textContent = newCarry.toString(base).toUpperCase();
   1589                     }
   1590                     
   1591                     const eqStr = `${digitVals.join('+')} + ${carry}(c) = ${sum}`;
   1592                     const writeStr = writeVal.toString(base).toUpperCase();
   1593                     const carryStr = newCarry.toString(base).toUpperCase();
   1594                     info.textContent = `Col ${maxLen - colIdx}: ${eqStr} (Base ${base}). Write ${writeStr}, Carry ${carryStr}`;
   1595 
   1596                     carry = newCarry;
   1597                     colIdx--;
   1598                     setTimeout(stepAdd, 1000);
   1599                 }
   1600                 stepAdd();
   1601             
   1602             // === SUBTRACTION (Grid with Borrows) ===
   1603             } else if (op === 'sub') {
   1604                 if(inputs.length > 2) {
   1605                     info.textContent = "Visualizer supports max 2 numbers for Subtraction.";
   1606                     return;
   1607                 }
   1608                 const num1 = inputs[0];
   1609                 const num2 = inputs[1];
   1610 
   1611                 if(num1 < num2) {
   1612                     info.textContent = "Visualization requires Positive Result (Num1 >= Num2).";
   1613                     return;
   1614                 }
   1615 
   1616                 const arr1 = num1.toString(base).toUpperCase().split('');
   1617                 const arr2 = num2.toString(base).toUpperCase().split('');
   1618                 const maxLen = Math.max(arr1.length, arr2.length);
   1619 
   1620                 const p1 = new Array(maxLen - arr1.length).fill('0').concat(arr1);
   1621                 const p2 = new Array(maxLen - arr2.length).fill('0').concat(arr2);
   1622 
   1623                 grid.style.gridTemplateColumns = `repeat(${maxLen}, 1fr)`;
   1624 
   1625                 // Row 0: Borrows
   1626                 for(let i=0; i<maxLen; i++) {
   1627                     const cell = document.createElement('div');
   1628                     cell.className = 'viz-cell borrow';
   1629                     cell.id = `borrow-${i}`;
   1630                     grid.appendChild(cell);
   1631                 }
   1632                 // Row 1: Minuend
   1633                 for(let i=0; i<maxLen; i++) {
   1634                     const cell = document.createElement('div');
   1635                     cell.className = 'viz-cell';
   1636                     cell.textContent = p1[i];
   1637                     grid.appendChild(cell);
   1638                 }
   1639                 // Row 2: Subtrahend
   1640                 for(let i=0; i<maxLen; i++) {
   1641                     const cell = document.createElement('div');
   1642                     cell.className = 'viz-cell border-bottom';
   1643                     cell.textContent = p2[i];
   1644                     if(i === 0) {
   1645                         cell.classList.add('operator');
   1646                         cell.innerHTML = `- ${p2[i]}`;
   1647                     }
   1648                     grid.appendChild(cell);
   1649                 }
   1650                 // Row 3: Result
   1651                 for(let i=0; i<maxLen; i++) {
   1652                     const cell = document.createElement('div');
   1653                     cell.className = 'viz-cell result';
   1654                     cell.id = `res-sub-${i}`;
   1655                     cell.textContent = '?';
   1656                     grid.appendChild(cell);
   1657                 }
   1658 
   1659                 let borrow = 0;
   1660                 let colIdx = maxLen - 1;
   1661 
   1662                 function stepSub() {
   1663                     if(colIdx < 0) {
   1664                         info.textContent = "Subtraction Complete.";
   1665                         return;
   1666                     }
   1667                     document.querySelectorAll('.active-col').forEach(el => el.classList.remove('active-col'));
   1668                     const resCell = document.getElementById(`res-sub-${colIdx}`);
   1669                     resCell.classList.add('active-col');
   1670 
   1671                     let val1 = parseInt(p1[colIdx], base);
   1672                     let val2 = parseInt(p2[colIdx], base);
   1673                     
   1674                     // Apply borrow from previous step
   1675                     let effectiveVal1 = val1 - borrow;
   1676                     
   1677                     let diff = effectiveVal1 - val2;
   1678                     let nextBorrow = 0;
   1679 
   1680                     if (diff < 0) {
   1681                         diff += base;
   1682                         nextBorrow = 1;
   1683                     }
   1684 
   1685                     resCell.textContent = diff.toString(base).toUpperCase();
   1686                     
   1687                     if(nextBorrow > 0 && colIdx > 0) {
   1688                         document.getElementById(`borrow-${colIdx-1}`).textContent = "1";
   1689                     }
   1690 
   1691                     const debugStr = `(${val1} - ${borrow}) - ${val2}`;
   1692                     const resStr = diff.toString(base).toUpperCase();
   1693                     info.textContent = `Col ${maxLen - colIdx}: ${debugStr}. Result ${resStr} (Base ${base}). ${nextBorrow ? 'Borrowed from Left.' : ''}`;
   1694 
   1695                     borrow = nextBorrow;
   1696                     colIdx--;
   1697                     setTimeout(stepSub, 1200);
   1698                 }
   1699                 stepSub();
   1700 
   1701             // === MULTIPLICATION (Text Steps) ===
   1702             } else if (op === 'mul') {
   1703                  if(inputs.length > 2) {
   1704                     info.textContent = "Visualizer supports max 2 numbers for Multiplication.";
   1705                     return;
   1706                 }
   1707                 grid.style.display = 'none'; // Hide grid for text mode
   1708                 info.style.whiteSpace = 'pre-wrap';
   1709                 
   1710                 const num1 = inputs[0];
   1711                 const num2 = inputs[1];
   1712                 let output = `Multiplying ${num1.toString(base).toUpperCase()} × ${num2.toString(base).toUpperCase()} (Base ${base})\n`;
   1713                 output += `-------------------------------------------------\n`;
   1714 
   1715                 const d1 = num1.toString(base).toUpperCase();
   1716                 const d2 = num2.toString(base).toUpperCase();
   1717                 
   1718                 // Show Partial Products
   1719                 const digits2 = d2.split('').reverse();
   1720                 let partials = [];
   1721                 
   1722                 digits2.forEach((digitChar, i) => {
   1723                     const digitVal = parseInt(digitChar, base);
   1724                     const product = num1 * digitVal;
   1725                     const shift = "0".repeat(i);
   1726                     const pStr = product.toString(base).toUpperCase();
   1727                     
   1728                     output += `Digit ${digitChar}: ${d1} × ${digitChar} = ${pStr}`;
   1729                     if(i > 0) output += ` (shift ${i}: ${pStr}${shift})`;
   1730                     output += "\n";
   1731                     partials.push(product * Math.pow(base, i));
   1732                 });
   1733 
   1734                 const total = partials.reduce((a,b) => a+b, 0);
   1735                 output += `-------------------------------------------------\n`;
   1736                 output += `Sum of Partials: ${total.toString(base).toUpperCase()}`;
   1737                 
   1738                 info.textContent = output;
   1739                 grid.style.display = 'grid'; // Reset for others
   1740 
   1741             // === DIVISION (Text Steps) ===
   1742             } else if (op === 'div') {
   1743                  if(inputs.length > 2) {
   1744                     info.textContent = "Visualizer supports max 2 numbers for Division.";
   1745                     return;
   1746                 }
   1747                 grid.style.display = 'none';
   1748                 info.style.whiteSpace = 'pre-wrap';
   1749                 
   1750                 const dividend = inputs[0];
   1751                 const divisor = inputs[1];
   1752 
   1753                 if(divisor === 0) {
   1754                     info.textContent = "Cannot divide by Zero.";
   1755                     grid.style.display = 'grid';
   1756                     return;
   1757                 }
   1758 
   1759                 let output = `Dividing ${dividend.toString(base).toUpperCase()} ÷ ${divisor.toString(base).toUpperCase()} (Base ${base})\n`;
   1760                 output += `-------------------------------------------------\n`;
   1761 
   1762                 const quotient = Math.floor(dividend / divisor);
   1763                 const remainder = dividend % divisor;
   1764 
   1765                 // Simple Euclidean explanation
   1766                 output += `1. Integer Division in Decimal:\n`;
   1767                 output += `   ${dividend} ÷ ${divisor} = ${quotient} (Quotient)\n`;
   1768                 output += `   Remainder: ${remainder}\n\n`;
   1769                 
   1770                 output += `2. Convert Results to Base ${base}:\n`;
   1771                 output += `   Quotient:  ${quotient.toString(base).toUpperCase()}\n`;
   1772                 output += `   Remainder: ${remainder.toString(base).toUpperCase()}\n`;
   1773                 output += `-------------------------------------------------\n`;
   1774                 output += `Result: ${quotient.toString(base).toUpperCase()} r${remainder.toString(base).toUpperCase()}`;
   1775 
   1776                 info.textContent = output;
   1777                 grid.style.display = 'grid';
   1778             }
   1779         }
   1780 
   1781         /* --- UTILS & SWAP --- */
   1782         function setupEventListeners() {
   1783             document.addEventListener('keydown', (e) => {
   1784                 if(e.ctrlKey && e.key === 'Enter') copyToClipboard('outputVal', 'copyBtn');
   1785                 if(e.altKey && e.key === 's') document.getElementById('swapBtn').click();
   1786             });
   1787 
   1788             document.getElementById('inputVal').addEventListener('input', convert);
   1789             document.getElementById('fromBase').addEventListener('change', convert);
   1790             document.getElementById('toBase').addEventListener('change', convert);
   1791 
   1792             document.getElementById('swapBtn').addEventListener('click', () => {
   1793                 const cardSource = document.getElementById('cardSource');
   1794                 const cardTarget = document.getElementById('cardTarget');
   1795                 const isMobile = window.innerWidth <= 768;
   1796 
   1797                 if(isMobile) {
   1798                     cardSource.classList.add('anim-move-down');
   1799                     cardTarget.classList.add('anim-move-up');
   1800                 } else {
   1801                     cardSource.classList.add('anim-move-right');
   1802                     cardTarget.classList.add('anim-move-left');
   1803                 }
   1804 
   1805                 setTimeout(() => {
   1806                     const inEl = document.getElementById('inputVal');
   1807                     const outEl = document.getElementById('outputVal');
   1808                     const from = document.getElementById('fromBase');
   1809                     const to = document.getElementById('toBase');
   1810 
   1811                     const tempVal = inEl.value;
   1812                     inEl.value = outEl.value;
   1813                     const tempBase = from.value;
   1814                     from.value = to.value;
   1815                     to.value = tempBase;
   1816                     
   1817                     updateDropdownUI('sourceSelectWrapper', from.value);
   1818                     updateDropdownUI('targetSelectWrapper', to.value);
   1819 
   1820                     cardSource.classList.remove('anim-move-down', 'anim-move-right');
   1821                     cardTarget.classList.remove('anim-move-up', 'anim-move-left');
   1822                     convert();
   1823                 }, 400); 
   1824             });
   1825         }
   1826 
   1827         function speakResult() {
   1828             const txt = document.getElementById('outputVal').value;
   1829             if(!txt) return;
   1830             let spoken = txt;
   1831             if(document.getElementById('toBase').value == 2) {
   1832                 spoken = txt.split('').join(' '); 
   1833             }
   1834             const utt = new SpeechSynthesisUtterance(spoken);
   1835             window.speechSynthesis.speak(utt);
   1836         }
   1837 
   1838         function copyToClipboard(id, btnId) {
   1839             const el = document.getElementById(id);
   1840             if(el) {
   1841                 el.select();
   1842                 document.execCommand('copy');
   1843             }
   1844             const btn = document.getElementById(btnId);
   1845             if(btn) {
   1846                 const originalHTML = btn.innerHTML;
   1847                 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>`;
   1848                 btn.classList.add('success');
   1849                 setTimeout(() => { 
   1850                     btn.innerHTML = originalHTML; 
   1851                     btn.classList.remove('success');
   1852                 }, 2000);
   1853             }
   1854         }
   1855         
   1856         function copyStepsText() {
   1857             const stepsText = document.getElementById('stepsOutput').innerText;
   1858             navigator.clipboard.writeText(stepsText).then(() => {
   1859                 const btn = document.getElementById('copyStepsBtn');
   1860                 const originalHTML = btn.innerHTML;
   1861                 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>`;
   1862                 btn.classList.add('success');
   1863                 setTimeout(() => { 
   1864                     btn.innerHTML = originalHTML; 
   1865                     btn.classList.remove('success');
   1866                 }, 2000);
   1867             });
   1868         }
   1869 
   1870         function clearAll() {
   1871             document.getElementById('inputVal').value = '';
   1872             document.getElementById('outputVal').value = '';
   1873             document.getElementById('sourceCount').textContent = '0 chars';
   1874             document.getElementById('errorChip').classList.remove('visible');
   1875             document.getElementById('stepsContainer').classList.remove('visible');
   1876             updateURL('');
   1877         }
   1878     </script>
   1879 </body>
   1880 </html>
© notamitgamer • Site Built: 2026-09-05 01:53:16 UTC • git-mirror commit: c170d72 [view raw info]
Originally created with stagit • modified by notamitgamer
Forked from github.com/notamitgamer/git-mirror