| |
| :root { |
| --primary: #667eea; |
| --primary-dark: #5568d3; |
| --secondary: #764ba2; |
| --accent: #f093fb; |
| --success: #10b981; |
| --warning: #f59e0b; |
| --error: #ef4444; |
| --info: #3b82f6; |
|
|
| --bg-gradient: linear-gradient(135deg, #667eea 0%, #764ba2 100%); |
| --bg-light: #f8f9fa; |
| --bg-white: #ffffff; |
| --bg-dark: #1a1a2e; |
|
|
| --text-primary: #1f2937; |
| --text-secondary: #6b7280; |
| --text-light: #9ca3af; |
|
|
| --border-color: #e5e7eb; |
| --border-radius: 12px; |
| --border-radius-lg: 16px; |
| --border-radius-sm: 8px; |
|
|
| --shadow-sm: 0 1px 3px rgba(0, 0, 0, 0.1); |
| --shadow-md: 0 4px 6px rgba(0, 0, 0, 0.1); |
| --shadow-lg: 0 10px 15px rgba(0, 0, 0, 0.1); |
| --shadow-xl: 0 20px 25px rgba(0, 0, 0, 0.15); |
|
|
| --transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1); |
| } |
|
|
| |
| * { |
| margin: 0; |
| padding: 0; |
| box-sizing: border-box; |
| } |
|
|
| body { |
| font-family: 'Inter', 'Segoe UI', -apple-system, BlinkMacSystemFont, sans-serif; |
| background: var(--bg-gradient); |
| color: var(--text-primary); |
| min-height: 100vh; |
| padding: 20px; |
| line-height: 1.6; |
| -webkit-font-smoothing: antialiased; |
| -moz-osx-font-smoothing: grayscale; |
| } |
|
|
| |
| .container { |
| max-width: 1600px; |
| margin: 0 auto; |
| background: var(--bg-white); |
| border-radius: var(--border-radius-lg); |
| box-shadow: var(--shadow-xl); |
| overflow: hidden; |
| animation: fadeIn 0.5s ease-out; |
| } |
|
|
| @keyframes fadeIn { |
| from { |
| opacity: 0; |
| transform: translateY(20px); |
| } |
| to { |
| opacity: 1; |
| transform: translateY(0); |
| } |
| } |
|
|
| |
| header { |
| background: var(--bg-gradient); |
| color: white; |
| padding: 40px 30px; |
| text-align: center; |
| position: relative; |
| overflow: hidden; |
| } |
|
|
| header::before { |
| content: ''; |
| position: absolute; |
| top: 0; |
| left: 0; |
| right: 0; |
| bottom: 0; |
| background: url('data:image/svg+xml,<svg width="100" height="100" xmlns="http://www.w3.org/2000/svg"><defs><pattern id="grid" width="20" height="20" patternUnits="userSpaceOnUse"><circle cx="10" cy="10" r="1" fill="white" opacity="0.1"/></pattern></defs><rect width="100" height="100" fill="url(%23grid)"/></svg>'); |
| opacity: 0.3; |
| } |
|
|
| header h1 { |
| font-size: 3em; |
| margin-bottom: 10px; |
| font-weight: 800; |
| position: relative; |
| letter-spacing: -1px; |
| text-shadow: 0 2px 10px rgba(0, 0, 0, 0.2); |
| } |
|
|
| .subtitle { |
| font-size: 1.2em; |
| opacity: 0.95; |
| position: relative; |
| font-weight: 400; |
| } |
|
|
| |
| .tabs { |
| display: flex; |
| background: var(--bg-light); |
| border-bottom: 2px solid var(--border-color); |
| position: sticky; |
| top: 0; |
| z-index: 100; |
| backdrop-filter: blur(10px); |
| } |
|
|
| .tab-button { |
| flex: 1; |
| padding: 18px 30px; |
| background: transparent; |
| border: none; |
| cursor: pointer; |
| font-size: 1.1em; |
| font-weight: 600; |
| color: var(--text-secondary); |
| transition: var(--transition); |
| position: relative; |
| border-bottom: 3px solid transparent; |
| } |
|
|
| .tab-button::before { |
| content: ''; |
| position: absolute; |
| bottom: 0; |
| left: 50%; |
| width: 0; |
| height: 3px; |
| background: var(--bg-gradient); |
| transition: var(--transition); |
| transform: translateX(-50%); |
| } |
|
|
| .tab-button:hover { |
| background: rgba(102, 126, 234, 0.05); |
| color: var(--primary); |
| } |
|
|
| .tab-button.active { |
| color: var(--primary); |
| background: var(--bg-white); |
| } |
|
|
| .tab-button.active::before { |
| width: 100%; |
| } |
|
|
| |
| .tab-content { |
| display: none; |
| padding: 30px; |
| animation: slideIn 0.3s ease-out; |
| } |
|
|
| .tab-content.active { |
| display: block; |
| } |
|
|
| @keyframes slideIn { |
| from { |
| opacity: 0; |
| transform: translateX(-10px); |
| } |
| to { |
| opacity: 1; |
| transform: translateX(0); |
| } |
| } |
|
|
| |
| .card { |
| background: var(--bg-white); |
| border-radius: var(--border-radius); |
| padding: 30px; |
| margin-bottom: 25px; |
| box-shadow: var(--shadow-md); |
| border: 1px solid var(--border-color); |
| transition: var(--transition); |
| } |
|
|
| .card:hover { |
| box-shadow: var(--shadow-lg); |
| transform: translateY(-2px); |
| } |
|
|
| .card h2 { |
| color: var(--primary); |
| margin-bottom: 20px; |
| font-size: 1.8em; |
| font-weight: 700; |
| display: flex; |
| align-items: center; |
| gap: 12px; |
| } |
|
|
| .card h2::before { |
| content: ''; |
| width: 4px; |
| height: 28px; |
| background: var(--bg-gradient); |
| border-radius: 2px; |
| } |
|
|
| .card h3 { |
| color: var(--text-primary); |
| margin-bottom: 15px; |
| font-size: 1.3em; |
| font-weight: 600; |
| } |
|
|
| |
| .info { |
| background: linear-gradient(135deg, #e0f2fe 0%, #dbeafe 100%); |
| border-left: 4px solid var(--info); |
| padding: 15px 20px; |
| margin: 15px 0; |
| border-radius: var(--border-radius-sm); |
| font-size: 0.95em; |
| color: #0c4a6e; |
| display: flex; |
| align-items: center; |
| gap: 12px; |
| } |
|
|
| .info::before { |
| content: 'ℹ️'; |
| font-size: 1.2em; |
| } |
|
|
| |
| .controls { |
| display: grid; |
| grid-template-columns: repeat(auto-fit, minmax(220px, 1fr)); |
| gap: 20px; |
| margin-bottom: 25px; |
| } |
|
|
| .control-group { |
| display: flex; |
| flex-direction: column; |
| gap: 8px; |
| } |
|
|
| .control-group label { |
| font-weight: 600; |
| color: var(--text-primary); |
| font-size: 0.95em; |
| display: flex; |
| align-items: center; |
| gap: 6px; |
| } |
|
|
| .control-group input, |
| .control-group select { |
| padding: 12px 16px; |
| border: 2px solid var(--border-color); |
| border-radius: var(--border-radius-sm); |
| font-size: 1em; |
| font-family: inherit; |
| transition: var(--transition); |
| background: var(--bg-white); |
| color: var(--text-primary); |
| } |
|
|
| .control-group input:focus, |
| .control-group select:focus { |
| outline: none; |
| border-color: var(--primary); |
| box-shadow: 0 0 0 3px rgba(102, 126, 234, 0.1); |
| } |
|
|
| .control-group input:hover, |
| .control-group select:hover { |
| border-color: var(--primary-dark); |
| } |
|
|
| |
| .btn { |
| padding: 14px 28px; |
| border: none; |
| border-radius: var(--border-radius-sm); |
| font-size: 1em; |
| font-weight: 600; |
| cursor: pointer; |
| transition: var(--transition); |
| font-family: inherit; |
| display: inline-flex; |
| align-items: center; |
| justify-content: center; |
| gap: 8px; |
| position: relative; |
| overflow: hidden; |
| } |
|
|
| .btn::before { |
| content: ''; |
| position: absolute; |
| top: 50%; |
| left: 50%; |
| width: 0; |
| height: 0; |
| border-radius: 50%; |
| background: rgba(255, 255, 255, 0.3); |
| transform: translate(-50%, -50%); |
| transition: width 0.6s, height 0.6s; |
| } |
|
|
| .btn:active::before { |
| width: 300px; |
| height: 300px; |
| } |
|
|
| .btn-primary { |
| background: var(--bg-gradient); |
| color: white; |
| box-shadow: var(--shadow-sm); |
| } |
|
|
| .btn-primary:hover { |
| transform: translateY(-2px); |
| box-shadow: var(--shadow-md); |
| } |
|
|
| .btn-primary:active { |
| transform: translateY(0); |
| } |
|
|
| .btn-secondary { |
| background: var(--text-secondary); |
| color: white; |
| } |
|
|
| .btn-secondary:hover { |
| background: var(--text-primary); |
| transform: translateY(-2px); |
| box-shadow: var(--shadow-sm); |
| } |
|
|
| .btn-danger { |
| background: var(--error); |
| color: white; |
| } |
|
|
| .btn-danger:hover { |
| background: #dc2626; |
| transform: translateY(-2px); |
| box-shadow: var(--shadow-sm); |
| } |
|
|
| .button-group { |
| display: flex; |
| flex-wrap: wrap; |
| gap: 12px; |
| margin-top: 20px; |
| } |
|
|
| |
| .progress-container { |
| margin: 25px 0; |
| background: var(--bg-light); |
| border-radius: var(--border-radius); |
| overflow: hidden; |
| padding: 20px; |
| box-shadow: var(--shadow-sm); |
| border: 1px solid var(--border-color); |
| } |
|
|
| .progress-bar { |
| height: 32px; |
| background: var(--bg-gradient); |
| border-radius: var(--border-radius-sm); |
| transition: width 0.3s ease; |
| width: 0%; |
| box-shadow: 0 2px 8px rgba(102, 126, 234, 0.3); |
| position: relative; |
| overflow: hidden; |
| } |
|
|
| .progress-bar::after { |
| content: ''; |
| position: absolute; |
| top: 0; |
| left: 0; |
| right: 0; |
| bottom: 0; |
| background: linear-gradient(90deg, transparent, rgba(255, 255, 255, 0.3), transparent); |
| animation: shimmer 2s infinite; |
| } |
|
|
| @keyframes shimmer { |
| 0% { |
| transform: translateX(-100%); |
| } |
| 100% { |
| transform: translateX(100%); |
| } |
| } |
|
|
| #progress-text { |
| text-align: center; |
| margin-top: 12px; |
| font-weight: 600; |
| color: var(--text-primary); |
| font-size: 0.95em; |
| } |
|
|
| |
| .output { |
| background: var(--bg-light); |
| border-radius: var(--border-radius); |
| padding: 25px; |
| margin-top: 20px; |
| border: 1px solid var(--border-color); |
| } |
|
|
| .output.hidden { |
| display: none; |
| } |
|
|
| |
| .stats-grid { |
| display: grid; |
| grid-template-columns: repeat(auto-fit, minmax(150px, 1fr)); |
| gap: 15px; |
| margin-bottom: 20px; |
| } |
|
|
| .stat-card { |
| background: var(--bg-white); |
| padding: 20px; |
| border-radius: var(--border-radius-sm); |
| text-align: center; |
| box-shadow: var(--shadow-sm); |
| border: 1px solid var(--border-color); |
| transition: var(--transition); |
| } |
|
|
| .stat-card:hover { |
| transform: translateY(-3px); |
| box-shadow: var(--shadow-md); |
| } |
|
|
| .stat-label { |
| font-size: 0.85em; |
| color: var(--text-secondary); |
| text-transform: uppercase; |
| letter-spacing: 0.5px; |
| font-weight: 600; |
| margin-bottom: 8px; |
| } |
|
|
| .stat-value { |
| font-size: 2em; |
| font-weight: 700; |
| background: var(--bg-gradient); |
| -webkit-background-clip: text; |
| -webkit-text-fill-color: transparent; |
| background-clip: text; |
| } |
|
|
| .distance-info { |
| background: var(--bg-white); |
| padding: 15px; |
| border-radius: var(--border-radius-sm); |
| border: 1px solid var(--border-color); |
| font-size: 0.95em; |
| } |
|
|
| |
| .table-wrapper { |
| overflow-x: auto; |
| border-radius: var(--border-radius-sm); |
| box-shadow: var(--shadow-sm); |
| margin-top: 15px; |
| } |
|
|
| .data-table { |
| width: 100%; |
| border-collapse: separate; |
| border-spacing: 0; |
| background: var(--bg-white); |
| border: 1px solid var(--border-color); |
| border-radius: var(--border-radius-sm); |
| overflow: hidden; |
| } |
|
|
| .data-table thead { |
| background: var(--bg-gradient); |
| color: white; |
| } |
|
|
| .data-table th, |
| .data-table td { |
| padding: 14px 18px; |
| text-align: left; |
| } |
|
|
| .data-table th { |
| font-weight: 600; |
| text-transform: uppercase; |
| font-size: 0.85em; |
| letter-spacing: 0.5px; |
| } |
|
|
| .data-table tbody tr { |
| transition: var(--transition); |
| border-bottom: 1px solid var(--border-color); |
| } |
|
|
| .data-table tbody tr:hover { |
| background: var(--bg-light); |
| } |
|
|
| .data-table tbody tr:last-child { |
| border-bottom: none; |
| } |
|
|
| .more-rows { |
| text-align: center; |
| font-style: italic; |
| color: var(--text-secondary); |
| background: var(--bg-light); |
| } |
|
|
| |
| .distance-matrix { |
| font-size: 0.85em; |
| } |
|
|
| .distance-matrix td { |
| text-align: center; |
| padding: 8px 12px; |
| } |
|
|
| .distance-matrix th { |
| text-align: center; |
| padding: 8px 12px; |
| } |
|
|
| .distance-matrix .diagonal { |
| background: var(--bg-light); |
| font-weight: 600; |
| color: var(--text-secondary); |
| } |
|
|
| |
| .matrix-grid { |
| font-size: 0.75em; |
| } |
|
|
| .matrix-grid td { |
| text-align: center; |
| padding: 6px 10px; |
| font-family: 'Courier New', monospace; |
| } |
|
|
| .matrix-grid th { |
| text-align: center; |
| padding: 6px 10px; |
| font-size: 0.9em; |
| } |
|
|
| .matrix-grid .diagonal { |
| background: #fff3cd; |
| font-weight: 600; |
| } |
|
|
| |
| .results-grid { |
| display: grid; |
| grid-template-columns: repeat(auto-fit, minmax(500px, 1fr)); |
| gap: 25px; |
| margin-top: 25px; |
| } |
|
|
| .result-box { |
| background: var(--bg-light); |
| border-radius: var(--border-radius); |
| padding: 25px; |
| box-shadow: var(--shadow-md); |
| border: 1px solid var(--border-color); |
| transition: var(--transition); |
| } |
|
|
| .result-box:hover { |
| box-shadow: var(--shadow-lg); |
| } |
|
|
| .result-box h3 { |
| color: var(--primary); |
| margin-bottom: 15px; |
| font-size: 1.2em; |
| font-weight: 600; |
| } |
|
|
| #tsne-plot, |
| #cost-plot, |
| #p-matrix-plot, |
| #q-matrix-plot { |
| min-height: 400px; |
| background: var(--bg-white); |
| border-radius: var(--border-radius-sm); |
| margin-top: 10px; |
| } |
|
|
| #coords-table { |
| max-height: 400px; |
| overflow-y: auto; |
| } |
|
|
| |
| .upload-section { |
| background: var(--bg-light); |
| border-radius: var(--border-radius); |
| padding: 25px; |
| margin-bottom: 25px; |
| border: 2px dashed var(--border-color); |
| transition: var(--transition); |
| } |
|
|
| .upload-section:hover { |
| border-color: var(--primary); |
| background: rgba(102, 126, 234, 0.02); |
| } |
|
|
| .upload-section h3 { |
| color: var(--primary); |
| margin-bottom: 15px; |
| font-size: 1.3em; |
| } |
|
|
| input[type="file"] { |
| display: none; |
| } |
|
|
| |
| .upload-list { |
| background: var(--bg-light); |
| border-radius: var(--border-radius); |
| padding: 25px; |
| border: 1px solid var(--border-color); |
| } |
|
|
| .dataset-list { |
| list-style: none; |
| } |
|
|
| .dataset-item { |
| padding: 15px 20px; |
| margin-bottom: 12px; |
| background: var(--bg-white); |
| border-radius: var(--border-radius-sm); |
| border-left: 4px solid var(--primary); |
| font-size: 1em; |
| box-shadow: var(--shadow-sm); |
| display: flex; |
| align-items: center; |
| gap: 12px; |
| transition: var(--transition); |
| } |
|
|
| .dataset-item:hover { |
| transform: translateX(5px); |
| box-shadow: var(--shadow-md); |
| } |
|
|
| .dataset-icon { |
| font-size: 1.5em; |
| } |
|
|
| .dataset-label { |
| flex: 1; |
| font-weight: 500; |
| } |
|
|
| .empty-state { |
| text-align: center; |
| padding: 40px; |
| color: var(--text-secondary); |
| font-size: 1.1em; |
| } |
|
|
| |
| #csv-columns-list { |
| background: var(--bg-white); |
| padding: 20px; |
| border-radius: var(--border-radius-sm); |
| max-height: 250px; |
| overflow-y: auto; |
| border: 2px solid var(--border-color); |
| margin-top: 10px; |
| } |
|
|
| .checkbox-label { |
| display: block; |
| padding: 12px; |
| cursor: pointer; |
| transition: var(--transition); |
| border-radius: var(--border-radius-sm); |
| font-weight: 500; |
| } |
|
|
| .checkbox-label:hover { |
| background: var(--bg-light); |
| } |
|
|
| .checkbox-label input[type="checkbox"] { |
| margin-right: 10px; |
| cursor: pointer; |
| width: 18px; |
| height: 18px; |
| } |
|
|
| |
| .embed-status { |
| margin-top: 15px; |
| padding: 12px 20px; |
| border-radius: var(--border-radius-sm); |
| font-weight: 500; |
| display: inline-block; |
| } |
|
|
| .embed-status.computing { |
| background: #fef3c7; |
| color: #92400e; |
| } |
|
|
| .embed-status.success { |
| background: #d1fae5; |
| color: #065f46; |
| } |
|
|
| .embed-status.error { |
| background: #fee2e2; |
| color: #991b1b; |
| } |
|
|
| |
| .modal { |
| position: fixed; |
| top: 0; |
| left: 0; |
| width: 100%; |
| height: 100%; |
| background: rgba(0, 0, 0, 0.8); |
| backdrop-filter: blur(5px); |
| display: flex; |
| justify-content: center; |
| align-items: center; |
| z-index: 1000; |
| animation: fadeIn 0.3s ease-out; |
| } |
|
|
| .modal.hidden { |
| display: none; |
| } |
|
|
| .modal-content { |
| background: var(--bg-white); |
| border-radius: var(--border-radius-lg); |
| padding: 40px; |
| max-width: 900px; |
| max-height: 90vh; |
| overflow: auto; |
| position: relative; |
| box-shadow: var(--shadow-xl); |
| animation: scaleIn 0.3s ease-out; |
| } |
|
|
| @keyframes scaleIn { |
| from { |
| opacity: 0; |
| transform: scale(0.9); |
| } |
| to { |
| opacity: 1; |
| transform: scale(1); |
| } |
| } |
|
|
| .modal-close { |
| position: absolute; |
| top: 20px; |
| right: 25px; |
| font-size: 2.5em; |
| cursor: pointer; |
| color: var(--text-secondary); |
| transition: var(--transition); |
| line-height: 1; |
| } |
|
|
| .modal-close:hover { |
| color: var(--error); |
| transform: rotate(90deg); |
| } |
|
|
| #modal-image { |
| max-width: 100%; |
| border-radius: var(--border-radius); |
| margin-top: 20px; |
| box-shadow: var(--shadow-lg); |
| } |
|
|
| |
| #notification { |
| position: fixed; |
| top: -100px; |
| left: 50%; |
| transform: translateX(-50%); |
| background: var(--bg-white); |
| color: var(--text-primary); |
| padding: 16px 32px; |
| border-radius: var(--border-radius); |
| box-shadow: var(--shadow-xl); |
| z-index: 2000; |
| font-weight: 600; |
| min-width: 300px; |
| text-align: center; |
| transition: top 0.3s cubic-bezier(0.4, 0, 0.2, 1); |
| border-left: 4px solid var(--primary); |
| } |
|
|
| #notification.show { |
| top: 30px; |
| } |
|
|
| #notification.success { |
| border-left-color: var(--success); |
| background: #ecfdf5; |
| color: #065f46; |
| } |
|
|
| #notification.error { |
| border-left-color: var(--error); |
| background: #fef2f2; |
| color: #991b1b; |
| } |
|
|
| #notification.warning { |
| border-left-color: var(--warning); |
| background: #fffbeb; |
| color: #92400e; |
| } |
|
|
| #notification.info { |
| border-left-color: var(--info); |
| background: #eff6ff; |
| color: #1e40af; |
| } |
|
|
| |
| .hidden { |
| display: none !important; |
| } |
|
|
| .text-center { |
| text-align: center; |
| } |
|
|
| |
| ::-webkit-scrollbar { |
| width: 12px; |
| height: 12px; |
| } |
|
|
| ::-webkit-scrollbar-track { |
| background: var(--bg-light); |
| border-radius: 10px; |
| } |
|
|
| ::-webkit-scrollbar-thumb { |
| background: var(--bg-gradient); |
| border-radius: 10px; |
| border: 2px solid var(--bg-light); |
| } |
|
|
| ::-webkit-scrollbar-thumb:hover { |
| background: linear-gradient(135deg, #5568d3 0%, #653a8a 100%); |
| } |
|
|
| |
| @media (max-width: 1200px) { |
| .results-grid { |
| grid-template-columns: 1fr; |
| } |
| } |
|
|
| @media (max-width: 768px) { |
| body { |
| padding: 10px; |
| } |
|
|
| .container { |
| border-radius: var(--border-radius); |
| } |
|
|
| header { |
| padding: 25px 20px; |
| } |
|
|
| header h1 { |
| font-size: 2em; |
| } |
|
|
| .subtitle { |
| font-size: 1em; |
| } |
|
|
| .tabs { |
| flex-direction: column; |
| } |
|
|
| .tab-button { |
| padding: 15px 20px; |
| } |
|
|
| .tab-content { |
| padding: 20px; |
| } |
|
|
| .card { |
| padding: 20px; |
| } |
|
|
| .controls { |
| grid-template-columns: 1fr; |
| } |
|
|
| .results-grid { |
| grid-template-columns: 1fr; |
| } |
|
|
| .stats-grid { |
| grid-template-columns: 1fr; |
| } |
|
|
| .modal-content { |
| margin: 20px; |
| padding: 25px; |
| max-width: 90%; |
| } |
| } |
|
|
| @media (max-width: 480px) { |
| header h1 { |
| font-size: 1.6em; |
| } |
|
|
| .btn { |
| padding: 12px 20px; |
| font-size: 0.9em; |
| } |
|
|
| .button-group { |
| flex-direction: column; |
| } |
|
|
| .button-group .btn { |
| width: 100%; |
| } |
| } |
|
|
| |
| @keyframes pulse { |
| 0%, 100% { |
| opacity: 1; |
| } |
| 50% { |
| opacity: 0.5; |
| } |
| } |
|
|
| .loading { |
| animation: pulse 2s ease-in-out infinite; |
| } |
|
|
| |
| @media print { |
| body { |
| background: white; |
| padding: 0; |
| } |
|
|
| .container { |
| box-shadow: none; |
| } |
|
|
| header { |
| background: none; |
| color: black; |
| } |
|
|
| .tabs, |
| .btn, |
| .upload-section { |
| display: none; |
| } |
| } |
|
|