bio-semantics / src /dashboard.html
SNAPKITTYWEST's picture
Sync with GitHub, license metadata from LICENSE files, commercial license notice
3afc617 verified
Raw History Blame Contribute Delete
72.8 kB
<!DOCTYPE html>
<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 += '&nbsp;'; 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>