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