Create an app for lung issue to react according to the pharmaceutical drugs molecules it can test multiple drugs

Asking for Preferences
Running Command

mkdir -p /home/daytona/workspace/lung-drug-app && ls /home/daytona/workspace/

Writing File

~/lung-drug-app/index.html

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8" />
  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
  <title>LungRx - Pulmonary Drug Molecular Analyzer</title>
  <script src="https://3Dmol.csb.pitt.edu/build/3Dmol-min.js"></script>
  <script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
  <link rel="preconnect" href="https://fonts.googleapis.com" />
  <link href="https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700&family=JetBrains+Mono:wght@400;500&display=swap" rel="stylesheet" />
  <style>
    :root {
      --bg: #0a0e1a;
      --surface: #111827;
      --surface2: #1a2234;
      --surface3: #1e2a3a;
      --border: #2d3a50;
      --accent: #3b82f6;
      --accent2: #06b6d4;
      --accent3: #8b5cf6;
      --success: #10b981;
      --warning: #f59e0b;
      --danger: #ef4444;
      --text: #e2e8f0;
      --text-muted: #94a3b8;
      --text-dim: #64748b;
      --lung-red: #ef4444;
      --lung-orange: #f97316;
      --lung-blue: #3b82f6;
    }

    * { box-sizing: border-box; margin: 0; padding: 0; }

    body {
      font-family: 'Inter', sans-serif;
      background: var(--bg);
      color: var(--text);
      min-height: 100vh;
      overflow-x: hidden;
    }

    /* ── HEADER ── */
    header {
      background: linear-gradient(135deg, #0f172a 0%, #1e1b4b 50%, #0f172a 100%);
      border-bottom: 1px solid var(--border);
      padding: 0 2rem;
      height: 64px;
      display: flex;
      align-items: center;
      justify-content: space-between;
      position: sticky;
      top: 0;
      z-index: 100;
      box-shadow: 0 4px 20px rgba(0,0,0,0.4);
    }

    .logo {
      display: flex;
      align-items: center;
      gap: 12px;
    }

    .logo-icon {
      width: 36px;
      height: 36px;
      background: linear-gradient(135deg, var(--accent), var(--accent3));
      border-radius: 10px;
      display: flex;
      align-items: center;
      justify-content: center;
      font-size: 18px;
    }

    .logo-text {
      font-size: 1.3rem;
      font-weight: 700;
      background: linear-gradient(135deg, var(--accent), var(--accent2));
      -webkit-background-clip: text;
      -webkit-text-fill-color: transparent;
    }

    .logo-sub {
      font-size: 0.7rem;
      color: var(--text-muted);
      letter-spacing: 0.1em;
      text-transform: uppercase;
    }

    .header-badges {
      display: flex;
      gap: 8px;
    }

    .badge {
      padding: 4px 10px;
      border-radius: 20px;
      font-size: 0.7rem;
      font-weight: 500;
      letter-spacing: 0.05em;
    }

    .badge-blue { background: rgba(59,130,246,0.2); color: var(--accent); border: 1px solid rgba(59,130,246,0.3); }
    .badge-purple { background: rgba(139,92,246,0.2); color: var(--accent3); border: 1px solid rgba(139,92,246,0.3); }
    .badge-cyan { background: rgba(6,182,212,0.2); color: var(--accent2); border: 1px solid rgba(6,182,212,0.3); }

    /* ── MAIN LAYOUT ── */
    .app-container {
      display: grid;
      grid-template-columns: 380px 1fr;
      grid-template-rows: auto 1fr;
      height: calc(100vh - 64px);
    }

    /* ── LEFT PANEL ── */
    .left-panel {
      grid-row: 1 / 3;
      background: var(--surface);
      border-right: 1px solid var(--border);
      overflow-y: auto;
      display: flex;
      flex-direction: column;
    }

    .panel-section {
      padding: 1.2rem;
      border-bottom: 1px solid var(--border);
    }

    .section-title {
      font-size: 0.72rem;
      font-weight: 600;
      letter-spacing: 0.12em;
      text-transform: uppercase;
      color: var(--text-muted);
      margin-bottom: 0.8rem;
      display: flex;
      align-items: center;
      gap: 6px;
    }

    .section-title::before {
      content: '';
      display: inline-block;
      width: 3px;
      height: 12px;
      background: var(--accent);
      border-radius: 2px;
    }

    /* ── INPUT TABS ── */
    .input-tabs {
      display: flex;
      gap: 4px;
      margin-bottom: 0.8rem;
      background: var(--bg);
      padding: 4px;
      border-radius: 8px;
    }

    .tab-btn {
      flex: 1;
      padding: 6px;
      border: none;
      background: transparent;
      color: var(--text-muted);
      font-size: 0.72rem;
      font-weight: 500;
      border-radius: 6px;
      cursor: pointer;
      transition: all 0.2s;
    }

    .tab-btn.active {
      background: var(--accent);
      color: white;
    }

    .tab-content { display: none; }
    .tab-content.active { display: block; }

    /* ── INPUTS ── */
    input[type=text], textarea, select {
      width: 100%;
      background: var(--bg);
      border: 1px solid var(--border);
      color: var(--text);
      padding: 8px 12px;
      border-radius: 8px;
      font-family: 'Inter', sans-serif;
      font-size: 0.85rem;
      outline: none;
      transition: border-color 0.2s;
    }

    input[type=text]:focus, textarea:focus {
      border-color: var(--accent);
      box-shadow: 0 0 0 3px rgba(59,130,246,0.1);
    }

    textarea { resize: vertical; min-height: 60px; font-family: 'JetBrains Mono', monospace; font-size: 0.78rem; }

    .input-row {
      display: flex;
      gap: 6px;
      margin-bottom: 8px;
    }

    .input-row input { flex: 1; }

    /* ── BUTTONS ── */
    .btn {
      padding: 8px 16px;
      border: none;
      border-radius: 8px;
      font-size: 0.82rem;
      font-weight: 500;
      cursor: pointer;
      transition: all 0.2s;
      display: inline-flex;
      align-items: center;
      gap: 6px;
    }

    .btn-primary {
      background: linear-gradient(135deg, var(--accent), #6366f1);
      color: white;
    }

    .btn-primary:hover { opacity: 0.9; transform: translateY(-1px); }

    .btn-secondary {
      background: var(--surface2);
      color: var(--text);
      border: 1px solid var(--border);
    }

    .btn-secondary:hover { background: var(--surface3); }

    .btn-danger {
      background: rgba(239,68,68,0.15);
      color: var(--danger);
      border: 1px solid rgba(239,68,68,0.3);
    }

    .btn-sm { padding: 4px 10px; font-size: 0.75rem; }

    .btn-full { width: 100%; justify-content: center; }

    /* ── FILE UPLOAD ── */
    .file-drop {
      border: 2px dashed var(--border);
      border-radius: 10px;
      padding: 20px;
      text-align: center;
      cursor: pointer;
      transition: all 0.2s;
      color: var(--text-muted);
      font-size: 0.82rem;
    }

    .file-drop:hover, .file-drop.dragover {
      border-color: var(--accent);
      background: rgba(59,130,246,0.05);
      color: var(--accent);
    }

    .file-drop .file-icon { font-size: 1.8rem; margin-bottom: 6px; }

    /* ── DRUG LIST ── */
    .drug-list {
      display: flex;
      flex-direction: column;
      gap: 8px;
    }

    .drug-card {
      background: var(--surface2);
      border: 1px solid var(--border);
      border-radius: 10px;
      padding: 10px 12px;
      cursor: pointer;
      transition: all 0.2s;
      position: relative;
    }

    .drug-card:hover { border-color: var(--accent); background: var(--surface3); }

    .drug-card.active {
      border-color: var(--accent);
      background: rgba(59,130,246,0.08);
      box-shadow: 0 0 0 1px var(--accent);
    }

    .drug-card-header {
      display: flex;
      align-items: center;
      justify-content: space-between;
      margin-bottom: 4px;
    }

    .drug-name { font-weight: 600; font-size: 0.88rem; }

    .drug-class {
      font-size: 0.68rem;
      padding: 2px 7px;
      border-radius: 10px;
      font-weight: 500;
    }

    .drug-smiles {
      font-family: 'JetBrains Mono', monospace;
      font-size: 0.65rem;
      color: var(--text-dim);
      white-space: nowrap;
      overflow: hidden;
      text-overflow: ellipsis;
    }

    .drug-card-actions {
      display: flex;
      gap: 4px;
      margin-top: 6px;
    }

    .drug-color-dot {
      width: 8px;
      height: 8px;
      border-radius: 50%;
      display: inline-block;
      margin-right: 4px;
    }

    /* ── PRESET DRUGS ── */
    .preset-grid {
      display: grid;
      grid-template-columns: 1fr 1fr;
      gap: 6px;
    }

    .preset-btn {
      background: var(--surface2);
      border: 1px solid var(--border);
      color: var(--text);
      padding: 8px;
      border-radius: 8px;
      font-size: 0.75rem;
      cursor: pointer;
      text-align: left;
      transition: all 0.2s;
    }

    .preset-btn:hover { border-color: var(--accent); background: var(--surface3); }
    .preset-btn .preset-name { font-weight: 600; display: block; }
    .preset-btn .preset-cat { font-size: 0.65rem; color: var(--text-muted); }

    /* ── RIGHT TOP: VIEWER ── */
    .right-top {
      background: var(--surface);
      border-bottom: 1px solid var(--border);
      position: relative;
      min-height: 320px;
    }

    #mol-viewer {
      width: 100%;
      height: 100%;
      min-height: 320px;
    }

    .viewer-overlay {
      position: absolute;
      top: 12px;
      left: 12px;
      display: flex;
      flex-direction: column;
      gap: 6px;
      z-index: 10;
    }

    .viewer-badge {
      background: rgba(10,14,26,0.85);
      border: 1px solid var(--border);
      border-radius: 8px;
      padding: 6px 10px;
      font-size: 0.75rem;
      backdrop-filter: blur(8px);
    }

    .viewer-controls {
      position: absolute;
      top: 12px;
      right: 12px;
      display: flex;
      flex-direction: column;
      gap: 6px;
      z-index: 10;
    }

    .ctrl-btn {
      width: 32px;
      height: 32px;
      background: rgba(10,14,26,0.85);
      border: 1px solid var(--border);
      border-radius: 8px;
      color: var(--text);
      cursor: pointer;
      display: flex;
      align-items: center;
      justify-content: center;
      font-size: 0.85rem;
      transition: all 0.2s;
      backdrop-filter: blur(8px);
    }

    .ctrl-btn:hover { border-color: var(--accent); color: var(--accent); }
    .ctrl-btn.active { background: var(--accent); color: white; border-color: var(--accent); }

    .viewer-footer {
      position: absolute;
      bottom: 0;
      left: 0;
      right: 0;
      background: linear-gradient(transparent, rgba(10,14,26,0.9));
      padding: 20px 12px 8px;
      display: flex;
      gap: 8px;
      flex-wrap: wrap;
    }

    .style-chip {
      background: rgba(30,42,58,0.9);
      border: 1px solid var(--border);
      color: var(--text-muted);
      padding: 4px 10px;
      border-radius: 20px;
      font-size: 0.7rem;
      cursor: pointer;
      transition: all 0.2s;
      backdrop-filter: blur(4px);
    }

    .style-chip:hover, .style-chip.active {
      border-color: var(--accent);
      color: var(--accent);
      background: rgba(59,130,246,0.1);
    }

    /* ── RIGHT BOTTOM: ANALYSIS ── */
    .right-bottom {
      overflow-y: auto;
      background: var(--bg);
    }

    .analysis-tabs {
      display: flex;
      background: var(--surface);
      border-bottom: 1px solid var(--border);
      padding: 0 1.5rem;
      gap: 0;
      overflow-x: auto;
    }

    .atab {
      padding: 12px 18px;
      border: none;
      background: transparent;
      color: var(--text-muted);
      font-size: 0.82rem;
      font-weight: 500;
      cursor: pointer;
      border-bottom: 2px solid transparent;
      white-space: nowrap;
      transition: all 0.2s;
    }

    .atab:hover { color: var(--text); }
    .atab.active { color: var(--accent); border-bottom-color: var(--accent); }

    .analysis-content {
      padding: 1.5rem;
      display: none;
    }

    .analysis-content.active { display: block; }

    /* ── CARDS ── */
    .info-grid {
      display: grid;
      grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
      gap: 12px;
      margin-bottom: 1.5rem;
    }

    .info-card {
      background: var(--surface);
      border: 1px solid var(--border);
      border-radius: 10px;
      padding: 14px;
    }

    .info-card-label {
      font-size: 0.7rem;
      color: var(--text-muted);
      text-transform: uppercase;
      letter-spacing: 0.08em;
      margin-bottom: 4px;
    }

    .info-card-value {
      font-size: 1.1rem;
      font-weight: 600;
      color: var(--text);
    }

    .info-card-sub { font-size: 0.75rem; color: var(--text-muted); margin-top: 2px; }

    /* ── EFFECT BARS ── */
    .effect-item {
      display: flex;
      align-items: center;
      gap: 10px;
      margin-bottom: 10px;
    }

    .effect-label {
      width: 180px;
      font-size: 0.82rem;
      flex-shrink: 0;
    }

    .effect-bar-wrap {
      flex: 1;
      height: 8px;
      background: var(--surface2);
      border-radius: 4px;
      overflow: hidden;
    }

    .effect-bar {
      height: 100%;
      border-radius: 4px;
      transition: width 0.8s ease;
    }

    .effect-value {
      width: 36px;
      text-align: right;
      font-size: 0.78rem;
      font-family: 'JetBrains Mono', monospace;
      color: var(--text-muted);
    }

    /* ── RECEPTOR TABLE ── */
    .receptor-table {
      width: 100%;
      border-collapse: collapse;
      font-size: 0.82rem;
    }

    .receptor-table th {
      text-align: left;
      padding: 8px 12px;
      font-size: 0.7rem;
      font-weight: 600;
      letter-spacing: 0.08em;
      text-transform: uppercase;
      color: var(--text-muted);
      border-bottom: 1px solid var(--border);
    }

    .receptor-table td {
      padding: 8px 12px;
      border-bottom: 1px solid rgba(45,58,80,0.5);
    }

    .receptor-table tr:hover td { background: var(--surface2); }

    .affinity-pill {
      padding: 2px 8px;
      border-radius: 10px;
      font-size: 0.72rem;
      font-weight: 500;
    }

    .aff-high { background: rgba(16,185,129,0.2); color: var(--success); }
    .aff-med { background: rgba(245,158,11,0.2); color: var(--warning); }
    .aff-low { background: rgba(239,68,68,0.2); color: var(--danger); }

    /* ── COMPARISON ── */
    .compare-grid {
      display: grid;
      gap: 1px;
      background: var(--border);
      border-radius: 10px;
      overflow: hidden;
    }

    .compare-row {
      display: grid;
      background: var(--surface);
    }

    .compare-cell {
      padding: 10px 14px;
      font-size: 0.82rem;
      border-right: 1px solid var(--border);
    }

    .compare-cell:last-child { border-right: none; }
    .compare-cell.header { font-size: 0.7rem; font-weight: 600; color: var(--text-muted); text-transform: uppercase; }
    .compare-cell.label { color: var(--text-muted); font-size: 0.78rem; }

    /* ── CHART ── */
    .chart-wrap {
      background: var(--surface);
      border: 1px solid var(--border);
      border-radius: 10px;
      padding: 16px;
      margin-bottom: 1.2rem;
    }

    .chart-wrap canvas { max-height: 280px; }

    /* ── PATHWAY MAP ── */
    .pathway-map {
      background: var(--surface);
      border: 1px solid var(--border);
      border-radius: 10px;
      padding: 20px;
      margin-bottom: 1.2rem;
    }

    .pathway-step {
      display: flex;
      align-items: center;
      gap: 12px;
      margin-bottom: 8px;
    }

    .pathway-node {
      background: var(--surface2);
      border: 2px solid var(--border);
      border-radius: 8px;
      padding: 8px 14px;
      font-size: 0.8rem;
      font-weight: 500;
      white-space: nowrap;
      transition: all 0.3s;
    }

    .pathway-node.active {
      border-color: var(--accent);
      background: rgba(59,130,246,0.1);
      color: var(--accent);
    }

    .pathway-node.inhibited {
      border-color: var(--danger);
      background: rgba(239,68,68,0.1);
      color: var(--danger);
    }

    .pathway-arrow { color: var(--text-dim); font-size: 1rem; }

    /* ── TAGS ── */
    .tags { display: flex; flex-wrap: wrap; gap: 6px; }
    .tag {
      padding: 3px 10px;
      border-radius: 20px;
      font-size: 0.72rem;
      font-weight: 500;
    }
    .tag-blue { background: rgba(59,130,246,0.15); color: var(--accent); border: 1px solid rgba(59,130,246,0.25); }
    .tag-green { background: rgba(16,185,129,0.15); color: var(--success); border: 1px solid rgba(16,185,129,0.25); }
    .tag-orange { background: rgba(249,115,22,0.15); color: var(--lung-orange); border: 1px solid rgba(249,115,22,0.25); }
    .tag-red { background: rgba(239,68,68,0.15); color: var(--danger); border: 1px solid rgba(239,68,68,0.25); }
    .tag-purple { background: rgba(139,92,246,0.15); color: var(--accent3); border: 1px solid rgba(139,92,246,0.25); }

    /* ── ADR SECTION ── */
    .adr-item {
      display: flex;
      align-items: flex-start;
      gap: 10px;
      padding: 10px;
      background: var(--surface);
      border-radius: 8px;
      margin-bottom: 8px;
      border-left: 3px solid;
    }

    .adr-sev-high { border-left-color: var(--danger); }
    .adr-sev-med { border-left-color: var(--warning); }
    .adr-sev-low { border-left-color: var(--success); }

    .adr-icon { font-size: 1rem; }
    .adr-name { font-size: 0.85rem; font-weight: 500; margin-bottom: 2px; }
    .adr-desc { font-size: 0.75rem; color: var(--text-muted); }

    /* ── STATUS ── */
    .status-bar {
      background: var(--surface2);
      border: 1px solid var(--border);
      border-radius: 8px;
      padding: 10px 14px;
      font-size: 0.78rem;
      color: var(--text-muted);
      display: flex;
      align-items: center;
      gap: 8px;
      margin-bottom: 1rem;
    }

    .status-dot {
      width: 8px;
      height: 8px;
      border-radius: 50%;
      background: var(--success);
      animation: pulse 2s infinite;
    }

    @keyframes pulse {
      0%, 100% { opacity: 1; }
      50% { opacity: 0.4; }
    }

    /* ── EMPTY STATE ── */
    .empty-state {
      text-align: center;
      padding: 4rem 2rem;
      color: var(--text-muted);
    }

    .empty-icon { font-size: 3rem; margin-bottom: 1rem; opacity: 0.5; }
    .empty-title { font-size: 1rem; font-weight: 600; color: var(--text); margin-bottom: 0.5rem; }
    .empty-sub { font-size: 0.82rem; }

    /* ── SCROLLBAR ── */
    ::-webkit-scrollbar { width: 5px; height: 5px; }
    ::-webkit-scrollbar-track { background: var(--bg); }
    ::-webkit-scrollbar-thumb { background: var(--border); border-radius: 3px; }

    /* ── LOADER ── */
    .loader {
      display: none;
      position: absolute;
      inset: 0;
      background: rgba(10,14,26,0.7);
      backdrop-filter: blur(4px);
      z-index: 50;
      align-items: center;
      justify-content: center;
      flex-direction: column;
      gap: 12px;
    }

    .loader.show { display: flex; }

    .spin {
      width: 36px;
      height: 36px;
      border: 3px solid var(--border);
      border-top-color: var(--accent);
      border-radius: 50%;
      animation: spin 0.8s linear infinite;
    }

    @keyframes spin { to { transform: rotate(360deg); } }

    /* ── NOTIFICATION ── */
    #notification {
      position: fixed;
      bottom: 20px;
      right: 20px;
      z-index: 999;
      display: flex;
      flex-direction: column;
      gap: 8px;
    }

    .notif {
      background: var(--surface);
      border: 1px solid var(--border);
      border-radius: 10px;
      padding: 10px 16px;
      font-size: 0.82rem;
      display: flex;
      align-items: center;
      gap: 8px;
      animation: slideIn 0.3s ease;
      min-width: 200px;
    }

    @keyframes slideIn {
      from { transform: translateX(120%); opacity: 0; }
      to { transform: translateX(0); opacity: 1; }
    }

    .notif.success { border-left: 3px solid var(--success); }
    .notif.error { border-left: 3px solid var(--danger); }
    .notif.info { border-left: 3px solid var(--accent); }

    /* ── RESPONSIVE ── */
    @media (max-width: 900px) {
      .app-container { grid-template-columns: 1fr; grid-template-rows: auto; }
      .left-panel { grid-row: auto; max-height: 50vh; }
      .right-top { min-height: 250px; }
    }
  </style>
</head>
<body>

<!-- HEADER -->
<header>
  <div class="logo">
    <div class="logo-icon">🫁</div>
    <div>
      <div class="logo-text">LungRx</div>
      <div class="logo-sub">Pulmonary Drug Molecular Analyzer</div>
    </div>
  </div>
  <div class="header-badges">
    <span class="badge badge-blue">3D Viewer</span>
    <span class="badge badge-purple">Multi-Drug</span>
    <span class="badge badge-cyan">Pharmacology</span>
  </div>
</header>

<div class="app-container">

  <!-- ========= LEFT PANEL ========= -->
  <aside class="left-panel">

    <!-- INPUT METHOD -->
    <div class="panel-section">
      <div class="section-title">Add Drug</div>
      <div class="input-tabs">
        <button class="tab-btn active" onclick="switchTab('name')">By Name</button>
        <button class="tab-btn" onclick="switchTab('smiles')">SMILES</button>
        <button class="tab-btn" onclick="switchTab('file')">Upload File</button>
      </div>

      <!-- TAB: NAME -->
      <div id="tab-name" class="tab-content active">
        <div class="input-row">
          <input type="text" id="drug-name-input" placeholder="e.g. Salbutamol, Budesonide…" onkeydown="if(event.key==='Enter') addByName()" />
          <button class="btn btn-primary btn-sm" onclick="addByName()">Add</button>
        </div>
        <div id="name-suggestions" style="font-size:0.72rem; color:var(--text-dim); margin-top:4px;"></div>
      </div>

      <!-- TAB: SMILES -->
      <div id="tab-smiles" class="tab-content">
        <div class="input-row" style="flex-direction:column; gap:6px;">
          <input type="text" id="smiles-name-input" placeholder="Drug label (optional)" />
          <textarea id="smiles-input" placeholder="Paste SMILES string here&#10;e.g. CC(CCc1ccc(O)cc1)NCC(O)c1ccc(O)c(O)c1"></textarea>
          <button class="btn btn-primary btn-full btn-sm" onclick="addBySMILES()">Visualize Molecule</button>
        </div>
      </div>

      <!-- TAB: FILE -->
      <div id="tab-file" class="tab-content">
        <div class="file-drop" id="file-drop" onclick="document.getElementById('mol-file').click()" ondragover="fileDragOver(event)" ondragleave="fileDragLeave(event)" ondrop="fileDrop(event)">
          <div class="file-icon">📂</div>
          <div>Click or drag &amp; drop</div>
          <div style="font-size:0.7rem; color:var(--text-dim); margin-top:4px;">SDF, MOL, PDB, XYZ supported</div>
        </div>
        <input type="file" id="mol-file" accept=".sdf,.mol,.pdb,.xyz,.mol2" style="display:none" onchange="handleFileUpload(event)" />
      </div>
    </div>

    <!-- PRESETS -->
    <div class="panel-section">
      <div class="section-title">Common Lung Drugs</div>
      <div class="preset-grid">
        <button class="preset-btn" onclick="loadPreset('salbutamol')">
          <span class="preset-name">Salbutamol</span>
          <span class="preset-cat">β2-Agonist</span>
        </button>
        <button class="preset-btn" onclick="loadPreset('budesonide')">
          <span class="preset-name">Budesonide</span>
          <span class="preset-cat">Corticosteroid</span>
        </button>
        <button class="preset-btn" onclick="loadPreset('tiotropium')">
          <span class="preset-name">Tiotropium</span>
          <span class="preset-cat">Anticholinergic</span>
        </button>
        <button class="preset-btn" onclick="loadPreset('montelukast')">
          <span class="preset-name">Montelukast</span>
          <span class="preset-cat">LTRA</span>
        </button>
        <button class="preset-btn" onclick="loadPreset('theophylline')">
          <span class="preset-name">Theophylline</span>
          <span class="preset-cat">Xanthine</span>
        </button>
        <button class="preset-btn" onclick="loadPreset('sildenafil')">
          <span class="preset-name">Sildenafil</span>
          <span class="preset-cat">PDE5 Inhib.</span>
        </button>
        <button class="preset-btn" onclick="loadPreset('nintedanib')">
          <span class="preset-name">Nintedanib</span>
          <span class="preset-cat">IPF / Antifibrotic</span>
        </button>
        <button class="preset-btn" onclick="loadPreset('ivacaftor')">
          <span class="preset-name">Ivacaftor</span>
          <span class="preset-cat">CFTR Modulator</span>
        </button>
      </div>
    </div>

    <!-- DRUG QUEUE -->
    <div class="panel-section" style="flex:1;">
      <div class="section-title" style="justify-content:space-between; display:flex; align-items:center; width:100%;">
        <span style="display:flex; align-items:center; gap:6px;"><span style="display:inline-block;width:3px;height:12px;background:var(--accent);border-radius:2px;"></span>Drug Queue</span>
        <button class="btn btn-sm btn-danger" onclick="clearAll()" style="padding:3px 8px; font-size:0.7rem;">Clear All</button>
      </div>
      <div class="drug-list" id="drug-list">
        <div class="empty-state" style="padding:1.5rem;">
          <div class="empty-icon" style="font-size:2rem;">💊</div>
          <div style="font-size:0.82rem; color:var(--text-muted);">Add drugs above or click a preset</div>
        </div>
      </div>
    </div>

  </aside>

  <!-- ========= RIGHT PANEL ========= -->
  <div style="display:flex; flex-direction:column; overflow:hidden;">

    <!-- 3D VIEWER -->
    <div class="right-top" style="flex:0 0 340px; position:relative;">
      <div id="mol-viewer"></div>

      <div class="viewer-overlay">
        <div class="viewer-badge" id="mol-label">No molecule selected</div>
        <div class="viewer-badge" id="mol-formula" style="font-family:'JetBrains Mono',monospace; font-size:0.7rem;">-</div>
      </div>

      <div class="viewer-controls">
        <button class="ctrl-btn" onclick="resetView()" title="Reset view">⟳</button>
        <button class="ctrl-btn" onclick="toggleSpin()" id="spin-btn" title="Toggle rotation">▶</button>
        <button class="ctrl-btn" onclick="toggleFullscreen()" title="Fullscreen">⛶</button>
      </div>

      <div class="viewer-footer">
        <span class="style-chip active" onclick="setMolStyle('stick')" id="chip-stick">Stick</span>
        <span class="style-chip" onclick="setMolStyle('sphere')" id="chip-sphere">Sphere</span>
        <span class="style-chip" onclick="setMolStyle('cartoon')" id="chip-cartoon">Cartoon</span>
        <span class="style-chip" onclick="setMolStyle('surface')" id="chip-surface">Surface</span>
        <span class="style-chip" onclick="setMolStyle('line')" id="chip-line">Line</span>
      </div>

      <div class="loader" id="viewer-loader">
        <div class="spin"></div>
        <span style="font-size:0.8rem; color:var(--text-muted);">Loading molecule…</span>
      </div>
    </div>

    <!-- ANALYSIS TABS -->
    <div class="right-bottom" style="flex:1;">
      <div class="analysis-tabs">
        <button class="atab active" onclick="switchAnalysis('overview')">Overview</button>
        <button class="atab" onclick="switchAnalysis('lung')">Lung Effects</button>
        <button class="atab" onclick="switchAnalysis('receptors')">Receptors</button>
        <button class="atab" onclick="switchAnalysis('pathway')">Pathway</button>
        <button class="atab" onclick="switchAnalysis('compare')">Compare</button>
        <button class="atab" onclick="switchAnalysis('adr')">Side Effects</button>
      </div>

      <!-- OVERVIEW -->
      <div id="analysis-overview" class="analysis-content active">
        <div id="overview-empty" class="empty-state">
          <div class="empty-icon">🔬</div>
          <div class="empty-title">Select a drug to analyze</div>
          <div class="empty-sub">Add a drug from the queue or click a preset</div>
        </div>
        <div id="overview-content" style="display:none;">
          <div class="status-bar">
            <div class="status-dot"></div>
            <span id="status-text">Ready — molecule loaded</span>
          </div>
          <div class="info-grid" id="prop-grid"></div>
          <div style="margin-bottom:1.2rem;">
            <div class="section-title">Mechanism of Action</div>
            <div id="moa-text" style="font-size:0.85rem; line-height:1.6; color:var(--text); background:var(--surface); border:1px solid var(--border); border-radius:10px; padding:14px;"></div>
          </div>
          <div>
            <div class="section-title">Indications</div>
            <div class="tags" id="indications-tags"></div>
          </div>
        </div>
      </div>

      <!-- LUNG EFFECTS -->
      <div id="analysis-lung" class="analysis-content">
        <div id="lung-empty" class="empty-state">
          <div class="empty-icon">🫁</div>
          <div class="empty-title">Select a drug to view lung effects</div>
        </div>
        <div id="lung-content" style="display:none;">
          <div style="margin-bottom:1.2rem;">
            <div class="section-title" style="margin-bottom:0.8rem;">Pulmonary Effect Profile</div>
            <div id="lung-effects-bars"></div>
          </div>
          <div class="chart-wrap">
            <div class="section-title" style="margin-bottom:0.8rem;">Effect Radar</div>
            <canvas id="radarChart"></canvas>
          </div>
          <div>
            <div class="section-title" style="margin-bottom:0.8rem;">Lung Compartment Activity</div>
            <div id="lung-compartments"></div>
          </div>
        </div>
      </div>

      <!-- RECEPTORS -->
      <div id="analysis-receptors" class="analysis-content">
        <div id="receptors-empty" class="empty-state">
          <div class="empty-icon">🎯</div>
          <div class="empty-title">Select a drug to view receptor binding</div>
        </div>
        <div id="receptors-content" style="display:none;">
          <table class="receptor-table" id="receptor-table">
            <thead>
              <tr>
                <th>Receptor / Target</th>
                <th>Action</th>
                <th>Affinity (Ki nM)</th>
                <th>Selectivity</th>
                <th>Lung Relevance</th>
              </tr>
            </thead>
            <tbody id="receptor-body"></tbody>
          </table>
        </div>
      </div>

      <!-- PATHWAY -->
      <div id="analysis-pathway" class="analysis-content">
        <div id="pathway-empty" class="empty-state">
          <div class="empty-icon">🔗</div>
          <div class="empty-title">Select a drug to view signaling pathway</div>
        </div>
        <div id="pathway-content" style="display:none;">
          <div class="pathway-map" id="pathway-map"></div>
          <div class="chart-wrap" style="margin-top:1rem;">
            <div class="section-title" style="margin-bottom:0.8rem;">Downstream Effects Timeline</div>
            <canvas id="timelineChart"></canvas>
          </div>
        </div>
      </div>

      <!-- COMPARE -->
      <div id="analysis-compare" class="analysis-content">
        <div id="compare-content">
          <div style="margin-bottom:1rem;">
            <div class="section-title">Multi-Drug Comparison</div>
            <div id="compare-empty" class="empty-state" style="padding:2rem;">
              <div class="empty-icon" style="font-size:2rem;">⚖️</div>
              <div class="empty-title">Add at least 2 drugs to compare</div>
            </div>
            <div id="compare-table-wrap" style="display:none;"></div>
          </div>
          <div class="chart-wrap" id="compare-chart-wrap" style="display:none;">
            <div class="section-title" style="margin-bottom:0.8rem;">Comparative Effect Profile</div>
            <canvas id="compareChart"></canvas>
          </div>
        </div>
      </div>

      <!-- SIDE EFFECTS -->
      <div id="analysis-adr" class="analysis-content">
        <div id="adr-empty" class="empty-state">
          <div class="empty-icon">⚠️</div>
          <div class="empty-title">Select a drug to view adverse effects</div>
        </div>
        <div id="adr-content" style="display:none;">
          <div id="adr-list"></div>
        </div>
      </div>

    </div>
  </div>
</div>

<!-- NOTIFICATION -->
<div id="notification"></div>

<!-- ============================================================
     JAVASCRIPT
     ============================================================ -->
<script>

// ── DRUG DATABASE ──────────────────────────────────────────────────────────────
const DRUG_DB = {
  salbutamol: {
    name: "Salbutamol (Albuterol)",
    class: "β2-Agonist",
    classColor: "#3b82f6",
    formula: "C₁₃H₂₁NO₃",
    mw: "239.31 g/mol",
    smiles: "CC(CCc1ccc(O)cc1)NCC(O)c1ccc(O)c(O)c1",
    pubchemCID: 2083,
    moa: "Salbutamol selectively activates β2-adrenergic receptors on bronchial smooth muscle, activating adenylyl cyclase and increasing intracellular cAMP. Elevated cAMP activates protein kinase A (PKA), which phosphorylates myosin light-chain kinase (MLCK), reducing its activity and causing smooth muscle relaxation. Onset is within 5 minutes; duration 4-6 hours.",
    indications: ["Acute Asthma", "COPD Bronchospasm", "Exercise-Induced Bronchoconstriction", "Hyperkalemia (off-label)"],
    properties: { halfLife: "3–8 hr", onset: "5 min", route: "Inhaled / IV", bioavail: "~90% inh.", mw: "239.31" },
    lungEffects: {
      bronchodilation: 95,
      antiInflammatory: 15,
      mucusClearance: 40,
      vasoconstriction: 5,
      surfactantProd: 20,
      fibrosis: 0
    },
    receptors: [
      { name: "β2-Adrenergic (ADRB2)", action: "Agonist", ki: "1.4", sel: "High", relevance: "Primary bronchodilation" },
      { name: "β1-Adrenergic (ADRB1)", action: "Partial Agonist", ki: "180", sel: "Low", relevance: "Cardiac side effect" },
      { name: "β3-Adrenergic (ADRB3)", action: "Weak Agonist", ki: "2100", sel: "Very Low", relevance: "Negligible" },
    ],
    pathway: [
      { label: "Salbutamol", type: "active" },
      { label: "β2-AR Activation", type: "active" },
      { label: "Gs Protein", type: "active" },
      { label: "Adenylyl Cyclase ↑", type: "active" },
      { label: "cAMP ↑", type: "active" },
      { label: "PKA Activation", type: "active" },
      { label: "MLCK Phosphorylation", type: "inhibited" },
      { label: "Bronchodilation ✓", type: "active" },
    ],
    adrs: [
      { name: "Tremor", sev: "med", icon: "🤝", desc: "Skeletal muscle tremor from β2 stimulation in peripheral muscles" },
      { name: "Tachycardia", sev: "med", icon: "💓", desc: "Reflex tachycardia due to vasodilation; direct β1 stimulation" },
      { name: "Hypokalemia", sev: "med", icon: "⚡", desc: "β2 stimulation drives K⁺ into cells via Na/K ATPase; risk at high doses" },
      { name: "Tolerance", sev: "low", icon: "📉", desc: "β2-receptor downregulation with chronic overuse" },
      { name: "Headache", sev: "low", icon: "🤕", desc: "Vasodilatory headache; usually mild" },
    ],
    compartments: [
      { name: "Large Airways", activity: 90, color: "#3b82f6" },
      { name: "Small Airways", activity: 85, color: "#6366f1" },
      { name: "Alveoli", activity: 30, color: "#8b5cf6" },
      { name: "Pulmonary Vasculature", activity: 25, color: "#06b6d4" },
    ]
  },

  budesonide: {
    name: "Budesonide",
    class: "Corticosteroid",
    classColor: "#10b981",
    formula: "C₂₅H₃₄O₆",
    mw: "430.53 g/mol",
    smiles: "O=C1OC(CC1)C(=O)OCC[C@@H]1[C@@H]2CC[C@H]3[C@@H](CCC(=O)[C@]3(C)[C@H]2[C@@H](O)C[C@@H]1O)C",
    pubchemCID: 5281004,
    moa: "Budesonide binds to intracellular glucocorticoid receptors (GR-α), forming a complex that translocates to the nucleus. It activates anti-inflammatory gene transcription (transactivation: lipocortin-1, β2-AR) and represses pro-inflammatory genes (transrepression: NF-κB, AP-1), reducing cytokine production (IL-4, IL-5, IL-13) and eosinophil recruitment. Results in reduced airway edema, mucus hypersecretion, and bronchial hyperresponsiveness.",
    indications: ["Asthma Prophylaxis", "COPD (with LABAs)", "Eosinophilic Airway Disease", "Nasal Polyposis"],
    properties: { halfLife: "2–3 hr", onset: "Hours-Days", route: "Inhaled / Oral / Nasal", bioavail: "~20% inh.", mw: "430.53" },
    lungEffects: {
      bronchodilation: 10,
      antiInflammatory: 95,
      mucusClearance: 60,
      vasoconstriction: 20,
      surfactantProd: 35,
      fibrosis: 5
    },
    receptors: [
      { name: "Glucocorticoid Receptor α (GR-α)", action: "Agonist", ki: "0.43", sel: "High", relevance: "Primary anti-inflammatory" },
      { name: "Mineralocorticoid Receptor (MR)", action: "Partial Agonist", ki: "980", sel: "Low", relevance: "Minimal" },
      { name: "NF-κB (Indirect)", action: "Inhibitor", ki: "-", sel: "High", relevance: "Cytokine suppression" },
    ],
    pathway: [
      { label: "Budesonide", type: "active" },
      { label: "GR-α Binding", type: "active" },
      { label: "Nuclear Translocation", type: "active" },
      { label: "NF-κB Inhibition", type: "inhibited" },
      { label: "Cytokine ↓ (IL-4,5,13)", type: "inhibited" },
      { label: "Eosinophil Recruitment ↓", type: "inhibited" },
      { label: "Airway Inflammation ↓ ✓", type: "active" },
    ],
    adrs: [
      { name: "Oral Candidiasis", sev: "med", icon: "🦠", desc: "Local immunosuppression in oropharynx; prevented by spacer and rinsing mouth" },
      { name: "Dysphonia", sev: "low", icon: "🗣️", desc: "Steroid myopathy of laryngeal muscles; reversible" },
      { name: "Adrenal Suppression", sev: "high", icon: "⚠️", desc: "At high inhaled doses; systemic absorption via swallowed fraction" },
      { name: "Bone Density Loss", sev: "med", icon: "🦴", desc: "Long-term high-dose use; less than systemic steroids" },
      { name: "Growth Suppression", sev: "med", icon: "📏", desc: "In children at high doses; monitor height annually" },
    ],
    compartments: [
      { name: "Large Airways", activity: 70, color: "#10b981" },
      { name: "Small Airways", activity: 65, color: "#059669" },
      { name: "Alveoli", activity: 55, color: "#0d9488" },
      { name: "Pulmonary Vasculature", activity: 15, color: "#0891b2" },
    ]
  },

  tiotropium: {
    name: "Tiotropium",
    class: "LAMA",
    classColor: "#f59e0b",
    formula: "C₁₉H₂₂NO₄S₂⁺",
    mw: "392.51 g/mol",
    smiles: "[O-][N+]1(CC(CC1(OC(=O)c2ccs(=O)c2)c2ccs(=O)c2)OC)C",
    pubchemCID: 5487426,
    moa: "Tiotropium is a long-acting muscarinic antagonist (LAMA) that competitively blocks M3 muscarinic receptors on airway smooth muscle and submucosal glands. Acetylcholine-induced bronchoconstriction and mucus secretion are inhibited. Its kinetic selectivity for M3 over M2 receptors (dissociates 10x slower from M3) provides sustained bronchodilation lasting >24 hours, making it suitable for once-daily dosing.",
    indications: ["COPD Maintenance", "Asthma (add-on)", "Chronic Bronchitis", "Emphysema"],
    properties: { halfLife: "5–6 days", onset: "30 min", route: "Inhaled (DPI/SMI)", bioavail: "~20% inh.", mw: "392.51" },
    lungEffects: {
      bronchodilation: 85,
      antiInflammatory: 20,
      mucusClearance: 55,
      vasoconstriction: 5,
      surfactantProd: 10,
      fibrosis: 0
    },
    receptors: [
      { name: "M3 Muscarinic (CHRM3)", action: "Antagonist", ki: "0.022", sel: "High", relevance: "Primary bronchodilation" },
      { name: "M2 Muscarinic (CHRM2)", action: "Antagonist", ki: "0.030", sel: "Med", relevance: "Kinetic selectivity for M3" },
      { name: "M1 Muscarinic (CHRM1)", action: "Antagonist", ki: "0.019", sel: "Low selectivity", relevance: "Salivary gland side effect" },
    ],
    pathway: [
      { label: "Tiotropium", type: "active" },
      { label: "M3-AChR Blockade", type: "inhibited" },
      { label: "PLC Inhibition", type: "inhibited" },
      { label: "IP3 / DAG ↓", type: "inhibited" },
      { label: "Intracellular Ca²⁺ ↓", type: "inhibited" },
      { label: "MLCK Activity ↓", type: "inhibited" },
      { label: "Bronchodilation + Mucus ↓ ✓", type: "active" },
    ],
    adrs: [
      { name: "Dry Mouth", sev: "med", icon: "😮", desc: "M1/M3 blockade in salivary glands; most common adverse effect" },
      { name: "Urinary Retention", sev: "med", icon: "🚽", desc: "Anticholinergic effect; caution in BPH" },
      { name: "Constipation", sev: "low", icon: "🪬", desc: "GI motility reduction from anticholinergic activity" },
      { name: "Narrow-Angle Glaucoma", sev: "high", icon: "👁️", desc: "Risk if nebulised form gets in eyes; avoid in uncontrolled narrow-angle glaucoma" },
      { name: "Tachycardia", sev: "low", icon: "💓", desc: "Occasionally reported; may relate to M2 blockade" },
    ],
    compartments: [
      { name: "Large Airways", activity: 80, color: "#f59e0b" },
      { name: "Small Airways", activity: 70, color: "#d97706" },
      { name: "Alveoli", activity: 20, color: "#b45309" },
      { name: "Pulmonary Vasculature", activity: 10, color: "#92400e" },
    ]
  },

  montelukast: {
    name: "Montelukast",
    class: "LTRA",
    classColor: "#ec4899",
    formula: "C₃₅H₃₆ClNO₃S",
    mw: "586.14 g/mol",
    smiles: "OC(=O)CC(CC1(CC(CC1(C)C)c1cc2ccc(Cl)cc2cc1)c1ccc(c(c1)SCc1cc(C=C)ccc1F)CC(=O)O)C",
    pubchemCID: 5281040,
    moa: "Montelukast selectively antagonizes the cysteinyl leukotriene receptor 1 (CysLT1), blocking the action of leukotrienes LTC4, LTD4, and LTE4 produced by mast cells and eosinophils. These leukotrienes mediate airway edema, bronchoconstriction, mucus secretion, and eosinophil chemotaxis. By blocking CysLT1, montelukast reduces bronchoconstriction, airway hyperresponsiveness, and allergic inflammation.",
    indications: ["Asthma Prophylaxis", "Allergic Rhinitis", "Exercise-Induced Bronchoconstriction", "Aspirin-Exacerbated Respiratory Disease"],
    properties: { halfLife: "2.7–5.5 hr", onset: "Hours", route: "Oral", bioavail: "~64%", mw: "586.14" },
    lungEffects: {
      bronchodilation: 55,
      antiInflammatory: 70,
      mucusClearance: 45,
      vasoconstriction: 10,
      surfactantProd: 15,
      fibrosis: 0
    },
    receptors: [
      { name: "CysLT1 (CYSLTR1)", action: "Antagonist", ki: "10", sel: "High", relevance: "Primary anti-leukotriene" },
      { name: "CysLT2 (CYSLTR2)", action: "Weak Antagonist", ki: "5000", sel: "Very Low", relevance: "Minimal" },
      { name: "PPARγ", action: "Partial Agonist", ki: "-", sel: "Unknown", relevance: "Possible add-on anti-inflam" },
    ],
    pathway: [
      { label: "Montelukast", type: "active" },
      { label: "CysLT1 Blockade", type: "inhibited" },
      { label: "LTD4 / LTE4 Signaling ↓", type: "inhibited" },
      { label: "Gq Protein Inhibition", type: "inhibited" },
      { label: "Eosinophil Chemotaxis ↓", type: "inhibited" },
      { label: "Bronchoconstriction ↓ ✓", type: "active" },
    ],
    adrs: [
      { name: "Neuropsychiatric Effects", sev: "high", icon: "🧠", desc: "FDA black box: depression, anxiety, suicidality; mechanism unclear (CNS CysLT receptors)" },
      { name: "Headache", sev: "low", icon: "🤕", desc: "Common; usually mild and resolves with continued use" },
      { name: "GI Disturbance", sev: "low", icon: "🤢", desc: "Nausea, diarrhea; generally mild" },
      { name: "Hepatotoxicity", sev: "med", icon: "🫀", desc: "Rare but reported; monitor LFTs in susceptible patients" },
    ],
    compartments: [
      { name: "Large Airways", activity: 60, color: "#ec4899" },
      { name: "Small Airways", activity: 65, color: "#be185d" },
      { name: "Alveoli", activity: 40, color: "#9d174d" },
      { name: "Pulmonary Vasculature", activity: 30, color: "#db2777" },
    ]
  },

  theophylline: {
    name: "Theophylline",
    class: "Xanthine / PDE Inhibitor",
    classColor: "#8b5cf6",
    formula: "C₇H₈N₄O₂",
    mw: "180.16 g/mol",
    smiles: "Cn1c(=O)c2[nH]cnc2n(c1=O)C",
    pubchemCID: 2153,
    moa: "Theophylline inhibits phosphodiesterase (PDE) enzymes (primarily PDE3 and PDE4), preventing the breakdown of cyclic AMP and cyclic GMP. Elevated cAMP leads to bronchial smooth muscle relaxation. It also antagonizes adenosine receptors (A1, A2A), blocks A2B receptors that mediate mast cell degranulation, and has weak anti-inflammatory properties through HAT inhibition and histone deacetylase (HDAC2) activation. At therapeutic concentrations (10–20 mg/L) it provides sustained bronchodilation and improves respiratory muscle function.",
    indications: ["COPD Maintenance", "Severe Asthma (add-on)", "Neonatal Apnea", "Cor Pulmonale"],
    properties: { halfLife: "8–9 hr", onset: "30–60 min", route: "Oral / IV", bioavail: "~96%", mw: "180.16" },
    lungEffects: {
      bronchodilation: 75,
      antiInflammatory: 40,
      mucusClearance: 50,
      vasoconstriction: 5,
      surfactantProd: 10,
      fibrosis: 0
    },
    receptors: [
      { name: "PDE3 / PDE4", action: "Inhibitor", ki: "50–100 µM", sel: "Low", relevance: "Bronchodilation via cAMP ↑" },
      { name: "Adenosine A1 (ADORA1)", action: "Antagonist", ki: "5–40 µM", sel: "Low", relevance: "CNS stimulation, bronchodilation" },
      { name: "Adenosine A2A (ADORA2A)", action: "Antagonist", ki: "3–10 µM", sel: "Low", relevance: "Anti-inflammatory" },
      { name: "HDAC2 (Indirect)", action: "Activator", ki: "-", sel: "-", relevance: "Steroid sensitivity" },
    ],
    pathway: [
      { label: "Theophylline", type: "active" },
      { label: "PDE3/4 Inhibition", type: "inhibited" },
      { label: "cAMP / cGMP ↑", type: "active" },
      { label: "PKA Activation", type: "active" },
      { label: "Adenosine-R Blockade", type: "inhibited" },
      { label: "Smooth Muscle Relaxation ✓", type: "active" },
    ],
    adrs: [
      { name: "Tachyarrhythmia", sev: "high", icon: "💓", desc: "At serum levels >20 mg/L; can be life-threatening; narrow therapeutic window" },
      { name: "Seizures", sev: "high", icon: "⚡", desc: "Toxic serum levels; due to adenosine A1 receptor antagonism in CNS" },
      { name: "Nausea / Vomiting", sev: "med", icon: "🤢", desc: "Common at initiation; stimulates CTZ via adenosine blockade" },
      { name: "Insomnia", sev: "med", icon: "😴", desc: "CNS stimulant effect; administer morning dose" },
      { name: "Drug Interactions", sev: "high", icon: "💊", desc: "CYP1A2 substrate; levels raised by ciprofloxacin, erythromycin; lowered by smoking" },
    ],
    compartments: [
      { name: "Large Airways", activity: 75, color: "#8b5cf6" },
      { name: "Small Airways", activity: 70, color: "#7c3aed" },
      { name: "Alveoli", activity: 40, color: "#6d28d9" },
      { name: "Pulmonary Vasculature", activity: 30, color: "#5b21b6" },
    ]
  },

  sildenafil: {
    name: "Sildenafil",
    class: "PDE5 Inhibitor",
    classColor: "#06b6d4",
    formula: "C₂₂H₃₀N₆O₄S",
    mw: "474.58 g/mol",
    smiles: "CCCC1=NN(C)C2=C1N=C(NC3=CC(=CC=C3)S(=O)(=O)N4CCN(CC4)C)NC2=O",
    pubchemCID: 135398744,
    moa: "In the pulmonary vasculature, endothelial NO stimulates guanylate cyclase → cGMP → PKG → smooth muscle relaxation and vasodilation. Sildenafil selectively inhibits PDE5 (predominant in pulmonary vasculature), preventing cGMP degradation and sustaining vasodilation. This reduces pulmonary vascular resistance (PVR) and right ventricular afterload in pulmonary arterial hypertension (PAH).",
    indications: ["Pulmonary Arterial Hypertension (PAH)", "Erectile Dysfunction (off-label in this context)"],
    properties: { halfLife: "3–5 hr", onset: "30–60 min", route: "Oral / IV", bioavail: "~41%", mw: "474.58" },
    lungEffects: {
      bronchodilation: 20,
      antiInflammatory: 25,
      mucusClearance: 10,
      vasoconstriction: -80,
      surfactantProd: 10,
      fibrosis: 5
    },
    receptors: [
      { name: "PDE5A (PDE5A)", action: "Inhibitor", ki: "3.9", sel: "High", relevance: "PAH vasodilation" },
      { name: "PDE6 (Retinal)", action: "Inhibitor", ki: "28", sel: "Moderate", relevance: "Visual side effects" },
      { name: "PDE1", action: "Weak Inhibitor", ki: "280", sel: "Low", relevance: "Minimal" },
    ],
    pathway: [
      { label: "Sildenafil", type: "active" },
      { label: "PDE5 Inhibition", type: "inhibited" },
      { label: "cGMP ↑", type: "active" },
      { label: "PKG Activation", type: "active" },
      { label: "MLCK Inhibition", type: "inhibited" },
      { label: "Pulmonary Vasodilatation ✓", type: "active" },
    ],
    adrs: [
      { name: "Systemic Hypotension", sev: "high", icon: "📉", desc: "Especially with nitrates (absolute CI); marked BP drop" },
      { name: "Visual Disturbance", sev: "med", icon: "👁️", desc: "Blue-tinted vision / photophobia; PDE6 inhibition in retinal cells" },
      { name: "Headache / Flushing", sev: "low", icon: "🤕", desc: "Vasodilatory; very common; mild" },
      { name: "Nasal Congestion", sev: "low", icon: "🤧", desc: "Vasodilation of nasal mucosa" },
      { name: "Priapism", sev: "med", icon: "⚠️", desc: "Rare; prolonged penile erection requiring urgent treatment" },
    ],
    compartments: [
      { name: "Large Airways", activity: 15, color: "#06b6d4" },
      { name: "Small Airways", activity: 20, color: "#0891b2" },
      { name: "Alveoli", activity: 30, color: "#0e7490" },
      { name: "Pulmonary Vasculature", activity: 95, color: "#155e75" },
    ]
  },

  nintedanib: {
    name: "Nintedanib",
    class: "Tyrosine Kinase Inhibitor",
    classColor: "#f97316",
    formula: "C₃₁H₃₃N₃O₄",
    mw: "539.62 g/mol",
    smiles: "O=C(/C=C/c1ccc(OCC)cc1)Nc1ccc(N2C(=O)c3cc(NC(=O)OC)ccc3N(C)C2=O)cc1",
    pubchemCID: 135398513,
    moa: "Nintedanib is an angiokinase inhibitor that targets multiple receptor tyrosine kinases involved in pulmonary fibrosis: VEGFR1-3, FGFR1-4, PDGFR-α/β, and Src/Lck/Lyn/FLT3. By inhibiting these receptors, it blocks fibroblast proliferation, migration, and differentiation into myofibroblasts, the cells responsible for excess collagen deposition in IPF. It slows the rate of lung function decline (FVC reduction) by ~50%.",
    indications: ["Idiopathic Pulmonary Fibrosis (IPF)", "Systemic Sclerosis ILD", "Progressive Fibrosing ILD"],
    properties: { halfLife: "10–15 hr", onset: "Weeks", route: "Oral", bioavail: "~5% (high first-pass)", mw: "539.62" },
    lungEffects: {
      bronchodilation: 5,
      antiInflammatory: 40,
      mucusClearance: 10,
      vasoconstriction: 25,
      surfactantProd: 20,
      fibrosis: -85
    },
    receptors: [
      { name: "VEGFR1-3", action: "Inhibitor", ki: "13–34", sel: "High", relevance: "Anti-angiogenic, anti-fibrotic" },
      { name: "FGFR1-4", action: "Inhibitor", ki: "13–69", sel: "High", relevance: "Fibroblast proliferation ↓" },
      { name: "PDGFRα/β", action: "Inhibitor", ki: "59–65", sel: "High", relevance: "Myofibroblast differentiation ↓" },
      { name: "Src Family Kinases", action: "Inhibitor", ki: "~16", sel: "Moderate", relevance: "Inflammatory signaling ↓" },
    ],
    pathway: [
      { label: "Nintedanib", type: "active" },
      { label: "VEGFR / FGFR / PDGFR Inhibition", type: "inhibited" },
      { label: "PI3K/AKT Pathway ↓", type: "inhibited" },
      { label: "Fibroblast Proliferation ↓", type: "inhibited" },
      { label: "TGF-β Signaling ↓", type: "inhibited" },
      { label: "Collagen Deposition ↓ ✓", type: "active" },
    ],
    adrs: [
      { name: "Diarrhea", sev: "high", icon: "🚽", desc: "Most common; occurs in ~60% of patients; managed with loperamide" },
      { name: "Hepatotoxicity", sev: "high", icon: "🫀", desc: "Elevated ALT/AST; monitor LFTs monthly for first 3 months" },
      { name: "Nausea / Vomiting", sev: "med", icon: "🤢", desc: "Common; take with food to reduce GI side effects" },
      { name: "Hypertension", sev: "med", icon: "🩺", desc: "VEGFR inhibition reduces NO production in vessels" },
      { name: "Bleeding Risk", sev: "med", icon: "🩸", desc: "Anti-VEGFR activity; caution with anticoagulants" },
    ],
    compartments: [
      { name: "Large Airways", activity: 10, color: "#f97316" },
      { name: "Small Airways", activity: 20, color: "#ea580c" },
      { name: "Alveoli", activity: 85, color: "#c2410c" },
      { name: "Pulmonary Vasculature", activity: 70, color: "#9a3412" },
    ]
  },

  ivacaftor: {
    name: "Ivacaftor (Kalydeco)",
    class: "CFTR Potentiator",
    classColor: "#a855f7",
    formula: "C₂₄H₂₈F₃NO₃S",
    mw: "392.45 g/mol",
    smiles: "CCC1=CC2=CC(=CC(=C2O1)C(=O)NC3=CC(=C(C(=C3)C(F)(F)F)OC)OC)CC(C)C",
    pubchemCID: 16220172,
    moa: "Ivacaftor potentiates (increases gate-open probability of) mutant CFTR channels carrying specific mutations (notably G551D and others). CFTR is a chloride channel critical for airway surface liquid homeostasis. In CF, defective CFTR leads to thick mucus, impaired mucociliary clearance, and chronic infection. Ivacaftor binds to CFTR and prolongs channel open time, restoring chloride transport and improving mucociliary function, lung function, and quality of life.",
    indications: ["Cystic Fibrosis (G551D mutation)", "Cystic Fibrosis (other gating mutations)", "CF (combination therapies)"],
    properties: { halfLife: "12 hr", onset: "2–4 weeks (FEV1 improvement)", route: "Oral", bioavail: "~?% food-dependent", mw: "392.45" },
    lungEffects: {
      bronchodilation: 30,
      antiInflammatory: 35,
      mucusClearance: 90,
      vasoconstriction: 0,
      surfactantProd: 20,
      fibrosis: 10
    },
    receptors: [
      { name: "CFTR (ABCC7) Channel", action: "Potentiator", ki: "~100 nM", sel: "High", relevance: "Restores Cl⁻ secretion" },
      { name: "ENaC (Indirect)", action: "Modulator", ki: "-", sel: "Indirect", relevance: "Restored ASL thickness" },
    ],
    pathway: [
      { label: "Ivacaftor", type: "active" },
      { label: "CFTR Channel Potentiation", type: "active" },
      { label: "Cl⁻ / HCO₃⁻ Secretion ↑", type: "active" },
      { label: "Airway Surface Liquid ↑", type: "active" },
      { label: "Mucociliary Clearance ↑", type: "active" },
      { label: "Bacterial Clearance ↑ ✓", type: "active" },
    ],
    adrs: [
      { name: "Liver Enzyme Elevation", sev: "med", icon: "🫀", desc: "Hepatotoxicity; monitor every 3 months in first year" },
      { name: "Rash", sev: "low", icon: "🔴", desc: "Non-specific rash; generally mild; discontinue if severe" },
      { name: "Cataracts", sev: "med", icon: "👁️", desc: "Non-congenital lens opacities in pediatric patients; ophthalmologic exams recommended" },
      { name: "Headache", sev: "low", icon: "🤕", desc: "Common; generally mild" },
    ],
    compartments: [
      { name: "Large Airways", activity: 85, color: "#a855f7" },
      { name: "Small Airways", activity: 90, color: "#9333ea" },
      { name: "Alveoli", activity: 70, color: "#7c3aed" },
      { name: "Pulmonary Vasculature", activity: 10, color: "#6d28d9" },
    ]
  }
};

// ── STATE ──────────────────────────────────────────────────────────────────────
const DRUG_COLORS = ['#3b82f6','#10b981','#f59e0b','#ec4899','#8b5cf6','#06b6d4','#f97316','#ef4444'];
let queue = [];       // [{id, name, smiles, dbKey, data, color}]
let activeDrug = null;
let viewer = null;
let currentStyle = 'stick';
let spinning = false;
let radarChart = null, timelineChart = null, compareChart = null;

// ── 3DMOL VIEWER INIT ─────────────────────────────────────────────────────────
window.addEventListener('load', () => {
  viewer = $3Dmol.createViewer(document.getElementById('mol-viewer'), {
    backgroundColor: '#0a0e1a',
    antialias: true
  });
  viewer.render();
});

// ── TAB SWITCHING ─────────────────────────────────────────────────────────────
function switchTab(tab) {
  document.querySelectorAll('.tab-btn').forEach((b,i) => {
    const tabs = ['name','smiles','file'];
    b.classList.toggle('active', tabs[i] === tab);
  });
  document.querySelectorAll('.tab-content').forEach(t => t.classList.remove('active'));
  document.getElementById('tab-' + tab).classList.add('active');
}

function switchAnalysis(tab) {
  document.querySelectorAll('.atab').forEach(b => b.classList.remove('active'));
  event.target.classList.add('active');
  document.querySelectorAll('.analysis-content').forEach(t => t.classList.remove('active'));
  document.getElementById('analysis-' + tab).classList.add('active');
  if (tab === 'compare') renderComparison();
  if (tab === 'lung' && activeDrug) renderLungChart(activeDrug);
  if (tab === 'pathway' && activeDrug) renderTimeline(activeDrug);
}

// ── ADD DRUG BY NAME ──────────────────────────────────────────────────────────
function addByName() {
  const input = document.getElementById('drug-name-input').value.trim();
  if (!input) return;

  // Check internal DB first
  const key = Object.keys(DRUG_DB).find(k =>
    DRUG_DB[k].name.toLowerCase().includes(input.toLowerCase()) ||
    k.toLowerCase().includes(input.toLowerCase())
  );

  if (key) {
    addFromDB(key);
    document.getElementById('drug-name-input').value = '';
  } else {
    // Try PubChem
    fetchFromPubChem(input);
    document.getElementById('drug-name-input').value = '';
  }
}

function addFromDB(key) {
  const data = DRUG_DB[key];
  if (queue.find(d => d.dbKey === key)) {
    notify('Already in queue', 'info');
    return;
  }
  const drug = {
    id: Date.now(),
    name: data.name,
    smiles: data.smiles,
    dbKey: key,
    data: data,
    color: DRUG_COLORS[queue.length % DRUG_COLORS.length]
  };
  queue.push(drug);
  renderDrugList();
  selectDrug(drug);
  notify(`${data.name} added`, 'success');
}

// ── PUBCHEM FETCH ─────────────────────────────────────────────────────────────
async function fetchFromPubChem(name) {
  showLoader(true);
  try {
    const url = `https://pubchem.ncbi.nlm.nih.gov/rest/pug/compound/name/${encodeURIComponent(name)}/property/IUPACName,MolecularFormula,MolecularWeight,IsomericSMILES,ExactMass/JSON`;
    const res = await fetch(url);
    if (!res.ok) throw new Error('Not found');
    const json = await res.json();
    const props = json.PropertyTable.Properties[0];

    const drug = {
      id: Date.now(),
      name: name.charAt(0).toUpperCase() + name.slice(1),
      smiles: props.IsomericSMILES,
      dbKey: null,
      color: DRUG_COLORS[queue.length % DRUG_COLORS.length],
      data: {
        name: props.IUPACName || name,
        class: "External Drug",
        classColor: "#64748b",
        formula: props.MolecularFormula,
        mw: props.MolecularWeight + " g/mol",
        smiles: props.IsomericSMILES,
        moa: `Retrieved from PubChem CID ${props.CID}. This molecule was fetched in real-time. For detailed lung pharmacology, consult the literature.`,
        indications: ["See literature"],
        properties: { halfLife: "N/A", onset: "N/A", route: "N/A", bioavail: "N/A", mw: props.MolecularWeight },
        lungEffects: { bronchodilation:0, antiInflammatory:0, mucusClearance:0, vasoconstriction:0, surfactantProd:0, fibrosis:0 },
        receptors: [],
        pathway: [{ label: name, type: "active" }, { label: "Unknown Pathway", type: "active" }],
        adrs: [],
        compartments: []
      }
    };

    queue.push(drug);
    renderDrugList();
    selectDrug(drug);
    notify(`${drug.name} loaded from PubChem`, 'success');
  } catch (e) {
    notify('Drug not found in PubChem', 'error');
  }
  showLoader(false);
}

// ── ADD BY SMILES ─────────────────────────────────────────────────────────────
function addBySMILES() {
  const smiles = document.getElementById('smiles-input').value.trim();
  const label = document.getElementById('smiles-name-input').value.trim() || 'Custom Molecule';
  if (!smiles) { notify('Enter a SMILES string', 'error'); return; }

  const drug = {
    id: Date.now(),
    name: label,
    smiles,
    dbKey: null,
    color: DRUG_COLORS[queue.length % DRUG_COLORS.length],
    data: {
      name: label, class: "Custom SMILES", classColor: "#64748b",
      formula: "Custom", mw: "Custom",
      smiles,
      moa: "Custom molecule entered via SMILES. No pharmacological data available in the database.",
      indications: ["Custom"],
      properties: { halfLife: "N/A", onset: "N/A", route: "N/A", bioavail: "N/A", mw: "Custom" },
      lungEffects: { bronchodilation:0, antiInflammatory:0, mucusClearance:0, vasoconstriction:0, surfactantProd:0, fibrosis:0 },
      receptors: [],
      pathway: [{ label, type: "active" }],
      adrs: [],
      compartments: []
    }
  };

  queue.push(drug);
  renderDrugList();
  selectDrug(drug);
  document.getElementById('smiles-input').value = '';
  document.getElementById('smiles-name-input').value = '';
  notify(`${label} added`, 'success');
}

// ── FILE UPLOAD ───────────────────────────────────────────────────────────────
function handleFileUpload(event) {
  const file = event.target.files[0];
  if (!file) return;
  readMolFile(file);
}

function readMolFile(file) {
  const reader = new FileReader();
  reader.onload = (e) => {
    const content = e.target.result;
    const ext = file.name.split('.').pop().toLowerCase();
    const formats = { sdf:'sdf', mol:'sdf', pdb:'pdb', xyz:'xyz', mol2:'mol2' };
    const fmt = formats[ext] || 'sdf';
    const name = file.name.replace(/\.[^.]+$/, '');

    const drug = {
      id: Date.now(),
      name,
      smiles: null,
      molData: content,
      molFormat: fmt,
      dbKey: null,
      color: DRUG_COLORS[queue.length % DRUG_COLORS.length],
      data: {
        name, class: "Uploaded File", classColor: "#64748b",
        formula: "From file", mw: "From file", smiles: null,
        moa: `Molecule loaded from ${file.name}. No pharmacological data in database.`,
        indications: ["See literature"],
        properties: { halfLife: "N/A", onset: "N/A", route: "N/A", bioavail: "N/A", mw: "N/A" },
        lungEffects: { bronchodilation:0, antiInflammatory:0, mucusClearance:0, vasoconstriction:0, surfactantProd:0, fibrosis:0 },
        receptors: [],
        pathway: [{ label: name, type: "active" }],
        adrs: [],
        compartments: []
      }
    };

    queue.push(drug);
    renderDrugList();
    selectDrug(drug);
    notify(`${name} loaded from file`, 'success');
  };
  reader.readAsText(file);
}

function fileDragOver(e) { e.preventDefault(); document.getElementById('file-drop').classList.add('dragover'); }
function fileDragLeave() { document.getElementById('file-drop').classList.remove('dragover'); }
function fileDrop(e) {
  e.preventDefault();
  document.getElementById('file-drop').classList.remove('dragover');
  const file = e.dataTransfer.files[0];
  if (file) readMolFile(file);
}

// ── LOAD PRESET ───────────────────────────────────────────────────────────────
function loadPreset(key) { addFromDB(key); }

// ── DRUG LIST RENDER ─────────────────────────────────────────────────────────
function renderDrugList() {
  const list = document.getElementById('drug-list');
  if (queue.length === 0) {
    list.innerHTML = `<div class="empty-state" style="padding:1.5rem;"><div class="empty-icon" style="font-size:2rem;">💊</div><div style="font-size:0.82rem; color:var(--text-muted);">Add drugs above or click a preset</div></div>`;
    return;
  }
  list.innerHTML = queue.map(d => `
    <div class="drug-card ${activeDrug && activeDrug.id === d.id ? 'active' : ''}" onclick="selectDrug(queue.find(x=>x.id===${d.id}))">
      <div class="drug-card-header">
        <span class="drug-name">
          <span class="drug-color-dot" style="background:${d.color}"></span>
          ${d.name}
        </span>
        <span class="drug-class" style="background:${d.data.classColor}22; color:${d.data.classColor}; border:1px solid ${d.data.classColor}44;">${d.data.class}</span>
      </div>
      <div class="drug-smiles">${d.smiles || d.molData?.slice(0,60) || 'No SMILES'}</div>
      <div class="drug-card-actions">
        <button class="btn btn-secondary btn-sm" onclick="event.stopPropagation(); visualizeDrug(queue.find(x=>x.id===${d.id}))">3D View</button>
        <button class="btn btn-danger btn-sm" onclick="event.stopPropagation(); removeDrug(${d.id})">✕</button>
      </div>
    </div>
  `).join('');
}

function removeDrug(id) {
  queue = queue.filter(d => d.id !== id);
  if (activeDrug && activeDrug.id === id) {
    activeDrug = null;
    viewer.clear();
    viewer.render();
    document.getElementById('mol-label').textContent = 'No molecule selected';
    document.getElementById('mol-formula').textContent = '-';
    hideAnalysis();
  }
  renderDrugList();
}

function clearAll() {
  queue = [];
  activeDrug = null;
  viewer.clear();
  viewer.render();
  document.getElementById('mol-label').textContent = 'No molecule selected';
  document.getElementById('mol-formula').textContent = '-';
  renderDrugList();
  hideAnalysis();
}

function hideAnalysis() {
  document.getElementById('overview-empty').style.display = 'block';
  document.getElementById('overview-content').style.display = 'none';
  document.getElementById('lung-empty').style.display = 'block';
  document.getElementById('lung-content').style.display = 'none';
  document.getElementById('receptors-empty').style.display = 'block';
  document.getElementById('receptors-content').style.display = 'none';
  document.getElementById('pathway-empty').style.display = 'block';
  document.getElementById('pathway-content').style.display = 'none';
  document.getElementById('adr-empty').style.display = 'block';
  document.getElementById('adr-content').style.display = 'none';
}

// ── SELECT & VISUALIZE ────────────────────────────────────────────────────────
function selectDrug(drug) {
  activeDrug = drug;
  renderDrugList();
  visualizeDrug(drug);
  renderOverview(drug);
  renderReceptors(drug);
  renderADR(drug);
  renderPathway(drug);

  // Only render chart if tab is visible
  const lungTab = document.getElementById('analysis-lung');
  if (lungTab.classList.contains('active')) renderLungChart(drug);
}

function visualizeDrug(drug) {
  showLoader(true);
  document.getElementById('mol-label').textContent = drug.name;
  document.getElementById('mol-formula').textContent = drug.data.formula || '-';

  viewer.clear();

  try {
    if (drug.molData) {
      // File-based molecule
      viewer.addModel(drug.molData, drug.molFormat);
    } else if (drug.smiles) {
      // SMILES-based
      viewer.addModel(drug.smiles, 'smi');
    }

    applyStyle(currentStyle, drug.color);
    viewer.zoomTo();
    viewer.render();
    if (spinning) viewer.spin('y', 1);
  } catch (err) {
    notify('Could not render molecule', 'error');
    console.warn(err);
  }
  showLoader(false);
}

function applyStyle(style, color) {
  viewer.setStyle({}, {});
  switch(style) {
    case 'stick':
      viewer.setStyle({}, { stick: { colorscheme: 'Jmol', radius: 0.15 } });
      break;
    case 'sphere':
      viewer.setStyle({}, { sphere: { colorscheme: 'Jmol', scale: 0.4 } });
      break;
    case 'line':
      viewer.setStyle({}, { line: { colorscheme: 'Jmol' } });
      break;
    case 'surface':
      viewer.setStyle({}, { stick: { colorscheme: 'Jmol', radius: 0.1 } });
      viewer.addSurface($3Dmol.SurfaceType.VDW, { opacity: 0.6, colorscheme: 'Jmol' });
      break;
    case 'cartoon':
      viewer.setStyle({}, { stick: { radius: 0.12, colorscheme: 'Jmol' } });
      viewer.setStyle({ hetflag: true }, { sphere: { colorscheme: 'Jmol', scale: 0.3 } });
      break;
  }
  viewer.render();
}

function setMolStyle(style) {
  currentStyle = style;
  document.querySelectorAll('.style-chip').forEach(c => c.classList.remove('active'));
  document.getElementById('chip-' + style).classList.add('active');
  if (activeDrug) applyStyle(style, activeDrug.color);
}

function resetView() {
  if (viewer) { viewer.zoomTo(); viewer.render(); }
}

function toggleSpin() {
  spinning = !spinning;
  const btn = document.getElementById('spin-btn');
  if (spinning) { viewer.spin('y', 1); btn.classList.add('active'); btn.textContent = '⏸'; }
  else { viewer.spin(false); btn.classList.remove('active'); btn.textContent = '▶'; }
}

function toggleFullscreen() {
  const el = document.querySelector('.right-top');
  if (!document.fullscreenElement) el.requestFullscreen().catch(()=>{});
  else document.exitFullscreen();
}

// ── OVERVIEW RENDER ───────────────────────────────────────────────────────────
function renderOverview(drug) {
  document.getElementById('overview-empty').style.display = 'none';
  document.getElementById('overview-content').style.display = 'block';

  document.getElementById('status-text').textContent = `Analyzing ${drug.name} — ${drug.data.class}`;

  const props = drug.data.properties;
  document.getElementById('prop-grid').innerHTML = `
    <div class="info-card"><div class="info-card-label">Molecular Weight</div><div class="info-card-value" style="font-size:0.95rem;">${drug.data.mw}</div></div>
    <div class="info-card"><div class="info-card-label">Formula</div><div class="info-card-value" style="font-size:0.95rem; font-family:'JetBrains Mono',monospace;">${drug.data.formula}</div></div>
    <div class="info-card"><div class="info-card-label">Half-Life</div><div class="info-card-value" style="font-size:0.95rem;">${props.halfLife}</div></div>
    <div class="info-card"><div class="info-card-label">Onset</div><div class="info-card-value" style="font-size:0.95rem;">${props.onset}</div></div>
    <div class="info-card"><div class="info-card-label">Route</div><div class="info-card-value" style="font-size:0.95rem;">${props.route}</div></div>
    <div class="info-card"><div class="info-card-label">Bioavailability</div><div class="info-card-value" style="font-size:0.95rem;">${props.bioavail}</div></div>
  `;

  document.getElementById('moa-text').textContent = drug.data.moa;

  const tags = drug.data.indications.map((ind, i) => {
    const colors = ['tag-blue','tag-green','tag-orange','tag-purple','tag-red'];
    return `<span class="tag ${colors[i % colors.length]}">${ind}</span>`;
  }).join('');
  document.getElementById('indications-tags').innerHTML = tags;
}

// ── LUNG EFFECTS ─────────────────────────────────────────────────────────────
function renderLungChart(drug) {
  document.getElementById('lung-empty').style.display = 'none';
  document.getElementById('lung-content').style.display = 'block';

  const effects = drug.data.lungEffects;
  const labels = {
    bronchodilation: 'Bronchodilation',
    antiInflammatory: 'Anti-inflammatory',
    mucusClearance: 'Mucociliary Clearance',
    vasoconstriction: 'Pulmonary Vasodilation',
    surfactantProd: 'Surfactant Production',
    fibrosis: 'Anti-fibrotic Activity'
  };

  const colors = {
    bronchodilation: '#3b82f6',
    antiInflammatory: '#10b981',
    mucusClearance: '#06b6d4',
    vasoconstriction: '#f59e0b',
    surfactantProd: '#a855f7',
    fibrosis: '#f97316'
  };

  let barsHTML = '';
  for (const [key, val] of Object.entries(effects)) {
    const absVal = Math.abs(val);
    const isNeg = val < 0;
    barsHTML += `
      <div class="effect-item">
        <span class="effect-label">${labels[key]}</span>
        <div class="effect-bar-wrap">
          <div class="effect-bar" style="width:${absVal}%; background:${isNeg ? 'var(--danger)' : colors[key]};"></div>
        </div>
        <span class="effect-value">${isNeg ? '-' : '+'}${absVal}%</span>
      </div>
    `;
  }
  document.getElementById('lung-effects-bars').innerHTML = barsHTML;

  // Radar chart
  const ctx = document.getElementById('radarChart').getContext('2d');
  if (radarChart) radarChart.destroy();
  radarChart = new Chart(ctx, {
    type: 'radar',
    data: {
      labels: Object.values(labels),
      datasets: [{
        label: drug.name,
        data: Object.values(effects).map(Math.abs),
        backgroundColor: drug.color + '33',
        borderColor: drug.color,
        borderWidth: 2,
        pointBackgroundColor: drug.color,
        pointRadius: 4
      }]
    },
    options: {
      responsive: true,
      plugins: { legend: { labels: { color: '#94a3b8', font: { size: 11 } } } },
      scales: {
        r: {
          beginAtZero: true, max: 100,
          ticks: { color: '#64748b', backdropColor: 'transparent', stepSize: 25 },
          grid: { color: '#2d3a50' },
          pointLabels: { color: '#94a3b8', font: { size: 10 } }
        }
      }
    }
  });

  // Compartments
  const comps = drug.data.compartments || [];
  document.getElementById('lung-compartments').innerHTML = comps.map(c => `
    <div class="effect-item">
      <span class="effect-label">${c.name}</span>
      <div class="effect-bar-wrap">
        <div class="effect-bar" style="width:${c.activity}%; background:${c.color};"></div>
      </div>
      <span class="effect-value">${c.activity}%</span>
    </div>
  `).join('');
}

// ── RECEPTORS ────────────────────────────────────────────────────────────────
function renderReceptors(drug) {
  document.getElementById('receptors-empty').style.display = drug.data.receptors.length ? 'none' : 'block';
  document.getElementById('receptors-content').style.display = drug.data.receptors.length ? 'block' : 'none';

  const affClass = (ki) => {
    if (ki === '-' || ki === 'N/A') return 'aff-med';
    const v = parseFloat(ki);
    if (isNaN(v)) return 'aff-med';
    if (v < 10) return 'aff-high';
    if (v < 100) return 'aff-med';
    return 'aff-low';
  };

  document.getElementById('receptor-body').innerHTML = drug.data.receptors.map(r => `
    <tr>
      <td><strong>${r.name}</strong></td>
      <td><span class="badge ${r.action.includes('Agonist') ? 'badge-blue' : r.action === 'Inhibitor' ? 'badge-purple' : 'badge-cyan'}">${r.action}</span></td>
      <td><span class="affinity-pill ${affClass(r.ki)}">${r.ki}</span></td>
      <td>${r.sel}</td>
      <td style="color:var(--text-muted);">${r.relevance}</td>
    </tr>
  `).join('');
}

// ── PATHWAY ──────────────────────────────────────────────────────────────────
function renderPathway(drug) {
  document.getElementById('pathway-empty').style.display = 'none';
  document.getElementById('pathway-content').style.display = 'block';

  const steps = drug.data.pathway;
  let html = '';
  steps.forEach((step, i) => {
    html += `
      <div class="pathway-step">
        <div class="pathway-node ${step.type}">${step.label}</div>
        ${i < steps.length - 1 ? '<span class="pathway-arrow">→</span>' : ''}
      </div>
    `;
  });
  document.getElementById('pathway-map').innerHTML = html;
  renderTimeline(drug);
}

function renderTimeline(drug) {
  const ctx = document.getElementById('timelineChart').getContext('2d');
  if (timelineChart) timelineChart.destroy();

  const timePoints = [0, 5, 15, 30, 60, 120, 240, 480];
  const curves = {
    salbutamol: [0, 40, 75, 90, 95, 85, 60, 30],
    budesonide: [0, 5, 10, 20, 35, 55, 75, 85],
    tiotropium: [0, 10, 30, 55, 75, 85, 88, 90],
    montelukast: [0, 5, 15, 30, 55, 70, 75, 65],
    theophylline: [0, 10, 25, 50, 70, 78, 75, 65],
    sildenafil: [0, 15, 35, 60, 80, 85, 75, 55],
    nintedanib: [0, 0, 5, 10, 20, 35, 55, 70],
    ivacaftor: [0, 0, 5, 10, 20, 40, 70, 85],
  };
  const data = drug.dbKey && curves[drug.dbKey] ? curves[drug.dbKey] : [0,5,10,20,30,40,50,55];

  timelineChart = new Chart(ctx, {
    type: 'line',
    data: {
      labels: timePoints.map(t => t < 60 ? t+'min' : (t/60)+'hr'),
      datasets: [{
        label: 'Effect Intensity (%)',
        data,
        borderColor: drug.color,
        backgroundColor: drug.color + '22',
        fill: true,
        tension: 0.4,
        borderWidth: 2,
        pointBackgroundColor: drug.color
      }]
    },
    options: {
      responsive: true,
      plugins: { legend: { labels: { color: '#94a3b8' } } },
      scales: {
        x: { ticks: { color: '#64748b' }, grid: { color: '#2d3a50' } },
        y: { ticks: { color: '#64748b' }, grid: { color: '#2d3a50' }, beginAtZero: true, max: 100 }
      }
    }
  });
}

// ── ADR ──────────────────────────────────────────────────────────────────────
function renderADR(drug) {
  document.getElementById('adr-empty').style.display = drug.data.adrs.length ? 'none' : 'block';
  document.getElementById('adr-content').style.display = drug.data.adrs.length ? 'block' : 'none';

  document.getElementById('adr-list').innerHTML = drug.data.adrs.map(a => `
    <div class="adr-item adr-sev-${a.sev}">
      <span class="adr-icon">${a.icon}</span>
      <div>
        <div class="adr-name">${a.name} <span class="badge ${a.sev === 'high' ? 'badge-purple' : a.sev === 'med' ? 'badge-cyan' : 'badge-blue'}">${a.sev === 'high' ? 'SEVERE' : a.sev === 'med' ? 'MODERATE' : 'MILD'}</span></div>
        <div class="adr-desc">${a.desc}</div>
      </div>
    </div>
  `).join('');
}

// ── COMPARISON ───────────────────────────────────────────────────────────────
function renderComparison() {
  if (queue.length < 2) {
    document.getElementById('compare-empty').style.display = 'block';
    document.getElementById('compare-table-wrap').style.display = 'none';
    document.getElementById('compare-chart-wrap').style.display = 'none';
    return;
  }

  document.getElementById('compare-empty').style.display = 'none';
  document.getElementById('compare-table-wrap').style.display = 'block';
  document.getElementById('compare-chart-wrap').style.display = 'block';

  const cols = queue.length + 1;
  const table = `
    <div class="compare-grid" style="grid-template-columns: 140px ${queue.map(()=>'1fr').join(' ')};">
      <div class="compare-row" style="grid-template-columns: 140px ${queue.map(()=>'1fr').join(' ')};">
        <div class="compare-cell header">Property</div>
        ${queue.map(d => `<div class="compare-cell header"><span class="drug-color-dot" style="background:${d.color}"></span>${d.name}</div>`).join('')}
      </div>
      ${buildCompareRow('Class', d => `<span style="color:${d.data.classColor}">${d.data.class}</span>`)}
      ${buildCompareRow('Formula', d => `<span style="font-family:monospace;font-size:0.78rem;">${d.data.formula}</span>`)}
      ${buildCompareRow('Mol. Weight', d => d.data.mw)}
      ${buildCompareRow('Half-Life', d => d.data.properties.halfLife)}
      ${buildCompareRow('Onset', d => d.data.properties.onset)}
      ${buildCompareRow('Route', d => d.data.properties.route)}
      ${buildCompareRow('Bioavailability', d => d.data.properties.bioavail)}
      ${buildCompareRow('Bronchodilation', d => scoreBar(d.data.lungEffects.bronchodilation, '#3b82f6'))}
      ${buildCompareRow('Anti-inflammatory', d => scoreBar(d.data.lungEffects.antiInflammatory, '#10b981'))}
      ${buildCompareRow('Mucus Clearance', d => scoreBar(d.data.lungEffects.mucusClearance, '#06b6d4'))}
      ${buildCompareRow('Anti-fibrotic', d => scoreBar(Math.abs(d.data.lungEffects.fibrosis), '#f97316'))}
    </div>
  `;
  document.getElementById('compare-table-wrap').innerHTML = table;

  // Comparison radar
  const ctx = document.getElementById('compareChart').getContext('2d');
  if (compareChart) compareChart.destroy();
  const radarLabels = ['Bronchodilation','Anti-inflammatory','Mucus Clearance','Vasodilation','Surfactant','Anti-fibrotic'];
  compareChart = new Chart(ctx, {
    type: 'radar',
    data: {
      labels: radarLabels,
      datasets: queue.map(d => ({
        label: d.name,
        data: [
          d.data.lungEffects.bronchodilation,
          d.data.lungEffects.antiInflammatory,
          d.data.lungEffects.mucusClearance,
          Math.abs(d.data.lungEffects.vasoconstriction),
          d.data.lungEffects.surfactantProd,
          Math.abs(d.data.lungEffects.fibrosis)
        ],
        backgroundColor: d.color + '22',
        borderColor: d.color,
        borderWidth: 2,
        pointBackgroundColor: d.color,
        pointRadius: 3
      }))
    },
    options: {
      responsive: true,
      plugins: { legend: { labels: { color: '#94a3b8', font: { size: 11 } } } },
      scales: {
        r: {
          beginAtZero: true, max: 100,
          ticks: { color: '#64748b', backdropColor: 'transparent', stepSize: 25 },
          grid: { color: '#2d3a50' },
          pointLabels: { color: '#94a3b8', font: { size: 10 } }
        }
      }
    }
  });
}

function buildCompareRow(label, fn) {
  return `<div class="compare-row" style="grid-template-columns: 140px ${queue.map(()=>'1fr').join(' ')};">
    <div class="compare-cell label">${label}</div>
    ${queue.map(d => `<div class="compare-cell">${fn(d)}</div>`).join('')}
  </div>`;
}

function scoreBar(val, color) {
  const abs = Math.abs(val);
  return `<div style="display:flex;align-items:center;gap:6px;">
    <div style="flex:1;height:6px;background:var(--surface3);border-radius:3px;overflow:hidden;">
      <div style="width:${abs}%;height:100%;background:${color};border-radius:3px;"></div>
    </div>
    <span style="font-size:0.72rem;font-family:'JetBrains Mono',monospace;color:var(--text-muted);width:28px;">${abs}%</span>
  </div>`;
}

// ── UTILITIES ─────────────────────────────────────────────────────────────────
function showLoader(show) {
  document.getElementById('viewer-loader').classList.toggle('show', show);
}

function notify(msg, type = 'info') {
  const n = document.getElementById('notification');
  const el = document.createElement('div');
  el.className = `notif ${type}`;
  el.innerHTML = `<span>${type === 'success' ? '✓' : type === 'error' ? '✕' : 'ℹ'}</span> ${msg}`;
  n.appendChild(el);
  setTimeout(() => el.remove(), 3000);
}

</script>
</body>
</html>
Running Command

ls -lh /home/daytona/workspace/lung-drug-app/ && wc -l /home/daytona/workspace/lung-drug-app/index.html

The app is built and ready. Download it here:

index

HTML File · HTML


What the app includes

Drug Input - 3 Methods

MethodHow it works
By NameType any drug name - matches internal DB instantly, or fetches live from PubChem for anything else
SMILES StringPaste any valid SMILES notation to render custom molecules
File UploadDrag-and-drop SDF, MOL, PDB, XYZ files

8 Pre-loaded Lung Drugs (full clinical data)

  • Salbutamol (β2-Agonist) - acute bronchodilator
  • Budesonide (Corticosteroid) - inhaled anti-inflammatory
  • Tiotropium (LAMA) - COPD maintenance
  • Montelukast (LTRA) - leukotriene antagonist
  • Theophylline (PDE inhibitor) - bronchodilator with narrow TI
  • Sildenafil (PDE5 inhibitor) - pulmonary arterial hypertension
  • Nintedanib (TKI) - IPF antifibrotic
  • Ivacaftor (CFTR modulator) - Cystic Fibrosis

Analysis Tabs (per drug)

  1. Overview - MW, half-life, bioavailability, mechanism of action, indications
  2. Lung Effects - quantified effect bars + interactive radar chart per lung compartment
  3. Receptors - binding targets with Ki values, selectivity, lung relevance
  4. Pathway - step-by-step signaling cascade with activation/inhibition visualization + effect timeline chart
  5. Compare - side-by-side table and multi-drug radar overlay for all queued drugs
  6. Side Effects - adverse reactions by severity (mild/moderate/severe) with mechanism

3D Viewer Features

  • Interactive rotate/zoom/pan
  • Style modes: Stick, Sphere, Cartoon, Surface, Line
  • Toggle auto-spin
  • Fullscreen mode
  • Jmol atom coloring (C=grey, O=red, N=blue, S=yellow, etc.)

Add All preload lung drugs

Reading File
Reading File
Reading File
Reading File
Reading File
Reading File
Editing File

~/lung-drug-app/index.html

Successfully replaced 1 instance(s) of the string in '/home/daytona/workspace/lung-drug-app/index.html'
Editing File

~/lung-drug-app/index.html

Successfully replaced 1 instance(s) of the string in '/home/daytona/workspace/lung-drug-app/index.html'
This is a shared conversation. Sign in to Orris to start your own chat.