/ app / spec / index.html
index.html
   1  <!DOCTYPE html>
   2  <html lang="en">
   3  <head>
   4      <meta charset="UTF-8">
   5      <meta name="viewport" content="width=device-width, initial-scale=1.0">
   6      <title>🔥 KAMAJI - Specification Archive</title>
   7      <script src="https://cdn.jsdelivr.net/npm/marked/marked.min.js"></script>
   8      <script src="https://cdn.jsdelivr.net/npm/animejs@3.2.1/lib/anime.min.js"></script>
   9      <style>
  10          * {
  11              margin: 0;
  12              padding: 0;
  13              box-sizing: border-box;
  14          }
  15  
  16          :root {
  17              /* 2003 Fire Theme - More refined */
  18              --fire-red: #ff3300;
  19              --fire-orange: #ff6600;
  20              --fire-yellow: #ffcc00;
  21              --fire-bright: #ff9933;
  22              --digital-black: #000000;
  23              --digital-dark: #111111;
  24              --digital-gray: #1a1a1a;
  25              --chrome-silver: #cccccc;
  26              --warm-gray: #666666;
  27  
  28              /* Text */
  29              --text-light: #ffffff;
  30              --text-gray: #aaaaaa;
  31              --text-dim: #777777;
  32  
  33              --sidebar-width: 280px;
  34          }
  35  
  36          @keyframes subtle-glow {
  37              0%, 100% { text-shadow: 0 0 8px rgba(255, 102, 0, 0.4); }
  38              50% { text-shadow: 0 0 12px rgba(255, 102, 0, 0.6); }
  39          }
  40  
  41          @keyframes slide-in {
  42              from { transform: translateX(-20px); opacity: 0; }
  43              to { transform: translateX(0); opacity: 1; }
  44          }
  45  
  46          @keyframes fade-in {
  47              from { opacity: 0; }
  48              to { opacity: 1; }
  49          }
  50  
  51          @keyframes pulse-soft {
  52              0%, 100% { transform: scale(1); }
  53              50% { transform: scale(1.05); }
  54          }
  55  
  56          body {
  57              font-family: Verdana, Tahoma, 'Trebuchet MS', sans-serif;
  58              background: linear-gradient(135deg, #000000 0%, #0a0a0a 50%, #111111 100%);
  59              color: var(--text-light);
  60              line-height: 1.6;
  61              overflow-x: hidden;
  62          }
  63  
  64          /* Subtle ambient background */
  65          body::before {
  66              content: '';
  67              position: fixed;
  68              top: 0;
  69              left: 0;
  70              right: 0;
  71              bottom: 0;
  72              background:
  73                  radial-gradient(ellipse at 20% 30%, rgba(255, 102, 0, 0.08) 0%, transparent 50%),
  74                  radial-gradient(ellipse at 80% 70%, rgba(255, 51, 0, 0.05) 0%, transparent 50%);
  75              pointer-events: none;
  76              z-index: 0;
  77          }
  78  
  79          /* Sidebar - 2003 polished style */
  80          nav#sidebar {
  81              position: fixed;
  82              top: 0;
  83              left: 0;
  84              width: var(--sidebar-width);
  85              height: 100vh;
  86              background: linear-gradient(180deg, #0d0d0d 0%, #080808 100%);
  87              border-right: 2px solid var(--fire-orange);
  88              box-shadow: 2px 0 15px rgba(255, 102, 0, 0.3);
  89              overflow-y: auto;
  90              z-index: 1001;
  91              padding-bottom: 240px;
  92          }
  93  
  94          nav::-webkit-scrollbar {
  95              width: 10px;
  96          }
  97  
  98          nav::-webkit-scrollbar-track {
  99              background: #000;
 100          }
 101  
 102          nav::-webkit-scrollbar-thumb {
 103              background: linear-gradient(180deg, var(--fire-orange), var(--fire-red));
 104              border-radius: 5px;
 105          }
 106  
 107          nav::-webkit-scrollbar-thumb:hover {
 108              background: var(--fire-bright);
 109          }
 110  
 111          /* Sidebar Header */
 112          .sidebar-header {
 113              padding: 1.5rem 1rem;
 114              text-align: center;
 115              background: linear-gradient(180deg, rgba(255, 102, 0, 0.15) 0%, transparent 100%);
 116              border-bottom: 2px solid var(--fire-orange);
 117              margin-bottom: 1.5rem;
 118          }
 119  
 120          #kamaji-illustration {
 121              width: 100%;
 122              height: auto;
 123              margin-bottom: 1rem;
 124              cursor: pointer;
 125              filter: drop-shadow(0 4px 12px rgba(255, 102, 0, 0.3));
 126              transition: all 0.4s ease;
 127          }
 128  
 129          #kamaji-illustration:hover {
 130              filter: drop-shadow(0 8px 20px rgba(255, 102, 0, 0.5));
 131          }
 132  
 133          .sidebar-logo {
 134              font-size: 1.35rem;
 135              font-weight: bold;
 136              color: var(--fire-orange);
 137              margin-bottom: 0.5rem;
 138              animation: subtle-glow 3s ease-in-out infinite;
 139          }
 140  
 141          .sidebar-version {
 142              font-size: 0.75rem;
 143              color: var(--text-gray);
 144              text-transform: uppercase;
 145              letter-spacing: 0.15em;
 146              font-family: 'Courier New', monospace;
 147          }
 148  
 149          .nav-section {
 150              margin-bottom: 1.5rem;
 151              animation: slide-in 0.5s ease-out;
 152          }
 153  
 154          .nav-section-title {
 155              padding: 0.875rem 1.5rem;
 156              font-size: 0.7rem;
 157              text-transform: uppercase;
 158              letter-spacing: 0.2em;
 159              color: var(--fire-bright);
 160              background: linear-gradient(90deg, rgba(255, 102, 0, 0.15) 0%, transparent 100%);
 161              border-left: 3px solid var(--fire-orange);
 162              font-weight: bold;
 163              margin-bottom: 0.5rem;
 164          }
 165  
 166          .nav-item {
 167              padding: 0.75rem 1.5rem;
 168              color: var(--text-gray);
 169              display: flex;
 170              align-items: center;
 171              gap: 0.75rem;
 172              cursor: pointer;
 173              transition: all 0.3s ease;
 174              border-left: 3px solid transparent;
 175              font-size: 0.85rem;
 176              border-radius: 0 4px 4px 0;
 177              margin: 2px 0;
 178          }
 179  
 180          .nav-item:hover {
 181              background: linear-gradient(90deg, rgba(255, 102, 0, 0.2) 0%, rgba(255, 102, 0, 0.05) 100%);
 182              color: var(--text-light);
 183              border-left-color: var(--fire-orange);
 184              transform: translateX(3px);
 185          }
 186  
 187          .nav-item.active {
 188              background: linear-gradient(90deg, rgba(255, 102, 0, 0.25) 0%, rgba(255, 102, 0, 0.1) 100%);
 189              color: var(--fire-orange);
 190              border-left-color: var(--fire-bright);
 191              font-weight: bold;
 192              box-shadow: inset 0 0 10px rgba(255, 102, 0, 0.2);
 193          }
 194  
 195          .nav-item-icon {
 196              font-size: 1.125rem;
 197              min-width: 1.5rem;
 198              filter: drop-shadow(0 0 2px rgba(255, 102, 0, 0.3));
 199          }
 200  
 201          /* Stats Panel - Refined 2003 style */
 202          .stats-panel {
 203              position: fixed;
 204              bottom: 0;
 205              left: 0;
 206              width: var(--sidebar-width);
 207              background: linear-gradient(180deg, #0a0a0a 0%, #000000 100%);
 208              border-right: 2px solid var(--fire-orange);
 209              border-top: 2px solid var(--fire-orange);
 210              padding: 1.5rem;
 211              z-index: 1002;
 212              box-shadow: 0 -4px 15px rgba(255, 102, 0, 0.2);
 213          }
 214  
 215          .stats-title {
 216              color: var(--fire-orange);
 217              font-size: 0.75rem;
 218              font-weight: bold;
 219              margin-bottom: 1rem;
 220              text-transform: uppercase;
 221              letter-spacing: 0.2em;
 222              text-align: center;
 223              border-bottom: 1px solid rgba(255, 102, 0, 0.3);
 224              padding-bottom: 0.75rem;
 225          }
 226  
 227          .stat-row {
 228              display: flex;
 229              justify-content: space-between;
 230              margin-bottom: 0.75rem;
 231              font-size: 0.8rem;
 232              padding: 0.625rem;
 233              background: rgba(255, 102, 0, 0.05);
 234              border-radius: 4px;
 235              border-left: 2px solid var(--fire-orange);
 236              transition: all 0.3s ease;
 237          }
 238  
 239          .stat-row:hover {
 240              background: rgba(255, 102, 0, 0.1);
 241              transform: translateX(2px);
 242          }
 243  
 244          .stat-label {
 245              color: var(--text-gray);
 246              font-family: Verdana, sans-serif;
 247              font-size: 0.75rem;
 248          }
 249  
 250          .stat-value {
 251              color: var(--fire-orange);
 252              font-weight: bold;
 253              font-family: 'Courier New', monospace;
 254          }
 255  
 256          /* Header - Polished 2003 */
 257          header {
 258              position: fixed;
 259              top: 0;
 260              left: var(--sidebar-width);
 261              right: 0;
 262              background: linear-gradient(180deg, #1a1a1a 0%, #0d0d0d 100%);
 263              border-bottom: 2px solid var(--fire-orange);
 264              z-index: 1000;
 265              box-shadow: 0 2px 20px rgba(255, 102, 0, 0.25);
 266          }
 267  
 268          .header-main {
 269              padding: 1.25rem 2.5rem;
 270              display: flex;
 271              align-items: center;
 272              justify-content: space-between;
 273              background: linear-gradient(90deg, rgba(255, 102, 0, 0.08) 0%, transparent 50%);
 274          }
 275  
 276          .logo {
 277              display: flex;
 278              align-items: center;
 279              gap: 1.25rem;
 280          }
 281  
 282          .logo-icon {
 283              font-size: 2.25rem;
 284              animation: pulse-soft 4s ease-in-out infinite;
 285              filter: drop-shadow(0 0 8px rgba(255, 102, 0, 0.4));
 286          }
 287  
 288          .logo-text {
 289              font-size: 1.75rem;
 290              font-weight: bold;
 291              color: var(--fire-orange);
 292              text-transform: uppercase;
 293              letter-spacing: 0.3em;
 294              text-shadow: 0 0 10px rgba(255, 102, 0, 0.5);
 295          }
 296  
 297          .header-nav {
 298              display: flex;
 299              gap: 2rem;
 300              align-items: center;
 301          }
 302  
 303          .status-badge {
 304              display: inline-block;
 305              padding: 0.375rem 1rem;
 306              background: linear-gradient(135deg, rgba(255, 102, 0, 0.2), rgba(255, 51, 0, 0.15));
 307              border: 1px solid var(--fire-orange);
 308              border-radius: 12px;
 309              color: var(--fire-orange);
 310              font-size: 0.7rem;
 311              text-transform: uppercase;
 312              letter-spacing: 0.15em;
 313              font-family: Verdana, sans-serif;
 314              box-shadow: inset 0 1px 2px rgba(255, 255, 255, 0.1);
 315          }
 316  
 317          /* Search - 2003 refined input */
 318          .search-box {
 319              position: relative;
 320          }
 321  
 322          #search-input {
 323              padding: 0.625rem 1.25rem;
 324              background: #000;
 325              border: 1px solid var(--fire-orange);
 326              border-radius: 6px;
 327              color: var(--text-light);
 328              font-family: Verdana, sans-serif;
 329              font-size: 0.875rem;
 330              width: 280px;
 331              transition: all 0.3s ease;
 332              box-shadow: inset 0 2px 4px rgba(0, 0, 0, 0.3);
 333          }
 334  
 335          #search-input:focus {
 336              outline: none;
 337              box-shadow: 0 0 15px rgba(255, 102, 0, 0.4), inset 0 2px 4px rgba(0, 0, 0, 0.3);
 338              border-color: var(--fire-bright);
 339              background: #0a0a0a;
 340          }
 341  
 342          #search-input::placeholder {
 343              color: var(--text-dim);
 344          }
 345  
 346          /* Main content */
 347          main {
 348              margin-left: var(--sidebar-width);
 349              margin-top: 90px;
 350              padding: 3rem 4rem;
 351              animation: fade-in 0.5s ease-out;
 352          }
 353  
 354          /* Typography - Refined 2003 */
 355          h1 {
 356              font-size: 2.5rem;
 357              margin-bottom: 1.5rem;
 358              color: var(--fire-orange);
 359              text-transform: uppercase;
 360              letter-spacing: 0.05em;
 361              border-bottom: 2px solid var(--fire-orange);
 362              padding-bottom: 1rem;
 363              font-weight: 700;
 364              text-shadow: 0 2px 4px rgba(255, 102, 0, 0.3);
 365          }
 366  
 367          h1::before {
 368              content: '▸ ';
 369              color: var(--fire-bright);
 370          }
 371  
 372          h2 {
 373              font-size: 1.85rem;
 374              margin-top: 3rem;
 375              margin-bottom: 1.25rem;
 376              color: var(--fire-bright);
 377              text-transform: uppercase;
 378              letter-spacing: 0.05em;
 379              border-left: 4px solid var(--fire-orange);
 380              padding-left: 1.25rem;
 381              text-shadow: 0 1px 3px rgba(255, 102, 0, 0.2);
 382          }
 383  
 384          h3 {
 385              font-size: 1.35rem;
 386              margin-top: 2rem;
 387              margin-bottom: 1rem;
 388              color: var(--text-light);
 389              font-weight: 600;
 390          }
 391  
 392          h4 {
 393              font-size: 1.05rem;
 394              margin-top: 1.5rem;
 395              margin-bottom: 0.75rem;
 396              color: var(--text-gray);
 397              text-transform: uppercase;
 398              letter-spacing: 0.1em;
 399          }
 400  
 401          p {
 402              margin-bottom: 1.25rem;
 403              color: var(--text-light);
 404              line-height: 1.7;
 405          }
 406  
 407          /* Code blocks - Polished terminal */
 408          pre {
 409              background: #000;
 410              border: 1px solid var(--fire-orange);
 411              border-radius: 6px;
 412              padding: 1.5rem;
 413              overflow-x: auto;
 414              margin: 1.5rem 0;
 415              font-family: 'Courier New', monospace;
 416              font-size: 0.875rem;
 417              line-height: 1.6;
 418              box-shadow: 0 4px 20px rgba(255, 102, 0, 0.15), inset 0 2px 4px rgba(0, 0, 0, 0.3);
 419          }
 420  
 421          code {
 422              font-family: 'Courier New', monospace;
 423              font-size: 0.875rem;
 424              background: rgba(255, 102, 0, 0.1);
 425              padding: 0.25rem 0.5rem;
 426              border-radius: 3px;
 427              color: var(--fire-bright);
 428              border: 1px solid rgba(255, 102, 0, 0.3);
 429          }
 430  
 431          pre code {
 432              background: none;
 433              padding: 0;
 434              color: var(--text-light);
 435              border: none;
 436          }
 437  
 438          /* Tables - Refined data grid */
 439          table {
 440              width: 100%;
 441              border-collapse: collapse;
 442              margin: 2rem 0;
 443              background: #000;
 444              border: 1px solid var(--fire-orange);
 445              border-radius: 6px;
 446              overflow: hidden;
 447              box-shadow: 0 4px 15px rgba(255, 102, 0, 0.15);
 448          }
 449  
 450          th {
 451              background: linear-gradient(135deg, rgba(255, 102, 0, 0.25), rgba(255, 102, 0, 0.15));
 452              padding: 1rem;
 453              text-align: left;
 454              font-weight: bold;
 455              color: var(--fire-orange);
 456              border-bottom: 2px solid var(--fire-orange);
 457              text-transform: uppercase;
 458              font-size: 0.8rem;
 459              letter-spacing: 0.15em;
 460          }
 461  
 462          td {
 463              padding: 0.875rem 1rem;
 464              border-bottom: 1px solid rgba(255, 102, 0, 0.15);
 465              color: var(--text-light);
 466          }
 467  
 468          tr:last-child td {
 469              border-bottom: none;
 470          }
 471  
 472          tr:hover {
 473              background: rgba(255, 102, 0, 0.08);
 474          }
 475  
 476          /* Links - 2003 style */
 477          a {
 478              color: var(--fire-orange);
 479              text-decoration: none;
 480              border-bottom: 1px dotted var(--fire-orange);
 481              transition: all 0.2s ease;
 482          }
 483  
 484          a:hover {
 485              color: var(--fire-bright);
 486              border-bottom-style: solid;
 487              text-shadow: 0 0 8px rgba(255, 102, 0, 0.4);
 488          }
 489  
 490          /* Lists */
 491          ul, ol {
 492              margin: 1.25rem 0;
 493              padding-left: 2rem;
 494          }
 495  
 496          li {
 497              margin: 0.75rem 0;
 498              color: var(--text-light);
 499          }
 500  
 501          li::marker {
 502              color: var(--fire-orange);
 503          }
 504  
 505          /* Blockquotes */
 506          blockquote {
 507              border-left: 4px solid var(--fire-orange);
 508              padding: 1.25rem 1.5rem;
 509              margin: 2rem 0;
 510              background: linear-gradient(90deg, rgba(255, 102, 0, 0.08) 0%, transparent 100%);
 511              color: var(--text-gray);
 512              font-style: italic;
 513              border-radius: 0 6px 6px 0;
 514          }
 515  
 516          /* Horizontal rules */
 517          hr {
 518              border: none;
 519              height: 2px;
 520              background: linear-gradient(90deg, transparent 0%, var(--fire-orange) 50%, transparent 100%);
 521              margin: 3rem 0;
 522          }
 523  
 524          /* Loading state - Refined */
 525          .loading {
 526              text-align: center;
 527              padding: 5rem 0;
 528              color: var(--fire-orange);
 529              font-size: 1.25rem;
 530              text-transform: uppercase;
 531              letter-spacing: 0.2em;
 532          }
 533  
 534          .loading::before {
 535              content: '';
 536              display: block;
 537              width: 240px;
 538              height: 6px;
 539              background: rgba(255, 102, 0, 0.15);
 540              border-radius: 3px;
 541              margin: 0 auto 2rem;
 542              position: relative;
 543              overflow: hidden;
 544              box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.3);
 545          }
 546  
 547          /* Responsive */
 548          @media (max-width: 1024px) {
 549              nav#sidebar, .stats-panel {
 550                  transform: translateX(-100%);
 551                  transition: transform 0.3s ease;
 552              }
 553  
 554              nav#sidebar.open, .stats-panel.open {
 555                  transform: translateX(0);
 556              }
 557  
 558              header {
 559                  left: 0;
 560              }
 561  
 562              main {
 563                  margin-left: 0;
 564                  padding: 2rem;
 565              }
 566  
 567              #search-input {
 568                  width: 200px;
 569              }
 570          }
 571  
 572          @media (max-width: 640px) {
 573              .header-main {
 574                  flex-direction: column;
 575                  gap: 1rem;
 576              }
 577  
 578              .logo-text {
 579                  font-size: 1.25rem;
 580              }
 581  
 582              main {
 583                  padding: 1.5rem 1rem;
 584              }
 585          }
 586  
 587          /* Print */
 588          @media print {
 589              nav, header, .stats-panel {
 590                  display: none;
 591              }
 592  
 593              main {
 594                  margin-left: 0;
 595                  margin-top: 0;
 596              }
 597  
 598              body {
 599                  background: white;
 600                  color: black;
 601              }
 602          }
 603      </style>
 604  </head>
 605  <body>
 606      <nav id="sidebar">
 607          <div class="sidebar-header">
 608              <canvas id="kamaji-illustration" width="280" height="280"></canvas>
 609              <div class="sidebar-logo">🔥 KAMAJI</div>
 610          </div>
 611  
 612          <div class="nav-section">
 613              <div class="nav-section-title">▸ OVERVIEW</div>
 614              <div class="nav-item" data-file="README.md">
 615                  <span class="nav-item-icon">📄</span>
 616                  <span class="nav-item-text">README</span>
 617              </div>
 618              <div class="nav-item" data-file="00_INDEX.md">
 619                  <span class="nav-item-icon">📑</span>
 620                  <span class="nav-item-text">Index</span>
 621              </div>
 622              <div class="nav-item" data-file="ARCHITECTURE.md">
 623                  <span class="nav-item-icon">🏗️</span>
 624                  <span class="nav-item-text">Architecture</span>
 625              </div>
 626          </div>
 627  
 628          <div class="nav-section">
 629              <div class="nav-section-title">▸ SPECIFICATIONS</div>
 630              <div class="nav-item" data-file="01_ui_components.md">
 631                  <span class="nav-item-icon">🎨</span>
 632                  <span class="nav-item-text">UI Components</span>
 633              </div>
 634              <div class="nav-item" data-file="02_input_interactions.md">
 635                  <span class="nav-item-icon">⌨️</span>
 636                  <span class="nav-item-text">Input System</span>
 637              </div>
 638              <div class="nav-item" data-file="03_consciousness_system.md">
 639                  <span class="nav-item-icon">🧠</span>
 640                  <span class="nav-item-text">Consciousness</span>
 641              </div>
 642              <div class="nav-item" data-file="04_agent_system.md">
 643                  <span class="nav-item-icon">🤖</span>
 644                  <span class="nav-item-text">Agent System</span>
 645              </div>
 646              <div class="nav-item" data-file="05_state_management.md">
 647                  <span class="nav-item-icon">🔄</span>
 648                  <span class="nav-item-text">State Management</span>
 649              </div>
 650              <div class="nav-item" data-file="06_llm_integration.md">
 651                  <span class="nav-item-icon">🔌</span>
 652                  <span class="nav-item-text">LLM Integration</span>
 653              </div>
 654              <div class="nav-item" data-file="07_commands_tools.md">
 655                  <span class="nav-item-icon">🔧</span>
 656                  <span class="nav-item-text">Commands & Tools</span>
 657              </div>
 658              <div class="nav-item" data-file="08_visual_styling.md">
 659                  <span class="nav-item-icon">🎭</span>
 660                  <span class="nav-item-text">Visual Styling</span>
 661              </div>
 662          </div>
 663  
 664          <div class="nav-section">
 665              <div class="nav-section-title">▸ SYNTHESIS</div>
 666              <div class="nav-item" data-file="USER_INTERACTIONS.md">
 667                  <span class="nav-item-icon">👤</span>
 668                  <span class="nav-item-text">User Guide</span>
 669              </div>
 670              <div class="nav-item" data-file="DATA_FLOW.md">
 671                  <span class="nav-item-icon">💧</span>
 672                  <span class="nav-item-text">Data Flow</span>
 673              </div>
 674          </div>
 675  
 676          <div class="nav-section">
 677              <div class="nav-section-title">▸ VERIFICATION</div>
 678              <div class="nav-item" data-file="VERIFICATION_SUMMARY.md">
 679                  <span class="nav-item-icon">📊</span>
 680                  <span class="nav-item-text">Summary</span>
 681              </div>
 682              <div class="nav-item" data-file="VERIFICATION_01_UI_COMPONENTS.md">
 683                  <span class="nav-item-icon">✓</span>
 684                  <span class="nav-item-text">UI Check</span>
 685              </div>
 686              <div class="nav-item" data-file="VERIFICATION_02_STATE.md">
 687                  <span class="nav-item-icon">✓</span>
 688                  <span class="nav-item-text">State Check</span>
 689              </div>
 690              <div class="nav-item" data-file="VERIFICATION_03_INPUT.md">
 691                  <span class="nav-item-icon">✓</span>
 692                  <span class="nav-item-text">Input Check</span>
 693              </div>
 694              <div class="nav-item" data-file="VERIFICATION_04_CONSCIOUSNESS.md">
 695                  <span class="nav-item-icon">✓</span>
 696                  <span class="nav-item-text">Consciousness Check</span>
 697              </div>
 698              <div class="nav-item" data-file="VERIFICATION_05_AGENTS_TOOLS.md">
 699                  <span class="nav-item-icon">✓</span>
 700                  <span class="nav-item-text">Agents Check</span>
 701              </div>
 702              <div class="nav-item" data-file="VERIFICATION_06_LLM.md">
 703                  <span class="nav-item-icon">✓</span>
 704                  <span class="nav-item-text">LLM Check</span>
 705              </div>
 706              <div class="nav-item" data-file="VERIFICATION_07_VISUAL.md">
 707                  <span class="nav-item-icon">✓</span>
 708                  <span class="nav-item-text">Visual Check</span>
 709              </div>
 710          </div>
 711      </nav>
 712  
 713      <div class="stats-panel">
 714          <div class="stats-title">SYSTEM STATUS</div>
 715          <div class="stat-row">
 716              <span class="stat-label">DOCS LOADED</span>
 717              <span class="stat-value" id="specs-loaded">0</span>
 718          </div>
 719          <div class="stat-row">
 720              <span class="stat-label">CACHE HITS</span>
 721              <span class="stat-value" id="cache-hits">0</span>
 722          </div>
 723          <div class="stat-row">
 724              <span class="stat-label">CURRENT</span>
 725              <span class="stat-value" id="current-view">README</span>
 726          </div>
 727          <div class="stat-row">
 728              <span class="stat-label">COMPLETE</span>
 729              <span class="stat-value">99.8%</span>
 730          </div>
 731      </div>
 732  
 733      <header>
 734          <div class="header-main">
 735              <div class="logo">
 736                  <div class="logo-icon">🔥</div>
 737                  <div class="logo-text">KAMAJI</div>
 738                  <span class="status-badge">SPECIFICATION v1.0.390</span>
 739              </div>
 740              <nav class="header-nav">
 741                  <div class="search-box">
 742                      <input type="text" id="search-input" placeholder="Search documentation...">
 743                  </div>
 744              </nav>
 745          </div>
 746      </header>
 747  
 748      <main id="content">
 749          <div class="loading">
 750              <div>LOADING SPECIFICATION...</div>
 751          </div>
 752      </main>
 753  
 754      <script>
 755          const SPEC_PATH = './';
 756          const DEFAULT_FILE = 'README.md';
 757  
 758          let currentFile = DEFAULT_FILE;
 759          let specCache = new Map();
 760          let specsLoaded = 0;
 761          let cacheHits = 0;
 762          let manifest = null;
 763  
 764          document.addEventListener('DOMContentLoaded', async () => {
 765              await loadManifest();
 766              initNavigation();
 767              initSearch();
 768              loadSpec(DEFAULT_FILE);
 769              updateMetrics();
 770          });
 771  
 772          async function loadManifest() {
 773              try {
 774                  const response = await fetch(SPEC_PATH + 'manifest.json');
 775                  if (response.ok) {
 776                      manifest = await response.json();
 777                      console.log('Manifest loaded:', manifest.files.length, 'files');
 778                  }
 779              } catch (error) {
 780                  console.warn('Could not load manifest:', error);
 781              }
 782          }
 783  
 784          function isValidFile(filename) {
 785              if (!manifest) return true; // Fallback if manifest not loaded
 786              return manifest.files.includes(filename);
 787          }
 788  
 789          function initNavigation() {
 790              document.querySelectorAll('.nav-item').forEach(item => {
 791                  item.addEventListener('click', () => {
 792                      const file = item.getAttribute('data-file');
 793                      loadSpec(file);
 794  
 795                      document.querySelectorAll('.nav-item').forEach(i => i.classList.remove('active'));
 796                      item.classList.add('active');
 797  
 798                      window.location.hash = file.replace('.md', '');
 799                  });
 800              });
 801  
 802              window.addEventListener('hashchange', () => {
 803                  const file = (window.location.hash.slice(1) || 'README') + '.md';
 804                  loadSpec(file);
 805              });
 806  
 807              if (window.location.hash) {
 808                  const file = window.location.hash.slice(1) + '.md';
 809                  const navItem = document.querySelector(`[data-file="${file}"]`);
 810                  if (navItem) navItem.click();
 811              }
 812          }
 813  
 814          function initSearch() {
 815              const searchInput = document.getElementById('search-input');
 816              let searchTimeout;
 817  
 818              searchInput.addEventListener('input', (e) => {
 819                  clearTimeout(searchTimeout);
 820                  searchTimeout = setTimeout(() => {
 821                      performSearch(e.target.value);
 822                  }, 300);
 823              });
 824  
 825              document.addEventListener('keydown', (e) => {
 826                  if (e.key === '/' && !e.ctrlKey && !e.metaKey && e.target.tagName !== 'INPUT') {
 827                      e.preventDefault();
 828                      searchInput.focus();
 829                  }
 830                  if (e.key === 'Escape' && e.target === searchInput) {
 831                      searchInput.value = '';
 832                      performSearch('');
 833                      searchInput.blur();
 834                  }
 835              });
 836          }
 837  
 838          function performSearch(query) {
 839              if (!query.trim()) {
 840                  document.querySelectorAll('.nav-item').forEach(item => {
 841                      item.style.display = 'flex';
 842                  });
 843                  document.querySelectorAll('.nav-section').forEach(section => {
 844                      section.style.display = 'block';
 845                  });
 846                  return;
 847              }
 848  
 849              const lowerQuery = query.toLowerCase();
 850  
 851              document.querySelectorAll('.nav-section').forEach(section => {
 852                  let hasVisibleItems = false;
 853                  section.querySelectorAll('.nav-item').forEach(item => {
 854                      const text = item.textContent.toLowerCase();
 855                      const visible = text.includes(lowerQuery);
 856                      item.style.display = visible ? 'flex' : 'none';
 857                      if (visible) hasVisibleItems = true;
 858                  });
 859                  section.style.display = hasVisibleItems ? 'block' : 'none';
 860              });
 861          }
 862  
 863          async function loadSpec(filename) {
 864              const content = document.getElementById('content');
 865  
 866              // Validate file before attempting to load
 867              if (manifest && !isValidFile(filename)) {
 868                  console.warn(`Invalid file requested: ${filename}, redirecting to fallback`);
 869                  const fallback = manifest.fallback || manifest.default || 'README.md';
 870                  window.location.hash = fallback.replace('.md', '');
 871                  return;
 872              }
 873  
 874              currentFile = filename;
 875  
 876              const displayName = filename
 877                  .replace('.md', '')
 878                  .replace(/_/g, ' ')
 879                  .replace(/VERIFICATION /g, 'VER ')
 880                  .replace(/^\d+_?/, '')
 881                  .trim()
 882                  .toUpperCase();
 883              document.getElementById('current-view').textContent = displayName;
 884  
 885              // Check cache first
 886              if (specCache.has(filename)) {
 887                  cacheHits++;
 888                  updateMetrics();
 889                  renderMarkdown(specCache.get(filename));
 890                  return;
 891              }
 892  
 893              content.innerHTML = '<div class="loading"><div>LOADING SPECIFICATION...</div></div>';
 894  
 895              try {
 896                  const response = await fetch(SPEC_PATH + filename);
 897  
 898                  if (!response.ok) {
 899                      throw new Error(`HTTP ${response.status}: ${response.statusText}`);
 900                  }
 901  
 902                  const markdown = await response.text();
 903  
 904                  // Validate markdown is not empty
 905                  if (!markdown || markdown.trim().length === 0) {
 906                      throw new Error('File is empty');
 907                  }
 908  
 909                  specCache.set(filename, markdown);
 910                  specsLoaded++;
 911                  updateMetrics();
 912                  renderMarkdown(markdown);
 913  
 914              } catch (error) {
 915                  console.error('Failed to load specification:', filename, error);
 916  
 917                  // Try fallback file if available
 918                  if (manifest && filename !== manifest.fallback && filename !== manifest.default) {
 919                      const fallback = manifest.fallback || manifest.default;
 920                      content.innerHTML = `
 921                          <div style="padding: 3rem; text-align: center;">
 922                              <h2 style="color: var(--fire-orange);">⚠️ DOCUMENT NOT FOUND</h2>
 923                              <p style="color: var(--text-gray); margin-top: 1.5rem; font-size: 1.1rem;">
 924                                  The requested document <strong>${filename}</strong> could not be loaded.
 925                              </p>
 926                              <p style="color: var(--text-light); margin-top: 1.5rem;">
 927                                  Redirecting to index...
 928                              </p>
 929                          </div>
 930                      `;
 931  
 932                      // Redirect after showing message
 933                      setTimeout(() => {
 934                          window.location.hash = fallback.replace('.md', '');
 935                      }, 2000);
 936                  } else {
 937                      // Show error if we can't fallback
 938                      content.innerHTML = `
 939                          <div style="padding: 3rem; text-align: center;">
 940                              <h2 style="color: var(--fire-red);">⚠️ LOAD ERROR</h2>
 941                              <p style="color: var(--text-gray); margin-top: 1.5rem; font-size: 1.1rem;">
 942                                  Document: <strong>${filename}</strong>
 943                              </p>
 944                              <p style="color: var(--fire-orange); margin-top: 1rem;">
 945                                  ${error.message}
 946                              </p>
 947                              <p style="color: var(--text-dim); margin-top: 2rem; font-size: 0.9rem;">
 948                                  Please check that the file exists and the server is running.
 949                              </p>
 950                          </div>
 951                      `;
 952                  }
 953              }
 954          }
 955  
 956          function renderMarkdown(markdown) {
 957              const content = document.getElementById('content');
 958  
 959              marked.setOptions({
 960                  breaks: true,
 961                  gfm: true
 962              });
 963  
 964              const html = marked.parse(markdown);
 965              content.innerHTML = html;
 966  
 967              window.scrollTo({ top: 0, behavior: 'smooth' });
 968  
 969              content.querySelectorAll('h2, h3, h4').forEach(heading => {
 970                  const id = heading.textContent.toLowerCase()
 971                      .replace(/[^\w\s-]/g, '')
 972                      .replace(/\s+/g, '-');
 973                  heading.id = id;
 974  
 975                  heading.style.cursor = 'pointer';
 976                  heading.title = 'Click to copy link';
 977                  heading.addEventListener('click', () => {
 978                      const url = `${window.location.origin}${window.location.pathname}#${currentFile.replace('.md', '')}/${id}`;
 979                      navigator.clipboard.writeText(url);
 980  
 981                      const originalText = heading.textContent;
 982                      heading.textContent = '✓ LINK COPIED';
 983                      setTimeout(() => {
 984                          heading.textContent = originalText;
 985                      }, 1500);
 986                  });
 987              });
 988          }
 989  
 990          function updateMetrics() {
 991              document.getElementById('specs-loaded').textContent = specsLoaded;
 992              document.getElementById('cache-hits').textContent = cacheHits;
 993          }
 994  
 995          // DRAW KAMAJI ON CANVAS
 996          document.addEventListener('DOMContentLoaded', () => {
 997              const canvas = document.getElementById('kamaji-illustration');
 998              const ctx = canvas.getContext('2d');
 999  
1000              function drawKamaji() {
1001                  ctx.clearRect(0, 0, canvas.width, canvas.height);
1002  
1003                  // DRAW BODY FIRST, THEN ARMS ON TOP
1004  
1005                  // OLIVE GREEN PANTS (cross-legged, low)
1006                  ctx.fillStyle = '#6b7c4a';
1007                  ctx.beginPath();
1008                  ctx.ellipse(140, 230, 35, 25, 0, 0, Math.PI * 2);
1009                  ctx.fill();
1010  
1011                  // DARK BLUE SHIRT (very hunched, compressed)
1012                  const shirtGrad = ctx.createRadialGradient(140, 195, 10, 140, 195, 35);
1013                  shirtGrad.addColorStop(0, '#3a4f6b');
1014                  shirtGrad.addColorStop(1, '#1f2d3d');
1015                  ctx.fillStyle = shirtGrad;
1016                  ctx.beginPath();
1017                  ctx.ellipse(140, 195, 38, 35, 0, 0, Math.PI * 2);
1018                  ctx.fill();
1019  
1020                  // LARGE SPHERICAL HEAD - peachy tan, positioned lower for hunch
1021                  const skinGrad = ctx.createRadialGradient(140, 140, 15, 140, 140, 45);
1022                  skinGrad.addColorStop(0, '#f5d5b8');
1023                  skinGrad.addColorStop(1, '#d4a574');
1024                  ctx.fillStyle = skinGrad;
1025                  ctx.beginPath();
1026                  ctx.ellipse(140, 140, 42, 45, 0, 0, Math.PI * 2);
1027                  ctx.fill();
1028  
1029                  // Wispy white hair
1030                  ctx.fillStyle = 'rgba(232, 232, 232, 0.75)';
1031                  ctx.beginPath();
1032                  ctx.ellipse(125, 100, 12, 8, -0.3, 0, Math.PI * 2);
1033                  ctx.fill();
1034                  ctx.beginPath();
1035                  ctx.ellipse(155, 102, 11, 7, 0.3, 0, Math.PI * 2);
1036                  ctx.fill();
1037  
1038                  // HUGE ROUND BLACK GLASSES - dominating the face
1039                  ctx.fillStyle = '#0a0a0a';
1040                  ctx.strokeStyle = '#2a2a2a';
1041                  ctx.lineWidth = 2.5;
1042  
1043                  ctx.beginPath();
1044                  ctx.arc(115, 135, 18, 0, Math.PI * 2);
1045                  ctx.fill();
1046                  ctx.stroke();
1047  
1048                  ctx.beginPath();
1049                  ctx.arc(165, 135, 18, 0, Math.PI * 2);
1050                  ctx.fill();
1051                  ctx.stroke();
1052  
1053                  // Thick glasses bridge
1054                  ctx.lineWidth = 3;
1055                  ctx.beginPath();
1056                  ctx.moveTo(133, 135);
1057                  ctx.lineTo(147, 135);
1058                  ctx.stroke();
1059  
1060                  // Lens reflections
1061                  ctx.fillStyle = 'rgba(255, 255, 255, 0.25)';
1062                  ctx.beginPath();
1063                  ctx.ellipse(110, 130, 3, 4, 0, 0, Math.PI * 2);
1064                  ctx.fill();
1065                  ctx.beginPath();
1066                  ctx.ellipse(160, 130, 3, 4, 0, 0, Math.PI * 2);
1067                  ctx.fill();
1068  
1069                  // Small nose
1070                  ctx.fillStyle = 'rgba(216, 196, 176, 0.5)';
1071                  ctx.beginPath();
1072                  ctx.ellipse(140, 143, 4, 6, 0, 0, Math.PI * 2);
1073                  ctx.fill();
1074  
1075                  // WILD BUSHY DARKER BROWN BEARD - more distinct
1076                  ctx.fillStyle = '#6b5638';  // DARKER BROWN base
1077  
1078                  // Left side - elongated downward
1079                  ctx.beginPath();
1080                  ctx.ellipse(100, 170, 24, 42, -0.2, 0, Math.PI * 2);
1081                  ctx.fill();
1082  
1083                  // Right side - elongated downward
1084                  ctx.beginPath();
1085                  ctx.ellipse(180, 170, 24, 42, 0.2, 0, Math.PI * 2);
1086                  ctx.fill();
1087  
1088                  // Center mass - elongated not round
1089                  ctx.fillStyle = '#7d6644';  // MID BROWN
1090                  ctx.beginPath();
1091                  ctx.ellipse(140, 175, 28, 45, 0, 0, Math.PI * 2);
1092                  ctx.fill();
1093  
1094                  // Lower layers - more elongated and distinct
1095                  ctx.fillStyle = '#8f7650';  // LIGHTER BROWN
1096                  ctx.beginPath();
1097                  ctx.ellipse(115, 188, 20, 38, -0.1, 0, Math.PI * 2);
1098                  ctx.fill();
1099  
1100                  ctx.beginPath();
1101                  ctx.ellipse(165, 188, 20, 38, 0.1, 0, Math.PI * 2);
1102                  ctx.fill();
1103  
1104                  // Inner highlights for volume
1105                  ctx.fillStyle = '#9d865a';
1106                  ctx.beginPath();
1107                  ctx.ellipse(140, 182, 20, 35, 0, 0, Math.PI * 2);
1108                  ctx.fill();
1109  
1110                  // FRAYED STRANDS at the bottom - darker and more visible
1111                  ctx.strokeStyle = '#7d6644';
1112                  ctx.lineWidth = 2;
1113                  ctx.lineCap = 'round';
1114  
1115                  const frayedStrands = [
1116                      [[95, 198], [92, 212], [90, 223]],
1117                      [[105, 203], [103, 217], [100, 230]],
1118                      [[115, 208], [113, 220], [110, 234]],
1119                      [[125, 212], [124, 224], [122, 237]],
1120                      [[135, 215], [135, 227], [135, 240]],
1121                      [[145, 215], [145, 227], [145, 240]],
1122                      [[155, 212], [156, 224], [158, 237]],
1123                      [[165, 208], [167, 220], [170, 234]],
1124                      [[175, 203], [177, 217], [180, 230]],
1125                      [[185, 198], [188, 212], [190, 223]],
1126                      [[100, 200], [98, 214], [95, 227]],
1127                      [[140, 216], [140, 229], [140, 242]],
1128                      [[180, 200], [182, 214], [185, 227]],
1129                      [[120, 210], [119, 222], [117, 235]],
1130                      [[160, 210], [161, 222], [163, 235]]
1131                  ];
1132  
1133                  frayedStrands.forEach(points => {
1134                      ctx.beginPath();
1135                      ctx.moveTo(points[0][0], points[0][1]);
1136                      points.slice(1).forEach(([x, y]) => ctx.lineTo(x, y));
1137                      ctx.stroke();
1138                  });
1139  
1140                  // PROMINENT WILD MUSTACHE - DARKER BROWN matching beard
1141                  ctx.strokeStyle = '#6b5638';
1142                  ctx.lineWidth = 8;
1143                  ctx.lineCap = 'round';
1144  
1145                  ctx.beginPath();
1146                  ctx.moveTo(100, 150);
1147                  ctx.quadraticCurveTo(80, 160, 60, 153);
1148                  ctx.stroke();
1149  
1150                  ctx.beginPath();
1151                  ctx.moveTo(180, 150);
1152                  ctx.quadraticCurveTo(200, 160, 220, 153);
1153                  ctx.stroke();
1154  
1155                  ctx.strokeStyle = '#7d6644';
1156                  ctx.lineWidth = 6;
1157  
1158                  ctx.beginPath();
1159                  ctx.moveTo(103, 152);
1160                  ctx.quadraticCurveTo(85, 161, 65, 155);
1161                  ctx.stroke();
1162  
1163                  ctx.beginPath();
1164                  ctx.moveTo(177, 152);
1165                  ctx.quadraticCurveTo(195, 161, 215, 155);
1166                  ctx.stroke();
1167  
1168                  // DRAW 6 PROPER HUMAN ARMS WITH LONG SLEEVES AND HANDS
1169                  function drawHumanArm(x1, y1, x2, y2, x3, y3) {
1170                      // DARK BLUE LONG SLEEVE - Upper arm
1171                      ctx.strokeStyle = '#3a4f6b';
1172                      ctx.lineWidth = 18;
1173                      ctx.lineCap = 'round';
1174                      ctx.beginPath();
1175                      ctx.moveTo(x1, y1);
1176                      ctx.lineTo(x2, y2);
1177                      ctx.stroke();
1178  
1179                      // Sleeve shadow for depth
1180                      ctx.strokeStyle = '#2a3f5b';
1181                      ctx.lineWidth = 16;
1182                      ctx.beginPath();
1183                      ctx.moveTo(x1 + 1, y1 + 1);
1184                      ctx.lineTo(x2 + 1, y2 + 1);
1185                      ctx.stroke();
1186  
1187                      // DARK BLUE LONG SLEEVE - Forearm
1188                      ctx.strokeStyle = '#3a4f6b';
1189                      ctx.lineWidth = 16;
1190                      ctx.beginPath();
1191                      ctx.moveTo(x2, y2);
1192                      ctx.lineTo(x3, y3);
1193                      ctx.stroke();
1194  
1195                      // Forearm sleeve shadow
1196                      ctx.strokeStyle = '#2a3f5b';
1197                      ctx.lineWidth = 14;
1198                      ctx.beginPath();
1199                      ctx.moveTo(x2 + 1, y2 + 1);
1200                      ctx.lineTo(x3 + 1, y3 + 1);
1201                      ctx.stroke();
1202  
1203                      // Elbow joint (covered by sleeve)
1204                      ctx.fillStyle = '#344a60';
1205                      ctx.beginPath();
1206                      ctx.arc(x2, y2, 10, 0, Math.PI * 2);
1207                      ctx.fill();
1208  
1209                      // PROPER HAND STRUCTURE (compact to fit viewport)
1210                      const isLeft = x3 < 140;
1211                      const direction = isLeft ? -1 : 1;
1212  
1213                      // Calculate hand angle based on arm direction
1214                      const armAngle = Math.atan2(y3 - y2, x3 - x2);
1215  
1216                      // WRIST
1217                      ctx.fillStyle = '#d4a574';
1218                      ctx.beginPath();
1219                      ctx.ellipse(x3, y3, 5, 6, armAngle, 0, Math.PI * 2);
1220                      ctx.fill();
1221  
1222                      // PALM - rectangular base of hand (smaller)
1223                      const palmWidth = 10;
1224                      const palmLength = 12;
1225                      const palmX = x3 + direction * 6;
1226                      const palmY = y3;
1227  
1228                      ctx.fillStyle = '#d4a574';
1229                      ctx.beginPath();
1230                      ctx.roundRect(
1231                          isLeft ? palmX - palmLength : palmX,
1232                          palmY - palmWidth/2,
1233                          palmLength,
1234                          palmWidth,
1235                          2
1236                      );
1237                      ctx.fill();
1238  
1239                      // Palm crease lines
1240                      ctx.strokeStyle = '#b8865a';
1241                      ctx.lineWidth = 0.5;
1242                      ctx.beginPath();
1243                      ctx.moveTo(palmX + direction * 3, palmY - 3);
1244                      ctx.lineTo(palmX + direction * 3, palmY + 3);
1245                      ctx.stroke();
1246  
1247                      // FIVE FINGERS extending from palm (shorter to fit)
1248                      const fingerStartX = palmX + direction * palmLength;
1249  
1250                      // Finger positions (y offsets from palm center) - COMPACT
1251                      const fingers = [
1252                          { y: -5, length: 8, width: 2 },   // pinky
1253                          { y: -2, length: 10, width: 2 },  // ring
1254                          { y: 1, length: 11, width: 2 },   // middle (longest)
1255                          { y: 4, length: 9, width: 2 },    // index
1256                          { y: 7, length: 6, width: 2.5 }   // thumb (shorter, thicker)
1257                      ];
1258  
1259                      fingers.forEach((finger, index) => {
1260                          const fy = palmY + finger.y;
1261                          const isThumb = index === 4;
1262  
1263                          // Draw finger
1264                          ctx.fillStyle = '#c89860';
1265                          ctx.beginPath();
1266  
1267                          if (isThumb) {
1268                              // Thumb at angle
1269                              ctx.ellipse(
1270                                  fingerStartX + direction * 3,
1271                                  fy,
1272                                  finger.width * 1.5,
1273                                  finger.length / 2,
1274                                  direction * 0.6,
1275                                  0,
1276                                  Math.PI * 2
1277                              );
1278                          } else {
1279                              // Regular finger
1280                              ctx.roundRect(
1281                                  isLeft ? fingerStartX - finger.length : fingerStartX,
1282                                  fy - finger.width/2,
1283                                  finger.length,
1284                                  finger.width,
1285                                  finger.width/2
1286                              );
1287                          }
1288                          ctx.fill();
1289  
1290                          // Finger joint (not on thumb)
1291                          if (!isThumb) {
1292                              ctx.strokeStyle = '#a67850';
1293                              ctx.lineWidth = 0.5;
1294                              ctx.beginPath();
1295                              ctx.moveTo(fingerStartX + direction * finger.length * 0.6, fy - finger.width/2);
1296                              ctx.lineTo(fingerStartX + direction * finger.length * 0.6, fy + finger.width/2);
1297                              ctx.stroke();
1298                          }
1299  
1300                          // Fingernail
1301                          ctx.fillStyle = 'rgba(240, 230, 220, 0.6)';
1302                          ctx.beginPath();
1303                          const nailX = isThumb ?
1304                              fingerStartX + direction * 4 :
1305                              fingerStartX + direction * finger.length * 0.85;
1306                          ctx.ellipse(nailX, fy, 0.8, 1, 0, 0, Math.PI * 2);
1307                          ctx.fill();
1308                      });
1309                  }
1310  
1311                  // LEFT ARM 1 - upper left (hands moved inward to stay on screen)
1312                  drawHumanArm(110, 165, 60, 100, 35, 95);
1313  
1314                  // LEFT ARM 2 - middle left
1315                  drawHumanArm(110, 180, 50, 160, 35, 165);
1316  
1317                  // LEFT ARM 3 - lower left
1318                  drawHumanArm(110, 195, 55, 225, 35, 255);
1319  
1320                  // RIGHT ARM 1 - upper right (hands moved inward to stay on screen)
1321                  drawHumanArm(170, 165, 220, 100, 245, 95);
1322  
1323                  // RIGHT ARM 2 - middle right
1324                  drawHumanArm(170, 180, 230, 160, 245, 165);
1325  
1326                  // RIGHT ARM 3 - lower right
1327                  drawHumanArm(170, 195, 225, 225, 245, 255);
1328  
1329                  // Version indicator
1330                  ctx.fillStyle = '#ff6600';
1331                  ctx.font = 'bold 10px Courier New';
1332                  ctx.fillText('v1.0.401', 5, 275);
1333              }
1334  
1335              drawKamaji();
1336  
1337              // ANIMATIONS with anime.js
1338              let breatheOffset = 0;
1339  
1340              function animate() {
1341                  breatheOffset += 0.02;
1342                  ctx.save();
1343                  ctx.clearRect(0, 0, canvas.width, canvas.height);
1344                  ctx.translate(0, Math.sin(breatheOffset) * 2);
1345                  drawKamaji();
1346                  ctx.restore();
1347                  requestAnimationFrame(animate);
1348              }
1349  
1350              animate();
1351  
1352              // Hover wobble
1353              canvas.addEventListener('mouseenter', () => {
1354                  anime({
1355                      targets: canvas,
1356                      rotate: [-2, 2, -1, 1, 0],
1357                      duration: 800,
1358                      easing: 'easeInOutQuad'
1359                  });
1360              });
1361          });
1362      </script>
1363  </body>
1364  </html>