quantum-dashboard / index.html
Toowired's picture
Update index.html
a7b8d19 verified
Raw History Blame Contribute Delete
73.7 kB
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Quantum API Dashboard</title> <script src="https://cdn.tailwindcss.com"></script> <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css"> <style> @import url('https://fonts.googleapis.com/css2?family=Orbitron:wght@400;500;600;700&family=Major+Mono+Display&display=swap'); :root { --primary-hue: 210; --primary-saturation: 80%; --primary-lightness: 50%; } body { font-family: 'Orbitron', sans-serif; background: radial-gradient(circle at center, #0f172a 0%, #020617 100%); overflow: hidden; color: #e2e8f0; touch-action: none; } .dashboard { position: relative; width: 100vw; height: 100vh; perspective: 2000px; transform-style: preserve-3d; } .button-3d { position: absolute; width: 140px; height: 140px; border-radius: 20px; display: flex; flex-direction: column; align-items: center; justify-content: center; cursor: move; user-select: none; transform-style: preserve-3d; transition: all 0.3s cubic-bezier(0.25, 0.8, 0.25, 1); box-shadow: 0 10px 25px -5px rgba(0, 0, 0, 0.5); z-index: 10; border: 1px solid rgba(255, 255, 255, 0.1); backdrop-filter: blur(5px); background: linear-gradient(145deg, rgba(255,255,255,0.1), rgba(255,255,255,0.05)); } .button-3d:hover { transform: translateY(-8px) rotateX(15deg) scale(1.05); box-shadow: 0 20px 40px -10px rgba(0, 0, 0, 0.6); z-index: 20; } .button-3d:active { transform: translateY(0) rotateX(0) scale(0.98); } .button-3d::before { content: ''; position: absolute; top: 0; left: 0; right: 0; bottom: 0; border-radius: inherit; background: linear-gradient(135deg, rgba(255,255,255,0.2) 0%, transparent 50%); opacity: 0; transition: opacity 0.3s; } .button-3d:hover::before { opacity: 1; } .button-icon { font-size: 3rem; margin-bottom: 12px; text-shadow: 0 4px 8px rgba(0,0,0,0.4); transition: all 0.3s; } .button-label { font-size: 1rem; font-weight: 600; text-shadow: 0 2px 4px rgba(0,0,0,0.4); text-align: center; max-width: 90%; word-break: break-word; } .button-status { position: absolute; bottom: 10px; right: 10px; width: 12px; height: 12px; border-radius: 50%; background: #4ade80; box-shadow: 0 0 10px #4ade80; } .modal { position: fixed; top: 0; left: 0; width: 100%; height: 100%; background-color: rgba(2, 6, 23, 0.95); backdrop-filter: blur(20px); display: flex; justify-content: center; align-items: center; z-index: 1000; opacity: 0; pointer-events: none; transition: opacity 0.4s, transform 0.4s; transform: scale(0.9); } .modal-content { background: linear-gradient(145deg, rgba(30, 41, 59, 0.9), rgba(15, 23, 42, 0.9)); border-radius: 24px; width: 85%; max-width: 800px; max-height: 90vh; overflow-y: auto; padding: 2.5rem; box-shadow: 0 35px 60px -15px rgba(0, 0, 0, 0.7); border: 1px solid rgba(255, 255, 255, 0.15); transform: translateY(30px); transition: transform 0.4s; position: relative; } .modal.active { opacity: 1; pointer-events: all; transform: scale(1); } .modal.active .modal-content { transform: translateY(0); } .control-panel { position: fixed; bottom: 3rem; right: 3rem; z-index: 500; display: flex; flex-direction: column; gap: 1rem; } .control-button { width: 70px; height: 70px; border-radius: 50%; background: linear-gradient(145deg, hsl(var(--primary-hue), var(--primary-saturation), var(--primary-lightness)), hsl(var(--primary-hue), calc(var(--primary-saturation) - 10%), calc(var(--primary-lightness) - 10%))); display: flex; justify-content: center; align-items: center; font-size: 1.8rem; color: white; box-shadow: 0 15px 25px -5px rgba(0, 0, 0, 0.4); cursor: pointer; transition: all 0.3s cubic-bezier(0.25, 0.8, 0.25, 1); position: relative; overflow: hidden; } .control-button::after { content: ''; position: absolute; top: 0; left: 0; width: 100%; height: 100%; background: linear-gradient(45deg, rgba(255,255,255,0.2) 0%, transparent 100%); opacity: 0; transition: opacity 0.3s; } .control-button:hover { transform: translateY(-5px) scale(1.1); box-shadow: 0 20px 30px -5px rgba(0, 0, 0, 0.5); } .control-button:hover::after { opacity: 1; } .control-button:active { transform: translateY(0) scale(0.98); } .grid-lines { position: absolute; top: 0; left: 0; width: 100%; height: 100%; background-image: linear-gradient(rgba(255, 255, 255, 0.03) 1px, transparent 1px), linear-gradient(90deg, rgba(255, 255, 255, 0.03) 1px, transparent 1px); background-size: 60px 60px; z-index: 0; opacity: 0.7; } .particles { position: absolute; top: 0; left: 0; width: 100%; height: 100%; z-index: 1; pointer-events: none; } .particle { position: absolute; width: 2px; height: 2px; background: rgba(255,255,255,0.5); border-radius: 50%; animation: float 15s infinite linear; } @keyframes float { 0% { transform: translateY(0) translateX(0); opacity: 0; } 10% { opacity: 1; } 90% { opacity: 1; } 100% { transform: translateY(-100vh) translateX(20px); opacity: 0; } } /* Custom scrollbar */ ::-webkit-scrollbar { width: 10px; } ::-webkit-scrollbar-track { background: rgba(255, 255, 255, 0.05); border-radius: 10px; } ::-webkit-scrollbar-thumb { background: hsl(var(--primary-hue), var(--primary-saturation), var(--primary-lightness)); border-radius: 10px; } /* Form elements */ input, textarea, select { background-color: rgba(15, 23, 42, 0.7); border: 1px solid rgba(255, 255, 255, 0.15); border-radius: 12px; padding: 0.9rem 1.2rem; color: white; width: 100%; margin-bottom: 1rem; transition: all 0.3s; font-family: 'Orbitron', sans-serif; } input:focus, textarea:focus, select:focus { outline: none; border-color: hsl(var(--primary-hue), var(--primary-saturation), var(--primary-lightness)); box-shadow: 0 0 0 3px hsla(var(--primary-hue), var(--primary-saturation), var(--primary-lightness), 0.3); background-color: rgba(15, 23, 42, 0.9); } .form-label { display: block; margin-bottom: 0.7rem; font-weight: 600; font-size: 0.95rem; letter-spacing: 0.5px; } .btn { background: linear-gradient(145deg, hsl(var(--primary-hue), var(--primary-saturation), var(--primary-lightness)), hsl(var(--primary-hue), calc(var(--primary-saturation) - 10%), calc(var(--primary-lightness) - 10%))); color: white; border: none; padding: 1rem 2rem; border-radius: 12px; font-weight: 600; cursor: pointer; transition: all 0.3s cubic-bezier(0.25, 0.8, 0.25, 1); font-family: 'Orbitron', sans-serif; letter-spacing: 0.5px; position: relative; overflow: hidden; } .btn::after { content: ''; position: absolute; top: 0; left: 0; width: 100%; height: 100%; background: linear-gradient(45deg, rgba(255,255,255,0.2) 0%, transparent 100%); opacity: 0; transition: opacity 0.3s; } .btn:hover { transform: translateY(-3px); box-shadow: 0 15px 25px -5px rgba(0, 0, 0, 0.4); } .btn:hover::after { opacity: 1; } .btn:active { transform: translateY(0); } .btn-secondary { background: rgba(255, 255, 255, 0.1); backdrop-filter: blur(10px); } .btn-secondary:hover { background: rgba(255, 255, 255, 0.15); } .color-option { width: 30px; height: 30px; border-radius: 50%; cursor: pointer; transition: all 0.3s; border: 2px solid transparent; } .color-option:hover { transform: scale(1.1); } .color-option.active { transform: scale(1.2); border: 2px solid white; box-shadow: 0 0 10px rgba(255,255,255,0.5); } .tab { padding: 0.8rem 1.5rem; border-radius: 8px; cursor: pointer; transition: all 0.3s; font-weight: 500; background: transparent; color: rgba(255,255,255,0.7); } .tab.active { background: rgba(255,255,255,0.1); color: white; font-weight: 600; } .tab:hover:not(.active) { background: rgba(255,255,255,0.05); } .tab-content { display: none; } .tab-content.active { display: block; animation: fadeIn 0.4s; } @keyframes fadeIn { from { opacity: 0; transform: translateY(10px); } to { opacity: 1; transform: translateY(0); } } .response-tabs { display: flex; border-bottom: 1px solid rgba(255,255,255,0.1); margin-bottom: 1.5rem; } .response-tab { padding: 0.7rem 1.5rem; cursor: pointer; border-bottom: 3px solid transparent; transition: all 0.3s; font-size: 0.9rem; color: rgba(255,255,255,0.7); } .response-tab.active { border-bottom: 3px solid hsl(var(--primary-hue), var(--primary-saturation), var(--primary-lightness)); color: white; } .response-tab:hover:not(.active) { color: white; } .response-content { display: none; } .response-content.active { display: block; } .code-block { background: rgba(0,0,0,0.3); border-radius: 8px; padding: 1rem; font-family: 'Major Mono Display', monospace; font-size: 0.85rem; line-height: 1.6; overflow-x: auto; margin-bottom: 1rem; } .connection-line { position: absolute; background: rgba(255,255,255,0.2); height: 2px; transform-origin: left center; z-index: 5; pointer-events: none; } .connection-line::after { content: ''; position: absolute; right: 0; top: 50%; width: 10px; height: 10px; background: hsl(var(--primary-hue), var(--primary-saturation), var(--primary-lightness)); border-radius: 50%; transform: translateY(-50%); box-shadow: 0 0 10px hsl(var(--primary-hue), var(--primary-saturation), var(--primary-lightness)); } .connection-mode { position: absolute; background: rgba(15, 23, 42, 0.9); border: 1px solid rgba(255,255,255,0.2); padding: 0.3rem 0.6rem; border-radius: 20px; font-size: 0.7rem; z-index: 6; pointer-events: none; transform: translate(-50%, -50%); } .mini-map { position: fixed; bottom: 3rem; left: 3rem; width: 200px; height: 200px; background: rgba(15, 23, 42, 0.7); border: 1px solid rgba(255,255,255,0.1); border-radius: 8px; z-index: 500; overflow: hidden; backdrop-filter: blur(5px); } .mini-map-viewport { position: absolute; border: 2px solid hsl(var(--primary-hue), var(--primary-saturation), var(--primary-lightness)); background: rgba(59, 130, 246, 0.2); z-index: 2; } .mini-map-content { position: absolute; top: 0; left: 0; width: 100%; height: 100%; transform-origin: 0 0; } .mini-map-button { position: absolute; width: 6px; height: 6px; border-radius: 50%; transform: translate(-50%, -50%); } .context-menu { position: absolute; background: rgba(30, 41, 59, 0.95); border: 1px solid rgba(255,255,255,0.1); border-radius: 8px; box-shadow: 0 10px 25px -5px rgba(0,0,0,0.5); z-index: 1000; backdrop-filter: blur(10px); display: none; overflow: hidden; } .context-menu-item { padding: 0.8rem 1.5rem; cursor: pointer; transition: all 0.2s; display: flex; align-items: center; gap: 0.8rem; } .context-menu-item:hover { background: rgba(255,255,255,0.1); } .context-menu-item i { width: 20px; text-align: center; } .dashboard-overlay { position: absolute; top: 0; left: 0; width: 100%; height: 100%; z-index: 5; pointer-events: none; } .dashboard-title { position: absolute; top: 2rem; left: 2rem; font-size: 1.8rem; font-weight: 700; z-index: 10; background: linear-gradient(to right, #fff, #aaa); -webkit-background-clip: text; -webkit-text-fill-color: transparent; text-shadow: 0 2px 10px rgba(0,0,0,0.3); } .dashboard-subtitle { position: absolute; top: 4.5rem; left: 2rem; font-size: 1rem; color: rgba(255,255,255,0.7); z-index: 10; max-width: 300px; } .dashboard-bg { position: absolute; top: 0; left: 0; width: 100%; height: 100%; background: radial-gradient(circle at 20% 30%, hsla(var(--primary-hue), 50%, 30%, 0.3) 0%, transparent 50%), radial-gradient(circle at 80% 70%, hsla(calc(var(--primary-hue) + 60), 50%, 30%, 0.3) 0%, transparent 50%); z-index: 0; transition: all 1s; } .connection-mode-selector { position: absolute; top: 0; left: 50%; transform: translateX(-50%); background: rgba(30, 41, 59, 0.9); border-radius: 0 0 8px 8px; padding: 0.5rem 1rem; display: flex; gap: 0.5rem; z-index: 100; border: 1px solid rgba(255,255,255,0.1); border-top: none; } .connection-mode-btn { padding: 0.3rem 0.8rem; border-radius: 4px; cursor: pointer; font-size: 0.8rem; transition: all 0.2s; } .connection-mode-btn.active { background: hsl(var(--primary-hue), var(--primary-saturation), var(--primary-lightness)); color: white; } .connection-mode-btn:not(.active):hover { background: rgba(255,255,255,0.1); } .button-tags { position: absolute; top: 10px; left: 10px; display: flex; gap: 5px; flex-wrap: wrap; max-width: 80%; } .button-tag { background: rgba(0,0,0,0.3); border-radius: 4px; padding: 2px 6px; font-size: 0.6rem; color: rgba(255,255,255,0.8); white-space: nowrap; } .button-actions { position: absolute; top: 10px; right: 10px; display: none; gap: 5px; } .button-3d:hover .button-actions { display: flex; } .button-action { width: 20px; height: 20px; border-radius: 4px; background: rgba(0,0,0,0.3); display: flex; align-items: center; justify-content: center; font-size: 0.7rem; cursor: pointer; transition: all 0.2s; } .button-action:hover { background: rgba(255,255,255,0.2); } .button-group { position: absolute; border: 2px dashed rgba(255,255,255,0.3); border-radius: 8px; z-index: 4; background: rgba(59, 130, 246, 0.1); display: none; } .button-group.active { display: block; } .button-group-label { position: absolute; top: -25px; left: 0; background: rgba(30, 41, 59, 0.9); padding: 0.3rem 0.6rem; border-radius: 4px; font-size: 0.8rem; white-space: nowrap; } .dashboard-toolbar { position: absolute; top: 2rem; right: 2rem; display: flex; gap: 1rem; z-index: 10; } .toolbar-btn { padding: 0.5rem 1rem; border-radius: 6px; background: rgba(255,255,255,0.1); backdrop-filter: blur(5px); cursor: pointer; transition: all 0.2s; display: flex; align-items: center; gap: 0.5rem; font-size: 0.9rem; } .toolbar-btn:hover { background: rgba(255,255,255,0.2); } .toolbar-btn.active { background: hsl(var(--primary-hue), var(--primary-saturation), var(--primary-lightness)); color: white; } .search-bar { position: absolute; top: 2rem; left: 50%; transform: translateX(-50%); width: 300px; z-index: 10; } .search-input { width: 100%; padding: 0.7rem 1.2rem; border-radius: 30px; background: rgba(30, 41, 59, 0.8); border: 1px solid rgba(255,255,255,0.1); color: white; font-size: 0.9rem; transition: all 0.3s; backdrop-filter: blur(5px); } .search-input:focus { outline: none; border-color: hsl(var(--primary-hue), var(--primary-saturation), var(--primary-lightness)); box-shadow: 0 0 0 3px hsla(var(--primary-hue), var(--primary-saturation), var(--primary-lightness), 0.3); width: 400px; } .search-results { position: absolute; top: 100%; left: 0; width: 100%; background: rgba(30, 41, 59, 0.95); border-radius: 0 0 12px 12px; max-height: 300px; overflow-y: auto; display: none; box-shadow: 0 10px 25px -5px rgba(0,0,0,0.5); border: 1px solid rgba(255,255,255,0.1); border-top: none; z-index: 100; } .search-result-item { padding: 0.8rem 1.2rem; cursor: pointer; transition: all 0.2s; border-bottom: 1px solid rgba(255,255,255,0.05); } .search-result-item:hover { background: rgba(255,255,255,0.1); } .search-result-item:last-child { border-bottom: none; } .search-result-icon { margin-right: 0.8rem; color: hsl(var(--primary-hue), var(--primary-saturation), var(--primary-lightness)); } .search-input.active + .search-results { display: block; } .dashboard-stats { position: absolute; bottom: 2rem; left: 50%; transform: translateX(-50%); display: flex; gap: 1.5rem; background: rgba(30, 41, 59, 0.8); border-radius: 30px; padding: 0.7rem 1.5rem; z-index: 10; backdrop-filter: blur(5px); border: 1px solid rgba(255,255,255,0.1); } .stat-item { display: flex; flex-direction: column; align-items: center; } .stat-value { font-size: 1.2rem; font-weight: 600; color: hsl(var(--primary-hue), var(--primary-saturation), var(--primary-lightness)); } .stat-label { font-size: 0.7rem; opacity: 0.8; margin-top: 0.2rem; } .dashboard-help { position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%); background: rgba(30, 41, 59, 0.95); border-radius: 16px; padding: 2rem; max-width: 600px; width: 80%; max-height: 80vh; overflow-y: auto; z-index: 1000; box-shadow: 0 25px 50px -12px rgba(0, 0, 0, 0.7); border: 1px solid rgba(255, 255, 255, 0.15); display: none; } .dashboard-help.active { display: block; animation: fadeIn 0.4s; } .help-close { position: absolute; top: 1rem; right: 1rem; cursor: pointer; font-size: 1.2rem; color: rgba(255,255,255,0.7); transition: all 0.2s; } .help-close:hover { color: white; } .help-section { margin-bottom: 2rem; } .help-title { font-size: 1.5rem; margin-bottom: 1rem; color: hsl(var(--primary-hue), var(--primary-saturation), var(--primary-lightness)); } .help-content { line-height: 1.6; color: rgba(255,255,255,0.8); } .help-shortcut { display: flex; justify-content: space-between; padding: 0.8rem 0; border-bottom: 1px solid rgba(255,255,255,0.1); } .help-shortcut:last-child { border-bottom: none; } .shortcut-keys { display: flex; gap: 0.5rem; } .shortcut-key { background: rgba(255,255,255,0.1); padding: 0.3rem 0.6rem; border-radius: 4px; font-family: 'Major Mono Display', monospace; } .api-visualizer { position: absolute; top: 0; left: 0; width: 100%; height: 100%; z-index: 1000; display: none; } .api-visualizer.active { display: block; } .visualizer-canvas { position: absolute; top: 0; left: 0; width: 100%; height: 100%; } .visualizer-controls { position: absolute; bottom: 2rem; left: 50%; transform: translateX(-50%); display: flex; gap: 1rem; z-index: 1001; } .visualizer-btn { padding: 0.8rem 1.5rem; border-radius: 30px; background: rgba(30, 41, 59, 0.8); border: 1px solid rgba(255,255,255,0.1); color: white; cursor: pointer; transition: all 0.2s; backdrop-filter: blur(5px); } .visualizer-btn:hover { background: rgba(255,255,255,0.1); } .visualizer-btn i { margin-right: 0.5rem; } .dashboard-tour { position: absolute; top: 0; left: 0; width: 100%; height: 100%; z-index: 10000; pointer-events: none; } .tour-highlight { position: absolute; border-radius: 8px; box-shadow: 0 0 0 9999px rgba(0,0,0,0.8), 0 0 0 3px hsl(var(--primary-hue), var(--primary-saturation), var(--primary-lightness)); z-index: 10001; transition: all 0.3s; } .tour-tooltip { position: absolute; background: rgba(30, 41, 59, 0.95); border-radius: 8px; padding: 1.5rem; max-width: 300px; box-shadow: 0 10px 25px -5px rgba(0,0,0,0.5); border: 1px solid rgba(255,255,255,0.1); z-index: 10002; pointer-events: auto; } .tour-tooltip-title { font-size: 1.2rem; margin-bottom: 0.8rem; color: hsl(var(--primary-hue), var(--primary-saturation), var(--primary-lightness)); } .tour-tooltip-content { margin-bottom: 1.5rem; line-height: 1.5; } .tour-tooltip-footer { display: flex; justify-content: space-between; align-items: center; } .tour-progress { font-size: 0.8rem; opacity: 0.7; } .tour-btn { padding: 0.5rem 1rem; border-radius: 4px; background: hsl(var(--primary-hue), var(--primary-saturation), var(--primary-lightness)); color: white; cursor: pointer; transition: all 0.2s; } .tour-btn:hover { opacity: 0.9; } .tour-btn-secondary { background: rgba(255,255,255,0.1); } .dashboard-loading { position: fixed; top: 0; left: 0; width: 100%; height: 100%; background: #0f172a; display: flex; flex-direction: column; justify-content: center; align-items: center; z-index: 10000; } .loading-spinner { width: 50px; height: 50px; border: 5px solid rgba(255,255,255,0.1); border-radius: 50%; border-top-color: hsl(var(--primary-hue), var(--primary-saturation), var(--primary-lightness)); animation: spin 1s linear infinite; margin-bottom: 1.5rem; } @keyframes spin { to { transform: rotate(360deg); } } .loading-text { font-size: 1.2rem; letter-spacing: 2px; background: linear-gradient(to right, #fff, #aaa); -webkit-background-clip: text; -webkit-text-fill-color: transparent; } .loading-progress { width: 200px; height: 4px; background: rgba(255,255,255,0.1); border-radius: 2px; margin-top: 1.5rem; overflow: hidden; } .loading-progress-bar { height: 100%; background: hsl(var(--primary-hue), var(--primary-saturation), var(--primary-lightness)); width: 0%; transition: width 0.3s; } /* Responsive adjustments */ @media (max-width: 768px) { .dashboard-title, .dashboard-subtitle { left: 1rem; } .dashboard-toolbar { right: 1rem; top: 1rem; } .control-panel { right: 1rem; bottom: 1rem; } .mini-map { left: 1rem; bottom: 1rem; width: 150px; height: 150px; } .dashboard-stats { bottom: 1rem; padding: 0.5rem 1rem; gap: 1rem; } .search-bar { top: 1rem; width: 200px; } .search-input:focus { width: 250px; } .modal-content { width: 90%; padding: 1.5rem; } .button-3d { width: 100px; height: 100px; } .button-icon { font-size: 2rem; } .button-label { font-size: 0.8rem; } } </style> </head> <body> <div class="dashboard-loading"> <div class="loading-spinner"></div> <div class="loading-text">QUANTUM DASHBOARD</div> <div class="loading-progress"> <div class="loading-progress-bar" id="loadingBar"></div> </div> </div> <div class="dashboard" id="dashboard"> <div class="dashboard-bg"></div> <div class="grid-lines"></div> <div class="particles" id="particles"></div> <div class="dashboard-overlay"></div> <div class="dashboard-title">Quantum API Dashboard</div> <div class="dashboard-subtitle">Visualize, connect and manage your API ecosystem in 3D space</div> <div class="dashboard-toolbar"> <div class="toolbar-btn" id="groupModeBtn"> <i class="fas fa-object-group"></i> Group Mode </div> <div class="toolbar-btn" id="connectionModeBtn"> <i class="fas fa-plug"></i> Connect APIs </div> <div class="toolbar-btn" id="visualizerBtn"> <i class="fas fa-project-diagram"></i> Visualizer </div> <div class="toolbar-btn" id="helpBtn"> <i class="fas fa-question-circle"></i> Help </div> </div> <div class="search-bar"> <input type="text" class="search-input" placeholder="Search APIs, endpoints, tags..." id="searchInput"> <div class="search-results" id="searchResults"></div> </div> <div class="dashboard-stats"> <div class="stat-item"> <div class="stat-value" id="apiCount">0</div> <div class="stat-label">APIs</div> </div> <div class="stat-item"> <div class="stat-value" id="connectionCount">0</div> <div class="stat-label">Connections</div> </div> <div class="stat-item"> <div class="stat-value" id="groupCount">0</div> <div class="stat-label">Groups</div> </div> <div class="stat-item"> <div class="stat-value" id="statusValue">100%</div> <div class="stat-label">Status</div> </div> </div> <!-- 3D Buttons will be added here dynamically --> <!-- Connection lines will be added here dynamically --> <!-- Button groups will be added here dynamically --> <!-- Connection mode selector (hidden by default) --> <div class="connection-mode-selector" id="connectionModeSelector" style="display: none;"> <div class="connection-mode-btn active" data-mode="sequential">Sequential</div> <div class="connection-mode-btn" data-mode="parallel">Parallel</div> <div class="connection-mode-btn" data-mode="conditional">Conditional</div> <div class="connection-mode-btn" data-mode="fallback">Fallback</div> </div> <!-- Control Panel --> <div class="control-panel"> <div class="control-button" id="settingsButton"> <i class="fas fa-cog"></i> </div> <div class="control-button" id="addButton"> <i class="fas fa-plus"></i> </div> <div class="control-button" id="minimapButton"> <i class="fas fa-map"></i> </div> </div> <!-- Mini Map --> <div class="mini-map" id="miniMap" style="display: none;"> <div class="mini-map-content" id="miniMapContent"></div> <div class="mini-map-viewport" id="miniMapViewport"></div> </div> </div> <!-- Context Menu --> <div class="context-menu" id="contextMenu"> <div class="context-menu-item" data-action="edit"> <i class="fas fa-edit"></i> Edit </div> <div class="context-menu-item" data-action="delete"> <i class="fas fa-trash"></i> Delete </div> <div class="context-menu-item" data-action="duplicate"> <i class="fas fa-copy"></i> Duplicate </div> <div class="context-menu-item" data-action="test"> <i class="fas fa-bolt"></i> Test API </div> <div class="context-menu-item" data-action="document"> <i class="fas fa-book"></i> Documentation </div> <div class="context-menu-item" data-action="group"> <i class="fas fa-object-group"></i> Create Group </div> </div> <!-- Settings Modal --> <div class="modal" id="settingsModal"> <div class="modal-content"> <div class="flex justify-between items-center mb-6"> <h2 class="text-2xl font-bold">Dashboard Settings</h2> <button class="text-gray-400 hover:text-white" id="closeSettings"> <i class="fas fa-times"></i> </button> </div> <div class="response-tabs mb-6"> <div class="response-tab active" data-tab="appearance">Appearance</div> <div class="response-tab" data-tab="behavior">Behavior</div> <div class="response-tab" data-tab="shortcuts">Shortcuts</div> <div class="response-tab" data-tab="importexport">Import/Export</div> </div> <div class="tab-content active" data-tab-content="appearance"> <div class="mb-8"> <h3 class="text-xl font-semibold mb-4">Color Theme</h3> <div class="grid grid-cols-2 md:grid-cols-4 gap-4 mb-6"> <div class="color-theme-option" data-theme="blue"> <div class="color-theme-preview bg-blue-500"></div> <div class="color-theme-label">Ocean</div> </div> <div class="color-theme-option" data-theme="purple"> <div class="color-theme-preview bg-purple-500"></div> <div class="color-theme-label">Royal</div> </div> <div class="color-theme-option" data-theme="green"> <div class="color-theme-preview bg-green-500"></div> <div class="color-theme-label">Emerald</div> </div> <div class="color-theme-option" data-theme="red"> <div class="color-theme-preview bg-red-500"></div> <div class="color-theme-label">Ruby</div> </div> <div class="color-theme-option" data-theme="yellow"> <div class="color-theme-preview bg-yellow-500"></div> <div class="color-theme-label">Gold</div> </div> <div class="color-theme-option" data-theme="pink"> <div class="color-theme-preview bg-pink-500"></div> <div class="color-theme-label">Rose</div> </div> <div class="color-theme-option" data-theme="indigo"> <div class="color-theme-preview bg-indigo-500"></div> <div class="color-theme-label">Sapphire</div> </div> <div class="color-theme-option" data-theme="teal"> <div class="color-theme-preview bg-teal-500"></div> <div class="color-theme-label">Aqua</div> </div> </div> <h3 class="text-xl font-semibold mb-4">Dashboard Appearance</h3> <div class="grid grid-cols-1 md:grid-cols-2 gap-4 mb-6"> <div> <label class="form-label">Background Intensity</label> <input type="range" id="bgIntensity" min="0" max="100" value="50" class="w-full"> </div> <div> <label class="form-label">Grid Visibility</label> <select id="gridVisibility" class="bg-gray-800"> <option value="visible">Visible</option> <option value="hidden">Hidden</option> </select> </div> <div> <label class="form-label">Particle Density</label> <input type="range" id="particleDensity" min="0" max="100" value="30" class="w-full"> </div> <div> <label class="form-label">Animation Speed</label> <select id="animationSpeed" class="bg-gray-800"> <option value="slow">Slow</option> <option value="normal" selected>Normal</option> <option value="fast">Fast</option> </select> </div> </div> </div> <div class="flex space-x-3"> <button class="btn">Save Changes</button> <button class="btn btn-secondary">Reset Defaults</button> </div> </div> <div class="tab-content" data-tab-content="behavior"> <div class="mb-8"> <h3 class="text-xl font-semibold mb-4">Interaction Settings</h3> <div class="space-y-4 mb-6"> <div class="flex items-center justify-between"> <label class="form-label">Enable Hover Effects</label> <label class="switch"> <input type="checkbox" checked> <span class="slider"></span> </label> </div> <div class="flex items-center justify-between"> <label class="form-label">Enable Dragging</label> <label class="switch"> <input type="checkbox" checked> <span class="slider"></span> </label> </div> <div class="flex items-center justify-between"> <label class="form-label">Enable Context Menu</label> <label class="switch"> <input type="checkbox" checked> <span class="slider"></span> </label> </div> <div class="flex items-center justify-between"> <label class="form-label">Auto-arrange on Add</label> <label class="switch"> <input type="checkbox"> <span class="slider"></span> </label> </div> </div> <h3 class="text-xl font-semibold mb-4">API Behavior</h3> <div class="space-y-4 mb-6"> <div class="flex items-center justify-between"> <label class="form-label">Auto-test on Click</label> <label class="switch"> <input type="checkbox"> <span class="slider"></span> </label> </div> <div class="flex items-center justify-between"> <label class="form-label">Show Response by Default</label> <label class="switch"> <input type="checkbox"> <span class="slider"></span> </label> </div> <div class="flex items-center justify-between"> <label class="form-label">Cache Responses</label> <label class="switch"> <input type="checkbox" checked> <span class="slider"></span> </label> </div> </div> </div> <div class="flex space-x-3"> <button class="btn">Save Changes</button> <button class="btn btn-secondary">Reset Defaults</button> </div> </div> <div class="tab-content" data-tab-content="shortcuts"> <div class="mb-8"> <h3 class="text-xl font-semibold mb-4">Keyboard Shortcuts</h3> <div class="space-y-2"> <div class="help-shortcut"> <div>Add New API</div> <div class="shortcut-keys"> <span class="shortcut-key">Ctrl</span> <span class="shortcut-key">N</span> </div> </div> <div class="help-shortcut"> <div>Toggle Group Mode</div> <div class="shortcut-keys"> <span class="shortcut-key">G</span> </div> </div> <div class="help-shortcut"> <div>Toggle Connection Mode</div> <div class="shortcut-keys"> <span class="shortcut-key">C</span> </div> </div> <div class="help-shortcut"> <div>Open Settings</div> <div class="shortcut-keys"> <span class="shortcut-key">Ctrl</span> <span class="shortcut-key">,</span> </div> </div> <div class="help-shortcut"> <div>Search APIs</div> <div class="shortcut-keys"> <span class="shortcut-key">Ctrl</span> <span class="shortcut-key">K</span> </div> </div> <div class="help-shortcut"> <div>Toggle Mini Map</div> <div class="shortcut-keys"> <span class="shortcut-key">M</span> </div> </div> </div> </div> <div class="flex space-x-3"> <button class="btn">Save Changes</button> <button class="btn btn-secondary">Reset Defaults</button> </div> </div> <div class="tab-content" data-tab-content="importexport"> <div class="mb-8"> <h3 class="text-xl font-semibold mb-4">Import Configuration</h3> <div class="mb-6"> <label class="form-label">Import JSON Configuration</label> <textarea class="w-full h-32 font-mono text-sm" id="importJson" placeholder="Paste your JSON configuration here..."></textarea> </div> <div class="mb-6"> <label class="form-label">Or Upload File</label> <div class="border-2 border-dashed border-gray-700 rounded-lg p-8 text-center"> <i class="fas fa-file-upload text-3xl mb-2 text-gray-500"></i> <p class="text-gray-400 mb-4">Drag & drop your JSON file here, or click to browse</p> <button class="btn btn-secondary">Select File</button> </div> </div> <h3 class="text-xl font-semibold mb-4">Export Configuration</h3> <div class="mb-6"> <label class="form-label">Current Configuration</label> <textarea class="w-full h-32 font-mono text-sm" id="exportJson" readonly></textarea> </div> <div class="grid grid-cols-2 gap-4"> <button class="btn btn-secondary">Copy to Clipboard</button> <button class="btn">Download JSON</button> </div> </div> </div> </div> </div> <!-- API Browser Modal --> <div class="modal" id="apiModal"> <div class="modal-content"> <div class="flex justify-between items-center mb-6"> <h2 class="text-2xl font-bold" id="apiModalTitle">API Endpoint</h2> <button class="text-gray-400 hover:text-white" id="closeApiModal"> <i class="fas fa-times"></i> </button> </div> <div class="mb-4"> <div class="flex items-center space-x-2 mb-2"> <span class="px-3 py-1 rounded-full bg-green-100 text-green-800 text-sm" id="apiMethod">GET</span> <span class="text-gray-300 text-sm truncate" id="apiUrl">https://api.val.town/v1/run/...</span> </div> <p class="text-gray-400" id="apiDescription">Accessing this API endpoint</p> </div> <div class="response-tabs mb-6"> <div class="response-tab active" data-tab="request">Request</div> <div class="response-tab" data-tab="documentation">Documentation</div> <div class="response-tab" data-tab="history">History</div> <div class="response-tab" data-tab="settings">Settings</div> </div> <div class="tab-content active" data-tab-content="request"> <div class="mb-6"> <h3 class="text-lg font-semibold mb-3">Request Parameters</h3> <div class="space-y-3" id="parametersContainer"> <div class="flex items-center space-x-2"> <input type="text" placeholder="Parameter" class="flex-1" value="user_id"> <input type="text" placeholder="Value" class="flex-1" value="12345"> <button class="text-red-400 hover:text-red-300"> <i class="fas fa-trash"></i> </button> </div> </div> <button class="text-blue-400 hover:text-blue-300 text-sm mt-2 flex items-center" id="addParameter"> <i class="fas fa-plus mr-1"></i> Add Parameter </button> </div> <div class="mb-6"> <h3 class="text-lg font-semibold mb-3">Headers</h3> <div class="space-y-3" id="headersContainer"> <div class="flex items-center space-x-2"> <input type="text" value="Content-Type" class="flex-1 bg-gray-800"> <input type="text" value="application/json" class="flex-1 bg-gray-800"> <button class="text-red-400 hover:text-red-300"> <i class="fas fa-trash"></i> </button> </div> <div class="flex items-center space-x-2"> <input type="text" value="Authorization" class="flex-1 bg-gray-800"> <input type="text" placeholder="Bearer token..." class="flex-1"> <button class="text-red-400 hover:text-red-300"> <i class="fas fa-trash"></i> </button> </div> </div> <button class="text-blue-400 hover:text-blue-300 text-sm mt-2 flex items-center" id="addHeader"> <i class="fas fa-plus mr-1"></i> Add Header </button> </div> <div class="mb-6"> <h3 class="text-lg font-semibold mb-3">Request Body</h3> <textarea class="w-full h-32 font-mono text-sm" id="requestBody" placeholder="Enter JSON body..."></textarea> </div> <div class="flex justify-end space-x-3"> <button class="btn btn-secondary" id="testRequest"> <i class="fas fa-bolt mr-2"></i> Test Request </button> <button class="btn" id="sendRequest"> <i class="fas fa-paper-plane mr-2"></i> Send </button> </div> <div class="mt-8 border-t border-gray-700 pt-6" id="responseSection" style="display: none;"> <div class="response-tabs mb-4"> <div class="response-tab active" data-response-tab="body">Body</div> <div class="response-tab" data-response-tab="headers">Headers</div> <div class="response-tab" data-response-tab="cookies">Cookies</div> <div class="response-tab" data-response-tab="timing">Timing</div> </div> <div class="response-content active" data-response-content="body"> <div class="flex items-center space-x-2 mb-3"> <span class="px-3 py-1 rounded-full bg-green-100 text-green-800 text-sm">200 OK</span> <span class="text-gray-400 text-sm">1.2s</span> <span class="text-gray-400 text-sm">2.4 KB</span> </div> <pre class="bg-gray-800 p-4 rounded-lg overflow-x-auto text-sm font-mono" id="responseBody">{ "status": "success", "data": { "user_id": 12345, "name": "John Doe", "email": "john@example.com" } }</pre> </div> <div class="response-content" data-response-content="headers"> <div class="code-block"> HTTP/1.1 200 OK<br> Content-Type: application/json<br> Content-Length: 2345<br> Date: Wed, 21 Oct 2023 07:28:00 GMT<br> Server: nginx/1.18.0<br> Cache-Control: no-cache<br> Connection: keep-alive </div> </div> <div class="response-content" data-response-content="cookies"> <div class="code-block"> No cookies received </div> </div> <div class="response-content" data-response-content="timing"> <div class="code-block"> DNS Lookup: 120ms<br> TCP Connection: 80ms<br> TLS Handshake: 220ms<br> Request Sent: 5ms<br> Waiting (TTFB): 350ms<br> Content Download: 15ms<br> <br> Total: 790ms </div> </div> </div> </div> <div class="tab-content" data-tab-content="documentation"> <div class="mb-6"> <h3 class="text-lg font-semibold mb-3">Endpoint Description</h3> <div class="bg-gray-800 p-4 rounded-lg"> <p>This endpoint retrieves user profile information based on the provided user ID.</p> </div> </div> <div class="mb-6"> <h3 class="text-lg font-semibold mb-3">Parameters</h3> <div class="overflow-x-auto"> <table class="w-full bg-gray-800 rounded-lg overflow-hidden"> <thead> <tr class="bg-gray-700"> <th class="p-3 text-left">Name</th> <th class="p-3 text-left">Type</th> <th class="p-3 text-left">Required</th> <th class="p-3 text-left">Description</th> </tr> </thead> <tbody> <tr class="border-b border-gray-700"> <td class="p-3">user_id</td> <td class="p-3">integer</td> <td class="p-3">Yes</td> <td class="p-3">The ID of the user to retrieve</td> </tr> <tr class="border-b border-gray-700"> <td class="p-3">fields</td> <td class="p-3">string</td> <td class="p-3">No</td> <td class="p-3">Comma-separated list of fields to return</td> </tr> </tbody> </table> </div> </div> <div class="mb-6"> <h3 class="text-lg font-semibold mb-3">Response</h3> <div class="bg-gray-800 p-4 rounded-lg"> <h4 class="font-semibold mb-2">Success Response (200 OK)</h4> <pre class="font-mono text-sm">{ "status": "success", "data": { "user_id": 12345, "name": "John Doe", "email": "john@example.com", "created_at": "2023-01-15T08:30:00Z" } }</pre> <h4 class="font-semibold mt-4 mb-2">Error Response (404 Not Found)</h4> <pre class="font-mono text-sm">{ "status": "error", "message": "User not found", "code": 404 }</pre> </div> </div> <div class="mb-6"> <h3 class="text-lg font-semibold mb-3">Example Code</h3> <div class="bg-gray-800 p-4 rounded-lg"> <h4 class="font-semibold mb-2">JavaScript (Fetch)</h4> <pre class="font-mono text-sm">fetch('https://api.example.com/users/12345') .then(response => response.json()) .then(data => console.log(data)) .catch(error => console.error('Error:', error));</pre> <h4 class="font-semibold mt-4 mb-2">cURL</h4> <pre class="font-mono text-sm">curl -X GET "https://api.example.com/users/12345" \ -H "Authorization: Bearer YOUR_TOKEN"</pre> </div> </div> </div> <div class="tab-content" data-tab-content="history"> <div class="mb-6"> <h3 class="text-lg font-semibold mb-3">Request History</h3> <div class="bg-gray-800 rounded-lg overflow-hidden"> <div class="history-item p-4 border-b border-gray-700"> <div class="flex justify-between items-center mb-2"> <div class="flex items-center space-x-2"> <span class="px-2 py-1 rounded-full bg-green-100 text-green-800 text-xs">200</span> <span class="text-sm">GET /users/12345</span> </div> <span class="text-xs text-gray-400">2 minutes ago</span> </div> <div class="text-xs text-gray-400 truncate">Response: {"status":"success","data":{"user_id":12345,"name":"John Doe"}}</div> </div> <div class="history-item p-4 border-b border-gray-700"> <div class="flex justify-between items-center mb-2"> <div class="flex items-center space-x-2"> <span class="px-2 py-1 rounded-full bg-red-100 text-red-800 text-xs">404</span> <span class="text-sm">GET /users/99999</span> </div> <span class="text-xs text-gray-400">1 hour ago</span> </div> <div class="text-xs text-gray-400 truncate">Response: {"status":"error","message":"User not found"}</div> </div> <div class="history-item p-4"> <div class="flex justify-between items-center mb-2"> <div class="flex items-center space-x-2"> <span class="px-2 py-1 rounded-full bg-green-100 text-green-800 text-xs">200</span> <span class="text-sm">GET /users/12345</span> </div> <span class="text-xs text-gray-400">Yesterday</span> </div> <div class="text-xs text-gray-400 truncate">Response: {"status":"success","data":{"user_id":12345,"name":"John Doe"}}</div> </div> </div> </div> </div> <div class="tab-content" data-tab-content="settings"> <div class="mb-6"> <h3 class="text-lg font-semibold mb-3">Endpoint Settings</h3> <div class="space-y-4"> <div> <label class="form-label">Endpoint Name</label> <input type="text" value="Get User Profile" class="w-full"> </div> <div> <label class="form-label">Endpoint URL</label> <input type="text" value="https://api.example.com/users/{user_id}" class="w-full"> </div> <div> <label class="form-label">HTTP Method</label> <select class="w-full"> <option>GET</option> <option>POST</option> <option>PUT</option> <option>DELETE</option> <option>PATCH</option> </select> </div> <div class="flex items-center justify-between"> <label class="form-label">Cache Responses</label> <label class="switch"> <input type="checkbox" checked> <span class="slider"></span> </label> </div> </div> </div> <div class="mb-6"> <h3 class="text-lg font-semibold mb-3">Button Appearance</h3> <div class="grid grid-cols-2 md:grid-cols-4 gap-4 mb-4"> <div> <label class="form-label">Icon</label> <select class="w-full"> <option value="fa-user">User</option> <option value="fa-database">Database</option> <option value="fa-plug">API</option> <option value="fa-bell">Notification</option> </select> </div> <div> <label class="form-label">Color</label> <div class="flex space-x-2"> <div class="color-option bg-blue-500 active" data-color="blue"></div> <div class="color-option bg-purple-500" data-color="purple"></div> <div class="color-option bg-green-500" data-color="green"></div> <div class="color-option bg-red-500" data-color="red"></div> </div> </div> </div> </div> <div class="flex space-x-3"> <button class="btn">Save Changes</button> <button class="btn btn-secondary">Reset</button> </div> </div> </div> </div> <!-- Help Modal --> <div class="dashboard-help" id="helpModal"> <div class="help-close" id="closeHelp"> <i class="fas fa-times"></i> </div> <div class="help-section"> <h2 class="help-title">Quantum API Dashboard Guide</h2> <div class="help-content"> <p>Welcome to the Quantum API Dashboard, a revolutionary way to visualize, connect and manage your API ecosystem in 3D space.</p> </div> </div> <div class="help-section"> <h3 class="help-title">Getting Started</h3> <div class="help-content"> <p>To begin, add your first API endpoint by clicking the <strong>+</strong> button in the control panel. Configure the endpoint details and click "Add".</p> <p>You can then click on any API button to view and test the endpoint, or drag buttons to arrange them in your preferred layout.</p> </div> </div> <div class="help-section"> <h3 class="help-title">Key Features</h3> <div class="help-content"> <p><strong>3D Visualization:</strong> APIs are represented as interactive 3D buttons that respond to your mouse movements.</p> <p><strong>Connection Mode:</strong> Create visual connections between APIs to represent workflows and dependencies.</p> <p><strong>Grouping:</strong> Organize related APIs into logical groups for better organization.</p> <p><strong>Mini Map:</strong> Navigate large dashboards easily with the overview mini map.</p> <p><strong>Visualizer:</strong> See your API ecosystem in a dynamic 3D graph visualization.</p> </div> </div> <div class="help-section"> <h3 class="help-title">Keyboard Shortcuts</h3> <div class="help-content"> <div class="help-shortcut"> <div>Add New API</div> <div class="shortcut-keys"> <span class="shortcut-key">Ctrl</span> <span class="shortcut-key">N</span> </div> </div> <div class="help-shortcut"> <div>Toggle Group Mode</div> <div class="shortcut-keys"> <span class="shortcut-key">G</span> </div> </div> <div class="help-shortcut"> <div>Toggle Connection Mode</div> <div class="shortcut-keys"> <span class="shortcut-key">C</span> </div> </div> <div class="help-shortcut"> <div>Open Settings</div> <div class="shortcut-keys"> <span class="shortcut-key">Ctrl</span> <span class="shortcut-key">,</span> </div> </div> <div class="help-shortcut"> <div>Search APIs</div> <div class="shortcut-keys"> <span class="shortcut-key">Ctrl</span> <span class="shortcut-key">K</span> </div> </div> <!-- final shortcut row --> <div class="help-shortcut"> <div>Toggle Mini Map</div> <div class="shortcut-keys"> <span class="shortcut-key">M</span> </div> </div> </div> <!-- .help-content --> </div> <!-- .help-section (Keyboard Shortcuts) --> </div> <!-- #helpModal.dashboard-help --> <!-- ────────────────────── EXTRA STYLES & CORE SCRIPT ────────────────────── --> <style> /* Toggle-switch styling (used in Settings modal) */ .switch{position:relative;display:inline-block;width:42px;height:24px} .switch input{display:none} .slider{position:absolute;cursor:pointer;top:0;left:0;right:0;bottom:0;border-radius:24px; background:rgba(255,255,255,.2);transition:.4s} .slider:before{content:"";position:absolute;height:18px;width:18px;left:3px;bottom:3px; background:#fff;border-radius:50%;transition:.4s} .switch input:checked + .slider{ background:hsl(var(--primary-hue),var(--primary-saturation),var(--primary-lightness)) } .switch input:checked + .slider:before{transform:translateX(18px)} </style> <script> document.addEventListener('DOMContentLoaded', () => { /* ─── Loading screen ─────────────────────────────────────────────── */ const loader = document.querySelector('.dashboard-loading'); const bar = document.getElementById('loadingBar'); let pct = 0, id = setInterval(() => { pct += 4; bar.style.width = pct + '%'; if (pct >= 100) { clearInterval(id); loader.style.opacity = 0; setTimeout(() => loader.remove(), 400); } }, 30); /* ─── Modal toggles ──────────────────────────────────────────────── */ const toggleModal = (btnId, modalId, closeBtnId) => { const btn = document.getElementById(btnId); const modal = document.getElementById(modalId); const close = document.getElementById(closeBtnId); if (!btn || !modal) return; btn.addEventListener('click', () => modal.classList.add('active')); (close || modal).addEventListener('click', e => { if (e.target === modal || e.target === close) modal.classList.remove('active'); }); }; toggleModal('helpBtn', 'helpModal', 'closeHelp'); toggleModal('settingsButton','settingsModal', 'closeSettings'); /* ─── Tab & subtabs (Settings / API modal) ──────────────────────── */ document.querySelectorAll('.response-tab').forEach(tab => { tab.addEventListener('click', () => { const siblings = tab.parentElement.querySelectorAll('.response-tab'); siblings.forEach(t => t.classList.remove('active')); tab.classList.add('active'); const wrapper = tab.closest('.modal-content') || document; wrapper.querySelectorAll('.tab-content, .response-content') .forEach(c => c.classList.remove('active')); const attr = tab.dataset.tabContent ? 'tabContent' : tab.dataset.tab ? 'tab' : 'responseTab'; const selector = `[data-${attr.toLowerCase()}="${tab.dataset[attr]}"]`; const dest = wrapper.querySelector(selector); dest && dest.classList.add('active'); }); }); }); </script> </body> </html>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Quantum API Dashboard</title>
<script src="https://cdn.tailwindcss.com"></script>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
<style>
@import url('https://fonts.googleapis.com/css2?family=Orbitron:wght@400;500;600;700&family=Major+Mono+Display&display=swap');
:root {
--primary-hue: 210;
--primary-saturation: 80%;
--primary-lightness: 50%;
}
body {
font-family: 'Orbitron', sans-serif;
background: radial-gradient(circle at center, #0f172a 0%, #020617 100%);
overflow: hidden;
color: #e2e8f0;
touch-action: none;
}
.dashboard {
position: relative;
width: 100vw;
height: 100vh;
perspective: 2000px;
transform-style: preserve-3d;
}
.button-3d {
position: absolute;
width: 140px;
height: 140px;
border-radius: 20px;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
cursor: move;
user-select: none;
transform-style: preserve-3d;
transition: all 0.3s cubic-bezier(0.25, 0.8, 0.25, 1);
box-shadow: 0 10px 25px -5px rgba(0, 0, 0, 0.5);
z-index: 10;
border: 1px solid rgba(255, 255, 255, 0.1);
backdrop-filter: blur(5px);
background: linear-gradient(145deg, rgba(255,255,255,0.1), rgba(255,255,255,0.05));
}
.button-3d:hover {
transform: translateY(-8px) rotateX(15deg) scale(1.05);
box-shadow: 0 20px 40px -10px rgba(0, 0, 0, 0.6);
z-index: 20;
}
.button-3d:active {
transform: translateY(0) rotateX(0) scale(0.98);
}
.button-3d::before {
content: '';
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
border-radius: inherit;
background: linear-gradient(135deg, rgba(255,255,255,0.2) 0%, transparent 50%);
opacity: 0;
transition: opacity 0.3s;
}
.button-3d:hover::before {
opacity: 1;
}
.button-icon {
font-size: 3rem;
margin-bottom: 12px;
text-shadow: 0 4px 8px rgba(0,0,0,0.4);
transition: all 0.3s;
}
.button-label {
font-size: 1rem;
font-weight: 600;
text-shadow: 0 2px 4px rgba(0,0,0,0.4);
text-align: center;
max-width: 90%;
word-break: break-word;
}
.button-status {
position: absolute;
bottom: 10px;
right: 10px;