Download src/dashboard.html from Snapkitty/bio-semantics: direct link, hf CLI and curl.
- Browser
- Download file 72.8 kB
-
https://huggingface.co/Snapkitty/bio-semantics/resolve/main/src/dashboard.html
- Command line
-
hf download hf://Snapkitty/bio-semantics/src/dashboard.html
-
curl -L -o dashboard.html https://huggingface.co/Snapkitty/bio-semantics/resolve/main/src/dashboard.html
72.8 kB
| <html lang="en"> | |
| <head> | |
| <meta charset="UTF-8"> | |
| <meta name="viewport" content="width=device-width, initial-scale=1.0"> | |
| <title>BIO-SEMANTICS — Gene-Expression Semantic Modeling Pipeline</title> | |
| <style> | |
| @import url('https://fonts.googleapis.com/css2?family=IBM+Plex+Mono:wght@300;400;500;600&family=IBM+Plex+Sans:wght@200;300;400;500;600&family=IBM+Plex+Serif:ital,wght@0,300;0,400;1,300&display=swap'); | |
| :root { | |
| --bg-primary: #030810; | |
| --bg-secondary: #060e1a; | |
| --bg-panel: rgba(6,18,36,0.85); | |
| --bg-panel-hover: rgba(10,26,52,0.92); | |
| --border-primary: rgba(0,200,255,0.12); | |
| --border-active: rgba(0,200,255,0.35); | |
| --border-glow: rgba(0,200,255,0.5); | |
| --cyan: #00c8ff; | |
| --cyan-dim: #006688; | |
| --blue: #2266ff; | |
| --blue-bright: #4488ff; | |
| --violet: #8844ff; | |
| --violet-dim: #5522aa; | |
| --green-bio: #22cc66; | |
| --green-dim: #0a6630; | |
| --amber: #ffaa22; | |
| --red: #ff3344; | |
| --white: #e8eef6; | |
| --white-dim: #8899aa; | |
| --text-primary: #c8d8e8; | |
| --text-secondary: #6688aa; | |
| --text-label: #446688; | |
| --mono: 'IBM Plex Mono', monospace; | |
| --sans: 'IBM Plex Sans', sans-serif; | |
| --serif: 'IBM Plex Serif', serif; | |
| --glow-cyan: 0 0 8px rgba(0,200,255,0.3), 0 0 20px rgba(0,200,255,0.1); | |
| --glow-violet: 0 0 8px rgba(136,68,255,0.3), 0 0 20px rgba(136,68,255,0.1); | |
| --glow-green: 0 0 8px rgba(34,204,102,0.3), 0 0 20px rgba(34,204,102,0.1); | |
| } | |
| *, *::before, *::after { margin:0; padding:0; box-sizing:border-box; } | |
| html { font-size: 13px; } | |
| body { | |
| background: var(--bg-primary); | |
| color: var(--text-primary); | |
| font-family: var(--sans); | |
| font-weight: 300; | |
| overflow-x: hidden; | |
| min-height: 100vh; | |
| } | |
| /* === GRID BACKGROUND === */ | |
| body::before { | |
| content: ''; | |
| position: fixed; | |
| inset: 0; | |
| background: | |
| linear-gradient(rgba(0,200,255,0.015) 1px, transparent 1px), | |
| linear-gradient(90deg, rgba(0,200,255,0.015) 1px, transparent 1px); | |
| background-size: 40px 40px; | |
| pointer-events: none; | |
| z-index: 0; | |
| } | |
| body::after { | |
| content: ''; | |
| position: fixed; | |
| inset: 0; | |
| background: radial-gradient(ellipse 80% 60% at 50% 30%, rgba(0,40,80,0.15), transparent); | |
| pointer-events: none; | |
| z-index: 0; | |
| } | |
| /* === HEADER === */ | |
| .header { | |
| position: relative; | |
| z-index: 10; | |
| text-align: center; | |
| padding: 28px 20px 16px; | |
| border-bottom: 1px solid var(--border-primary); | |
| background: linear-gradient(180deg, rgba(6,18,36,0.95), rgba(3,8,16,0.8)); | |
| } | |
| .header-title { | |
| font-family: var(--sans); | |
| font-weight: 200; | |
| font-size: 2.6rem; | |
| letter-spacing: 0.45em; | |
| color: var(--cyan); | |
| text-shadow: var(--glow-cyan); | |
| margin-bottom: 6px; | |
| } | |
| .header-subtitle { | |
| font-family: var(--serif); | |
| font-weight: 300; | |
| font-style: italic; | |
| font-size: 1.05rem; | |
| color: var(--text-secondary); | |
| letter-spacing: 0.08em; | |
| margin-bottom: 12px; | |
| } | |
| .header-authors { | |
| font-family: var(--mono); | |
| font-size: 0.72rem; | |
| letter-spacing: 0.18em; | |
| color: var(--text-label); | |
| text-transform: uppercase; | |
| } | |
| .header-authors span { margin: 0 14px; } | |
| .header-authors span.sep { color: var(--cyan-dim); } | |
| /* === NAV === */ | |
| .nav { | |
| position: relative; | |
| z-index: 10; | |
| display: flex; | |
| justify-content: center; | |
| gap: 2px; | |
| padding: 8px 20px; | |
| border-bottom: 1px solid var(--border-primary); | |
| background: rgba(3,8,16,0.9); | |
| } | |
| .nav-item { | |
| font-family: var(--mono); | |
| font-size: 0.68rem; | |
| font-weight: 400; | |
| letter-spacing: 0.12em; | |
| text-transform: uppercase; | |
| color: var(--text-label); | |
| padding: 6px 14px; | |
| cursor: pointer; | |
| border: 1px solid transparent; | |
| border-radius: 2px; | |
| transition: all 0.3s ease; | |
| user-select: none; | |
| } | |
| .nav-item:hover { | |
| color: var(--cyan); | |
| border-color: var(--border-active); | |
| background: rgba(0,200,255,0.04); | |
| } | |
| .nav-item.active { | |
| color: var(--cyan); | |
| border-color: var(--border-active); | |
| background: rgba(0,200,255,0.06); | |
| text-shadow: var(--glow-cyan); | |
| } | |
| /* === MAIN LAYOUT === */ | |
| .main { | |
| position: relative; | |
| z-index: 5; | |
| display: grid; | |
| grid-template-columns: 1fr 380px 1fr; | |
| grid-template-rows: auto auto auto; | |
| gap: 12px; | |
| padding: 14px; | |
| max-width: 1920px; | |
| margin: 0 auto; | |
| } | |
| /* === PANELS === */ | |
| .panel { | |
| background: var(--bg-panel); | |
| border: 1px solid var(--border-primary); | |
| border-radius: 3px; | |
| padding: 14px; | |
| position: relative; | |
| overflow: hidden; | |
| transition: border-color 0.4s ease, box-shadow 0.4s ease; | |
| } | |
| .panel::before { | |
| content: ''; | |
| position: absolute; | |
| top: 0; left: 0; right: 0; | |
| height: 1px; | |
| background: linear-gradient(90deg, transparent, var(--cyan-dim), transparent); | |
| opacity: 0.5; | |
| } | |
| .panel:hover { | |
| border-color: var(--border-active); | |
| box-shadow: var(--glow-cyan); | |
| } | |
| .panel-title { | |
| font-family: var(--mono); | |
| font-size: 0.65rem; | |
| font-weight: 500; | |
| letter-spacing: 0.2em; | |
| text-transform: uppercase; | |
| color: var(--cyan); | |
| margin-bottom: 10px; | |
| padding-bottom: 6px; | |
| border-bottom: 1px solid var(--border-primary); | |
| display: flex; | |
| align-items: center; | |
| gap: 8px; | |
| } | |
| .panel-title .indicator { | |
| width: 5px; height: 5px; | |
| border-radius: 50%; | |
| background: var(--cyan); | |
| box-shadow: 0 0 6px var(--cyan); | |
| animation: pulse-indicator 2s ease-in-out infinite; | |
| } | |
| @keyframes pulse-indicator { | |
| 0%, 100% { opacity: 0.6; } | |
| 50% { opacity: 1; } | |
| } | |
| /* === LEFT COLUMN === */ | |
| .left-column { | |
| display: flex; | |
| flex-direction: column; | |
| gap: 12px; | |
| } | |
| /* === CENTER COLUMN === */ | |
| .center-column { | |
| display: flex; | |
| flex-direction: column; | |
| gap: 12px; | |
| align-items: center; | |
| } | |
| /* === RIGHT COLUMN === */ | |
| .right-column { | |
| display: flex; | |
| flex-direction: column; | |
| gap: 12px; | |
| } | |
| /* === HELIX CANVAS === */ | |
| .helix-container { | |
| position: relative; | |
| width: 380px; | |
| height: 720px; | |
| border: 1px solid var(--border-primary); | |
| border-radius: 3px; | |
| overflow: hidden; | |
| background: radial-gradient(ellipse at 50% 30%, rgba(0,40,80,0.12), transparent); | |
| } | |
| #helixCanvas { | |
| width: 100%; | |
| height: 100%; | |
| } | |
| .helix-labels { | |
| position: absolute; | |
| top: 0; left: 0; right: 0; bottom: 0; | |
| pointer-events: none; | |
| } | |
| .helix-label { | |
| position: absolute; | |
| font-family: var(--mono); | |
| font-size: 0.58rem; | |
| letter-spacing: 0.15em; | |
| text-transform: uppercase; | |
| color: var(--text-label); | |
| white-space: nowrap; | |
| } | |
| .helix-label.left { right: calc(50% + 50px); text-align: right; } | |
| .helix-label.right { left: calc(50% + 50px); } | |
| .helix-label .hl-val { | |
| color: var(--cyan); | |
| font-weight: 500; | |
| } | |
| /* === PIPELINE FLOW NODES === */ | |
| .pipeline-flow { | |
| display: flex; | |
| flex-direction: column; | |
| align-items: center; | |
| gap: 0; | |
| width: 100%; | |
| } | |
| .pipeline-node { | |
| font-family: var(--mono); | |
| font-size: 0.62rem; | |
| font-weight: 400; | |
| letter-spacing: 0.15em; | |
| text-transform: uppercase; | |
| color: var(--text-secondary); | |
| padding: 5px 12px; | |
| border: 1px solid var(--border-primary); | |
| border-radius: 2px; | |
| text-align: center; | |
| cursor: pointer; | |
| transition: all 0.3s ease; | |
| position: relative; | |
| background: rgba(3,8,16,0.6); | |
| width: 100%; | |
| max-width: 240px; | |
| } | |
| .pipeline-node:hover, .pipeline-node.active { | |
| color: var(--cyan); | |
| border-color: var(--border-active); | |
| background: rgba(0,200,255,0.05); | |
| text-shadow: 0 0 8px rgba(0,200,255,0.4); | |
| } | |
| .pipeline-connector { | |
| width: 1px; | |
| height: 14px; | |
| position: relative; | |
| overflow: visible; | |
| } | |
| .pipeline-connector::before { | |
| content: ''; | |
| position: absolute; | |
| left: 0; top: 0; bottom: 0; | |
| width: 1px; | |
| background: linear-gradient(180deg, var(--cyan-dim), rgba(0,200,255,0.05)); | |
| } | |
| /* Particle animation on connectors */ | |
| .pipeline-connector::after { | |
| content: ''; | |
| position: absolute; | |
| left: -1px; top: 0; | |
| width: 3px; height: 3px; | |
| border-radius: 50%; | |
| background: var(--cyan); | |
| box-shadow: 0 0 4px var(--cyan); | |
| animation: flow-particle 1.5s linear infinite; | |
| } | |
| @keyframes flow-particle { | |
| 0% { top: 0; opacity: 1; } | |
| 100% { top: 100%; opacity: 0; } | |
| } | |
| /* === CDNA PANEL === */ | |
| .seq-trace { | |
| display: flex; | |
| gap: 1px; | |
| margin: 6px 0; | |
| height: 32px; | |
| align-items: flex-end; | |
| } | |
| .seq-bar { | |
| width: 3px; | |
| background: var(--cyan); | |
| border-radius: 1px 1px 0 0; | |
| opacity: 0.7; | |
| transition: opacity 0.3s; | |
| } | |
| .seq-bar:hover { opacity: 1; } | |
| .seq-bar.a { background: var(--green-bio); } | |
| .seq-bar.t { background: var(--red); } | |
| .seq-bar.g { background: var(--amber); } | |
| .seq-bar.c { background: var(--blue-bright); } | |
| .seq-reads { | |
| font-family: var(--mono); | |
| font-size: 0.6rem; | |
| color: var(--text-label); | |
| line-height: 1.4; | |
| margin-top: 8px; | |
| } | |
| .seq-reads .base { padding: 0 1px; } | |
| .seq-reads .base.a { color: var(--green-bio); } | |
| .seq-reads .base.t { color: var(--red); } | |
| .seq-reads .base.g { color: var(--amber); } | |
| .seq-reads .base.c { color: var(--blue-bright); } | |
| /* === EXPRESSION MATRIX === */ | |
| .expr-matrix { | |
| display: grid; | |
| gap: 1px; | |
| margin: 8px 0; | |
| } | |
| .expr-cell { | |
| width: 100%; | |
| aspect-ratio: 1; | |
| border-radius: 1px; | |
| transition: transform 0.2s; | |
| } | |
| .expr-cell:hover { transform: scale(1.8); z-index: 10; } | |
| .expr-axis-label { | |
| font-family: var(--mono); | |
| font-size: 0.52rem; | |
| color: var(--text-label); | |
| letter-spacing: 0.1em; | |
| text-transform: uppercase; | |
| } | |
| /* === VALIDATION PANEL === */ | |
| .validation-grid { | |
| display: grid; | |
| grid-template-columns: 1fr auto; | |
| gap: 3px 12px; | |
| font-family: var(--mono); | |
| font-size: 0.6rem; | |
| } | |
| .val-rule { | |
| color: var(--text-secondary); | |
| letter-spacing: 0.08em; | |
| text-transform: uppercase; | |
| padding: 2px 0; | |
| } | |
| .val-status { | |
| text-align: right; | |
| font-weight: 500; | |
| } | |
| .val-status.pass { color: var(--green-bio); } | |
| .val-status.warn { color: var(--amber); } | |
| .val-status.fail { color: var(--red); } | |
| /* === QUANTITY KIND === */ | |
| .qty-kinds { | |
| display: flex; | |
| flex-direction: column; | |
| gap: 6px; | |
| } | |
| .qty-kind { | |
| display: flex; | |
| align-items: center; | |
| gap: 10px; | |
| padding: 4px 8px; | |
| border: 1px solid transparent; | |
| border-radius: 2px; | |
| cursor: pointer; | |
| transition: all 0.3s ease; | |
| } | |
| .qty-kind:hover, .qty-kind.active { | |
| border-color: var(--border-active); | |
| background: rgba(0,200,255,0.04); | |
| } | |
| .qty-glyph { | |
| width: 20px; height: 20px; | |
| display: flex; | |
| align-items: center; | |
| justify-content: center; | |
| flex-shrink: 0; | |
| } | |
| .qty-glyph svg { width: 16px; height: 16px; } | |
| .qty-label { | |
| font-family: var(--mono); | |
| font-size: 0.62rem; | |
| letter-spacing: 0.12em; | |
| text-transform: uppercase; | |
| color: var(--text-secondary); | |
| } | |
| .qty-kind:hover .qty-label, | |
| .qty-kind.active .qty-label { color: var(--cyan); } | |
| .qty-source { | |
| font-family: var(--serif); | |
| font-size: 0.58rem; | |
| font-style: italic; | |
| color: var(--text-label); | |
| margin-left: auto; | |
| } | |
| /* === STAN PANEL === */ | |
| .stan-chart { | |
| position: relative; | |
| height: 120px; | |
| margin: 8px 0; | |
| border-left: 1px solid var(--border-primary); | |
| border-bottom: 1px solid var(--border-primary); | |
| } | |
| .stan-chart canvas { width: 100%; height: 100%; } | |
| .stan-table { | |
| width: 100%; | |
| border-collapse: collapse; | |
| margin-top: 8px; | |
| } | |
| .stan-table th { | |
| font-family: var(--mono); | |
| font-size: 0.52rem; | |
| letter-spacing: 0.12em; | |
| text-transform: uppercase; | |
| color: var(--text-label); | |
| text-align: left; | |
| padding: 3px 6px; | |
| border-bottom: 1px solid var(--border-primary); | |
| font-weight: 400; | |
| } | |
| .stan-table td { | |
| font-family: var(--mono); | |
| font-size: 0.58rem; | |
| color: var(--text-secondary); | |
| padding: 3px 6px; | |
| border-bottom: 1px solid rgba(0,200,255,0.04); | |
| } | |
| .stan-table td.num { color: var(--cyan); text-align: right; } | |
| .stan-flow { | |
| display: flex; | |
| justify-content: center; | |
| align-items: center; | |
| gap: 12px; | |
| margin: 10px 0 6px; | |
| font-family: var(--mono); | |
| font-size: 0.58rem; | |
| letter-spacing: 0.1em; | |
| text-transform: uppercase; | |
| } | |
| .stan-flow-node { | |
| color: var(--text-secondary); | |
| padding: 3px 8px; | |
| border: 1px solid var(--border-primary); | |
| border-radius: 2px; | |
| } | |
| .stan-flow-arrow { color: var(--cyan-dim); } | |
| /* === MINIZINC PANEL === */ | |
| .mzn-graph { | |
| position: relative; | |
| height: 160px; | |
| margin: 8px 0; | |
| } | |
| .mzn-graph canvas { width: 100%; height: 100%; } | |
| .mzn-notation { | |
| font-family: var(--serif); | |
| font-style: italic; | |
| font-size: 0.72rem; | |
| color: var(--text-secondary); | |
| text-align: center; | |
| margin: 8px 0; | |
| line-height: 1.6; | |
| } | |
| /* === PTM PANEL === */ | |
| .ptm-tape { | |
| display: flex; | |
| justify-content: center; | |
| gap: 2px; | |
| margin: 10px 0; | |
| position: relative; | |
| } | |
| .ptm-cell { | |
| width: 28px; height: 28px; | |
| border: 1px solid var(--border-primary); | |
| display: flex; | |
| align-items: center; | |
| justify-content: center; | |
| font-family: var(--mono); | |
| font-size: 0.8rem; | |
| font-weight: 500; | |
| color: var(--cyan); | |
| transition: all 0.3s ease; | |
| position: relative; | |
| } | |
| .ptm-cell.active { | |
| border-color: var(--cyan); | |
| background: rgba(0,200,255,0.1); | |
| box-shadow: var(--glow-cyan); | |
| } | |
| .ptm-head { | |
| position: absolute; | |
| bottom: -12px; | |
| left: 50%; | |
| transform: translateX(-50%); | |
| width: 0; height: 0; | |
| border-left: 5px solid transparent; | |
| border-right: 5px solid transparent; | |
| border-bottom: 6px solid var(--cyan); | |
| filter: drop-shadow(0 0 4px var(--cyan)); | |
| } | |
| .ptm-states { | |
| display: flex; | |
| justify-content: center; | |
| gap: 16px; | |
| margin: 16px 0 8px; | |
| } | |
| .ptm-state { | |
| width: 52px; height: 52px; | |
| border-radius: 50%; | |
| border: 1px solid var(--border-primary); | |
| display: flex; | |
| align-items: center; | |
| justify-content: center; | |
| font-family: var(--mono); | |
| font-size: 0.56rem; | |
| color: var(--text-secondary); | |
| transition: all 0.4s ease; | |
| position: relative; | |
| } | |
| .ptm-state.current { | |
| border-color: var(--cyan); | |
| color: var(--cyan); | |
| box-shadow: var(--glow-cyan); | |
| } | |
| .ptm-state.accept { | |
| border-width: 2px; | |
| } | |
| .ptm-transition-table { | |
| width: 100%; | |
| border-collapse: collapse; | |
| margin-top: 8px; | |
| } | |
| .ptm-transition-table th { | |
| font-family: var(--mono); | |
| font-size: 0.5rem; | |
| letter-spacing: 0.12em; | |
| text-transform: uppercase; | |
| color: var(--text-label); | |
| text-align: center; | |
| padding: 3px 4px; | |
| border-bottom: 1px solid var(--border-primary); | |
| font-weight: 400; | |
| } | |
| .ptm-transition-table td { | |
| font-family: var(--mono); | |
| font-size: 0.56rem; | |
| color: var(--text-secondary); | |
| text-align: center; | |
| padding: 2px 4px; | |
| border-bottom: 1px solid rgba(0,200,255,0.04); | |
| } | |
| .ptm-transition-table tr.active-row td { | |
| color: var(--cyan); | |
| background: rgba(0,200,255,0.04); | |
| } | |
| .ptm-complexity { | |
| font-family: var(--mono); | |
| font-size: 0.56rem; | |
| color: var(--text-label); | |
| text-align: center; | |
| margin-top: 8px; | |
| line-height: 1.6; | |
| } | |
| .ptm-complexity span { color: var(--cyan); } | |
| /* === BINARY PANEL === */ | |
| .binary-stream { | |
| font-family: var(--mono); | |
| font-size: 0.7rem; | |
| color: var(--cyan-dim); | |
| line-height: 1.6; | |
| word-break: break-all; | |
| margin: 6px 0; | |
| letter-spacing: 0.05em; | |
| } | |
| .binary-stream .one { color: var(--cyan); } | |
| .binary-stream .zero { color: var(--cyan-dim); opacity: 0.5; } | |
| .binary-table { | |
| width: 100%; | |
| border-collapse: collapse; | |
| margin-top: 6px; | |
| } | |
| .binary-table th { | |
| font-family: var(--mono); | |
| font-size: 0.5rem; | |
| letter-spacing: 0.1em; | |
| text-transform: uppercase; | |
| color: var(--text-label); | |
| text-align: left; | |
| padding: 2px 6px; | |
| border-bottom: 1px solid var(--border-primary); | |
| font-weight: 400; | |
| } | |
| .binary-table td { | |
| font-family: var(--mono); | |
| font-size: 0.56rem; | |
| color: var(--text-secondary); | |
| padding: 2px 6px; | |
| border-bottom: 1px solid rgba(0,200,255,0.04); | |
| } | |
| /* === QUIPPER PANEL === */ | |
| .quipper-circuit { | |
| position: relative; | |
| height: 140px; | |
| margin: 8px 0; | |
| } | |
| .quipper-circuit canvas { width: 100%; height: 100%; } | |
| .quipper-stats { | |
| display: grid; | |
| grid-template-columns: repeat(3, 1fr); | |
| gap: 8px; | |
| margin-top: 8px; | |
| } | |
| .quipper-stat { | |
| text-align: center; | |
| } | |
| .quipper-stat .qs-val { | |
| font-family: var(--mono); | |
| font-size: 1.1rem; | |
| font-weight: 500; | |
| color: var(--violet); | |
| text-shadow: var(--glow-violet); | |
| } | |
| .quipper-stat .qs-label { | |
| font-family: var(--mono); | |
| font-size: 0.48rem; | |
| letter-spacing: 0.15em; | |
| text-transform: uppercase; | |
| color: var(--text-label); | |
| margin-top: 2px; | |
| } | |
| /* === PROVENANCE PANEL === */ | |
| .prov-chain { | |
| display: flex; | |
| flex-direction: column; | |
| gap: 0; | |
| } | |
| .prov-node { | |
| display: grid; | |
| grid-template-columns: auto 1fr auto; | |
| gap: 8px; | |
| padding: 4px 8px; | |
| align-items: center; | |
| border: 1px solid transparent; | |
| border-radius: 2px; | |
| cursor: pointer; | |
| transition: all 0.3s ease; | |
| } | |
| .prov-node:hover { | |
| border-color: var(--border-active); | |
| background: rgba(0,200,255,0.03); | |
| } | |
| .prov-icon { | |
| width: 8px; height: 8px; | |
| border: 1px solid var(--cyan-dim); | |
| border-radius: 50%; | |
| } | |
| .prov-node:hover .prov-icon { | |
| background: var(--cyan); | |
| border-color: var(--cyan); | |
| box-shadow: 0 0 6px var(--cyan); | |
| } | |
| .prov-name { | |
| font-family: var(--mono); | |
| font-size: 0.58rem; | |
| letter-spacing: 0.1em; | |
| text-transform: uppercase; | |
| color: var(--text-secondary); | |
| } | |
| .prov-hash { | |
| font-family: var(--mono); | |
| font-size: 0.5rem; | |
| color: var(--text-label); | |
| letter-spacing: 0.02em; | |
| } | |
| .prov-connector { | |
| width: 1px; | |
| height: 8px; | |
| margin-left: 12px; | |
| background: var(--border-primary); | |
| position: relative; | |
| } | |
| .prov-detail { | |
| display: none; | |
| font-family: var(--mono); | |
| font-size: 0.52rem; | |
| color: var(--text-label); | |
| padding: 6px 8px 6px 28px; | |
| line-height: 1.6; | |
| border-left: 1px solid var(--border-primary); | |
| margin-left: 12px; | |
| } | |
| .prov-detail .pd-key { color: var(--text-label); text-transform: uppercase; letter-spacing: 0.1em; } | |
| .prov-detail .pd-val { color: var(--text-secondary); } | |
| /* === RPG/JCL PANEL === */ | |
| .rpg-block { | |
| font-family: var(--mono); | |
| font-size: 0.56rem; | |
| line-height: 1.7; | |
| color: var(--text-label); | |
| background: rgba(0,0,0,0.3); | |
| border: 1px solid rgba(0,200,255,0.06); | |
| padding: 8px 10px; | |
| margin: 6px 0; | |
| border-radius: 2px; | |
| position: relative; | |
| } | |
| .rpg-block .rpg-kw { color: var(--amber); } | |
| .rpg-block .rpg-id { color: var(--cyan-dim); } | |
| .rpg-block .rpg-st { color: var(--green-bio); } | |
| .rpg-block .rpg-comment { color: var(--text-label); opacity: 0.5; } | |
| .rpg-job-status { | |
| display: grid; | |
| grid-template-columns: auto 1fr auto; | |
| gap: 4px 10px; | |
| font-family: var(--mono); | |
| font-size: 0.54rem; | |
| margin-top: 6px; | |
| } | |
| .rpg-job-id { color: var(--text-label); } | |
| .rpg-job-name { color: var(--text-secondary); text-transform: uppercase; letter-spacing: 0.08em; } | |
| .rpg-job-state { font-weight: 500; } | |
| .rpg-job-state.complete { color: var(--green-bio); } | |
| .rpg-job-state.running { color: var(--amber); } | |
| .rpg-job-state.queued { color: var(--text-label); } | |
| /* === REPO PANEL === */ | |
| .repo-tree { | |
| font-family: var(--mono); | |
| font-size: 0.58rem; | |
| line-height: 1.8; | |
| color: var(--text-label); | |
| } | |
| .repo-tree .dir { color: var(--cyan-dim); } | |
| .repo-tree .file { color: var(--text-secondary); } | |
| .repo-tree .file-py { color: var(--green-bio); } | |
| .repo-tree .file-ts { color: var(--blue-bright); } | |
| .repo-tree .file-md { color: var(--text-secondary); } | |
| .repo-tree .file-rpg { color: var(--amber); } | |
| .repo-tree .file-jcl { color: var(--amber); opacity: 0.7; } | |
| .repo-tree .file-html { color: var(--violet); } | |
| .repo-indent { display: inline-block; } | |
| .repo-indent-1 { width: 16px; } | |
| .repo-indent-2 { width: 32px; } | |
| /* === BOTTOM STRIP === */ | |
| .bottom-strip { | |
| grid-column: 1 / -1; | |
| display: grid; | |
| grid-template-columns: 1fr 1fr 1fr; | |
| gap: 12px; | |
| } | |
| /* === ARCHITECTURE PROPOSITION === */ | |
| .proposition { | |
| grid-column: 1 / -1; | |
| text-align: center; | |
| padding: 20px; | |
| border-top: 1px solid var(--border-primary); | |
| margin-top: 4px; | |
| } | |
| .proposition-line { | |
| font-family: var(--serif); | |
| font-size: 0.72rem; | |
| color: var(--text-label); | |
| line-height: 2.2; | |
| font-style: italic; | |
| } | |
| .proposition-line .prop-subject { | |
| font-family: var(--mono); | |
| font-style: normal; | |
| font-size: 0.62rem; | |
| letter-spacing: 0.12em; | |
| text-transform: uppercase; | |
| color: var(--cyan-dim); | |
| } | |
| .proposition-line .prop-verb { | |
| color: var(--text-secondary); | |
| } | |
| /* === ANIMATIONS === */ | |
| @keyframes scan-line { | |
| 0% { transform: translateY(-100%); } | |
| 100% { transform: translateY(100%); } | |
| } | |
| @keyframes data-pulse { | |
| 0%, 100% { opacity: 0.3; } | |
| 50% { opacity: 0.8; } | |
| } | |
| @keyframes tape-scan { | |
| 0% { left: 0; } | |
| 100% { left: calc(100% - 28px); } | |
| } | |
| /* === SCROLLBAR === */ | |
| ::-webkit-scrollbar { width: 4px; } | |
| ::-webkit-scrollbar-track { background: var(--bg-primary); } | |
| ::-webkit-scrollbar-thumb { background: var(--cyan-dim); border-radius: 2px; } | |
| /* === RESPONSIVE === */ | |
| @media (max-width: 1400px) { | |
| .main { | |
| grid-template-columns: 1fr 340px 1fr; | |
| } | |
| } | |
| @media (max-width: 1100px) { | |
| .main { | |
| grid-template-columns: 1fr; | |
| } | |
| .helix-container { width: 100%; max-width: 380px; margin: 0 auto; } | |
| .bottom-strip { grid-template-columns: 1fr; } | |
| } | |
| /* === MISC === */ | |
| .glow-text { text-shadow: var(--glow-cyan); } | |
| .small-label { | |
| font-family: var(--mono); | |
| font-size: 0.48rem; | |
| letter-spacing: 0.15em; | |
| text-transform: uppercase; | |
| color: var(--text-label); | |
| } | |
| .divider { | |
| height: 1px; | |
| background: linear-gradient(90deg, transparent, var(--border-primary), transparent); | |
| margin: 8px 0; | |
| } | |
| .semantic-flow-label { | |
| font-family: var(--mono); | |
| font-size: 0.5rem; | |
| letter-spacing: 0.15em; | |
| text-transform: uppercase; | |
| color: var(--text-label); | |
| text-align: center; | |
| margin: 4px 0 2px; | |
| } | |
| .semantic-flow-arrow { | |
| text-align: center; | |
| color: var(--cyan-dim); | |
| font-size: 0.6rem; | |
| line-height: 1; | |
| } | |
| </style> | |
| </head> | |
| <body> | |
| <!-- HEADER --> | |
| <header class="header"> | |
| <div class="header-title">BIO-SEMANTICS</div> | |
| <div class="header-subtitle">Gene-Expression Semantic Modeling Pipeline</div> | |
| <div class="header-authors"> | |
| <span>Ahmad Ali Parr</span> | |
| <span class="sep">·</span> | |
| <span>Jessica L. Williams</span> | |
| <span class="sep">·</span> | |
| <span>SNAPKITTYWEST</span> | |
| </div> | |
| </header> | |
| <!-- NAV --> | |
| <nav class="nav"> | |
| <div class="nav-item active" data-target="observations">Observations</div> | |
| <div class="nav-item" data-target="semantics">Semantics</div> | |
| <div class="nav-item" data-target="bayesian">Bayesian</div> | |
| <div class="nav-item" data-target="constraints">Constraints</div> | |
| <div class="nav-item" data-target="binary">Binary</div> | |
| <div class="nav-item" data-target="ptm">PTM</div> | |
| <div class="nav-item" data-target="quipper">Quipper</div> | |
| <div class="nav-item" data-target="provenance">Provenance</div> | |
| <div class="nav-item" data-target="rpgjcl">RPG/JCL</div> | |
| </nav> | |
| <!-- MAIN --> | |
| <main class="main"> | |
| <!-- LEFT COLUMN --> | |
| <div class="left-column"> | |
| <!-- cDNA SEQUENCING --> | |
| <div class="panel" id="panel-cdna"> | |
| <div class="panel-title"><span class="indicator"></span>cDNA Sequencing Observations</div> | |
| <div class="seq-trace" id="seqTrace"></div> | |
| <div class="seq-reads" id="seqReads"></div> | |
| <div class="divider"></div> | |
| <div style="display:grid;grid-template-columns:1fr 1fr;gap:8px;font-family:var(--mono);font-size:0.54rem;"> | |
| <div><span class="small-label">coverage</span><br><span style="color:var(--cyan)">42.7×</span></div> | |
| <div><span class="small-label">reads</span><br><span style="color:var(--cyan)">2.4M</span></div> | |
| <div><span class="small-label">quality</span><br><span style="color:var(--green-bio)">Q38.2</span></div> | |
| <div><span class="small-label">samples</span><br><span style="color:var(--cyan)">48</span></div> | |
| </div> | |
| </div> | |
| <!-- GENE EXPRESSION MATRIX --> | |
| <div class="panel" id="panel-expression"> | |
| <div class="panel-title"><span class="indicator"></span>Gene Expression Matrix</div> | |
| <div style="display:flex;justify-content:space-between;margin-bottom:4px;"> | |
| <span class="expr-axis-label">← genes (n=2,847) →</span> | |
| <span class="expr-axis-label">samples ↓</span> | |
| </div> | |
| <div class="expr-matrix" id="exprMatrix"></div> | |
| <div class="divider"></div> | |
| <div style="display:flex;gap:12px;font-family:var(--mono);font-size:0.5rem;color:var(--text-label);"> | |
| <span>CONDITION <span style="color:var(--cyan)">4</span></span> | |
| <span>TIMEPOINT <span style="color:var(--cyan)">6</span></span> | |
| <span>REPLICATE <span style="color:var(--cyan)">2</span></span> | |
| <span>CELL TYPE <span style="color:var(--cyan)">3</span></span> | |
| </div> | |
| </div> | |
| <!-- DATA VALIDATION --> | |
| <div class="panel" id="panel-validation"> | |
| <div class="panel-title"><span class="indicator"></span>Data Validation · semantics.py</div> | |
| <div style="font-family:var(--mono);font-size:0.54rem;color:var(--text-label);margin-bottom:6px;">10 VALIDATOR RULES</div> | |
| <div class="validation-grid"> | |
| <div class="val-rule">version</div><div class="val-status pass">■ PASS</div> | |
| <div class="val-rule">discriminant</div><div class="val-status pass">■ PASS</div> | |
| <div class="val-rule">value/missing</div><div class="val-status pass">■ PASS</div> | |
| <div class="val-rule">raw count</div><div class="val-status pass">■ PASS</div> | |
| <div class="val-rule">decimal</div><div class="val-status pass">■ PASS</div> | |
| <div class="val-rule">reference</div><div class="val-status warn">■ WARN</div> | |
| <div class="val-rule">quantity kind</div><div class="val-status pass">■ PASS</div> | |
| <div class="val-rule">exclusion</div><div class="val-status pass">■ PASS</div> | |
| <div class="val-rule">source bytes</div><div class="val-status pass">■ PASS</div> | |
| <div class="val-rule">synthetic origin</div><div class="val-status pass">■ PASS</div> | |
| </div> | |
| </div> | |
| <!-- QUANTITY KIND --> | |
| <div class="panel" id="panel-qtykind"> | |
| <div class="panel-title"><span class="indicator"></span>Semantic Quantity Classification</div> | |
| <div class="qty-kinds"> | |
| <div class="qty-kind" data-kind="observed"> | |
| <div class="qty-glyph"><svg viewBox="0 0 16 16"><circle cx="8" cy="8" r="5" fill="none" stroke="#22cc66" stroke-width="1"/><circle cx="8" cy="8" r="1.5" fill="#22cc66"/></svg></div> | |
| <span class="qty-label">Observed</span> | |
| <span class="qty-source">measurement</span> | |
| </div> | |
| <div class="qty-kind" data-kind="inferred"> | |
| <div class="qty-glyph"><svg viewBox="0 0 16 16"><path d="M3,12 Q8,2 13,12" fill="none" stroke="#8844ff" stroke-width="1"/><line x1="8" y1="4" x2="8" y2="12" stroke="#8844ff" stroke-width="0.5" stroke-dasharray="1,1"/></svg></div> | |
| <span class="qty-label">Inferred</span> | |
| <span class="qty-source">posterior estimation</span> | |
| </div> | |
| <div class="qty-kind" data-kind="derived"> | |
| <div class="qty-glyph"><svg viewBox="0 0 16 16"><polyline points="3,12 8,4 13,12" fill="none" stroke="#00c8ff" stroke-width="1"/></svg></div> | |
| <span class="qty-label">Derived</span> | |
| <span class="qty-source">deterministic transform</span> | |
| </div> | |
| <div class="qty-kind" data-kind="predicted"> | |
| <div class="qty-glyph"><svg viewBox="0 0 16 16"><line x1="3" y1="10" x2="8" y2="6" stroke="#4488ff" stroke-width="1"/><line x1="8" y1="6" x2="13" y2="4" stroke="#4488ff" stroke-width="1" stroke-dasharray="2,1"/><circle cx="13" cy="4" r="1.5" fill="none" stroke="#4488ff" stroke-width="0.8"/></svg></div> | |
| <span class="qty-label">Predicted</span> | |
| <span class="qty-source">model output</span> | |
| </div> | |
| <div class="qty-kind" data-kind="constrained"> | |
| <div class="qty-glyph"><svg viewBox="0 0 16 16"><rect x="3" y="3" width="10" height="10" fill="none" stroke="#ffaa22" stroke-width="1" rx="1"/><line x1="6" y1="3" x2="6" y2="13" stroke="#ffaa22" stroke-width="0.5" stroke-dasharray="1,1"/><line x1="3" y1="8" x2="13" y2="8" stroke="#ffaa22" stroke-width="0.5" stroke-dasharray="1,1"/></svg></div> | |
| <span class="qty-label">Constrained</span> | |
| <span class="qty-source">MiniZinc</span> | |
| </div> | |
| <div class="qty-kind" data-kind="encoded"> | |
| <div class="qty-glyph"><svg viewBox="0 0 16 16"><text x="2" y="12" font-family="monospace" font-size="9" fill="#006688">01</text></svg></div> | |
| <span class="qty-label">Encoded</span> | |
| <span class="qty-source">representation</span> | |
| </div> | |
| <div class="qty-kind" data-kind="executed"> | |
| <div class="qty-glyph"><svg viewBox="0 0 16 16"><polygon points="5,3 13,8 5,13" fill="none" stroke="#22cc66" stroke-width="1"/></svg></div> | |
| <span class="qty-label">Executed</span> | |
| <span class="qty-source">run evidence</span> | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| <!-- CENTER COLUMN --> | |
| <div class="center-column"> | |
| <!-- HELIX --> | |
| <div class="helix-container"> | |
| <canvas id="helixCanvas"></canvas> | |
| <div class="helix-labels"> | |
| <div class="helix-label left" style="top:5%"><span class="hl-val">DNA</span></div> | |
| <div class="helix-label right" style="top:14%"><span class="hl-val">cDNA READS</span></div> | |
| <div class="helix-label left" style="top:24%"><span class="hl-val">EXPRESSION</span></div> | |
| <div class="helix-label right" style="top:34%"><span class="hl-val">SEMANTICS</span></div> | |
| <div class="helix-label left" style="top:44%"><span class="hl-val">BINARY</span></div> | |
| <div class="helix-label right" style="top:54%"><span class="hl-val">PTM STATES</span></div> | |
| <div class="helix-label left" style="top:64%"><span class="hl-val">POSTERIOR</span></div> | |
| <div class="helix-label right" style="top:74%"><span class="hl-val">CONSTRAINTS</span></div> | |
| <div class="helix-label left" style="top:86%"><span class="hl-val">MODEL</span></div> | |
| </div> | |
| </div> | |
| <!-- PIPELINE --> | |
| <div class="pipeline-flow"> | |
| <div class="pipeline-node" data-stage="bio">Biological Observations</div> | |
| <div class="pipeline-connector"></div> | |
| <div class="pipeline-node" data-stage="cdna">cDNA Expression Data</div> | |
| <div class="pipeline-connector"></div> | |
| <div class="pipeline-node" data-stage="validation">Data Validation</div> | |
| <div class="pipeline-connector"></div> | |
| <div class="pipeline-node" data-stage="expression">Gene Expression</div> | |
| <div class="pipeline-connector"></div> | |
| <div class="pipeline-node" data-stage="stan">Stan Model</div> | |
| <div class="pipeline-connector"></div> | |
| <div class="pipeline-node" data-stage="posterior">Posterior State</div> | |
| <div class="pipeline-connector"></div> | |
| <div class="pipeline-node" data-stage="minizinc">MiniZinc Model</div> | |
| <div class="pipeline-connector"></div> | |
| <div class="pipeline-node" data-stage="result">Computational Result</div> | |
| <div class="pipeline-connector"></div> | |
| <div class="pipeline-node" data-stage="viz">Visualizer</div> | |
| <div class="pipeline-connector"></div> | |
| <div class="pipeline-node" data-stage="model">Human-Readable Model</div> | |
| </div> | |
| </div> | |
| <!-- RIGHT COLUMN --> | |
| <div class="right-column"> | |
| <!-- STAN --> | |
| <div class="panel" id="panel-stan"> | |
| <div class="panel-title"><span class="indicator"></span>Bayesian Inference · Stan</div> | |
| <div class="stan-flow"> | |
| <span class="stan-flow-node">Prior</span> | |
| <span class="stan-flow-arrow">→</span> | |
| <span class="stan-flow-node">Likelihood</span> | |
| <span class="stan-flow-arrow">→</span> | |
| <span class="stan-flow-node">Posterior</span> | |
| </div> | |
| <div class="stan-chart"><canvas id="stanCanvas"></canvas></div> | |
| <table class="stan-table"> | |
| <thead> | |
| <tr><th>Parameter</th><th>Mean</th><th>Median</th><th>σ</th><th>R̂</th><th>ESS</th></tr> | |
| </thead> | |
| <tbody> | |
| <tr><td>α</td><td class="num">2.347</td><td class="num">2.341</td><td class="num">0.128</td><td class="num">1.001</td><td class="num">4200</td></tr> | |
| <tr><td>β₁</td><td class="num">-0.892</td><td class="num">-0.887</td><td class="num">0.064</td><td class="num">1.002</td><td class="num">3800</td></tr> | |
| <tr><td>β₂</td><td class="num">1.156</td><td class="num">1.149</td><td class="num">0.091</td><td class="num">1.001</td><td class="num">4100</td></tr> | |
| <tr><td>σ</td><td class="num">0.423</td><td class="num">0.419</td><td class="num">0.032</td><td class="num">1.000</td><td class="num">4500</td></tr> | |
| <tr><td>φ</td><td class="num">0.067</td><td class="num">0.065</td><td class="num">0.018</td><td class="num">1.003</td><td class="num">3600</td></tr> | |
| </tbody> | |
| </table> | |
| <div style="font-family:var(--mono);font-size:0.48rem;color:var(--text-label);margin-top:6px;text-align:right;letter-spacing:0.08em;">RUN ID <span style="color:var(--cyan-dim)">BSM-2026-0908-001</span></div> | |
| </div> | |
| <!-- MINIZINC --> | |
| <div class="panel" id="panel-minizinc"> | |
| <div class="panel-title"><span class="indicator"></span>Constraint Solver · MiniZinc</div> | |
| <div class="mzn-graph"><canvas id="mznCanvas"></canvas></div> | |
| <div class="mzn-notation"> | |
| minimize <span style="color:var(--cyan)">f</span>(<span style="color:var(--violet)">x</span>) subject to <span style="color:var(--amber)">C</span><sub>1</sub> ∧ <span style="color:var(--amber)">C</span><sub>2</sub> ∧ … ∧ <span style="color:var(--amber)">C</span><sub><span style="font-style:normal;color:var(--text-label)">k</span></sub> | |
| </div> | |
| <div style="display:flex;justify-content:center;gap:16px;font-family:var(--mono);font-size:0.5rem;color:var(--text-label);letter-spacing:0.1em;text-transform:uppercase;"> | |
| <span>Variables <span style="color:var(--cyan)">12</span></span> | |
| <span>Domains <span style="color:var(--cyan)">3</span></span> | |
| <span>Constraints <span style="color:var(--amber)">8</span></span> | |
| <span>Objective <span style="color:var(--violet)">1</span></span> | |
| </div> | |
| </div> | |
| <!-- QUIPPER --> | |
| <div class="panel" id="panel-quipper"> | |
| <div class="panel-title"><span class="indicator"></span>Quantum Circuit · Quipper</div> | |
| <div class="semantic-flow-label">semantic transformation → quantum circuit → quipper</div> | |
| <div class="quipper-circuit"><canvas id="quipperCanvas"></canvas></div> | |
| <div class="quipper-stats"> | |
| <div class="quipper-stat"> | |
| <div class="qs-val">5</div> | |
| <div class="qs-label">Qubits</div> | |
| </div> | |
| <div class="quipper-stat"> | |
| <div class="qs-val">14</div> | |
| <div class="qs-label">Gate Count</div> | |
| </div> | |
| <div class="quipper-stat"> | |
| <div class="qs-val">7</div> | |
| <div class="qs-label">Circuit Depth</div> | |
| </div> | |
| </div> | |
| </div> | |
| <!-- PTM --> | |
| <div class="panel" id="panel-ptm"> | |
| <div class="panel-title"><span class="indicator"></span>PTM-BIT-PARITY/1</div> | |
| <div class="ptm-states" id="ptmStates"> | |
| <div class="ptm-state current" data-state="q_even">q<sub>even</sub></div> | |
| <div class="ptm-state" data-state="q_odd">q<sub>odd</sub></div> | |
| <div class="ptm-state accept" data-state="q_accept">q<sub>accept</sub></div> | |
| </div> | |
| <div class="ptm-tape" id="ptmTape"></div> | |
| <div class="divider"></div> | |
| <table class="ptm-transition-table"> | |
| <thead><tr><th>State</th><th>Read</th><th>Write</th><th>Move</th><th>Next</th></tr></thead> | |
| <tbody id="ptmTransitions"> | |
| <tr><td>q<sub>even</sub></td><td>0</td><td>0</td><td>R</td><td>q<sub>even</sub></td></tr> | |
| <tr class="active-row"><td>q<sub>even</sub></td><td>1</td><td>1</td><td>R</td><td>q<sub>odd</sub></td></tr> | |
| <tr><td>q<sub>odd</sub></td><td>0</td><td>0</td><td>R</td><td>q<sub>odd</sub></td></tr> | |
| <tr><td>q<sub>odd</sub></td><td>1</td><td>1</td><td>R</td><td>q<sub>even</sub></td></tr> | |
| <tr><td>q<sub>even</sub></td><td>□</td><td>□</td><td>—</td><td>q<sub>accept</sub></td></tr> | |
| </tbody> | |
| </table> | |
| <div class="ptm-complexity"> | |
| <span>8</span> input symbols · <span>9</span> transitions · <span>O(1)</span> auxiliary storage | |
| </div> | |
| </div> | |
| </div> | |
| <!-- BOTTOM STRIP --> | |
| <div class="bottom-strip"> | |
| <!-- BINARY SEMANTICS --> | |
| <div class="panel" id="panel-binary"> | |
| <div class="panel-title"><span class="indicator"></span>Binary Semantics</div> | |
| <div class="semantic-flow-label">semantic value → encoding → bits → state → transition</div> | |
| <div class="binary-stream" id="binaryStream"></div> | |
| <table class="binary-table"> | |
| <thead><tr><th>State ID</th><th>Tape Pos</th><th>Symbol</th><th>Transition</th><th>Next</th></tr></thead> | |
| <tbody> | |
| <tr><td>S₀</td><td>0x0000</td><td>0</td><td>δ(S₀,0)</td><td>S₀</td></tr> | |
| <tr><td>S₀</td><td>0x0001</td><td>1</td><td>δ(S₀,1)</td><td>S₁</td></tr> | |
| <tr><td>S₁</td><td>0x0002</td><td>0</td><td>δ(S₁,0)</td><td>S₁</td></tr> | |
| <tr><td>S₁</td><td>0x0003</td><td>1</td><td>δ(S₁,1)</td><td>S₀</td></tr> | |
| </tbody> | |
| </table> | |
| </div> | |
| <!-- PROVENANCE --> | |
| <div class="panel" id="panel-provenance"> | |
| <div class="panel-title"><span class="indicator"></span>Immutable Artifact Provenance</div> | |
| <div class="prov-chain" id="provChain"></div> | |
| </div> | |
| <!-- RPG/JCL + REPO --> | |
| <div class="panel" id="panel-rpg"> | |
| <div class="panel-title"><span class="indicator"></span>RPG/JCL · Legacy Integration</div> | |
| <div class="rpg-block"> | |
| <span class="rpg-comment">//* BIO-SEMANTICS VAULT CONTROL</span> | |
| <span class="rpg-kw">//BSEM001</span> <span class="rpg-kw">JOB</span> <span class="rpg-id">BIOSEM,CLASS=A</span> | |
| <span class="rpg-kw">//STEP01</span> <span class="rpg-kw">EXEC</span> <span class="rpg-id">PGM=BSEMVLT</span> | |
| <span class="rpg-kw">//VAULT</span> <span class="rpg-kw">DD</span> <span class="rpg-id">DSN=BSEM.PROV.VAULT,DISP=SHR</span> | |
| <span class="rpg-kw">//ARCHIVE</span> <span class="rpg-kw">DD</span> <span class="rpg-id">DSN=BSEM.ARCH.V001,DISP=NEW</span> | |
| <span class="rpg-kw">//SYSPRINT</span> <span class="rpg-kw">DD</span> <span class="rpg-id">SYSOUT=*</span> | |
| </div> | |
| <div class="rpg-job-status"> | |
| <span class="rpg-job-id">JOB0001</span><span class="rpg-job-name">BSEM Vault Seal</span><span class="rpg-job-state complete">COMPLETE</span> | |
| <span class="rpg-job-id">JOB0002</span><span class="rpg-job-name">Provenance Archive</span><span class="rpg-job-state complete">COMPLETE</span> | |
| <span class="rpg-job-id">JOB0003</span><span class="rpg-job-name">Artifact Verify</span><span class="rpg-job-state running">RUNNING</span> | |
| <span class="rpg-job-id">JOB0004</span><span class="rpg-job-name">Batch Export</span><span class="rpg-job-state queued">QUEUED</span> | |
| </div> | |
| <div class="divider"></div> | |
| <div class="panel-title" style="margin-top:6px;"><span class="indicator"></span>Repository Structure</div> | |
| <div class="repo-tree"> | |
| <span class="dir">bio-semantics/</span><br> | |
| <span class="repo-indent repo-indent-1"></span><span class="dir">src/</span><br> | |
| <span class="repo-indent repo-indent-2"></span><span class="file file-py">semantics.py</span><br> | |
| <span class="repo-indent repo-indent-2"></span><span class="file file-py">test_semantics.py</span><br> | |
| <span class="repo-indent repo-indent-2"></span><span class="file file-html">dashboard.html</span><br> | |
| <span class="repo-indent repo-indent-1"></span><span class="dir">spec/</span><br> | |
| <span class="repo-indent repo-indent-2"></span><span class="file file-ts">types.ts</span><br> | |
| <span class="repo-indent repo-indent-2"></span><span class="file file-md">contract.md</span><br> | |
| <span class="repo-indent repo-indent-2"></span><span class="file file-md">system_prompt.md</span><br> | |
| <span class="repo-indent repo-indent-1"></span><span class="dir">templates/</span><br> | |
| <span class="repo-indent repo-indent-2"></span><span class="file file-rpg">workflow.rpgle</span><br> | |
| <span class="repo-indent repo-indent-2"></span><span class="file file-jcl">vault.jcl</span> | |
| </div> | |
| </div> | |
| </div> | |
| <!-- PROPOSITION --> | |
| <div class="proposition"> | |
| <div class="proposition-line"><span class="prop-subject">Biological Observations</span> <span class="prop-verb">are not the same thing as</span> <span class="prop-subject">Model Inference</span></div> | |
| <div class="proposition-line"><span class="prop-subject">Model Inference</span> <span class="prop-verb">is not the same thing as</span> <span class="prop-subject">Semantic Transformation</span></div> | |
| <div class="proposition-line"><span class="prop-subject">Semantic Transformation</span> <span class="prop-verb">is not the same thing as</span> <span class="prop-subject">Binary Representation</span></div> | |
| <div class="proposition-line"><span class="prop-subject">Binary Representation</span> <span class="prop-verb">can be represented as</span> <span class="prop-subject">Formal Machine State</span></div> | |
| <div class="proposition-line"><span class="prop-subject">Formal Machine State</span> <span class="prop-verb">can be verified through</span> <span class="prop-subject">PTM Transitions</span></div> | |
| <div class="proposition-line"><span class="prop-subject">Selected Transformations</span> <span class="prop-verb">can be represented through</span> <span class="prop-subject">Quipper</span></div> | |
| <div class="proposition-line"><span class="prop-subject">RPG and JCL</span> <span class="prop-verb">provide</span> <span class="prop-subject">Integration Infrastructure</span></div> | |
| <div class="proposition-line"><span class="prop-subject">Immutable Provenance</span> <span class="prop-verb">connects</span> <span class="prop-subject">Every Artifact</span></div> | |
| </div> | |
| </main> | |
| <script> | |
| // ===================================================== | |
| // BIO-SEMANTICS COMPUTATIONAL FRONTEND | |
| // ===================================================== | |
| const C = { | |
| cyan: '#00c8ff', | |
| cyanDim: '#006688', | |
| blue: '#2266ff', | |
| blueBright: '#4488ff', | |
| violet: '#8844ff', | |
| violetDim: '#5522aa', | |
| greenBio: '#22cc66', | |
| greenDim: '#0a6630', | |
| amber: '#ffaa22', | |
| red: '#ff3344', | |
| white: '#e8eef6', | |
| whiteDim: '#8899aa', | |
| bg: '#030810', | |
| textLabel: '#4466888' | |
| }; | |
| // ===================================================== | |
| // DNA HELIX | |
| // ===================================================== | |
| (function initHelix() { | |
| const canvas = document.getElementById('helixCanvas'); | |
| const ctx = canvas.getContext('2d'); | |
| let W, H, dpr; | |
| function resize() { | |
| const rect = canvas.parentElement.getBoundingClientRect(); | |
| dpr = window.devicePixelRatio || 1; | |
| W = rect.width; | |
| H = rect.height; | |
| canvas.width = W * dpr; | |
| canvas.height = H * dpr; | |
| ctx.setTransform(dpr, 0, 0, dpr, 0, 0); | |
| } | |
| resize(); | |
| window.addEventListener('resize', resize); | |
| const BASES = ['A','T','G','C']; | |
| const BASE_COLORS = { A: C.greenBio, T: C.red, G: C.amber, C: C.blueBright }; | |
| const COMPLEMENT = { A: 'T', T: 'A', G: 'C', C: 'G' }; | |
| let time = 0; | |
| const particles = []; | |
| for (let i = 0; i < 80; i++) { | |
| particles.push({ | |
| y: Math.random() * 1, | |
| x: (Math.random() - 0.5) * 0.4, | |
| speed: 0.0002 + Math.random() * 0.0003, | |
| size: 0.5 + Math.random() * 1.5, | |
| alpha: 0.1 + Math.random() * 0.3 | |
| }); | |
| } | |
| function draw() { | |
| ctx.clearRect(0, 0, W, H); | |
| const cx = W / 2; | |
| const amplitude = 45; | |
| const rungs = 60; | |
| const twist = 0.12; | |
| const yStart = 20; | |
| const yEnd = H - 20; | |
| const totalH = yEnd - yStart; | |
| // Transformation zones | |
| const zones = [ | |
| { start: 0, end: 0.12, color1: C.greenBio, color2: C.greenBio, label: 'DNA', rungStyle: 'bio' }, | |
| { start: 0.12, end: 0.22, color1: C.greenBio, color2: C.cyan, label: 'cDNA', rungStyle: 'reads' }, | |
| { start: 0.22, end: 0.32, color1: C.cyan, color2: C.cyan, label: 'EXPR', rungStyle: 'expr' }, | |
| { start: 0.32, end: 0.42, color1: C.cyan, color2: C.violet, label: 'SEM', rungStyle: 'semantic' }, | |
| { start: 0.42, end: 0.52, color1: C.violet, color2: C.cyanDim, label: 'BIN', rungStyle: 'binary' }, | |
| { start: 0.52, end: 0.62, color1: C.cyanDim, color2: C.cyan, label: 'PTM', rungStyle: 'state' }, | |
| { start: 0.62, end: 0.72, color1: C.cyan, color2: C.violet, label: 'POST', rungStyle: 'posterior' }, | |
| { start: 0.72, end: 0.84, color1: C.violet, color2: C.amber, label: 'CST', rungStyle: 'constraint' }, | |
| { start: 0.84, end: 1.00, color1: C.amber, color2: C.white, label: 'MDL', rungStyle: 'model' }, | |
| ]; | |
| function getZone(t) { | |
| for (const z of zones) { | |
| if (t >= z.start && t < z.end) return z; | |
| } | |
| return zones[zones.length - 1]; | |
| } | |
| function lerpColor(c1, c2, t) { | |
| const r1 = parseInt(c1.slice(1,3),16), g1 = parseInt(c1.slice(3,5),16), b1 = parseInt(c1.slice(5,7),16); | |
| const r2 = parseInt(c2.slice(1,3),16), g2 = parseInt(c2.slice(3,5),16), b2 = parseInt(c2.slice(5,7),16); | |
| const r = Math.round(r1 + (r2-r1)*t), g = Math.round(g1 + (g2-g1)*t), b = Math.round(b1 + (b2-b1)*t); | |
| return `rgb(${r},${g},${b})`; | |
| } | |
| // Draw backbone strands | |
| ctx.lineWidth = 1.2; | |
| for (let strand = 0; strand < 2; strand++) { | |
| ctx.beginPath(); | |
| for (let i = 0; i <= 200; i++) { | |
| const t = i / 200; | |
| const y = yStart + t * totalH; | |
| const phase = t * rungs * twist + time + (strand * Math.PI); | |
| const x = cx + Math.sin(phase) * amplitude; | |
| const zone = getZone(t); | |
| const zt = (t - zone.start) / (zone.end - zone.start); | |
| const color = lerpColor(zone.color1, zone.color2, zt); | |
| if (i === 0) { | |
| ctx.moveTo(x, y); | |
| } else { | |
| ctx.lineTo(x, y); | |
| } | |
| if (i > 0 && i % 2 === 0) { | |
| ctx.strokeStyle = color; | |
| ctx.globalAlpha = 0.7; | |
| ctx.stroke(); | |
| ctx.beginPath(); | |
| ctx.moveTo(x, y); | |
| } | |
| } | |
| ctx.stroke(); | |
| } | |
| ctx.globalAlpha = 1; | |
| // Draw rungs / connections | |
| for (let i = 0; i < rungs; i++) { | |
| const t = i / rungs; | |
| const y = yStart + t * totalH; | |
| const phase = t * rungs * twist + time; | |
| const x1 = cx + Math.sin(phase) * amplitude; | |
| const x2 = cx + Math.sin(phase + Math.PI) * amplitude; | |
| const depth = Math.cos(phase); | |
| const zone = getZone(t); | |
| if (depth > -0.2) { | |
| const alpha = 0.15 + depth * 0.35; | |
| ctx.globalAlpha = alpha; | |
| if (zone.rungStyle === 'bio') { | |
| const base = BASES[i % 4]; | |
| const comp = COMPLEMENT[base]; | |
| ctx.strokeStyle = BASE_COLORS[base]; | |
| ctx.lineWidth = 1; | |
| ctx.beginPath(); | |
| ctx.moveTo(x1, y); | |
| ctx.lineTo(cx, y); | |
| ctx.stroke(); | |
| ctx.strokeStyle = BASE_COLORS[comp]; | |
| ctx.beginPath(); | |
| ctx.moveTo(cx, y); | |
| ctx.lineTo(x2, y); | |
| ctx.stroke(); | |
| // Base pair dots | |
| ctx.fillStyle = BASE_COLORS[base]; | |
| ctx.beginPath(); | |
| ctx.arc(cx - 4, y, 1.5, 0, Math.PI*2); | |
| ctx.fill(); | |
| ctx.fillStyle = BASE_COLORS[comp]; | |
| ctx.beginPath(); | |
| ctx.arc(cx + 4, y, 1.5, 0, Math.PI*2); | |
| ctx.fill(); | |
| } else if (zone.rungStyle === 'binary') { | |
| ctx.strokeStyle = C.cyanDim; | |
| ctx.lineWidth = 0.5; | |
| ctx.beginPath(); | |
| ctx.moveTo(x1, y); | |
| ctx.lineTo(x2, y); | |
| ctx.stroke(); | |
| // Binary values | |
| ctx.fillStyle = C.cyan; | |
| ctx.font = '8px "IBM Plex Mono"'; | |
| ctx.textAlign = 'center'; | |
| const bit = (i + Math.floor(time * 2)) % 2; | |
| ctx.fillText(bit.toString(), cx, y + 3); | |
| } else if (zone.rungStyle === 'state') { | |
| ctx.strokeStyle = C.cyan; | |
| ctx.lineWidth = 0.5; | |
| ctx.setLineDash([2, 3]); | |
| ctx.beginPath(); | |
| ctx.moveTo(x1, y); | |
| ctx.lineTo(x2, y); | |
| ctx.stroke(); | |
| ctx.setLineDash([]); | |
| // State node | |
| ctx.strokeStyle = C.cyan; | |
| ctx.lineWidth = 0.8; | |
| ctx.beginPath(); | |
| ctx.arc(cx, y, 4, 0, Math.PI*2); | |
| ctx.stroke(); | |
| } else if (zone.rungStyle === 'posterior') { | |
| const zt = (t - zone.start) / (zone.end - zone.start); | |
| ctx.strokeStyle = lerpColor(C.cyan, C.violet, zt); | |
| ctx.lineWidth = 0.8; | |
| // Probability curve fragment | |
| const mid = (x1 + x2) / 2; | |
| ctx.beginPath(); | |
| ctx.moveTo(x1, y); | |
| const cpx = mid; | |
| const cpy = y - 5 * Math.sin(i * 0.8); | |
| ctx.quadraticCurveTo(cpx, cpy, x2, y); | |
| ctx.stroke(); | |
| } else if (zone.rungStyle === 'constraint') { | |
| ctx.strokeStyle = C.amber; | |
| ctx.lineWidth = 0.5; | |
| ctx.beginPath(); | |
| ctx.moveTo(x1, y); | |
| ctx.lineTo(x2, y); | |
| ctx.stroke(); | |
| // Constraint node | |
| ctx.strokeStyle = C.amber; | |
| ctx.lineWidth = 0.6; | |
| const s = 3; | |
| ctx.strokeRect(cx - s, y - s, s * 2, s * 2); | |
| } else { | |
| const zt = (t - zone.start) / (zone.end - zone.start); | |
| ctx.strokeStyle = lerpColor(zone.color1, zone.color2, zt); | |
| ctx.lineWidth = 0.6; | |
| ctx.beginPath(); | |
| ctx.moveTo(x1, y); | |
| ctx.lineTo(x2, y); | |
| ctx.stroke(); | |
| } | |
| } | |
| } | |
| ctx.globalAlpha = 1; | |
| // Flowing particles | |
| for (const p of particles) { | |
| p.y += p.speed; | |
| if (p.y > 1) p.y -= 1; | |
| const y = yStart + p.y * totalH; | |
| const phase = p.y * rungs * twist + time; | |
| const x = cx + Math.sin(phase + p.x * 6) * (amplitude * 0.6 + p.x * amplitude); | |
| const zone = getZone(p.y); | |
| const zt = (p.y - zone.start) / (zone.end - zone.start); | |
| ctx.fillStyle = lerpColor(zone.color1, zone.color2, zt); | |
| ctx.globalAlpha = p.alpha; | |
| ctx.beginPath(); | |
| ctx.arc(x, y, p.size, 0, Math.PI*2); | |
| ctx.fill(); | |
| } | |
| ctx.globalAlpha = 1; | |
| // Scanline | |
| const scanY = yStart + ((time * 40) % totalH); | |
| const scanGrad = ctx.createLinearGradient(0, scanY - 15, 0, scanY + 15); | |
| scanGrad.addColorStop(0, 'rgba(0,200,255,0)'); | |
| scanGrad.addColorStop(0.5, 'rgba(0,200,255,0.05)'); | |
| scanGrad.addColorStop(1, 'rgba(0,200,255,0)'); | |
| ctx.fillStyle = scanGrad; | |
| ctx.fillRect(0, scanY - 15, W, 30); | |
| time += 0.008; | |
| requestAnimationFrame(draw); | |
| } | |
| draw(); | |
| })(); | |
| // ===================================================== | |
| // SEQUENCING TRACE | |
| // ===================================================== | |
| (function initSeqTrace() { | |
| const container = document.getElementById('seqTrace'); | |
| const bases = 'ATCGATCGATTGCCATGCAATCGGATCGATTGCCATGCAATCGGATCGATTGCCATGCAATCGGATCGATCGATCGATCGGA'; | |
| const baseClass = { A: 'a', T: 't', G: 'g', C: 'c' }; | |
| for (let i = 0; i < bases.length; i++) { | |
| const bar = document.createElement('div'); | |
| bar.className = `seq-bar ${baseClass[bases[i]]}`; | |
| bar.style.height = (8 + Math.random() * 24) + 'px'; | |
| container.appendChild(bar); | |
| } | |
| const readsEl = document.getElementById('seqReads'); | |
| const reads = [ | |
| 'ATCGATCGATTGCCATGCAA', | |
| ' GATCGATTGCCATGCAATCGG', | |
| ' CGATTGCCATGCAATCGGATCG', | |
| ' TTGCCATGCAATCGGATCGATT' | |
| ]; | |
| reads.forEach(read => { | |
| const line = document.createElement('div'); | |
| let html = ''; | |
| for (const ch of read) { | |
| if (ch === ' ') { html += ' '; continue; } | |
| const cls = baseClass[ch] || ''; | |
| html += `<span class="base ${cls}">${ch}</span>`; | |
| } | |
| line.innerHTML = html; | |
| readsEl.appendChild(line); | |
| }); | |
| })(); | |
| // ===================================================== | |
| // EXPRESSION MATRIX | |
| // ===================================================== | |
| (function initExprMatrix() { | |
| const container = document.getElementById('exprMatrix'); | |
| const rows = 16; | |
| const cols = 24; | |
| container.style.gridTemplateColumns = `repeat(${cols}, 1fr)`; | |
| for (let r = 0; r < rows; r++) { | |
| for (let c = 0; c < cols; c++) { | |
| const cell = document.createElement('div'); | |
| cell.className = 'expr-cell'; | |
| const v = Math.random(); | |
| const hue = 200 + v * 80; | |
| const sat = 60 + v * 30; | |
| const light = 5 + v * 35; | |
| cell.style.background = `hsl(${hue}, ${sat}%, ${light}%)`; | |
| container.appendChild(cell); | |
| } | |
| } | |
| })(); | |
| // ===================================================== | |
| // BINARY STREAM | |
| // ===================================================== | |
| (function initBinaryStream() { | |
| const container = document.getElementById('binaryStream'); | |
| let html = ''; | |
| for (let i = 0; i < 256; i++) { | |
| const bit = Math.random() > 0.5 ? 1 : 0; | |
| html += `<span class="${bit ? 'one' : 'zero'}">${bit}</span>`; | |
| if ((i + 1) % 8 === 0) html += ' '; | |
| } | |
| container.innerHTML = html; | |
| // Animate binary stream | |
| setInterval(() => { | |
| const spans = container.querySelectorAll('span'); | |
| const idx = Math.floor(Math.random() * spans.length); | |
| if (spans[idx] && spans[idx].textContent.trim().length === 1) { | |
| const newBit = Math.random() > 0.5 ? 1 : 0; | |
| spans[idx].textContent = newBit.toString(); | |
| spans[idx].className = newBit ? 'one' : 'zero'; | |
| } | |
| }, 100); | |
| })(); | |
| // ===================================================== | |
| // PTM TAPE | |
| // ===================================================== | |
| (function initPTM() { | |
| const container = document.getElementById('ptmTape'); | |
| const tape = [0, 1, 0, 1, 1, 0, 1, 0]; | |
| let headPos = 0; | |
| let state = 'q_even'; | |
| function render() { | |
| container.innerHTML = ''; | |
| tape.forEach((bit, i) => { | |
| const cell = document.createElement('div'); | |
| cell.className = 'ptm-cell' + (i === headPos ? ' active' : ''); | |
| cell.textContent = bit; | |
| if (i === headPos) { | |
| const head = document.createElement('div'); | |
| head.className = 'ptm-head'; | |
| cell.appendChild(head); | |
| } | |
| container.appendChild(cell); | |
| }); | |
| document.querySelectorAll('.ptm-state').forEach(el => { | |
| el.classList.toggle('current', el.dataset.state === state); | |
| }); | |
| } | |
| render(); | |
| setInterval(() => { | |
| if (headPos < tape.length) { | |
| const bit = tape[headPos]; | |
| if (bit === 1) { | |
| state = state === 'q_even' ? 'q_odd' : 'q_even'; | |
| } | |
| headPos++; | |
| if (headPos >= tape.length) { | |
| state = 'q_accept'; | |
| setTimeout(() => { headPos = 0; state = 'q_even'; render(); }, 2000); | |
| } | |
| render(); | |
| } | |
| }, 800); | |
| })(); | |
| // ===================================================== | |
| // PROVENANCE CHAIN | |
| // ===================================================== | |
| (function initProvenance() { | |
| const container = document.getElementById('provChain'); | |
| const chain = [ | |
| { name: 'Source', hash: 'a7f3e9c2d1b8' }, | |
| { name: 'Hash', hash: '4e2a8f1c7d39' }, | |
| { name: 'Semantics', hash: 'b8d4f2e6a1c5' }, | |
| { name: 'Encoding', hash: 'c1e7a3f9d5b2' }, | |
| { name: 'PTM', hash: '3f9b2e8c1d7a' }, | |
| { name: 'Stan', hash: 'd5a1c8f3e7b4' }, | |
| { name: 'MiniZinc', hash: 'e2c6b9d4f1a8' }, | |
| { name: 'Quipper', hash: 'f8d3a7e1c2b5' }, | |
| { name: 'Result', hash: '1b5e9a3f7d2c' }, | |
| ]; | |
| chain.forEach((item, i) => { | |
| const node = document.createElement('div'); | |
| node.className = 'prov-node'; | |
| node.innerHTML = ` | |
| <div class="prov-icon"></div> | |
| <div class="prov-name">${item.name}</div> | |
| <div class="prov-hash">${item.hash}…${(parseInt(item.hash.slice(0,4),16) % 9999).toString().padStart(4,'0')}</div> | |
| `; | |
| node.addEventListener('click', () => { | |
| const existing = node.nextElementSibling; | |
| if (existing && existing.classList.contains('prov-detail')) { | |
| existing.style.display = existing.style.display === 'none' ? 'block' : 'none'; | |
| return; | |
| } | |
| const detail = document.createElement('div'); | |
| detail.className = 'prov-detail'; | |
| detail.style.display = 'block'; | |
| detail.innerHTML = ` | |
| <span class="pd-key">artifact id</span> <span class="pd-val">BSM-${item.name.toUpperCase()}-${String(i+1).padStart(3,'0')}</span><br> | |
| <span class="pd-key">hash</span> <span class="pd-val">sha256:${item.hash}e4f8a2c1d7b3…</span><br> | |
| <span class="pd-key">version</span> <span class="pd-val">1.0.${i}</span><br> | |
| <span class="pd-key">parent</span> <span class="pd-val">${i > 0 ? chain[i-1].hash + '…' : '∅ (root)'}</span><br> | |
| <span class="pd-key">timestamp</span> <span class="pd-val">2026-09-08T${String(10+i).padStart(2,'0')}:${String(i*7%60).padStart(2,'0')}:00Z</span><br> | |
| <span class="pd-key">status</span> <span class="pd-val" style="color:#22cc66">SEALED</span> | |
| `; | |
| node.after(detail); | |
| }); | |
| container.appendChild(node); | |
| if (i < chain.length - 1) { | |
| const conn = document.createElement('div'); | |
| conn.className = 'prov-connector'; | |
| container.appendChild(conn); | |
| } | |
| }); | |
| })(); | |
| // ===================================================== | |
| // STAN POSTERIOR VISUALIZATION | |
| // ===================================================== | |
| (function initStan() { | |
| const canvas = document.getElementById('stanCanvas'); | |
| const ctx = canvas.getContext('2d'); | |
| const rect = canvas.parentElement.getBoundingClientRect(); | |
| const dpr = window.devicePixelRatio || 1; | |
| canvas.width = rect.width * dpr; | |
| canvas.height = rect.height * dpr; | |
| ctx.setTransform(dpr, 0, 0, dpr, 0, 0); | |
| const W = rect.width; | |
| const H = rect.height; | |
| function gaussian(x, mu, sigma) { | |
| return Math.exp(-0.5 * ((x - mu) / sigma) ** 2) / (sigma * Math.sqrt(2 * Math.PI)); | |
| } | |
| // Posterior samples | |
| const samples = []; | |
| for (let i = 0; i < 200; i++) { | |
| samples.push({ | |
| x: 0.15 + Math.random() * 0.7, | |
| y: Math.random(), | |
| alpha: 0.05 + Math.random() * 0.15, | |
| size: 0.8 + Math.random() * 1.2 | |
| }); | |
| } | |
| let time = 0; | |
| function draw() { | |
| ctx.clearRect(0, 0, W, H); | |
| // Posterior sample particles | |
| samples.forEach(s => { | |
| ctx.globalAlpha = s.alpha * (0.5 + 0.5 * Math.sin(time * 0.5 + s.x * 10)); | |
| ctx.fillStyle = C.violet; | |
| ctx.beginPath(); | |
| ctx.arc(s.x * W, (0.3 + s.y * 0.6) * H, s.size, 0, Math.PI * 2); | |
| ctx.fill(); | |
| }); | |
| ctx.globalAlpha = 1; | |
| // Prior (wider, dimmer) | |
| ctx.beginPath(); | |
| ctx.strokeStyle = C.cyanDim; | |
| ctx.lineWidth = 0.8; | |
| ctx.setLineDash([3, 3]); | |
| for (let i = 0; i <= 100; i++) { | |
| const x = i / 100; | |
| const val = gaussian(x, 0.5, 0.2); | |
| const px = x * W; | |
| const py = H - (val / 2.5) * H * 0.8 - 5; | |
| if (i === 0) ctx.moveTo(px, py); else ctx.lineTo(px, py); | |
| } | |
| ctx.stroke(); | |
| ctx.setLineDash([]); | |
| // Likelihood | |
| ctx.beginPath(); | |
| ctx.strokeStyle = C.blue; | |
| ctx.lineWidth = 0.8; | |
| ctx.setLineDash([1, 2]); | |
| for (let i = 0; i <= 100; i++) { | |
| const x = i / 100; | |
| const val = gaussian(x, 0.55, 0.12); | |
| const px = x * W; | |
| const py = H - (val / 3.5) * H * 0.8 - 5; | |
| if (i === 0) ctx.moveTo(px, py); else ctx.lineTo(px, py); | |
| } | |
| ctx.stroke(); | |
| ctx.setLineDash([]); | |
| // Posterior (sharp, bright) | |
| ctx.beginPath(); | |
| ctx.strokeStyle = C.cyan; | |
| ctx.lineWidth = 1.5; | |
| for (let i = 0; i <= 100; i++) { | |
| const x = i / 100; | |
| const val = gaussian(x, 0.53, 0.09); | |
| const px = x * W; | |
| const py = H - (val / 4.5) * H * 0.8 - 5; | |
| if (i === 0) ctx.moveTo(px, py); else ctx.lineTo(px, py); | |
| } | |
| ctx.stroke(); | |
| // Posterior fill | |
| ctx.beginPath(); | |
| for (let i = 0; i <= 100; i++) { | |
| const x = i / 100; | |
| const val = gaussian(x, 0.53, 0.09); | |
| const px = x * W; | |
| const py = H - (val / 4.5) * H * 0.8 - 5; | |
| if (i === 0) ctx.moveTo(px, py); else ctx.lineTo(px, py); | |
| } | |
| ctx.lineTo(W, H); | |
| ctx.lineTo(0, H); | |
| ctx.closePath(); | |
| ctx.fillStyle = 'rgba(0,200,255,0.06)'; | |
| ctx.fill(); | |
| // Credible interval | |
| const ci_low = 0.53 - 0.09 * 1.96; | |
| const ci_high = 0.53 + 0.09 * 1.96; | |
| ctx.strokeStyle = C.violet; | |
| ctx.lineWidth = 0.8; | |
| ctx.beginPath(); | |
| ctx.moveTo(ci_low * W, H - 5); | |
| ctx.lineTo(ci_low * W, H - 20); | |
| ctx.stroke(); | |
| ctx.beginPath(); | |
| ctx.moveTo(ci_high * W, H - 5); | |
| ctx.lineTo(ci_high * W, H - 20); | |
| ctx.stroke(); | |
| ctx.strokeStyle = 'rgba(136,68,255,0.4)'; | |
| ctx.setLineDash([2, 2]); | |
| ctx.beginPath(); | |
| ctx.moveTo(ci_low * W, H - 12); | |
| ctx.lineTo(ci_high * W, H - 12); | |
| ctx.stroke(); | |
| ctx.setLineDash([]); | |
| // Labels | |
| ctx.font = '8px "IBM Plex Mono"'; | |
| ctx.fillStyle = C.cyanDim; | |
| ctx.textAlign = 'right'; | |
| ctx.fillText('PRIOR', W * 0.28, 16); | |
| ctx.fillStyle = C.blue; | |
| ctx.fillText('LIKELIHOOD', W * 0.78, 25); | |
| ctx.fillStyle = C.cyan; | |
| ctx.textAlign = 'center'; | |
| ctx.fillText('POSTERIOR', W * 0.53, 12); | |
| ctx.fillStyle = C.violet; | |
| ctx.font = '7px "IBM Plex Mono"'; | |
| ctx.fillText('95% CI', (ci_low + ci_high) / 2 * W, H - 16); | |
| time += 0.02; | |
| requestAnimationFrame(draw); | |
| } | |
| draw(); | |
| })(); | |
| // ===================================================== | |
| // MINIZINC CONSTRAINT GRAPH | |
| // ===================================================== | |
| (function initMzn() { | |
| const canvas = document.getElementById('mznCanvas'); | |
| const ctx = canvas.getContext('2d'); | |
| const rect = canvas.parentElement.getBoundingClientRect(); | |
| const dpr = window.devicePixelRatio || 1; | |
| canvas.width = rect.width * dpr; | |
| canvas.height = rect.height * dpr; | |
| ctx.setTransform(dpr, 0, 0, dpr, 0, 0); | |
| const W = rect.width; | |
| const H = rect.height; | |
| const nodes = [ | |
| { x: 0.2, y: 0.25, label: 'x₁', type: 'var', color: C.cyan }, | |
| { x: 0.4, y: 0.15, label: 'x₂', type: 'var', color: C.cyan }, | |
| { x: 0.6, y: 0.2, label: 'x₃', type: 'var', color: C.cyan }, | |
| { x: 0.8, y: 0.3, label: 'x₄', type: 'var', color: C.cyan }, | |
| { x: 0.15, y: 0.6, label: 'C₁', type: 'con', color: C.amber }, | |
| { x: 0.38, y: 0.55, label: 'C₂', type: 'con', color: C.amber }, | |
| { x: 0.62, y: 0.6, label: 'C₃', type: 'con', color: C.amber }, | |
| { x: 0.85, y: 0.55, label: 'C₄', type: 'con', color: C.amber }, | |
| { x: 0.3, y: 0.85, label: 'D₁', type: 'dom', color: C.violet }, | |
| { x: 0.7, y: 0.85, label: 'D₂', type: 'dom', color: C.violet }, | |
| { x: 0.5, y: 0.42, label: 'obj', type: 'obj', color: C.greenBio }, | |
| ]; | |
| const edges = [ | |
| [0,4],[0,5],[1,5],[1,6],[2,6],[2,7],[3,7],[3,4], | |
| [4,8],[5,8],[6,9],[7,9], | |
| [0,10],[1,10],[2,10],[3,10], | |
| ]; | |
| let time = 0; | |
| function draw() { | |
| ctx.clearRect(0, 0, W, H); | |
| // Feasible space | |
| ctx.beginPath(); | |
| ctx.moveTo(nodes[0].x * W, nodes[0].y * H); | |
| ctx.lineTo(nodes[1].x * W, nodes[1].y * H); | |
| ctx.lineTo(nodes[2].x * W, nodes[2].y * H); | |
| ctx.lineTo(nodes[3].x * W, nodes[3].y * H); | |
| ctx.closePath(); | |
| ctx.fillStyle = 'rgba(0,200,255,0.03)'; | |
| ctx.fill(); | |
| ctx.strokeStyle = 'rgba(0,200,255,0.08)'; | |
| ctx.lineWidth = 0.5; | |
| ctx.stroke(); | |
| // Edges | |
| edges.forEach(([a, b], i) => { | |
| const na = nodes[a], nb = nodes[b]; | |
| ctx.beginPath(); | |
| ctx.moveTo(na.x * W, na.y * H); | |
| ctx.lineTo(nb.x * W, nb.y * H); | |
| ctx.strokeStyle = 'rgba(0,200,255,0.15)'; | |
| ctx.lineWidth = 0.6; | |
| ctx.stroke(); | |
| // Animated pulse | |
| const t = ((time * 0.3 + i * 0.15) % 1); | |
| const px = na.x + (nb.x - na.x) * t; | |
| const py = na.y + (nb.y - na.y) * t; | |
| ctx.beginPath(); | |
| ctx.arc(px * W, py * H, 1.5, 0, Math.PI * 2); | |
| ctx.fillStyle = `rgba(0,200,255,${0.5 - t * 0.5})`; | |
| ctx.fill(); | |
| }); | |
| // Nodes | |
| nodes.forEach(n => { | |
| const x = n.x * W, y = n.y * H; | |
| const r = n.type === 'obj' ? 10 : n.type === 'con' ? 7 : 8; | |
| if (n.type === 'con') { | |
| ctx.beginPath(); | |
| ctx.rect(x - r, y - r, r * 2, r * 2); | |
| ctx.fillStyle = 'rgba(255,170,34,0.08)'; | |
| ctx.fill(); | |
| ctx.strokeStyle = n.color; | |
| ctx.lineWidth = 0.8; | |
| ctx.stroke(); | |
| } else if (n.type === 'obj') { | |
| ctx.beginPath(); | |
| ctx.moveTo(x, y - r); | |
| ctx.lineTo(x + r, y + r * 0.6); | |
| ctx.lineTo(x - r, y + r * 0.6); | |
| ctx.closePath(); | |
| ctx.fillStyle = 'rgba(34,204,102,0.08)'; | |
| ctx.fill(); | |
| ctx.strokeStyle = n.color; | |
| ctx.lineWidth = 0.8; | |
| ctx.stroke(); | |
| } else { | |
| ctx.beginPath(); | |
| ctx.arc(x, y, r, 0, Math.PI * 2); | |
| ctx.fillStyle = 'rgba(0,200,255,0.06)'; | |
| ctx.fill(); | |
| ctx.strokeStyle = n.color; | |
| ctx.lineWidth = 0.8; | |
| ctx.stroke(); | |
| } | |
| ctx.fillStyle = n.color; | |
| ctx.font = '9px "IBM Plex Mono"'; | |
| ctx.textAlign = 'center'; | |
| ctx.textBaseline = 'middle'; | |
| ctx.fillText(n.label, x, y); | |
| }); | |
| // Legend | |
| ctx.font = '7px "IBM Plex Mono"'; | |
| ctx.textAlign = 'left'; | |
| ctx.fillStyle = C.cyan; | |
| ctx.fillText('○ VARIABLE', 4, H - 24); | |
| ctx.fillStyle = C.amber; | |
| ctx.fillText('□ CONSTRAINT', 4, H - 14); | |
| ctx.fillStyle = C.violet; | |
| ctx.fillText('○ DOMAIN', 4, H - 4); | |
| ctx.fillStyle = C.greenBio; | |
| ctx.textAlign = 'right'; | |
| ctx.fillText('△ OBJECTIVE', W - 4, H - 4); | |
| time += 0.008; | |
| requestAnimationFrame(draw); | |
| } | |
| draw(); | |
| })(); | |
| // ===================================================== | |
| // QUIPPER CIRCUIT | |
| // ===================================================== | |
| (function initQuipper() { | |
| const canvas = document.getElementById('quipperCanvas'); | |
| const ctx = canvas.getContext('2d'); | |
| const rect = canvas.parentElement.getBoundingClientRect(); | |
| const dpr = window.devicePixelRatio || 1; | |
| canvas.width = rect.width * dpr; | |
| canvas.height = rect.height * dpr; | |
| ctx.setTransform(dpr, 0, 0, dpr, 0, 0); | |
| const W = rect.width; | |
| const H = rect.height; | |
| const qubits = 5; | |
| const wireY = (i) => 18 + i * (H - 36) / (qubits - 1); | |
| const gateX = (i) => 40 + i * ((W - 60) / 7); | |
| const gates = [ | |
| { type: 'H', qubit: 0, col: 0 }, | |
| { type: 'H', qubit: 1, col: 0 }, | |
| { type: 'CNOT', control: 0, target: 2, col: 1 }, | |
| { type: 'T', qubit: 1, col: 2 }, | |
| { type: 'CNOT', control: 2, target: 3, col: 2 }, | |
| { type: 'S', qubit: 0, col: 3 }, | |
| { type: 'H', qubit: 4, col: 3 }, | |
| { type: 'CNOT', control: 4, target: 1, col: 4 }, | |
| { type: 'T', qubit: 3, col: 4 }, | |
| { type: 'CNOT', control: 1, target: 4, col: 5 }, | |
| { type: 'H', qubit: 2, col: 5 }, | |
| { type: 'M', qubit: 0, col: 6 }, | |
| { type: 'M', qubit: 3, col: 6 }, | |
| { type: 'M', qubit: 4, col: 7 }, | |
| ]; | |
| let time = 0; | |
| function draw() { | |
| ctx.clearRect(0, 0, W, H); | |
| // Qubit wires | |
| for (let i = 0; i < qubits; i++) { | |
| const y = wireY(i); | |
| ctx.beginPath(); | |
| ctx.moveTo(10, y); | |
| ctx.lineTo(W - 10, y); | |
| ctx.strokeStyle = 'rgba(0,200,255,0.15)'; | |
| ctx.lineWidth = 0.8; | |
| ctx.stroke(); | |
| // Qubit labels | |
| ctx.fillStyle = C.cyanDim; | |
| ctx.font = '8px "IBM Plex Mono"'; | |
| ctx.textAlign = 'right'; | |
| ctx.fillText(`q${i}`, 18, y + 3); | |
| } | |
| // Gates | |
| gates.forEach((g, gi) => { | |
| const x = gateX(g.col); | |
| const pulse = 0.7 + 0.3 * Math.sin(time * 2 + gi * 0.5); | |
| if (g.type === 'CNOT') { | |
| const cy = wireY(g.control); | |
| const ty = wireY(g.target); | |
| // Control-target line | |
| ctx.beginPath(); | |
| ctx.moveTo(x, cy); | |
| ctx.lineTo(x, ty); | |
| ctx.strokeStyle = `rgba(136,68,255,${0.4 * pulse})`; | |
| ctx.lineWidth = 0.8; | |
| ctx.stroke(); | |
| // Control dot | |
| ctx.beginPath(); | |
| ctx.arc(x, cy, 3, 0, Math.PI * 2); | |
| ctx.fillStyle = C.violet; | |
| ctx.fill(); | |
| // Target circle | |
| ctx.beginPath(); | |
| ctx.arc(x, ty, 7, 0, Math.PI * 2); | |
| ctx.strokeStyle = C.violet; | |
| ctx.lineWidth = 1; | |
| ctx.stroke(); | |
| ctx.beginPath(); | |
| ctx.moveTo(x - 7, ty); | |
| ctx.lineTo(x + 7, ty); | |
| ctx.moveTo(x, ty - 7); | |
| ctx.lineTo(x, ty + 7); | |
| ctx.strokeStyle = `rgba(136,68,255,${0.5 * pulse})`; | |
| ctx.lineWidth = 0.5; | |
| ctx.stroke(); | |
| } else if (g.type === 'M') { | |
| const y = wireY(g.qubit); | |
| ctx.strokeStyle = C.cyan; | |
| ctx.lineWidth = 0.8; | |
| ctx.strokeRect(x - 10, y - 8, 20, 16); | |
| // Meter arc | |
| ctx.beginPath(); | |
| ctx.arc(x, y + 2, 5, Math.PI, 0); | |
| ctx.stroke(); | |
| ctx.beginPath(); | |
| ctx.moveTo(x, y + 2); | |
| ctx.lineTo(x + 4, y - 4); | |
| ctx.stroke(); | |
| // Double line out | |
| ctx.beginPath(); | |
| ctx.moveTo(x, y + 8); | |
| ctx.lineTo(x, y + 14); | |
| ctx.strokeStyle = 'rgba(0,200,255,0.3)'; | |
| ctx.stroke(); | |
| } else { | |
| const y = wireY(g.qubit); | |
| const boxW = 18, boxH = 16; | |
| ctx.fillStyle = `rgba(136,68,255,${0.08 * pulse})`; | |
| ctx.fillRect(x - boxW/2, y - boxH/2, boxW, boxH); | |
| ctx.strokeStyle = `rgba(136,68,255,${0.6 * pulse})`; | |
| ctx.lineWidth = 0.8; | |
| ctx.strokeRect(x - boxW/2, y - boxH/2, boxW, boxH); | |
| ctx.fillStyle = C.violet; | |
| ctx.font = '9px "IBM Plex Mono"'; | |
| ctx.textAlign = 'center'; | |
| ctx.textBaseline = 'middle'; | |
| ctx.fillText(g.type, x, y); | |
| } | |
| }); | |
| // Animated photon | |
| const photonX = 10 + ((time * 60) % (W - 20)); | |
| for (let i = 0; i < qubits; i++) { | |
| ctx.beginPath(); | |
| ctx.arc(photonX, wireY(i), 2, 0, Math.PI * 2); | |
| ctx.fillStyle = `rgba(136,68,255,${0.3 + 0.2 * Math.sin(time * 3 + i)})`; | |
| ctx.fill(); | |
| } | |
| time += 0.016; | |
| requestAnimationFrame(draw); | |
| } | |
| draw(); | |
| })(); | |
| // ===================================================== | |
| // NAV INTERACTION | |
| // ===================================================== | |
| document.querySelectorAll('.nav-item').forEach(item => { | |
| item.addEventListener('click', () => { | |
| document.querySelectorAll('.nav-item').forEach(n => n.classList.remove('active')); | |
| item.classList.add('active'); | |
| const target = item.dataset.target; | |
| const panelMap = { | |
| observations: ['panel-cdna', 'panel-expression'], | |
| semantics: ['panel-qtykind', 'panel-validation'], | |
| bayesian: ['panel-stan'], | |
| constraints: ['panel-minizinc'], | |
| binary: ['panel-binary'], | |
| ptm: ['panel-ptm'], | |
| quipper: ['panel-quipper'], | |
| provenance: ['panel-provenance'], | |
| rpgjcl: ['panel-rpg'], | |
| }; | |
| // Highlight relevant panels | |
| document.querySelectorAll('.panel').forEach(p => { | |
| p.style.borderColor = ''; | |
| p.style.boxShadow = ''; | |
| }); | |
| const targets = panelMap[target] || []; | |
| targets.forEach(id => { | |
| const el = document.getElementById(id); | |
| if (el) { | |
| el.style.borderColor = 'rgba(0,200,255,0.4)'; | |
| el.style.boxShadow = '0 0 12px rgba(0,200,255,0.15), 0 0 30px rgba(0,200,255,0.05)'; | |
| el.scrollIntoView({ behavior: 'smooth', block: 'nearest' }); | |
| } | |
| }); | |
| }); | |
| }); | |
| // ===================================================== | |
| // PIPELINE NODE INTERACTION | |
| // ===================================================== | |
| document.querySelectorAll('.pipeline-node').forEach(node => { | |
| node.addEventListener('click', () => { | |
| document.querySelectorAll('.pipeline-node').forEach(n => n.classList.remove('active')); | |
| node.classList.add('active'); | |
| }); | |
| }); | |
| // ===================================================== | |
| // QUANTITY KIND INTERACTION | |
| // ===================================================== | |
| document.querySelectorAll('.qty-kind').forEach(kind => { | |
| kind.addEventListener('click', () => { | |
| const wasActive = kind.classList.contains('active'); | |
| document.querySelectorAll('.qty-kind').forEach(k => k.classList.remove('active')); | |
| if (!wasActive) kind.classList.add('active'); | |
| // Highlight related panel | |
| const kindName = kind.dataset.kind; | |
| document.querySelectorAll('.panel').forEach(p => { | |
| p.style.borderColor = ''; | |
| p.style.boxShadow = ''; | |
| }); | |
| const highlight = { | |
| observed: 'panel-cdna', | |
| inferred: 'panel-stan', | |
| derived: 'panel-expression', | |
| predicted: 'panel-stan', | |
| constrained: 'panel-minizinc', | |
| encoded: 'panel-binary', | |
| executed: 'panel-ptm', | |
| }; | |
| if (!wasActive && highlight[kindName]) { | |
| const el = document.getElementById(highlight[kindName]); | |
| if (el) { | |
| el.style.borderColor = 'rgba(0,200,255,0.4)'; | |
| el.style.boxShadow = '0 0 12px rgba(0,200,255,0.15), 0 0 30px rgba(0,200,255,0.05)'; | |
| } | |
| } | |
| }); | |
| }); | |
| // ===================================================== | |
| // AMBIENT GRID ANIMATION | |
| // ===================================================== | |
| (function ambientScan() { | |
| let scanEl = document.createElement('div'); | |
| scanEl.style.cssText = ` | |
| position: fixed; top: 0; left: 0; right: 0; height: 2px; | |
| background: linear-gradient(90deg, transparent, rgba(0,200,255,0.08), transparent); | |
| pointer-events: none; z-index: 100; | |
| animation: ambient-scan 8s linear infinite; | |
| `; | |
| document.body.appendChild(scanEl); | |
| const style = document.createElement('style'); | |
| style.textContent = ` | |
| @keyframes ambient-scan { | |
| 0% { transform: translateY(0); } | |
| 100% { transform: translateY(100vh); } | |
| } | |
| `; | |
| document.head.appendChild(style); | |
| })(); | |
| </script> | |
| </body> | |
| </html> | |