| <!DOCTYPE html> |
| <html lang="en"> |
| <head> |
| <meta charset="UTF-8"> |
| <meta name="viewport" content="width=device-width, initial-scale=1.0"> |
| <title>ARIA — Biomedical Research Intelligence</title> |
| <link href="https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700;900&family=JetBrains+Mono:wght@300;400;600&display=swap" rel="stylesheet"> |
| <style> |
| :root { |
| --bg: #050505; |
| --surface: #0f172a; |
| --border: #1e293b; |
| --accent: #00f5ff; |
| --accent-dim: #00f5ff15; |
| --text: #c9d1d9; |
| --text-dim: #5a6a7a; |
| --text-bright: #e6edf3; |
| --red: #ff4d6d; |
| --yellow: #f0b429; |
| --mono: 'JetBrains Mono', monospace; |
| --sans: 'Inter', sans-serif; |
| } |
| |
| * { margin: 0; padding: 0; box-sizing: border-box; } |
| |
| body { |
| background: var(--bg); |
| color: var(--text); |
| font-family: var(--sans); |
| font-size: 15px; |
| line-height: 1.6; |
| min-height: 100vh; |
| } |
| |
| body::before { |
| content: ''; |
| position: fixed; |
| inset: 0; |
| background-image: |
| linear-gradient(var(--border) 1px, transparent 1px), |
| linear-gradient(90deg, var(--border) 1px, transparent 1px); |
| background-size: 40px 40px; |
| opacity: 0.3; |
| pointer-events: none; |
| z-index: 0; |
| } |
| |
| .container { |
| max-width: 900px; |
| margin: 0 auto; |
| padding: 40px 24px 80px; |
| position: relative; |
| z-index: 1; |
| } |
| |
| header { |
| margin-bottom: 48px; |
| border-bottom: 1px solid var(--border); |
| padding-bottom: 24px; |
| } |
| |
| .logo { |
| font-family: var(--mono); |
| font-size: 11px; |
| letter-spacing: 0.2em; |
| color: var(--accent); |
| text-transform: uppercase; |
| margin-bottom: 8px; |
| } |
| |
| h1 { |
| font-family: var(--mono); |
| font-size: 32px; |
| font-weight: 600; |
| color: var(--text-bright); |
| letter-spacing: -0.02em; |
| } |
| |
| h1 span { color: var(--accent); } |
| |
| .subtitle { |
| font-size: 13px; |
| color: var(--text-dim); |
| font-family: var(--mono); |
| margin-top: 6px; |
| } |
| |
| .search-section { margin-bottom: 32px; } |
| |
| .input-wrapper { |
| position: relative; |
| border: 1px solid var(--border); |
| border-radius: 4px; |
| background: var(--surface); |
| transition: border-color 0.2s; |
| } |
| |
| .input-wrapper:focus-within { |
| border-color: var(--accent); |
| box-shadow: 0 0 0 1px var(--accent)40; |
| } |
| |
| .input-label { |
| font-family: var(--mono); |
| font-size: 10px; |
| letter-spacing: 0.15em; |
| color: var(--accent); |
| padding: 12px 16px 0; |
| text-transform: uppercase; |
| } |
| |
| textarea { |
| width: 100%; |
| background: transparent; |
| border: none; |
| color: var(--text-bright); |
| font-family: var(--sans); |
| font-size: 15px; |
| line-height: 1.6; |
| padding: 8px 16px 12px; |
| resize: none; |
| outline: none; |
| } |
| |
| textarea::placeholder { color: var(--text-dim); } |
| |
| .search-actions { |
| display: flex; |
| align-items: center; |
| justify-content: space-between; |
| padding: 10px 12px; |
| border-top: 1px solid var(--border); |
| } |
| |
| .hint { |
| font-family: var(--mono); |
| font-size: 11px; |
| color: var(--text-dim); |
| } |
| |
| button#submitBtn { |
| background: var(--accent); |
| color: #000; |
| border: none; |
| border-radius: 3px; |
| padding: 8px 20px; |
| font-family: var(--mono); |
| font-size: 12px; |
| font-weight: 600; |
| letter-spacing: 0.08em; |
| cursor: pointer; |
| transition: opacity 0.2s; |
| } |
| |
| button#submitBtn:hover { opacity: 0.85; } |
| button#submitBtn:disabled { opacity: 0.4; cursor: not-allowed; } |
| |
| .pipeline { |
| display: none; |
| margin-bottom: 32px; |
| border: 1px solid var(--border); |
| border-radius: 4px; |
| overflow: hidden; |
| } |
| |
| .pipeline.visible { display: block; } |
| |
| .pipeline-header { |
| background: var(--surface); |
| padding: 10px 16px; |
| font-family: var(--mono); |
| font-size: 11px; |
| color: var(--text-dim); |
| letter-spacing: 0.1em; |
| text-transform: uppercase; |
| border-bottom: 1px solid var(--border); |
| display: flex; |
| align-items: center; |
| justify-content: space-between; |
| } |
| |
| .progress-wrap { |
| width: 160px; |
| height: 4px; |
| background: var(--border); |
| border-radius: 2px; |
| overflow: hidden; |
| } |
| |
| .progress-fill { |
| height: 100%; |
| width: 0%; |
| background: var(--accent); |
| border-radius: 2px; |
| transition: width 0.6s ease; |
| box-shadow: 0 0 8px var(--accent); |
| } |
| |
| .progress-pct { |
| font-family: var(--mono); |
| font-size: 11px; |
| color: var(--accent); |
| min-width: 36px; |
| text-align: right; |
| } |
| |
| .stage { |
| display: flex; |
| align-items: center; |
| gap: 12px; |
| padding: 10px 16px; |
| border-bottom: 1px solid var(--border); |
| font-family: var(--mono); |
| font-size: 12px; |
| color: var(--text-dim); |
| transition: color 0.3s; |
| } |
| |
| .stage:last-child { border-bottom: none; } |
| .stage.active { color: var(--accent); } |
| .stage.done { color: var(--text); } |
| |
| .stage-dot { |
| width: 6px; |
| height: 6px; |
| border-radius: 50%; |
| background: var(--border); |
| flex-shrink: 0; |
| transition: background 0.3s; |
| } |
| |
| .stage.active .stage-dot { |
| background: var(--accent); |
| box-shadow: 0 0 8px var(--accent); |
| animation: pulse 1s infinite; |
| } |
| |
| .stage.done .stage-dot { background: var(--text-dim); } |
| |
| @keyframes pulse { |
| 0%, 100% { opacity: 1; } |
| 50% { opacity: 0.3; } |
| } |
| |
| .results { display: none; } |
| .results.visible { display: block; } |
| |
| .meta-bar { |
| display: flex; |
| gap: 16px; |
| margin-bottom: 24px; |
| flex-wrap: wrap; |
| } |
| |
| .badge { |
| font-family: var(--mono); |
| font-size: 11px; |
| padding: 4px 10px; |
| border-radius: 2px; |
| border: 1px solid var(--border); |
| color: var(--text-dim); |
| } |
| |
| .badge.green { |
| border-color: var(--accent); |
| color: var(--accent); |
| background: var(--accent-dim); |
| } |
| |
| .synthesis { margin-bottom: 32px; } |
| |
| .section { |
| margin-bottom: 2px; |
| border: 1px solid var(--border); |
| border-radius: 4px; |
| overflow: hidden; |
| } |
| |
| .section-header { |
| display: flex; |
| align-items: center; |
| gap: 10px; |
| padding: 12px 16px; |
| background: var(--surface); |
| cursor: pointer; |
| user-select: none; |
| position: relative; |
| } |
| |
| .section-header:hover { background: #111820; } |
| |
| .section-tag { |
| font-family: var(--mono); |
| font-size: 10px; |
| letter-spacing: 0.15em; |
| text-transform: uppercase; |
| color: var(--accent); |
| min-width: 180px; |
| } |
| |
| .section-chevron { |
| margin-left: auto; |
| font-size: 10px; |
| color: var(--text-dim); |
| transition: transform 0.2s; |
| } |
| |
| .section.open .section-chevron { transform: rotate(180deg); } |
| |
| .section-body { |
| display: none; |
| padding: 16px; |
| border-top: 1px solid var(--border); |
| font-size: 14px; |
| line-height: 1.75; |
| color: var(--text); |
| } |
| |
| .section.open .section-body { display: block; } |
| |
| |
| |
| .citations-block { |
| border: 1px solid var(--border); |
| border-radius: 4px; |
| overflow: hidden; |
| } |
| |
| .citations-header { |
| padding: 12px 16px; |
| background: var(--surface); |
| font-family: var(--mono); |
| font-size: 11px; |
| letter-spacing: 0.15em; |
| text-transform: uppercase; |
| color: var(--text-dim); |
| border-bottom: 1px solid var(--border); |
| cursor: pointer; |
| display: flex; |
| align-items: center; |
| justify-content: space-between; |
| } |
| |
| .citations-list { |
| display: none; |
| padding: 16px; |
| } |
| |
| .citations-list.open { display: block; } |
| |
| .citation-item { |
| font-family: var(--mono); |
| font-size: 12px; |
| color: var(--text-dim); |
| padding: 8px 0; |
| border-bottom: 1px solid var(--border); |
| line-height: 1.6; |
| display: flex; |
| align-items: flex-start; |
| gap: 4px; |
| cursor: pointer; |
| } |
| |
| .citation-item:last-child { border-bottom: none; } |
| .citation-item:hover { color: var(--text); } |
| |
| .citation-pmid { color: var(--accent); font-weight: 600; } |
| |
| .error-box { |
| display: none; |
| padding: 16px; |
| border: 1px solid var(--red); |
| border-radius: 4px; |
| background: #ff4d6d10; |
| font-family: var(--mono); |
| font-size: 13px; |
| color: var(--red); |
| margin-bottom: 24px; |
| } |
| |
| .error-box.visible { display: block; } |
| |
| footer { |
| margin-top: 64px; |
| padding-top: 24px; |
| border-top: 1px solid var(--border); |
| font-family: var(--mono); |
| font-size: 11px; |
| color: var(--text-dim); |
| display: flex; |
| justify-content: space-between; |
| flex-wrap: wrap; |
| gap: 8px; |
| } |
| |
| .confidence-badge { |
| font-family: var(--mono); |
| font-size: 10px; |
| padding: 2px 8px; |
| border-radius: 2px; |
| margin-left: auto; |
| margin-right: 8px; |
| font-weight: 600; |
| letter-spacing: 0.05em; |
| } |
| |
| .conf-strong { background: #00e5a020; color: #00e5a0; border: 1px solid #00e5a0; } |
| .conf-moderate { background: #f0b42920; color: #f0b429; border: 1px solid #f0b429; } |
| .conf-weak { background: #ff4d6d20; color: #ff4d6d; border: 1px solid #ff4d6d; } |
| |
| .conf-tooltip { |
| display: none; |
| position: absolute; |
| background: #0d1117; |
| border: 1px solid var(--border); |
| padding: 6px 10px; |
| font-family: var(--mono); |
| font-size: 11px; |
| color: var(--text); |
| border-radius: 3px; |
| max-width: 280px; |
| z-index: 10; |
| top: 100%; |
| right: 0; |
| margin-top: 4px; |
| } |
| |
| .confidence-badge:hover .conf-tooltip { display: block; } |
| |
| .abstract-body { |
| display: none; |
| margin-top: 8px; |
| padding: 10px 12px; |
| background: #0d1117; |
| border-left: 2px solid var(--accent); |
| font-family: var(--sans); |
| font-size: 12px; |
| line-height: 1.7; |
| color: var(--text); |
| border-radius: 0 3px 3px 0; |
| } |
| |
| .citation-item.expanded .abstract-body { display: block; } |
| |
| .citation-toggle { color: var(--accent); font-size: 10px; margin-left: 6px; } |
| .citation-text { flex: 1; } |
| |
| .citation-checkbox { |
| width: 14px; |
| height: 14px; |
| margin-right: 8px; |
| accent-color: var(--accent); |
| cursor: pointer; |
| flex-shrink: 0; |
| } |
| |
| .review-bar { |
| display: none; |
| align-items: center; |
| justify-content: space-between; |
| padding: 10px 16px; |
| background: var(--accent-dim); |
| border: 1px solid var(--accent); |
| border-radius: 4px; |
| margin-top: 12px; |
| font-family: var(--mono); |
| font-size: 12px; |
| color: var(--accent); |
| } |
| |
| .review-bar.visible { display: flex; } |
| |
| .review-btn { |
| background: var(--accent); |
| color: #000; |
| border: none; |
| padding: 6px 16px; |
| border-radius: 3px; |
| font-family: var(--mono); |
| font-size: 11px; |
| font-weight: 600; |
| cursor: pointer; |
| } |
| |
| .review-btn:disabled { opacity: 0.5; cursor: not-allowed; } |
| |
| .review-output { |
| display: none; |
| margin-top: 16px; |
| border: 1px solid var(--accent); |
| border-radius: 4px; |
| overflow: hidden; |
| } |
| |
| .review-output.visible { display: block; } |
| |
| .review-output-header { |
| background: var(--accent-dim); |
| padding: 10px 16px; |
| font-family: var(--mono); |
| font-size: 11px; |
| color: var(--accent); |
| letter-spacing: 0.1em; |
| text-transform: uppercase; |
| } |
| |
| .review-output-body { |
| padding: 16px; |
| font-size: 14px; |
| line-height: 1.8; |
| color: var(--text); |
| } |
| |
| .prediction-block { |
| display: none; |
| margin-bottom: 32px; |
| border: 1px solid #f0b42940; |
| border-radius: 4px; |
| overflow: hidden; |
| } |
| |
| .prediction-block.visible { display: block; } |
| |
| .prediction-header { |
| background: #f0b42910; |
| padding: 12px 16px; |
| font-family: var(--mono); |
| font-size: 11px; |
| letter-spacing: 0.15em; |
| text-transform: uppercase; |
| color: #f0b429; |
| border-bottom: 1px solid #f0b42940; |
| display: flex; |
| align-items: center; |
| justify-content: space-between; |
| } |
| |
| .prediction-tag { |
| font-family: var(--mono); |
| font-size: 9px; |
| padding: 2px 8px; |
| border-radius: 2px; |
| border: 1px solid #f0b429; |
| color: #f0b429; |
| } |
| |
| .prediction-section { |
| padding: 16px; |
| border-bottom: 1px solid #f0b42920; |
| } |
| |
| .prediction-section:last-child { border-bottom: none; } |
| |
| .prediction-label { |
| font-family: var(--mono); |
| font-size: 10px; |
| letter-spacing: 0.1em; |
| text-transform: uppercase; |
| margin-bottom: 8px; |
| } |
| |
| .prediction-label.constructive { color: var(--accent); } |
| .prediction-label.destructive { color: var(--red); } |
| |
| .prediction-text { font-size: 14px; line-height: 1.75; color: var(--text); } |
| |
| .history-toggle { |
| position: fixed; |
| top: 20px; |
| right: 20px; |
| background: var(--surface); |
| border: 1px solid var(--border); |
| color: var(--text-dim); |
| font-family: var(--mono); |
| font-size: 11px; |
| padding: 6px 12px; |
| border-radius: 3px; |
| cursor: pointer; |
| z-index: 100; |
| letter-spacing: 0.08em; |
| } |
| |
| .history-toggle:hover { border-color: var(--accent); color: var(--accent); } |
| |
| .history-panel { |
| position: fixed; |
| top: 0; |
| right: -320px; |
| width: 300px; |
| height: 100vh; |
| background: var(--surface); |
| border-left: 1px solid var(--border); |
| z-index: 200; |
| transition: right 0.3s ease; |
| display: flex; |
| flex-direction: column; |
| overflow: hidden; |
| } |
| |
| .history-panel.open { right: 0; } |
| |
| .history-panel-header { |
| padding: 16px; |
| border-bottom: 1px solid var(--border); |
| font-family: var(--mono); |
| font-size: 11px; |
| letter-spacing: 0.15em; |
| text-transform: uppercase; |
| color: var(--accent); |
| display: flex; |
| justify-content: space-between; |
| align-items: center; |
| } |
| |
| .history-close { cursor: pointer; color: var(--text-dim); font-size: 14px; } |
| .history-close:hover { color: var(--text); } |
| |
| .history-list { overflow-y: auto; flex: 1; padding: 8px; } |
| |
| .history-item { |
| padding: 10px 12px; |
| border: 1px solid var(--border); |
| border-radius: 3px; |
| margin-bottom: 6px; |
| cursor: pointer; |
| transition: border-color 0.2s; |
| } |
| |
| .history-item:hover { border-color: var(--accent); } |
| |
| .history-item-query { |
| font-family: var(--sans); |
| font-size: 12px; |
| color: var(--text); |
| margin-bottom: 4px; |
| white-space: nowrap; |
| overflow: hidden; |
| text-overflow: ellipsis; |
| } |
| |
| .history-item-meta { font-family: var(--mono); font-size: 10px; color: var(--text-dim); } |
| |
| .history-empty { |
| padding: 24px 16px; |
| font-family: var(--mono); |
| font-size: 11px; |
| color: var(--text-dim); |
| text-align: center; |
| } |
| |
| .table-block { |
| display: none; |
| margin-bottom: 32px; |
| border: 1px solid var(--border); |
| border-radius: 4px; |
| overflow: hidden; |
| } |
| |
| .table-block.visible { display: block; } |
| |
| .table-block-header { |
| background: var(--surface); |
| padding: 12px 16px; |
| font-family: var(--mono); |
| font-size: 11px; |
| letter-spacing: 0.15em; |
| text-transform: uppercase; |
| color: var(--text-dim); |
| border-bottom: 1px solid var(--border); |
| } |
| |
| .table-wrap { overflow-x: auto; } |
| |
| table.data-table { width: 100%; border-collapse: collapse; font-size: 12px; } |
| |
| table.data-table th { |
| background: var(--surface); |
| padding: 10px 14px; |
| text-align: left; |
| font-family: var(--mono); |
| font-size: 10px; |
| letter-spacing: 0.1em; |
| text-transform: uppercase; |
| color: var(--accent); |
| border-bottom: 1px solid var(--border); |
| white-space: nowrap; |
| } |
| |
| table.data-table td { |
| padding: 10px 14px; |
| border-bottom: 1px solid var(--border); |
| color: var(--text); |
| line-height: 1.5; |
| vertical-align: top; |
| } |
| |
| table.data-table tr:last-child td { border-bottom: none; } |
| table.data-table tr:hover td { background: #ffffff05; } |
| |
| |
| .followup-block { |
| display: none; |
| margin-bottom: 32px; |
| border: 1px solid var(--border); |
| border-radius: 4px; |
| overflow: hidden; |
| } |
| |
| .followup-block.visible { display: block; } |
| |
| .followup-header { |
| background: var(--surface); |
| padding: 10px 16px; |
| font-family: var(--mono); |
| font-size: 11px; |
| letter-spacing: 0.15em; |
| text-transform: uppercase; |
| color: var(--text-dim); |
| border-bottom: 1px solid var(--border); |
| } |
| |
| .followup-input-row { |
| padding: 12px 16px; |
| display: flex; |
| gap: 8px; |
| } |
| |
| .followup-input-row input { |
| flex: 1; |
| background: #0d1117; |
| border: 1px solid var(--border); |
| color: var(--text-bright); |
| font-family: var(--sans); |
| font-size: 13px; |
| padding: 8px 12px; |
| border-radius: 3px; |
| outline: none; |
| transition: border-color 0.2s; |
| } |
| |
| .followup-input-row input:focus { border-color: var(--accent); } |
| |
| .followup-ask-btn { |
| background: var(--accent); |
| color: #000; |
| border: none; |
| padding: 8px 16px; |
| font-family: var(--mono); |
| font-size: 11px; |
| font-weight: 600; |
| border-radius: 3px; |
| cursor: pointer; |
| transition: opacity 0.2s; |
| } |
| |
| .followup-ask-btn:disabled { opacity: 0.4; cursor: not-allowed; } |
| |
| .followup-answer { |
| display: none; |
| padding: 16px; |
| border-top: 1px solid var(--border); |
| font-size: 14px; |
| line-height: 1.75; |
| color: var(--text); |
| } |
| |
| .followup-answer.visible { display: block; } |
| |
| |
| .suggestions-block { |
| display: none; |
| margin-bottom: 32px; |
| border: 1px solid var(--border); |
| border-radius: 4px; |
| overflow: hidden; |
| } |
| .suggestions-block.visible { display: block; } |
| .suggestions-header { |
| background: var(--surface); |
| padding: 10px 16px; |
| font-family: var(--mono); |
| font-size: 11px; |
| letter-spacing: 0.15em; |
| text-transform: uppercase; |
| color: var(--text-dim); |
| border-bottom: 1px solid var(--border); |
| display: flex; |
| align-items: center; |
| gap: 10px; |
| } |
| .suggestions-loading { |
| display: inline-block; |
| width: 8px; height: 8px; |
| border-radius: 50%; |
| border: 1px solid var(--border); |
| border-top-color: var(--accent); |
| animation: spin 0.7s linear infinite; |
| } |
| @keyframes spin { to { transform: rotate(360deg); } } |
| .suggestions-list { padding: 12px 16px; display: flex; flex-direction: column; gap: 6px; } |
| .suggestion-btn { |
| background: transparent; |
| border: 1px solid var(--border); |
| border-radius: 3px; |
| padding: 10px 14px; |
| color: var(--text); |
| font-family: var(--sans); |
| font-size: 13px; |
| text-align: left; |
| cursor: pointer; |
| transition: border-color 0.2s, color 0.2s; |
| line-height: 1.5; |
| } |
| .suggestion-btn:hover { border-color: var(--accent); color: var(--accent); } |
| .suggestion-prefix { |
| font-family: var(--mono); |
| font-size: 9px; |
| letter-spacing: 0.12em; |
| text-transform: uppercase; |
| color: var(--accent); |
| display: block; |
| margin-bottom: 3px; |
| } |
| |
| @keyframes hexTrace { |
| 0% { stroke-dashoffset: 200; opacity: 0.3; } |
| 50% { opacity: 1; } |
| 100% { stroke-dashoffset: 0; opacity: 0.3; } |
| } |
| @keyframes fadeUp { |
| from { opacity: 0; transform: translateY(4px); } |
| to { opacity: 1; transform: translateY(0); } |
| } |
| @keyframes scaleIn { |
| from { transform: scaleX(0); } |
| to { transform: scaleX(1); } |
| } |
| @keyframes glowPulse { |
| 0%, 100% { opacity: 0; transform: scale(0.8); } |
| 50% { opacity: 1; transform: scale(1.1); } |
| } |
| |
| .aria-logo-hex-trace { |
| stroke-dasharray: 200; |
| stroke-dashoffset: 200; |
| animation: hexTrace 3s ease-in-out infinite; |
| } |
| .aria-logo-a { animation: fadeUp 0.8s ease 0.2s both; } |
| .aria-logo-crossbar { transform-origin: center; animation: scaleIn 0.6s ease 0.8s both; } |
| .aria-logo-glow { animation: glowPulse 3s ease-in-out infinite; } |
| </style> |
| </head> |
| <body> |
| <button class="history-toggle" onclick="toggleHistory()">☰ History</button> |
| <div class="history-panel" id="historyPanel"> |
| <div class="history-panel-header"> |
| <span>Query History</span> |
| <span class="history-close" onclick="toggleHistory()">✕</span> |
| </div> |
| <div class="history-list" id="historyList"> |
| <div class="history-empty">No history yet</div> |
| </div> |
| </div> |
| <div class="container"> |
| <header> |
| <div style="display:flex;align-items:center;gap:16px;margin-bottom:16px;"> |
| <div style="position:relative;width:40px;height:40px;flex-shrink:0;"> |
| <svg width="40" height="40" viewBox="0 0 40 40" fill="none" xmlns="http://www.w3.org/2000/svg"> |
| <path d="M20 4L34 12V28L20 36L6 28V12L20 4Z" stroke="#00f5ff" stroke-width="1" stroke-opacity="0.25"/> |
| <path class="aria-logo-hex-trace" d="M20 4L34 12V28L20 36L6 28V12L20 4Z" stroke="#00f5ff" stroke-width="2" stroke-linecap="round"/> |
| <path class="aria-logo-a" d="M12 28L20 12L28 28" stroke="white" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"/> |
| <path class="aria-logo-crossbar" d="M16 22H24" stroke="#00f5ff" stroke-width="2" stroke-linecap="round"/> |
| </svg> |
| <div class="aria-logo-glow" style="position:absolute;inset:0;background:#00f5ff;border-radius:50%;filter:blur(16px);opacity:0;pointer-events:none;"></div> |
| </div> |
| <div style="display:flex;flex-direction:column;gap:2px;"> |
| <span style="font-family:var(--sans);font-weight:900;font-size:24px;letter-spacing:-0.03em;color:#f1f5f9;line-height:1;">ARIA</span> |
| <div style="display:flex;align-items:center;gap:6px;"> |
| <span style="width:6px;height:1px;background:#00f5ff;display:inline-block;"></span> |
| <span style="font-family:var(--mono);font-size:9px;text-transform:uppercase;letter-spacing:0.25em;color:#475569;font-weight:700;">Research</span> |
| </div> |
| </div> |
| </div> |
| <div style="font-family:var(--mono);font-size:10px;letter-spacing:0.15em;color:#00f5ff;text-transform:uppercase;margin-bottom:6px;">AMD Developer Hackathon 2026 — Glitch Squad</div> |
| <h1>Autonomous Research Intelligence Agent</h1> |
| <div class="subtitle">Multi-agent biomedical literature synthesis · PubMed · Qwen2.5-72B on AMD MI300X · by Azlaan Mohammad</div> |
| </header> |
|
|
| <div class="search-section"> |
| <div class="input-wrapper"> |
| <div class="input-label">Clinical Question</div> |
| <textarea id="queryInput" rows="3" placeholder="e.g. What machine learning methods are most effective for epilepsy detection from EEG signals?"></textarea> |
| <div class="search-actions"> |
| <span class="hint">Enter to submit · Shift+Enter for new line</span> |
| <button id="submitBtn" onclick="submitQuery()">Run Pipeline</button> |
| </div> |
| </div> |
| </div> |
|
|
| <div class="pipeline" id="pipeline"> |
| <div class="pipeline-header"> |
| <span>Pipeline Status</span> |
| <div style="display:flex;align-items:center;gap:10px;"> |
| <div class="progress-wrap"><div class="progress-fill" id="progressFill"></div></div> |
| <span class="progress-pct" id="progressPct">0%</span> |
| </div> |
| </div> |
| <div class="stage" id="stage1"><div class="stage-dot"></div>Query Architect — generating MeSH-optimised PubMed queries</div> |
| <div class="stage" id="stage2"><div class="stage-dot"></div>Literature Scout — fetching fetching from PubMed and Europe PMC</div> |
| <div class="stage" id="stage3"><div class="stage-dot"></div>PRISMA Filter — screening papers for relevance</div> |
| <div class="stage" id="stage4"><div class="stage-dot"></div>Evidence Synthesiser — building structured synthesis</div> |
| <div class="stage" id="stage5"><div class="stage-dot"></div>Citation Builder — formatting references</div> |
| </div> |
|
|
| <div class="error-box" id="errorBox"></div> |
|
|
| <div class="results" id="results"> |
| <div class="meta-bar" id="metaBar"></div> |
| <div class="synthesis" id="synthesis"></div> |
|
|
| <div style="display:flex;justify-content:flex-end;margin-bottom:12px;"> |
| <button id="exportBtn" onclick="exportPDF()" style="background:transparent;border:1px solid var(--accent);color:var(--accent);padding:7px 18px;font-family:var(--mono);font-size:11px;border-radius:3px;cursor:pointer;letter-spacing:0.08em;">Export PDF</button> |
| </div> |
|
|
| |
| <div class="followup-block" id="followupBlock"> |
| <div class="followup-header">Ask a Follow-up</div> |
| <div class="followup-input-row"> |
| <input id="followupInput" type="text" placeholder="e.g. What are the side effects of these treatments?" onkeydown="if(event.key==='Enter') askFollowup()"> |
| <button class="followup-ask-btn" id="followupBtn" onclick="askFollowup()">Ask</button> |
| </div> |
| <div class="followup-answer" id="followupAnswer"></div> |
| </div> |
|
|
| |
| <div class="suggestions-block" id="suggestionsBlock"> |
| <div class="suggestions-header"> |
| <span id="suggestionsSpinner" class="suggestions-loading"></span> |
| Suggested Follow-up Queries |
| </div> |
| <div class="suggestions-list" id="suggestionsList"></div> |
| </div> |
|
|
| <div class="table-block" id="tableBlock"> |
| <div class="table-block-header" id="tableTitle">Evidence Comparison Table</div> |
| <div class="table-wrap"> |
| <table class="data-table" id="dataTable"> |
| <thead id="tableHead"></thead> |
| <tbody id="tableBody"></tbody> |
| </table> |
| </div> |
| </div> |
|
|
| <div class="prediction-block" id="predictionBlock"> |
| <div class="prediction-header"> |
| <span>Predictive Model</span> |
| <span class="prediction-tag">AI FORECAST</span> |
| </div> |
| <div class="prediction-section"> |
| <div class="prediction-label constructive">Constructive Forecast</div> |
| <div class="prediction-text" id="constructiveForecast"></div> |
| </div> |
| <div class="prediction-section"> |
| <div class="prediction-label destructive">Destructive Forecast</div> |
| <div class="prediction-text" id="destructiveForecast"></div> |
| </div> |
| </div> |
|
|
| <div class="citations-block"> |
| <div class="citations-header" onclick="toggleCitations()"> |
| <span>References</span><span id="citChevron">▼</span> |
| </div> |
| <div class="citations-list" id="citationsList"></div> |
| </div> |
| </div> |
|
|
| <div class="review-bar" id="reviewBar"> |
| <span id="reviewBarText">0 papers selected</span> |
| <button class="review-btn" id="reviewBtn" onclick="generateReview()" disabled>Generate Literature Review</button> |
| </div> |
| <div class="review-output" id="reviewOutput"> |
| <div class="review-output-header">Generated Literature Review</div> |
| <div class="review-output-body" id="reviewText"></div> |
| </div> |
|
|
| <footer> |
| <span>Powered by LangGraph · Qwen2.5-72B · AMD MI300X · PubMed NCBI</span> |
| <span>AI-generated synthesis — verify against primary sources</span> |
| <span style="color:var(--text-dim);font-family:var(--mono);font-size:10px;">Azlaan Mohammad · 2026</span> |
| </footer> |
| </div> |
|
|
| <script> |
| const SECTIONS = [ |
| { key: "## Background", label: "Background" }, |
| { key: "## Key Findings", label: "Key Findings" }, |
| { key: "## Level of Evidence", label: "Level of Evidence" }, |
| { key: "## Conflicting Evidence", label: "Conflicting Evidence" }, |
| { key: "## Research Gaps", label: "Research Gaps" }, |
| { key: "## Clinical Implications", label: "Clinical Implications" }, |
| ]; |
| |
| function parseSynthesis(text) { |
| const result = {}; |
| for (let i = 0; i < SECTIONS.length; i++) { |
| const start = text.indexOf(SECTIONS[i].key); |
| if (start === -1) continue; |
| const contentStart = start + SECTIONS[i].key.length; |
| const nextSection = i + 1 < SECTIONS.length ? text.indexOf(SECTIONS[i + 1].key) : -1; |
| result[SECTIONS[i].label] = nextSection === -1 |
| ? text.slice(contentStart).trim() |
| : text.slice(contentStart, nextSection).trim(); |
| } |
| return result; |
| } |
| |
| const STAGE_PCT = { 1: 10, 2: 35, 3: 70, 4: 90, 5: 100 }; |
| |
| function setStage(n) { |
| for (let i = 1; i <= 5; i++) { |
| const el = document.getElementById("stage" + i); |
| el.classList.remove("active", "done"); |
| if (i < n) el.classList.add("done"); |
| else if (i === n) el.classList.add("active"); |
| } |
| const pct = STAGE_PCT[n] || 0; |
| document.getElementById("progressFill").style.width = pct + "%"; |
| document.getElementById("progressPct").textContent = pct + "%"; |
| } |
| |
| function toggleSection(el) { el.classList.toggle("open"); } |
| |
| function toggleCitations() { |
| document.getElementById("citationsList").classList.toggle("open"); |
| document.getElementById("citChevron").textContent = |
| document.getElementById("citationsList").classList.contains("open") ? "▲" : "▼"; |
| } |
| |
| |
| async function renderResults(data) { |
| const meta = document.getElementById("metaBar"); |
| meta.innerHTML = `<div class="badge green">${data.paper_count} papers retrieved</div><div class="badge">${data.queries ? data.queries.length : 0} PubMed queries</div><div class="badge">Qwen2.5-72B on AMD MI300X</div>`; |
| const sections = parseSynthesis(data.synthesis); |
| const synthEl = document.getElementById("synthesis"); |
| synthEl.innerHTML = ""; |
| |
| { |
| SECTIONS.forEach(s => { |
| if (!sections[s.label]) return; |
| const div = document.createElement("div"); |
| div.className = "section open"; |
| div.innerHTML = `<div class="section-header" onclick="toggleSection(this.parentElement)"><span class="section-tag">${s.label}</span><span class="section-chevron">▲</span></div><div class="section-body">${sections[s.label].replace(/\n/g, "<br>")}</div>`; |
| synthEl.appendChild(div); |
| }); |
| } |
| |
| const citList = document.getElementById("citationsList"); |
| citList.innerHTML = ""; |
| citList.classList.add("open"); |
| const papersMap = data.papers || {}; |
| selectedPapers = {}; |
| updateReviewBar(); |
| data.citations.split("\n").forEach(line => { |
| if (!line.trim()) return; |
| const pmidMatch = line.match(/PMID:\s*(\d+)/); |
| const pmid = pmidMatch ? pmidMatch[1] : null; |
| const paper = pmid ? papersMap[pmid] : null; |
| const div = document.createElement("div"); |
| div.className = "citation-item"; |
| const citText = pmid ? line.replace(/PMID:\s*\d+/, `<span class="citation-pmid">PMID: ${pmid}</span>`) : line; |
| const inner = document.createElement("div"); |
| inner.className = "citation-text"; |
| inner.innerHTML = citText + (paper ? `<span class="citation-toggle">▶ abstract</span><div class="abstract-body">${paper.abstract ? paper.abstract.replace(/\n/g,"<br>") : "Abstract not available"}</div>` : ""); |
| if (paper) { |
| inner.addEventListener("click", (e) => { |
| if (e.target.type === "checkbox") return; |
| inner.parentElement.classList.toggle("expanded"); |
| const tog = inner.querySelector(".citation-toggle"); |
| if (tog) tog.textContent = inner.parentElement.classList.contains("expanded") ? "▼ abstract" : "▶ abstract"; |
| }); |
| } |
| if (pmid && paper) { |
| const cb = document.createElement("input"); |
| cb.type = "checkbox"; |
| cb.className = "citation-checkbox"; |
| cb.addEventListener("change", () => { |
| if (cb.checked) selectedPapers[pmid] = paper; |
| else delete selectedPapers[pmid]; |
| updateReviewBar(); |
| }); |
| div.appendChild(cb); |
| } |
| div.appendChild(inner); |
| citList.appendChild(div); |
| }); |
| document.getElementById("results").classList.add("visible"); |
| document.getElementById("followupBlock").classList.add("visible"); |
| document.getElementById("followupAnswer").classList.remove("visible"); |
| document.getElementById("followupAnswer").innerHTML = ""; |
| document.getElementById("followupInput").value = ""; |
| } |
| |
| function updateProgress(pct) { |
| document.getElementById("progressFill").style.width = pct + "%"; |
| document.getElementById("progressPct").textContent = pct + "%"; |
| } |
| |
| async function submitQuery() { |
| const q = document.getElementById("queryInput").value.trim(); |
| if (!q) return; |
| const btn = document.getElementById("submitBtn"); |
| btn.disabled = true; |
| document.getElementById("results").classList.remove("visible"); |
| document.getElementById("errorBox").classList.remove("visible"); |
| document.getElementById("predictionBlock").classList.remove("visible"); |
| document.getElementById("tableBlock").classList.remove("visible"); |
| document.getElementById("suggestionsBlock").classList.remove("visible"); |
| document.getElementById("suggestionsList").innerHTML = ""; |
| document.getElementById("pipeline").classList.add("visible"); |
| setStage(1); |
| updateProgress(5); |
| lastQuery = q; |
| |
| const es = new EventSource("/stream?query=" + encodeURIComponent(q)); |
| |
| es.addEventListener("stage", e => { const d = JSON.parse(e.data); setStage(d.stage); updateProgress(d.pct); }); |
| es.addEventListener("queries", e => { updateProgress(JSON.parse(e.data).pct); }); |
| es.addEventListener("papers", e => { updateProgress(JSON.parse(e.data).pct); }); |
| es.addEventListener("synthesis", e => { updateProgress(JSON.parse(e.data).pct); }); |
| es.addEventListener("done", async e => { |
| const data = JSON.parse(e.data); |
| lastResult = data; |
| updateProgress(100); |
| setStage(5); |
| es.close(); |
| await renderResults(data); |
| setTimeout(() => scoreResults(data.synthesis), 2000); |
| setTimeout(() => runPredictiveModel(lastQuery, data.synthesis), 20000); |
| setTimeout(() => buildTable(lastQuery, data.synthesis, data.papers || {}), 38000); |
| setTimeout(() => fetchQuerySuggestions(lastQuery, data.synthesis), 56000); |
| saveSession(data, q); |
| btn.disabled = false; |
| }); |
| es.addEventListener("error", e => { |
| es.close(); |
| let msg = "Pipeline error"; |
| try { msg = JSON.parse(e.data).message; } catch(err) {} |
| document.getElementById("errorBox").textContent = "Error: " + msg; |
| document.getElementById("errorBox").classList.add("visible"); |
| btn.disabled = false; |
| }); |
| } |
| |
| document.getElementById("queryInput").addEventListener("keydown", e => { |
| if (e.key === "Enter" && !e.shiftKey) { e.preventDefault(); submitQuery(); } |
| }); |
| |
| let lastResult = null; |
| let lastQuery = ''; |
| |
| |
| async function fetchQuerySuggestions(query, synthesis) { |
| const block = document.getElementById('suggestionsBlock'); |
| const list = document.getElementById('suggestionsList'); |
| const spinner = document.getElementById('suggestionsSpinner'); |
| block.classList.add('visible'); |
| spinner.style.display = 'inline-block'; |
| list.innerHTML = ''; |
| try { |
| const res = await fetch('/suggest-queries', { |
| method: 'POST', |
| headers: { 'Content-Type': 'application/json' }, |
| body: JSON.stringify({ query, synthesis }) |
| }); |
| const data = await res.json(); |
| spinner.style.display = 'none'; |
| if (data.error || !data.suggestions || data.suggestions.length === 0) { |
| list.innerHTML = '<span style="font-family:var(--mono);font-size:11px;color:var(--text-dim);">Could not generate suggestions.</span>'; |
| return; |
| } |
| data.suggestions.forEach((s, i) => { |
| const btn = document.createElement('button'); |
| btn.className = 'suggestion-btn'; |
| btn.innerHTML = `<span class="suggestion-prefix">Suggestion ${i + 1}</span>${s}`; |
| btn.addEventListener('click', () => { |
| document.getElementById('queryInput').value = s; |
| submitQuery(); |
| }); |
| list.appendChild(btn); |
| }); |
| } catch(e) { |
| spinner.style.display = 'none'; |
| list.innerHTML = '<span style="font-family:var(--mono);font-size:11px;color:var(--text-dim);">Could not generate suggestions.</span>'; |
| } |
| } |
| |
| async function exportPDF() { |
| const btn = document.getElementById('exportBtn'); |
| btn.textContent = 'Generating...'; |
| btn.disabled = true; |
| try { |
| const res = await fetch('/export-pdf', { |
| method: 'POST', |
| headers: {'Content-Type': 'application/json'}, |
| body: JSON.stringify({ |
| synthesis: lastResult.synthesis, |
| citations: lastResult.citations, |
| query: lastQuery, |
| paper_count: lastResult.paper_count |
| }) |
| }); |
| const blob = await res.blob(); |
| const url = URL.createObjectURL(blob); |
| const a = document.createElement('a'); |
| a.href = url; a.download = 'ARIA_report.pdf'; a.click(); |
| URL.revokeObjectURL(url); |
| } finally { |
| btn.textContent = 'Export PDF'; |
| btn.disabled = false; |
| } |
| } |
| |
| async function askFollowup() { |
| const q = document.getElementById('followupInput').value.trim(); |
| if (!q || !lastResult) return; |
| const btn = document.getElementById('followupBtn'); |
| const answerEl = document.getElementById('followupAnswer'); |
| btn.disabled = true; |
| btn.textContent = 'Thinking...'; |
| answerEl.classList.add('visible'); |
| answerEl.innerHTML = '<span style="color:var(--text-dim);font-family:var(--mono);font-size:12px;">Analysing papers...</span>'; |
| try { |
| const res = await fetch('/followup', { |
| method: 'POST', |
| headers: {'Content-Type': 'application/json'}, |
| body: JSON.stringify({ |
| question: q, |
| original_question: lastQuery, |
| synthesis: lastResult.synthesis, |
| papers: lastResult.papers || {} |
| }) |
| }); |
| const data = await res.json(); |
| if (data.error) throw new Error(data.error); |
| answerEl.innerHTML = data.answer.replace(/\n/g, '<br>'); |
| } catch(e) { |
| answerEl.innerHTML = '<span style="color:var(--red);">Error: ' + e.message + '</span>'; |
| } finally { |
| btn.disabled = false; |
| btn.textContent = 'Ask'; |
| } |
| } |
| |
| async function scoreResults(synthesis) { |
| try { |
| const res = await fetch('/score', { method: 'POST', headers: {'Content-Type': 'application/json'}, body: JSON.stringify({synthesis}) }); |
| const data = await res.json(); |
| if (data.error || !data.scores) return; |
| const scores = data.scores; |
| document.querySelectorAll('.section').forEach(sec => { |
| const tag = sec.querySelector('.section-tag'); |
| if (!tag) return; |
| const s = scores[tag.textContent.trim()]; |
| if (!s) return; |
| const cls = s.score >= 8 ? 'conf-strong' : s.score >= 5 ? 'conf-moderate' : 'conf-weak'; |
| const text = s.score >= 8 ? 'Strong' : s.score >= 5 ? 'Moderate' : 'Weak'; |
| const badge = document.createElement('div'); |
| badge.className = 'confidence-badge ' + cls; |
| badge.innerHTML = text + ' ' + s.score + '/10<div class="conf-tooltip">' + s.rationale + '</div>'; |
| sec.querySelector('.section-header').insertBefore(badge, sec.querySelector('.section-chevron')); |
| }); |
| } catch(e) { console.warn('Scoring failed:', e); } |
| } |
| |
| let selectedPapers = {}; |
| |
| function updateReviewBar() { |
| const count = Object.keys(selectedPapers).length; |
| document.getElementById('reviewBar').classList.toggle('visible', count > 0); |
| document.getElementById('reviewBarText').textContent = count + ' paper' + (count !== 1 ? 's' : '') + ' selected'; |
| document.getElementById('reviewBtn').disabled = count < 2; |
| } |
| |
| async function generateReview() { |
| const btn = document.getElementById('reviewBtn'); |
| btn.disabled = true; |
| btn.textContent = 'Generating...'; |
| try { |
| const res = await fetch('/selective-review', { |
| method: 'POST', |
| headers: {'Content-Type': 'application/json'}, |
| body: JSON.stringify({ question: lastQuery, papers: selectedPapers }) |
| }); |
| const data = await res.json(); |
| if (data.error) throw new Error(data.error); |
| document.getElementById('reviewText').textContent = data.review; |
| document.getElementById('reviewOutput').classList.add('visible'); |
| document.getElementById('reviewOutput').scrollIntoView({behavior: 'smooth'}); |
| } catch(e) { |
| alert('Error: ' + e.message); |
| } finally { |
| btn.disabled = false; |
| btn.textContent = 'Generate Literature Review'; |
| updateReviewBar(); |
| } |
| } |
| |
| async function runPredictiveModel(question, synthesis) { |
| try { |
| const res = await fetch('/predict', { method: 'POST', headers: {'Content-Type': 'application/json'}, body: JSON.stringify({question, synthesis}) }); |
| const data = await res.json(); |
| if (data.error || !data.prediction) return; |
| const text = data.prediction; |
| const cm = text.match(/## Constructive Forecast\n([\s\S]*?)(?=## Destructive Forecast|$)/); |
| const dm = text.match(/## Destructive Forecast\n([\s\S]*?)$/); |
| if (cm) document.getElementById('constructiveForecast').textContent = cm[1].trim(); |
| if (dm) document.getElementById('destructiveForecast').textContent = dm[1].trim(); |
| document.getElementById('predictionBlock').classList.add('visible'); |
| } catch(e) { console.warn('Prediction failed:', e); } |
| } |
| |
| function toggleHistory() { |
| document.getElementById('historyPanel').classList.toggle('open'); |
| if (document.getElementById('historyPanel').classList.contains('open')) loadHistory(); |
| } |
| |
| async function loadHistory() { |
| try { |
| const res = await fetch('/sessions'); |
| const data = await res.json(); |
| const list = document.getElementById('historyList'); |
| if (!data.sessions || data.sessions.length === 0) { |
| list.innerHTML = '<div class="history-empty">No history yet</div>'; |
| return; |
| } |
| list.innerHTML = ''; |
| data.sessions.forEach(s => { |
| const div = document.createElement('div'); |
| div.className = 'history-item'; |
| div.innerHTML = `<div class="history-item-query">${s.query}</div><div class="history-item-meta">${s.timestamp} · ${s.paper_count} papers</div>`; |
| div.addEventListener('click', () => { |
| lastResult = s; lastQuery = s.query; |
| document.getElementById('queryInput').value = s.query; |
| document.getElementById('results').classList.remove('visible'); |
| document.getElementById('predictionBlock').classList.remove('visible'); |
| document.getElementById('reviewOutput').classList.remove('visible'); |
| document.getElementById('errorBox').classList.remove('visible'); |
| document.getElementById('suggestionsBlock').classList.remove('visible'); |
| renderResults(s); |
| document.getElementById('historyPanel').classList.remove('open'); |
| document.getElementById('results').scrollIntoView({behavior: 'smooth'}); |
| }); |
| list.appendChild(div); |
| }); |
| } catch(e) { console.warn('History load failed:', e); } |
| } |
| |
| async function saveSession(data, query) { |
| try { |
| await fetch('/sessions/save', { method: 'POST', headers: {'Content-Type': 'application/json'}, body: JSON.stringify({...data, query}) }); |
| } catch(e) { console.warn('Session save failed:', e); } |
| } |
| |
| async function buildTable(question, synthesis, papers) { |
| try { |
| const res = await fetch('/extract-table', { method: 'POST', headers: {'Content-Type': 'application/json'}, body: JSON.stringify({question, synthesis, papers}) }); |
| const data = await res.json(); |
| if (data.error || !data.table) return; |
| const t = data.table; |
| if (!t.rows || t.rows.length === 0) return; |
| document.getElementById('tableTitle').textContent = t.title || 'Evidence Comparison Table'; |
| document.getElementById('tableHead').innerHTML = '<tr>' + (t.columns || []).map(c => `<th>${c}</th>`).join('') + '</tr>'; |
| document.getElementById('tableBody').innerHTML = t.rows.map(row => '<tr>' + row.map(cell => `<td>${cell}</td>`).join('') + '</tr>').join(''); |
| document.getElementById('tableBlock').classList.add('visible'); |
| } catch(e) { console.warn('Table extraction failed:', e); } |
| } |
| </script> |
| </body> |
| </html> |