SNAPKITTYWEST commited on
Commit
3afc617
·
verified ·
1 Parent(s): d0cd1d7

Sync with GitHub, license metadata from LICENSE files, commercial license notice

Browse files

sync from SNAPKITTYWEST/bio-semantics: 1 files
added AGPL-3.0 LICENSE (default)
metadata license: None -> agpl-3.0
README: License section kept (already accurate) + commercial notice
README: GitHub source link added

Files changed (3) hide show
  1. LICENSE +661 -0
  2. README.md +14 -0
  3. src/dashboard.html +2292 -200
LICENSE ADDED
@@ -0,0 +1,661 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ GNU AFFERO GENERAL PUBLIC LICENSE
2
+ Version 3, 19 November 2007
3
+
4
+ Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/>
5
+ Everyone is permitted to copy and distribute verbatim copies
6
+ of this license document, but changing it is not allowed.
7
+
8
+ Preamble
9
+
10
+ The GNU Affero General Public License is a free, copyleft license for
11
+ software and other kinds of works, specifically designed to ensure
12
+ cooperation with the community in the case of network server software.
13
+
14
+ The licenses for most software and other practical works are designed
15
+ to take away your freedom to share and change the works. By contrast,
16
+ our General Public Licenses are intended to guarantee your freedom to
17
+ share and change all versions of a program--to make sure it remains free
18
+ software for all its users.
19
+
20
+ When we speak of free software, we are referring to freedom, not
21
+ price. Our General Public Licenses are designed to make sure that you
22
+ have the freedom to distribute copies of free software (and charge for
23
+ them if you wish), that you receive source code or can get it if you
24
+ want it, that you can change the software or use pieces of it in new
25
+ free programs, and that you know you can do these things.
26
+
27
+ Developers that use our General Public Licenses protect your rights
28
+ with two steps: (1) assert copyright on the software, and (2) offer
29
+ you this License which gives you legal permission to copy, distribute
30
+ and/or modify the software.
31
+
32
+ A secondary benefit of defending all users' freedom is that
33
+ improvements made in alternate versions of the program, if they
34
+ receive widespread use, become available for other developers to
35
+ incorporate. Many developers of free software are heartened and
36
+ encouraged by the resulting cooperation. However, in the case of
37
+ software used on network servers, this result may fail to come about.
38
+ The GNU General Public License permits making a modified version and
39
+ letting the public access it on a server without ever releasing its
40
+ source code to the public.
41
+
42
+ The GNU Affero General Public License is designed specifically to
43
+ ensure that, in such cases, the modified source code becomes available
44
+ to the community. It requires the operator of a network server to
45
+ provide the source code of the modified version running there to the
46
+ users of that server. Therefore, public use of a modified version, on
47
+ a publicly accessible server, gives the public access to the source
48
+ code of the modified version.
49
+
50
+ An older license, called the Affero General Public License and
51
+ published by Affero, was designed to accomplish similar goals. This is
52
+ a different license, not a version of the Affero GPL, but Affero has
53
+ released a new version of the Affero GPL which permits relicensing under
54
+ this license.
55
+
56
+ The precise terms and conditions for copying, distribution and
57
+ modification follow.
58
+
59
+ TERMS AND CONDITIONS
60
+
61
+ 0. Definitions.
62
+
63
+ "This License" refers to version 3 of the GNU Affero General Public License.
64
+
65
+ "Copyright" also means copyright-like laws that apply to other kinds of
66
+ works, such as semiconductor masks.
67
+
68
+ "The Program" refers to any copyrightable work licensed under this
69
+ License. Each licensee is addressed as "you". "Licensees" and
70
+ "recipients" may be individuals or organizations.
71
+
72
+ To "modify" a work means to copy from or adapt all or part of the work
73
+ in a fashion requiring copyright permission, other than the making of an
74
+ exact copy. The resulting work is called a "modified version" of the
75
+ earlier work or a work "based on" the earlier work.
76
+
77
+ A "covered work" means either the unmodified Program or a work based
78
+ on the Program.
79
+
80
+ To "propagate" a work means to do anything with it that, without
81
+ permission, would make you directly or secondarily liable for
82
+ infringement under applicable copyright law, except executing it on a
83
+ computer or modifying a private copy. Propagation includes copying,
84
+ distribution (with or without modification), making available to the
85
+ public, and in some countries other activities as well.
86
+
87
+ To "convey" a work means any kind of propagation that enables other
88
+ parties to make or receive copies. Mere interaction with a user through
89
+ a computer network, with no transfer of a copy, is not conveying.
90
+
91
+ An interactive user interface displays "Appropriate Legal Notices"
92
+ to the extent that it includes a convenient and prominently visible
93
+ feature that (1) displays an appropriate copyright notice, and (2)
94
+ tells the user that there is no warranty for the work (except to the
95
+ extent that warranties are provided), that licensees may convey the
96
+ work under this License, and how to view a copy of this License. If
97
+ the interface presents a list of user commands or options, such as a
98
+ menu, a prominent item in the list meets this criterion.
99
+
100
+ 1. Source Code.
101
+
102
+ The "source code" for a work means the preferred form of the work
103
+ for making modifications to it. "Object code" means any non-source
104
+ form of a work.
105
+
106
+ A "Standard Interface" means an interface that either is an official
107
+ standard defined by a recognized standards body, or, in the case of
108
+ interfaces specified for a particular programming language, one that
109
+ is widely used among developers working in that language.
110
+
111
+ The "System Libraries" of an executable work include anything, other
112
+ than the work as a whole, that (a) is included in the normal form of
113
+ packaging a Major Component, but which is not part of that Major
114
+ Component, and (b) serves only to enable use of the work with that
115
+ Major Component, or to implement a Standard Interface for which an
116
+ implementation is available to the public in source code form. A
117
+ "Major Component", in this context, means a major essential component
118
+ (kernel, window system, and so on) of the specific operating system
119
+ (if any) on which the executable work runs, or a compiler used to
120
+ produce the work, or an object code interpreter used to run it.
121
+
122
+ The "Corresponding Source" for a work in object code form means all
123
+ the source code needed to generate, install, and (for an executable
124
+ work) run the object code and to modify the work, including scripts to
125
+ control those activities. However, it does not include the work's
126
+ System Libraries, or general-purpose tools or generally available free
127
+ programs which are used unmodified in performing those activities but
128
+ which are not part of the work. For example, Corresponding Source
129
+ includes interface definition files associated with source files for
130
+ the work, and the source code for shared libraries and dynamically
131
+ linked subprograms that the work is specifically designed to require,
132
+ such as by intimate data communication or control flow between those
133
+ subprograms and other parts of the work.
134
+
135
+ The Corresponding Source need not include anything that users
136
+ can regenerate automatically from other parts of the Corresponding
137
+ Source.
138
+
139
+ The Corresponding Source for a work in source code form is that
140
+ same work.
141
+
142
+ 2. Basic Permissions.
143
+
144
+ All rights granted under this License are granted for the term of
145
+ copyright on the Program, and are irrevocable provided the stated
146
+ conditions are met. This License explicitly affirms your unlimited
147
+ permission to run the unmodified Program. The output from running a
148
+ covered work is covered by this License only if the output, given its
149
+ content, constitutes a covered work. This License acknowledges your
150
+ rights of fair use or other equivalent, as provided by copyright law.
151
+
152
+ You may make, run and propagate covered works that you do not
153
+ convey, without conditions so long as your license otherwise remains
154
+ in force. You may convey covered works to others for the sole purpose
155
+ of having them make modifications exclusively for you, or provide you
156
+ with facilities for running those works, provided that you comply with
157
+ the terms of this License in conveying all material for which you do
158
+ not control copyright. Those thus making or running the covered works
159
+ for you must do so exclusively on your behalf, under your direction
160
+ and control, on terms that prohibit them from making any copies of
161
+ your copyrighted material outside their relationship with you.
162
+
163
+ Conveying under any other circumstances is permitted solely under
164
+ the conditions stated below. Sublicensing is not allowed; section 10
165
+ makes it unnecessary.
166
+
167
+ 3. Protecting Users' Legal Rights From Anti-Circumvention Law.
168
+
169
+ No covered work shall be deemed part of an effective technological
170
+ measure under any applicable law fulfilling obligations under article
171
+ 11 of the WIPO copyright treaty adopted on 20 December 1996, or
172
+ similar laws prohibiting or restricting circumvention of such
173
+ measures.
174
+
175
+ When you convey a covered work, you waive any legal power to forbid
176
+ circumvention of technological measures to the extent such circumvention
177
+ is effected by exercising rights under this License with respect to
178
+ the covered work, and you disclaim any intention to limit operation or
179
+ modification of the work as a means of enforcing, against the work's
180
+ users, your or third parties' legal rights to forbid circumvention of
181
+ technological measures.
182
+
183
+ 4. Conveying Verbatim Copies.
184
+
185
+ You may convey verbatim copies of the Program's source code as you
186
+ receive it, in any medium, provided that you conspicuously and
187
+ appropriately publish on each copy an appropriate copyright notice;
188
+ keep intact all notices stating that this License and any
189
+ non-permissive terms added in accord with section 7 apply to the code;
190
+ keep intact all notices of the absence of any warranty; and give all
191
+ recipients a copy of this License along with the Program.
192
+
193
+ You may charge any price or no price for each copy that you convey,
194
+ and you may offer support or warranty protection for a fee.
195
+
196
+ 5. Conveying Modified Source Versions.
197
+
198
+ You may convey a work based on the Program, or the modifications to
199
+ produce it from the Program, in the form of source code under the
200
+ terms of section 4, provided that you also meet all of these conditions:
201
+
202
+ a) The work must carry prominent notices stating that you modified
203
+ it, and giving a relevant date.
204
+
205
+ b) The work must carry prominent notices stating that it is
206
+ released under this License and any conditions added under section
207
+ 7. This requirement modifies the requirement in section 4 to
208
+ "keep intact all notices".
209
+
210
+ c) You must license the entire work, as a whole, under this
211
+ License to anyone who comes into possession of a copy. This
212
+ License will therefore apply, along with any applicable section 7
213
+ additional terms, to the whole of the work, and all its parts,
214
+ regardless of how they are packaged. This License gives no
215
+ permission to license the work in any other way, but it does not
216
+ invalidate such permission if you have separately received it.
217
+
218
+ d) If the work has interactive user interfaces, each must display
219
+ Appropriate Legal Notices; however, if the Program has interactive
220
+ interfaces that do not display Appropriate Legal Notices, your
221
+ work need not make them do so.
222
+
223
+ A compilation of a covered work with other separate and independent
224
+ works, which are not by their nature extensions of the covered work,
225
+ and which are not combined with it such as to form a larger program,
226
+ in or on a volume of a storage or distribution medium, is called an
227
+ "aggregate" if the compilation and its resulting copyright are not
228
+ used to limit the access or legal rights of the compilation's users
229
+ beyond what the individual works permit. Inclusion of a covered work
230
+ in an aggregate does not cause this License to apply to the other
231
+ parts of the aggregate.
232
+
233
+ 6. Conveying Non-Source Forms.
234
+
235
+ You may convey a covered work in object code form under the terms
236
+ of sections 4 and 5, provided that you also convey the
237
+ machine-readable Corresponding Source under the terms of this License,
238
+ in one of these ways:
239
+
240
+ a) Convey the object code in, or embodied in, a physical product
241
+ (including a physical distribution medium), accompanied by the
242
+ Corresponding Source fixed on a durable physical medium
243
+ customarily used for software interchange.
244
+
245
+ b) Convey the object code in, or embodied in, a physical product
246
+ (including a physical distribution medium), accompanied by a
247
+ written offer, valid for at least three years and valid for as
248
+ long as you offer spare parts or customer support for that product
249
+ model, to give anyone who possesses the object code either (1) a
250
+ copy of the Corresponding Source for all the software in the
251
+ product that is covered by this License, on a durable physical
252
+ medium customarily used for software interchange, for a price no
253
+ more than your reasonable cost of physically performing this
254
+ conveying of source, or (2) access to copy the
255
+ Corresponding Source from a network server at no charge.
256
+
257
+ c) Convey individual copies of the object code with a copy of the
258
+ written offer to provide the Corresponding Source. This
259
+ alternative is allowed only occasionally and noncommercially, and
260
+ only if you received the object code with such an offer, in accord
261
+ with subsection 6b.
262
+
263
+ d) Convey the object code by offering access from a designated
264
+ place (gratis or for a charge), and offer equivalent access to the
265
+ Corresponding Source in the same way through the same place at no
266
+ further charge. You need not require recipients to copy the
267
+ Corresponding Source along with the object code. If the place to
268
+ copy the object code is a network server, the Corresponding Source
269
+ may be on a different server (operated by you or a third party)
270
+ that supports equivalent copying facilities, provided you maintain
271
+ clear directions next to the object code saying where to find the
272
+ Corresponding Source. Regardless of what server hosts the
273
+ Corresponding Source, you remain obligated to ensure that it is
274
+ available for as long as needed to satisfy these requirements.
275
+
276
+ e) Convey the object code using peer-to-peer transmission, provided
277
+ you inform other peers where the object code and Corresponding
278
+ Source of the work are being offered to the general public at no
279
+ charge under subsection 6d.
280
+
281
+ A separable portion of the object code, whose source code is excluded
282
+ from the Corresponding Source as a System Library, need not be
283
+ included in conveying the object code work.
284
+
285
+ A "User Product" is either (1) a "consumer product", which means any
286
+ tangible personal property which is normally used for personal, family,
287
+ or household purposes, or (2) anything designed or sold for incorporation
288
+ into a dwelling. In determining whether a product is a consumer product,
289
+ doubtful cases shall be resolved in favor of coverage. For a particular
290
+ product received by a particular user, "normally used" refers to a
291
+ typical or common use of that class of product, regardless of the status
292
+ of the particular user or of the way in which the particular user
293
+ actually uses, or expects or is expected to use, the product. A product
294
+ is a consumer product regardless of whether the product has substantial
295
+ commercial, industrial or non-consumer uses, unless such uses represent
296
+ the only significant mode of use of the product.
297
+
298
+ "Installation Information" for a User Product means any methods,
299
+ procedures, authorization keys, or other information required to install
300
+ and execute modified versions of a covered work in that User Product from
301
+ a modified version of its Corresponding Source. The information must
302
+ suffice to ensure that the continued functioning of the modified object
303
+ code is in no case prevented or interfered with solely because
304
+ modification has been made.
305
+
306
+ If you convey an object code work under this section in, or with, or
307
+ specifically for use in, a User Product, and the conveying occurs as
308
+ part of a transaction in which the right of possession and use of the
309
+ User Product is transferred to the recipient in perpetuity or for a
310
+ fixed term (regardless of how the transaction is characterized), the
311
+ Corresponding Source conveyed under this section must be accompanied
312
+ by the Installation Information. But this requirement does not apply
313
+ if neither you nor any third party retains the ability to install
314
+ modified object code on the User Product (for example, the work has
315
+ been installed in ROM).
316
+
317
+ The requirement to provide Installation Information does not include a
318
+ requirement to continue to provide support service, warranty, or updates
319
+ for a work that has been modified or installed by the recipient, or for
320
+ the User Product in which it has been modified or installed. Access to a
321
+ network may be denied when the modification itself materially and
322
+ adversely affects the operation of the network or violates the rules and
323
+ protocols for communication across the network.
324
+
325
+ Corresponding Source conveyed, and Installation Information provided,
326
+ in accord with this section must be in a format that is publicly
327
+ documented (and with an implementation available to the public in
328
+ source code form), and must require no special password or key for
329
+ unpacking, reading or copying.
330
+
331
+ 7. Additional Terms.
332
+
333
+ "Additional permissions" are terms that supplement the terms of this
334
+ License by making exceptions from one or more of its conditions.
335
+ Additional permissions that are applicable to the entire Program shall
336
+ be treated as though they were included in this License, to the extent
337
+ that they are valid under applicable law. If additional permissions
338
+ apply only to part of the Program, that part may be used separately
339
+ under those permissions, but the entire Program remains governed by
340
+ this License without regard to the additional permissions.
341
+
342
+ When you convey a copy of a covered work, you may at your option
343
+ remove any additional permissions from that copy, or from any part of
344
+ it. (Additional permissions may be written to require their own
345
+ removal in certain cases when you modify the work.) You may place
346
+ additional permissions on material, added by you to a covered work,
347
+ for which you have or can give appropriate copyright permission.
348
+
349
+ Notwithstanding any other provision of this License, for material you
350
+ add to a covered work, you may (if authorized by the copyright holders of
351
+ that material) supplement the terms of this License with terms:
352
+
353
+ a) Disclaiming warranty or limiting liability differently from the
354
+ terms of sections 15 and 16 of this License; or
355
+
356
+ b) Requiring preservation of specified reasonable legal notices or
357
+ author attributions in that material or in the Appropriate Legal
358
+ Notices displayed by works containing it; or
359
+
360
+ c) Prohibiting misrepresentation of the origin of that material, or
361
+ requiring that modified versions of such material be marked in
362
+ reasonable ways as different from the original version; or
363
+
364
+ d) Limiting the use for publicity purposes of names of licensors or
365
+ authors of the material; or
366
+
367
+ e) Declining to grant rights under trademark law for use of some
368
+ trade names, trademarks, or service marks; or
369
+
370
+ f) Requiring indemnification of licensors and authors of that
371
+ material by anyone who conveys the material (or modified versions of
372
+ it) with contractual assumptions of liability to the recipient, for
373
+ any liability that these contractual assumptions directly impose on
374
+ those licensors and authors.
375
+
376
+ All other non-permissive additional terms are considered "further
377
+ restrictions" within the meaning of section 10. If the Program as you
378
+ received it, or any part of it, contains a notice stating that it is
379
+ governed by this License along with a term that is a further
380
+ restriction, you may remove that term. If a license document contains
381
+ a further restriction but permits relicensing or conveying under this
382
+ License, you may add to a covered work material governed by the terms
383
+ of that license document, provided that the further restriction does
384
+ not survive such relicensing or conveying.
385
+
386
+ If you add terms to a covered work in accord with this section, you
387
+ must place, in the relevant source files, a statement of the
388
+ additional terms that apply to those files, or a notice indicating
389
+ where to find the applicable terms.
390
+
391
+ Additional terms, permissive or non-permissive, may be stated in the
392
+ form of a separately written license, or stated as exceptions;
393
+ the above requirements apply either way.
394
+
395
+ 8. Termination.
396
+
397
+ You may not propagate or modify a covered work except as expressly
398
+ provided under this License. Any attempt otherwise to propagate or
399
+ modify it is void, and will automatically terminate your rights under
400
+ this License (including any patent licenses granted under the third
401
+ paragraph of section 11).
402
+
403
+ However, if you cease all violation of this License, then your
404
+ license from a particular copyright holder is reinstated (a)
405
+ provisionally, unless and until the copyright holder explicitly and
406
+ finally terminates your license, and (b) permanently, if the copyright
407
+ holder fails to notify you of the violation by some reasonable means
408
+ prior to 60 days after the cessation.
409
+
410
+ Moreover, your license from a particular copyright holder is
411
+ reinstated permanently if the copyright holder notifies you of the
412
+ violation by some reasonable means, this is the first time you have
413
+ received notice of violation of this License (for any work) from that
414
+ copyright holder, and you cure the violation prior to 30 days after
415
+ your receipt of the notice.
416
+
417
+ Termination of your rights under this section does not terminate the
418
+ licenses of parties who have received copies or rights from you under
419
+ this License. If your rights have been terminated and not permanently
420
+ reinstated, you do not qualify to receive new licenses for the same
421
+ material under section 10.
422
+
423
+ 9. Acceptance Not Required for Having Copies.
424
+
425
+ You are not required to accept this License in order to receive or
426
+ run a copy of the Program. Ancillary propagation of a covered work
427
+ occurring solely as a consequence of using peer-to-peer transmission
428
+ to receive a copy likewise does not require acceptance. However,
429
+ nothing other than this License grants you permission to propagate or
430
+ modify any covered work. These actions infringe copyright if you do
431
+ not accept this License. Therefore, by modifying or propagating a
432
+ covered work, you indicate your acceptance of this License to do so.
433
+
434
+ 10. Automatic Licensing of Downstream Recipients.
435
+
436
+ Each time you convey a covered work, the recipient automatically
437
+ receives a license from the original licensors, to run, modify and
438
+ propagate that work, subject to this License. You are not responsible
439
+ for enforcing compliance by third parties with this License.
440
+
441
+ An "entity transaction" is a transaction transferring control of an
442
+ organization, or substantially all assets of one, or subdividing an
443
+ organization, or merging organizations. If propagation of a covered
444
+ work results from an entity transaction, each party to that
445
+ transaction who receives a copy of the work also receives whatever
446
+ licenses to the work the party's predecessor in interest had or could
447
+ give under the previous paragraph, plus a right to possession of the
448
+ Corresponding Source of the work from the predecessor in interest, if
449
+ the predecessor has it or can get it with reasonable efforts.
450
+
451
+ You may not impose any further restrictions on the exercise of the
452
+ rights granted or affirmed under this License. For example, you may
453
+ not impose a license fee, royalty, or other charge for exercise of
454
+ rights granted under this License, and you may not initiate litigation
455
+ (including a cross-claim or counterclaim in a lawsuit) alleging that
456
+ any patent claim is infringed by making, using, selling, offering for
457
+ sale, or importing the Program or any portion of it.
458
+
459
+ 11. Patents.
460
+
461
+ A "contributor" is a copyright holder who authorizes use under this
462
+ License of the Program or a work on which the Program is based. The
463
+ work thus licensed is called the contributor's "contributor version".
464
+
465
+ A contributor's "essential patent claims" are all patent claims
466
+ owned or controlled by the contributor, whether already acquired or
467
+ hereafter acquired, that would be infringed by some manner, permitted
468
+ by this License, of making, using, or selling its contributor version,
469
+ but do not include claims that would be infringed only as a
470
+ consequence of further modification of the contributor version. For
471
+ purposes of this definition, "control" includes the right to grant
472
+ patent sublicenses in a manner consistent with the requirements of
473
+ this License.
474
+
475
+ Each contributor grants you a non-exclusive, worldwide, royalty-free
476
+ patent license under the contributor's essential patent claims, to
477
+ make, use, sell, offer for sale, import and otherwise run, modify and
478
+ propagate the contents of its contributor version.
479
+
480
+ In the following three paragraphs, a "patent license" is any express
481
+ agreement or commitment, however denominated, not to enforce a patent
482
+ (such as an express permission to practice a patent or covenant not to
483
+ sue for patent infringement). To "grant" such a patent license to a
484
+ party means to make such an agreement or commitment not to enforce a
485
+ patent against the party.
486
+
487
+ If you convey a covered work, knowingly relying on a patent license,
488
+ and the Corresponding Source of the work is not available for anyone
489
+ to copy, free of charge and under the terms of this License, through a
490
+ publicly available network server or other readily accessible means,
491
+ then you must either (1) cause the Corresponding Source to be so
492
+ available, or (2) arrange to deprive yourself of the benefit of the
493
+ patent license for this particular work, or (3) arrange, in a manner
494
+ consistent with the requirements of this License, to extend the patent
495
+ license to downstream recipients. "Knowingly relying" means you have
496
+ actual knowledge that, but for the patent license, your conveying the
497
+ covered work in a country, or your recipient's use of the covered work
498
+ in a country, would infringe one or more identifiable patents in that
499
+ country that you have reason to believe are valid.
500
+
501
+ If, pursuant to or in connection with a single transaction or
502
+ arrangement, you convey, or propagate by procuring conveyance of, a
503
+ covered work, and grant a patent license to some of the parties
504
+ receiving the covered work authorizing them to use, propagate, modify
505
+ or convey a specific copy of the covered work, then the patent license
506
+ you grant is automatically extended to all recipients of the covered
507
+ work and works based on it.
508
+
509
+ A patent license is "discriminatory" if it does not include within
510
+ the scope of its coverage, prohibits the exercise of, or is
511
+ conditioned on the non-exercise of one or more of the rights that are
512
+ specifically granted under this License. You may not convey a covered
513
+ work if you are a party to an arrangement with a third party that is
514
+ in the business of distributing software, under which you make payment
515
+ to the third party based on the extent of your activity of conveying
516
+ the work, and under which the third party grants, to any of the
517
+ parties who would receive the covered work from you, a discriminatory
518
+ patent license (a) in connection with copies of the covered work
519
+ conveyed by you (or copies made from those copies), or (b) primarily
520
+ for and in connection with specific products or compilations that
521
+ contain the covered work, unless you entered into that arrangement,
522
+ or that patent license was granted, prior to 28 March 2007.
523
+
524
+ Nothing in this License shall be construed as excluding or limiting
525
+ any implied license or other defenses to infringement that may
526
+ otherwise be available to you under applicable patent law.
527
+
528
+ 12. No Surrender of Others' Freedom.
529
+
530
+ If conditions are imposed on you (whether by court order, agreement or
531
+ otherwise) that contradict the conditions of this License, they do not
532
+ excuse you from the conditions of this License. If you cannot convey a
533
+ covered work so as to satisfy simultaneously your obligations under this
534
+ License and any other pertinent obligations, then as a consequence you may
535
+ not convey it at all. For example, if you agree to terms that obligate you
536
+ to collect a royalty for further conveying from those to whom you convey
537
+ the Program, the only way you could satisfy both those terms and this
538
+ License would be to refrain entirely from conveying the Program.
539
+
540
+ 13. Remote Network Interaction; Use with the GNU General Public License.
541
+
542
+ Notwithstanding any other provision of this License, if you modify the
543
+ Program, your modified version must prominently offer all users
544
+ interacting with it remotely through a computer network (if your version
545
+ supports such interaction) an opportunity to receive the Corresponding
546
+ Source of your version by providing access to the Corresponding Source
547
+ from a network server at no charge, through some standard or customary
548
+ means of facilitating copying of software. This Corresponding Source
549
+ shall include the Corresponding Source for any work covered by version 3
550
+ of the GNU General Public License that is incorporated pursuant to the
551
+ following paragraph.
552
+
553
+ Notwithstanding any other provision of this License, you have
554
+ permission to link or combine any covered work with a work licensed
555
+ under version 3 of the GNU General Public License into a single
556
+ combined work, and to convey the resulting work. The terms of this
557
+ License will continue to apply to the part which is the covered work,
558
+ but the work with which it is combined will remain governed by version
559
+ 3 of the GNU General Public License.
560
+
561
+ 14. Revised Versions of this License.
562
+
563
+ The Free Software Foundation may publish revised and/or new versions of
564
+ the GNU Affero General Public License from time to time. Such new versions
565
+ will be similar in spirit to the present version, but may differ in detail to
566
+ address new problems or concerns.
567
+
568
+ Each version is given a distinguishing version number. If the
569
+ Program specifies that a certain numbered version of the GNU Affero General
570
+ Public License "or any later version" applies to it, you have the
571
+ option of following the terms and conditions either of that numbered
572
+ version or of any later version published by the Free Software
573
+ Foundation. If the Program does not specify a version number of the
574
+ GNU Affero General Public License, you may choose any version ever published
575
+ by the Free Software Foundation.
576
+
577
+ If the Program specifies that a proxy can decide which future
578
+ versions of the GNU Affero General Public License can be used, that proxy's
579
+ public statement of acceptance of a version permanently authorizes you
580
+ to choose that version for the Program.
581
+
582
+ Later license versions may give you additional or different
583
+ permissions. However, no additional obligations are imposed on any
584
+ author or copyright holder as a result of your choosing to follow a
585
+ later version.
586
+
587
+ 15. Disclaimer of Warranty.
588
+
589
+ THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
590
+ APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
591
+ HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
592
+ OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
593
+ THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
594
+ PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
595
+ IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
596
+ ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
597
+
598
+ 16. Limitation of Liability.
599
+
600
+ IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
601
+ WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
602
+ THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
603
+ GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
604
+ USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
605
+ DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
606
+ PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
607
+ EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
608
+ SUCH DAMAGES.
609
+
610
+ 17. Interpretation of Sections 15 and 16.
611
+
612
+ If the disclaimer of warranty and limitation of liability provided
613
+ above cannot be given local legal effect according to their terms,
614
+ reviewing courts shall apply local law that most closely approximates
615
+ an absolute waiver of all civil liability in connection with the
616
+ Program, unless a warranty or assumption of liability accompanies a
617
+ copy of the Program in return for a fee.
618
+
619
+ END OF TERMS AND CONDITIONS
620
+
621
+ How to Apply These Terms to Your New Programs
622
+
623
+ If you develop a new program, and you want it to be of the greatest
624
+ possible use to the public, the best way to achieve this is to make it
625
+ free software which everyone can redistribute and change under these terms.
626
+
627
+ To do so, attach the following notices to the program. It is safest
628
+ to attach them to the start of each source file to most effectively
629
+ state the exclusion of warranty; and each file should have at least
630
+ the "copyright" line and a pointer to where the full notice is found.
631
+
632
+ <one line to give the program's name and a brief idea of what it does.>
633
+ Copyright (C) <year> <name of author>
634
+
635
+ This program is free software: you can redistribute it and/or modify
636
+ it under the terms of the GNU Affero General Public License as published
637
+ by the Free Software Foundation, either version 3 of the License, or
638
+ (at your option) any later version.
639
+
640
+ This program is distributed in the hope that it will be useful,
641
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
642
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
643
+ GNU Affero General Public License for more details.
644
+
645
+ You should have received a copy of the GNU Affero General Public License
646
+ along with this program. If not, see <https://www.gnu.org/licenses/>.
647
+
648
+ Also add information on how to contact you by electronic and paper mail.
649
+
650
+ If your software can interact with users remotely through a computer
651
+ network, you should also make sure that it provides a way for users to
652
+ get its source. For example, if your program is a web application, its
653
+ interface could display a "Source" link that leads users to an archive
654
+ of the code. There are many ways you could offer source, and different
655
+ solutions will be better for different programs; see section 13 for the
656
+ specific requirements.
657
+
658
+ You should also get your employer (if you work as a programmer) or school,
659
+ if any, to sign a "copyright disclaimer" for the program, if necessary.
660
+ For more information on this, and how to apply and follow the GNU AGPL, see
661
+ <https://www.gnu.org/licenses/>.
README.md CHANGED
@@ -1,3 +1,11 @@
 
 
 
 
 
 
 
 
1
  [![License: BSL-1.1](https://img.shields.io/badge/License-BSL--1.1-blue.svg)](https://opensource.org/licenses/BSL-1.0)
2
  [![License: AGPL-3.0](https://img.shields.io/badge/License-AGPL--3.0-purple.svg)](https://www.gnu.org/licenses/agpl-3.0)
3
  [![License: MPL-2.0](https://img.shields.io/badge/License-MPL--2.0-orange.svg)](https://www.mozilla.org/en-US/MPL/2.0/)
@@ -161,3 +169,9 @@ Unauthorized cloud SaaS redistribution without source disclosure is prohibited u
161
  ---
162
 
163
  SnapKitty West / SNAPKITTYWEST — Evidence or Silence — 2026
 
 
 
 
 
 
 
1
+ ---
2
+ license: agpl-3.0
3
+ tags:
4
+ - snapkitty
5
+ ---
6
+
7
+ > Source: [github.com/SNAPKITTYWEST/bio-semantics](https://github.com/SNAPKITTYWEST/bio-semantics)
8
+
9
  [![License: BSL-1.1](https://img.shields.io/badge/License-BSL--1.1-blue.svg)](https://opensource.org/licenses/BSL-1.0)
10
  [![License: AGPL-3.0](https://img.shields.io/badge/License-AGPL--3.0-purple.svg)](https://www.gnu.org/licenses/agpl-3.0)
11
  [![License: MPL-2.0](https://img.shields.io/badge/License-MPL--2.0-orange.svg)](https://www.mozilla.org/en-US/MPL/2.0/)
 
169
  ---
170
 
171
  SnapKitty West / SNAPKITTYWEST — Evidence or Silence — 2026
172
+
173
+ ### 💼 Commercial License
174
+
175
+ Snapkitty code is free and open under **AGPL-3.0** for open-source use. Building a commercial product or service? A **proprietary commercial license** from Snapkitty Collective LLC lets you ship this code without the AGPL's source-sharing and network-use obligations.
176
+
177
+ **[→ Get a commercial license](mailto:A.parr@belespritdaccord.uk?subject=Commercial%20license:%20bio-semantics)** · A.parr@belespritdaccord.uk
src/dashboard.html CHANGED
@@ -1,214 +1,2306 @@
1
- <!doctype html>
2
  <html lang="en">
3
- <meta charset="utf-8">
4
- <meta name="viewport" content="width=device-width,initial-scale=1">
5
- <title>Gene-expression semantic report</title>
 
6
  <style>
7
- :root{font:16px system-ui;color:#182536;background:#eef2f6}
8
- body{max-width:1200px;margin:auto;padding:24px}
9
- h1{margin-bottom:8px}h2{font-size:1.15rem}
10
- header,section{background:white;border:1px solid #ced7e0;border-radius:12px;padding:20px;margin-bottom:16px}
11
- label{display:inline-flex;flex-direction:column;gap:5px;margin:8px 12px 8px 0}
12
- select,input{font:inherit;max-width:100%;padding:7px}
13
- .grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(320px,1fr));gap:16px}
14
- .grid section{min-width:0}
15
- pre{white-space:pre-wrap;overflow-wrap:anywhere;background:#f4f6f8;padding:12px}
16
- .scroll{overflow:auto;max-height:420px}
17
- table{border-collapse:collapse;width:100%;font-size:.9rem}
18
- th,td{text-align:left;border-bottom:1px solid #ddd;padding:8px;vertical-align:top}
19
- .tag{display:inline-block;border:1px solid;padding:3px 8px;border-radius:5px;font-size:.8rem}
20
- .notice{color:#614700;background:#fff5d6;padding:12px}
21
- #error{color:#9c1927}
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
22
  </style>
23
- <header>
24
- <h1>Gene-expression semantic report</h1>
25
- <p>Observed measurements, inferred results, and computational representations retain their declared meaning.</p>
26
- <label>Open report JSON<input id="file" type="file" accept=".json,application/json"></label>
27
- <p id="error" role="alert"></p>
28
- <p id="state" aria-live="polite">No report loaded. No analysis has been executed.</p>
29
- <p class="notice">This viewer displays supplied artifacts. Importing a report does not verify provenance, run native tools, establish convergence, or execute a quantum circuit.</p>
 
 
 
 
 
 
 
30
  </header>
31
- <section>
32
- <h2>Observations</h2><span class="tag">OBSERVED — as declared by the report</span>
33
- <div id="filters"></div><p id="counts"></p>
34
- <div id="observations" class="scroll"></div>
35
- <h3>Sequencing and quality metadata</h3><pre id="sequencing">Not supplied</pre>
36
- </section>
37
- <div class="grid">
38
- <section><h2>Biological model and comparison</h2><pre id="models"></pre></section>
39
- <section><h2>Parameters</h2><span class="tag">UNKNOWN / PRIOR</span><pre id="parameters"></pre></section>
40
- <section><h2>Posteriors</h2><span class="tag">INFERRED</span><pre id="posteriors"></pre><h3>Predictions</h3><span class="tag">PREDICTED</span><pre id="predictions"></pre></section>
41
- <section><h2>Constraints</h2><span class="tag">CONSTRAINED</span><pre id="constraints"></pre></section>
42
- <section><h2>Binary semantics</h2><span class="tag">ENCODED</span><pre id="binary"></pre></section>
43
- <section><h2>PTM</h2><pre id="ptm"></pre></section>
44
- <section><h2>Quipper</h2><pre id="quipper"></pre></section>
45
- <section><h2>Provenance</h2><pre id="artifacts"></pre><h3>Stage execution records</h3><pre id="stages"></pre></section>
46
- </div>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
47
  <script>
48
- "use strict";
49
- const $ = id => document.getElementById(id);
50
- const keys = ["gene_id","sample_id","condition","timepoint"];
51
- const valueKeys = ["expression_count","normalized_expression"];
52
- const missing = value => value === null || value === undefined;
53
- const display = value => missing(value) ? "MISSING" :
54
- typeof value === "object" ? JSON.stringify(value) : String(value);
55
- const token = value => missing(value) ? "missing:" :
56
- typeof value + ":" + JSON.stringify(value);
57
- let report = null, observations = [], selections = {};
58
- function show(id, value) {
59
- $(id).textContent = missing(value) ? "Not supplied" :
60
- JSON.stringify(value, null, 2);
61
- }
62
- function table(container, rows, columns) {
63
- container.replaceChildren();
64
- const table = document.createElement("table");
65
- const head = document.createElement("thead");
66
- const header = document.createElement("tr");
67
- for (const name of columns) {
68
- const th = document.createElement("th");
69
- th.scope = "col"; th.textContent = name; header.append(th);
70
- }
71
- head.append(header); table.append(head);
72
- const body = document.createElement("tbody");
73
- for (const row of rows) {
74
- const tr = document.createElement("tr");
75
- for (const name of columns) {
76
- const td = document.createElement("td");
77
- td.textContent = display(row[name]); tr.append(td);
78
- }
79
- body.append(tr);
 
 
 
 
 
80
  }
81
- table.append(body); container.append(table);
82
- }
83
- function renderObservations() {
84
- const rows = observations.filter(row =>
85
- keys.every(key => selections[key] === "" ||
86
- token(row[key]) === selections[key]));
87
- const unique = key => new Set(rows.filter(row =>
88
- !missing(row[key])).map(row => token(row[key]))).size;
89
- const absent = rows.reduce((count,row) =>
90
- count + valueKeys.filter(key => missing(row[key])).length, 0);
91
- $("counts").textContent =
92
- `${rows.length} of ${observations.length} observations; ` +
93
- `${unique("sample_id")} samples; ${unique("gene_id")} genes; ` +
94
- `${unique("condition")} conditions. ` +
95
- `${absent} missing cells across expression_count and normalized_expression. ` +
96
- `An absent measurement channel counts as missing, not zero.`;
97
- table($("observations"), rows, [
98
- ...keys,"replicate","expression_count","normalized_expression",
99
- "batch","cell_type","measurement_quality","synthetic","source_id"
100
- ]);
101
- }
102
- function renderFilters() {
103
- $("filters").replaceChildren(); selections = {};
104
- for (const key of keys) {
105
- selections[key] = "";
106
- const label = document.createElement("label");
107
- label.textContent = key;
108
- const select = document.createElement("select");
109
- const all = document.createElement("option");
110
- all.value = ""; all.textContent = "All"; select.append(all);
111
- const values = new Map(observations.map(row =>
112
- [token(row[key]), display(row[key])]));
113
- for (const [id,name] of values) {
114
- const option = document.createElement("option");
115
- option.value = id; option.textContent = name; select.append(option);
116
- }
117
- select.addEventListener("change", () => {
118
- selections[key] = select.value; renderObservations();
119
  });
120
- label.append(select); $("filters").append(label);
121
  }
122
- }
123
- function validate(candidate) {
124
- if (!candidate || typeof candidate !== "object" || Array.isArray(candidate))
125
- throw Error("Report must be a JSON object.");
126
- if (!candidate.dataset ||
127
- !Array.isArray(candidate.dataset.observations))
128
- throw Error("dataset.observations must be an array.");
129
- if (!candidate.dataset.observations.every(row =>
130
- row && typeof row === "object" && !Array.isArray(row)))
131
- throw Error("Each observation must be an object.");
132
- if (!Array.isArray(candidate.models))
133
- throw Error("models must be an array, including when empty.");
134
- if (!candidate.models.every(model =>
135
- model && typeof model === "object" && !Array.isArray(model)))
136
- throw Error("Each model must be an object.");
137
- for (const row of candidate.dataset.observations) {
138
- for (const key of keys) {
139
- if (!missing(row[key]) && !["string","number"].includes(typeof row[key]))
140
- throw Error(`${key} must be a string, number, or null.`);
141
- }
142
- for (const key of valueKeys) {
143
- if (!missing(row[key]) &&
144
- (typeof row[key] !== "number" || !Number.isFinite(row[key])))
145
- throw Error(`${key} must be a finite number or null.`);
146
- }
147
- if (!missing(row.expression_count) &&
148
- (!Number.isSafeInteger(row.expression_count) || row.expression_count < 0))
149
- throw Error("expression_count must be a nonnegative safe integer.");
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
150
  }
151
- }
152
- function render() {
153
- observations = report.dataset.observations;
154
- renderFilters(); renderObservations();
155
- show("sequencing", {
156
- dataset_id: report.dataset.id ?? null,
157
- synthetic: report.dataset.synthetic ?? "undeclared",
158
- sequencing: report.dataset.sequencing ?? null,
159
- quality: report.dataset.quality ?? null,
160
- preprocessing: report.dataset.preprocessing ?? null
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
161
  });
162
- show("models", report.models.map(model => ({
163
- id: model.id ?? null, status: model.status ?? "not supplied",
164
- assumptions: model.assumptions ?? null,
165
- structure: model.structure ?? null, priors: model.priors ?? null,
166
- likelihood: model.likelihood ?? null, fit: model.fit ?? null,
167
- limitations: model.limitations ?? null
168
- })));
169
- show("parameters", report.models.map(model => ({
170
- id: model.id ?? null, parameters: model.parameters ?? null,
171
- dimensions: model.dimensions ?? null, priors: model.priors ?? null
172
- })));
173
- show("posteriors", report.models.map(model => ({
174
- id: model.id ?? null, status: model.status ?? "not supplied",
175
- summary: model.summary ?? null,
176
- diagnostics: model.diagnostics ?? null,
177
- uncertainty_status: model.summary ?
178
- "Inspect supplied intervals, standard deviations and diagnostics; viewer has not validated them." :
179
- "No posterior summary supplied."
180
- })));
181
- show("predictions", report.models.map(model => ({
182
- id: model.id ?? null, predictions: model.predictions ?? null
183
- })));
184
- for (const key of ["constraints","binary","ptm","quipper","artifacts","stages"])
185
- show(key, report[key]);
186
- $("state").textContent =
187
- "Report loaded. Observation filters do not refit models or change posterior summaries. " +
188
- "Model comparison is descriptive; no biological explanation is selected.";
189
- }
190
- let loadNumber = 0;
191
- $("file").addEventListener("change", async event => {
192
- const current = ++loadNumber;
193
- const file = event.target.files[0];
194
- if (!file) return;
195
- $("error").textContent = "";
196
- try {
197
- if (file.size > 10 * 1024 * 1024)
198
- throw Error("Report exceeds this viewer's 10 MiB import limit.");
199
- const candidate = JSON.parse(await file.text());
200
- if (current !== loadNumber) return;
201
- validate(candidate); report = candidate; render();
202
- } catch (error) {
203
- if (current !== loadNumber) return;
204
- $("error").textContent = error.message;
205
- $("state").textContent =
206
- report ? "Import rejected. Previous report remains displayed." :
207
- "Import rejected. No report loaded.";
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
208
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
209
  });
210
- for (const id of ["models","parameters","posteriors","predictions",
211
- "constraints","binary","ptm","quipper","artifacts","stages"])
212
- show(id, null);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
213
  </script>
 
214
  </html>
 
1
+ <!DOCTYPE html>
2
  <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
+ <title>BIO-SEMANTICS — Gene-Expression Semantic Modeling Pipeline</title>
7
  <style>
8
+ @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');
9
+
10
+ :root {
11
+ --bg-primary: #030810;
12
+ --bg-secondary: #060e1a;
13
+ --bg-panel: rgba(6,18,36,0.85);
14
+ --bg-panel-hover: rgba(10,26,52,0.92);
15
+ --border-primary: rgba(0,200,255,0.12);
16
+ --border-active: rgba(0,200,255,0.35);
17
+ --border-glow: rgba(0,200,255,0.5);
18
+ --cyan: #00c8ff;
19
+ --cyan-dim: #006688;
20
+ --blue: #2266ff;
21
+ --blue-bright: #4488ff;
22
+ --violet: #8844ff;
23
+ --violet-dim: #5522aa;
24
+ --green-bio: #22cc66;
25
+ --green-dim: #0a6630;
26
+ --amber: #ffaa22;
27
+ --red: #ff3344;
28
+ --white: #e8eef6;
29
+ --white-dim: #8899aa;
30
+ --text-primary: #c8d8e8;
31
+ --text-secondary: #6688aa;
32
+ --text-label: #446688;
33
+ --mono: 'IBM Plex Mono', monospace;
34
+ --sans: 'IBM Plex Sans', sans-serif;
35
+ --serif: 'IBM Plex Serif', serif;
36
+ --glow-cyan: 0 0 8px rgba(0,200,255,0.3), 0 0 20px rgba(0,200,255,0.1);
37
+ --glow-violet: 0 0 8px rgba(136,68,255,0.3), 0 0 20px rgba(136,68,255,0.1);
38
+ --glow-green: 0 0 8px rgba(34,204,102,0.3), 0 0 20px rgba(34,204,102,0.1);
39
+ }
40
+
41
+ *, *::before, *::after { margin:0; padding:0; box-sizing:border-box; }
42
+
43
+ html { font-size: 13px; }
44
+
45
+ body {
46
+ background: var(--bg-primary);
47
+ color: var(--text-primary);
48
+ font-family: var(--sans);
49
+ font-weight: 300;
50
+ overflow-x: hidden;
51
+ min-height: 100vh;
52
+ }
53
+
54
+ /* === GRID BACKGROUND === */
55
+ body::before {
56
+ content: '';
57
+ position: fixed;
58
+ inset: 0;
59
+ background:
60
+ linear-gradient(rgba(0,200,255,0.015) 1px, transparent 1px),
61
+ linear-gradient(90deg, rgba(0,200,255,0.015) 1px, transparent 1px);
62
+ background-size: 40px 40px;
63
+ pointer-events: none;
64
+ z-index: 0;
65
+ }
66
+
67
+ body::after {
68
+ content: '';
69
+ position: fixed;
70
+ inset: 0;
71
+ background: radial-gradient(ellipse 80% 60% at 50% 30%, rgba(0,40,80,0.15), transparent);
72
+ pointer-events: none;
73
+ z-index: 0;
74
+ }
75
+
76
+ /* === HEADER === */
77
+ .header {
78
+ position: relative;
79
+ z-index: 10;
80
+ text-align: center;
81
+ padding: 28px 20px 16px;
82
+ border-bottom: 1px solid var(--border-primary);
83
+ background: linear-gradient(180deg, rgba(6,18,36,0.95), rgba(3,8,16,0.8));
84
+ }
85
+
86
+ .header-title {
87
+ font-family: var(--sans);
88
+ font-weight: 200;
89
+ font-size: 2.6rem;
90
+ letter-spacing: 0.45em;
91
+ color: var(--cyan);
92
+ text-shadow: var(--glow-cyan);
93
+ margin-bottom: 6px;
94
+ }
95
+
96
+ .header-subtitle {
97
+ font-family: var(--serif);
98
+ font-weight: 300;
99
+ font-style: italic;
100
+ font-size: 1.05rem;
101
+ color: var(--text-secondary);
102
+ letter-spacing: 0.08em;
103
+ margin-bottom: 12px;
104
+ }
105
+
106
+ .header-authors {
107
+ font-family: var(--mono);
108
+ font-size: 0.72rem;
109
+ letter-spacing: 0.18em;
110
+ color: var(--text-label);
111
+ text-transform: uppercase;
112
+ }
113
+
114
+ .header-authors span { margin: 0 14px; }
115
+ .header-authors span.sep { color: var(--cyan-dim); }
116
+
117
+ /* === NAV === */
118
+ .nav {
119
+ position: relative;
120
+ z-index: 10;
121
+ display: flex;
122
+ justify-content: center;
123
+ gap: 2px;
124
+ padding: 8px 20px;
125
+ border-bottom: 1px solid var(--border-primary);
126
+ background: rgba(3,8,16,0.9);
127
+ }
128
+
129
+ .nav-item {
130
+ font-family: var(--mono);
131
+ font-size: 0.68rem;
132
+ font-weight: 400;
133
+ letter-spacing: 0.12em;
134
+ text-transform: uppercase;
135
+ color: var(--text-label);
136
+ padding: 6px 14px;
137
+ cursor: pointer;
138
+ border: 1px solid transparent;
139
+ border-radius: 2px;
140
+ transition: all 0.3s ease;
141
+ user-select: none;
142
+ }
143
+
144
+ .nav-item:hover {
145
+ color: var(--cyan);
146
+ border-color: var(--border-active);
147
+ background: rgba(0,200,255,0.04);
148
+ }
149
+
150
+ .nav-item.active {
151
+ color: var(--cyan);
152
+ border-color: var(--border-active);
153
+ background: rgba(0,200,255,0.06);
154
+ text-shadow: var(--glow-cyan);
155
+ }
156
+
157
+ /* === MAIN LAYOUT === */
158
+ .main {
159
+ position: relative;
160
+ z-index: 5;
161
+ display: grid;
162
+ grid-template-columns: 1fr 380px 1fr;
163
+ grid-template-rows: auto auto auto;
164
+ gap: 12px;
165
+ padding: 14px;
166
+ max-width: 1920px;
167
+ margin: 0 auto;
168
+ }
169
+
170
+ /* === PANELS === */
171
+ .panel {
172
+ background: var(--bg-panel);
173
+ border: 1px solid var(--border-primary);
174
+ border-radius: 3px;
175
+ padding: 14px;
176
+ position: relative;
177
+ overflow: hidden;
178
+ transition: border-color 0.4s ease, box-shadow 0.4s ease;
179
+ }
180
+
181
+ .panel::before {
182
+ content: '';
183
+ position: absolute;
184
+ top: 0; left: 0; right: 0;
185
+ height: 1px;
186
+ background: linear-gradient(90deg, transparent, var(--cyan-dim), transparent);
187
+ opacity: 0.5;
188
+ }
189
+
190
+ .panel:hover {
191
+ border-color: var(--border-active);
192
+ box-shadow: var(--glow-cyan);
193
+ }
194
+
195
+ .panel-title {
196
+ font-family: var(--mono);
197
+ font-size: 0.65rem;
198
+ font-weight: 500;
199
+ letter-spacing: 0.2em;
200
+ text-transform: uppercase;
201
+ color: var(--cyan);
202
+ margin-bottom: 10px;
203
+ padding-bottom: 6px;
204
+ border-bottom: 1px solid var(--border-primary);
205
+ display: flex;
206
+ align-items: center;
207
+ gap: 8px;
208
+ }
209
+
210
+ .panel-title .indicator {
211
+ width: 5px; height: 5px;
212
+ border-radius: 50%;
213
+ background: var(--cyan);
214
+ box-shadow: 0 0 6px var(--cyan);
215
+ animation: pulse-indicator 2s ease-in-out infinite;
216
+ }
217
+
218
+ @keyframes pulse-indicator {
219
+ 0%, 100% { opacity: 0.6; }
220
+ 50% { opacity: 1; }
221
+ }
222
+
223
+ /* === LEFT COLUMN === */
224
+ .left-column {
225
+ display: flex;
226
+ flex-direction: column;
227
+ gap: 12px;
228
+ }
229
+
230
+ /* === CENTER COLUMN === */
231
+ .center-column {
232
+ display: flex;
233
+ flex-direction: column;
234
+ gap: 12px;
235
+ align-items: center;
236
+ }
237
+
238
+ /* === RIGHT COLUMN === */
239
+ .right-column {
240
+ display: flex;
241
+ flex-direction: column;
242
+ gap: 12px;
243
+ }
244
+
245
+ /* === HELIX CANVAS === */
246
+ .helix-container {
247
+ position: relative;
248
+ width: 380px;
249
+ height: 720px;
250
+ border: 1px solid var(--border-primary);
251
+ border-radius: 3px;
252
+ overflow: hidden;
253
+ background: radial-gradient(ellipse at 50% 30%, rgba(0,40,80,0.12), transparent);
254
+ }
255
+
256
+ #helixCanvas {
257
+ width: 100%;
258
+ height: 100%;
259
+ }
260
+
261
+ .helix-labels {
262
+ position: absolute;
263
+ top: 0; left: 0; right: 0; bottom: 0;
264
+ pointer-events: none;
265
+ }
266
+
267
+ .helix-label {
268
+ position: absolute;
269
+ font-family: var(--mono);
270
+ font-size: 0.58rem;
271
+ letter-spacing: 0.15em;
272
+ text-transform: uppercase;
273
+ color: var(--text-label);
274
+ white-space: nowrap;
275
+ }
276
+
277
+ .helix-label.left { right: calc(50% + 50px); text-align: right; }
278
+ .helix-label.right { left: calc(50% + 50px); }
279
+
280
+ .helix-label .hl-val {
281
+ color: var(--cyan);
282
+ font-weight: 500;
283
+ }
284
+
285
+ /* === PIPELINE FLOW NODES === */
286
+ .pipeline-flow {
287
+ display: flex;
288
+ flex-direction: column;
289
+ align-items: center;
290
+ gap: 0;
291
+ width: 100%;
292
+ }
293
+
294
+ .pipeline-node {
295
+ font-family: var(--mono);
296
+ font-size: 0.62rem;
297
+ font-weight: 400;
298
+ letter-spacing: 0.15em;
299
+ text-transform: uppercase;
300
+ color: var(--text-secondary);
301
+ padding: 5px 12px;
302
+ border: 1px solid var(--border-primary);
303
+ border-radius: 2px;
304
+ text-align: center;
305
+ cursor: pointer;
306
+ transition: all 0.3s ease;
307
+ position: relative;
308
+ background: rgba(3,8,16,0.6);
309
+ width: 100%;
310
+ max-width: 240px;
311
+ }
312
+
313
+ .pipeline-node:hover, .pipeline-node.active {
314
+ color: var(--cyan);
315
+ border-color: var(--border-active);
316
+ background: rgba(0,200,255,0.05);
317
+ text-shadow: 0 0 8px rgba(0,200,255,0.4);
318
+ }
319
+
320
+ .pipeline-connector {
321
+ width: 1px;
322
+ height: 14px;
323
+ position: relative;
324
+ overflow: visible;
325
+ }
326
+
327
+ .pipeline-connector::before {
328
+ content: '';
329
+ position: absolute;
330
+ left: 0; top: 0; bottom: 0;
331
+ width: 1px;
332
+ background: linear-gradient(180deg, var(--cyan-dim), rgba(0,200,255,0.05));
333
+ }
334
+
335
+ /* Particle animation on connectors */
336
+ .pipeline-connector::after {
337
+ content: '';
338
+ position: absolute;
339
+ left: -1px; top: 0;
340
+ width: 3px; height: 3px;
341
+ border-radius: 50%;
342
+ background: var(--cyan);
343
+ box-shadow: 0 0 4px var(--cyan);
344
+ animation: flow-particle 1.5s linear infinite;
345
+ }
346
+
347
+ @keyframes flow-particle {
348
+ 0% { top: 0; opacity: 1; }
349
+ 100% { top: 100%; opacity: 0; }
350
+ }
351
+
352
+ /* === CDNA PANEL === */
353
+ .seq-trace {
354
+ display: flex;
355
+ gap: 1px;
356
+ margin: 6px 0;
357
+ height: 32px;
358
+ align-items: flex-end;
359
+ }
360
+
361
+ .seq-bar {
362
+ width: 3px;
363
+ background: var(--cyan);
364
+ border-radius: 1px 1px 0 0;
365
+ opacity: 0.7;
366
+ transition: opacity 0.3s;
367
+ }
368
+
369
+ .seq-bar:hover { opacity: 1; }
370
+
371
+ .seq-bar.a { background: var(--green-bio); }
372
+ .seq-bar.t { background: var(--red); }
373
+ .seq-bar.g { background: var(--amber); }
374
+ .seq-bar.c { background: var(--blue-bright); }
375
+
376
+ .seq-reads {
377
+ font-family: var(--mono);
378
+ font-size: 0.6rem;
379
+ color: var(--text-label);
380
+ line-height: 1.4;
381
+ margin-top: 8px;
382
+ }
383
+
384
+ .seq-reads .base { padding: 0 1px; }
385
+ .seq-reads .base.a { color: var(--green-bio); }
386
+ .seq-reads .base.t { color: var(--red); }
387
+ .seq-reads .base.g { color: var(--amber); }
388
+ .seq-reads .base.c { color: var(--blue-bright); }
389
+
390
+ /* === EXPRESSION MATRIX === */
391
+ .expr-matrix {
392
+ display: grid;
393
+ gap: 1px;
394
+ margin: 8px 0;
395
+ }
396
+
397
+ .expr-cell {
398
+ width: 100%;
399
+ aspect-ratio: 1;
400
+ border-radius: 1px;
401
+ transition: transform 0.2s;
402
+ }
403
+
404
+ .expr-cell:hover { transform: scale(1.8); z-index: 10; }
405
+
406
+ .expr-axis-label {
407
+ font-family: var(--mono);
408
+ font-size: 0.52rem;
409
+ color: var(--text-label);
410
+ letter-spacing: 0.1em;
411
+ text-transform: uppercase;
412
+ }
413
+
414
+ /* === VALIDATION PANEL === */
415
+ .validation-grid {
416
+ display: grid;
417
+ grid-template-columns: 1fr auto;
418
+ gap: 3px 12px;
419
+ font-family: var(--mono);
420
+ font-size: 0.6rem;
421
+ }
422
+
423
+ .val-rule {
424
+ color: var(--text-secondary);
425
+ letter-spacing: 0.08em;
426
+ text-transform: uppercase;
427
+ padding: 2px 0;
428
+ }
429
+
430
+ .val-status {
431
+ text-align: right;
432
+ font-weight: 500;
433
+ }
434
+
435
+ .val-status.pass { color: var(--green-bio); }
436
+ .val-status.warn { color: var(--amber); }
437
+ .val-status.fail { color: var(--red); }
438
+
439
+ /* === QUANTITY KIND === */
440
+ .qty-kinds {
441
+ display: flex;
442
+ flex-direction: column;
443
+ gap: 6px;
444
+ }
445
+
446
+ .qty-kind {
447
+ display: flex;
448
+ align-items: center;
449
+ gap: 10px;
450
+ padding: 4px 8px;
451
+ border: 1px solid transparent;
452
+ border-radius: 2px;
453
+ cursor: pointer;
454
+ transition: all 0.3s ease;
455
+ }
456
+
457
+ .qty-kind:hover, .qty-kind.active {
458
+ border-color: var(--border-active);
459
+ background: rgba(0,200,255,0.04);
460
+ }
461
+
462
+ .qty-glyph {
463
+ width: 20px; height: 20px;
464
+ display: flex;
465
+ align-items: center;
466
+ justify-content: center;
467
+ flex-shrink: 0;
468
+ }
469
+
470
+ .qty-glyph svg { width: 16px; height: 16px; }
471
+
472
+ .qty-label {
473
+ font-family: var(--mono);
474
+ font-size: 0.62rem;
475
+ letter-spacing: 0.12em;
476
+ text-transform: uppercase;
477
+ color: var(--text-secondary);
478
+ }
479
+
480
+ .qty-kind:hover .qty-label,
481
+ .qty-kind.active .qty-label { color: var(--cyan); }
482
+
483
+ .qty-source {
484
+ font-family: var(--serif);
485
+ font-size: 0.58rem;
486
+ font-style: italic;
487
+ color: var(--text-label);
488
+ margin-left: auto;
489
+ }
490
+
491
+ /* === STAN PANEL === */
492
+ .stan-chart {
493
+ position: relative;
494
+ height: 120px;
495
+ margin: 8px 0;
496
+ border-left: 1px solid var(--border-primary);
497
+ border-bottom: 1px solid var(--border-primary);
498
+ }
499
+
500
+ .stan-chart canvas { width: 100%; height: 100%; }
501
+
502
+ .stan-table {
503
+ width: 100%;
504
+ border-collapse: collapse;
505
+ margin-top: 8px;
506
+ }
507
+
508
+ .stan-table th {
509
+ font-family: var(--mono);
510
+ font-size: 0.52rem;
511
+ letter-spacing: 0.12em;
512
+ text-transform: uppercase;
513
+ color: var(--text-label);
514
+ text-align: left;
515
+ padding: 3px 6px;
516
+ border-bottom: 1px solid var(--border-primary);
517
+ font-weight: 400;
518
+ }
519
+
520
+ .stan-table td {
521
+ font-family: var(--mono);
522
+ font-size: 0.58rem;
523
+ color: var(--text-secondary);
524
+ padding: 3px 6px;
525
+ border-bottom: 1px solid rgba(0,200,255,0.04);
526
+ }
527
+
528
+ .stan-table td.num { color: var(--cyan); text-align: right; }
529
+
530
+ .stan-flow {
531
+ display: flex;
532
+ justify-content: center;
533
+ align-items: center;
534
+ gap: 12px;
535
+ margin: 10px 0 6px;
536
+ font-family: var(--mono);
537
+ font-size: 0.58rem;
538
+ letter-spacing: 0.1em;
539
+ text-transform: uppercase;
540
+ }
541
+
542
+ .stan-flow-node {
543
+ color: var(--text-secondary);
544
+ padding: 3px 8px;
545
+ border: 1px solid var(--border-primary);
546
+ border-radius: 2px;
547
+ }
548
+
549
+ .stan-flow-arrow { color: var(--cyan-dim); }
550
+
551
+ /* === MINIZINC PANEL === */
552
+ .mzn-graph {
553
+ position: relative;
554
+ height: 160px;
555
+ margin: 8px 0;
556
+ }
557
+
558
+ .mzn-graph canvas { width: 100%; height: 100%; }
559
+
560
+ .mzn-notation {
561
+ font-family: var(--serif);
562
+ font-style: italic;
563
+ font-size: 0.72rem;
564
+ color: var(--text-secondary);
565
+ text-align: center;
566
+ margin: 8px 0;
567
+ line-height: 1.6;
568
+ }
569
+
570
+ /* === PTM PANEL === */
571
+ .ptm-tape {
572
+ display: flex;
573
+ justify-content: center;
574
+ gap: 2px;
575
+ margin: 10px 0;
576
+ position: relative;
577
+ }
578
+
579
+ .ptm-cell {
580
+ width: 28px; height: 28px;
581
+ border: 1px solid var(--border-primary);
582
+ display: flex;
583
+ align-items: center;
584
+ justify-content: center;
585
+ font-family: var(--mono);
586
+ font-size: 0.8rem;
587
+ font-weight: 500;
588
+ color: var(--cyan);
589
+ transition: all 0.3s ease;
590
+ position: relative;
591
+ }
592
+
593
+ .ptm-cell.active {
594
+ border-color: var(--cyan);
595
+ background: rgba(0,200,255,0.1);
596
+ box-shadow: var(--glow-cyan);
597
+ }
598
+
599
+ .ptm-head {
600
+ position: absolute;
601
+ bottom: -12px;
602
+ left: 50%;
603
+ transform: translateX(-50%);
604
+ width: 0; height: 0;
605
+ border-left: 5px solid transparent;
606
+ border-right: 5px solid transparent;
607
+ border-bottom: 6px solid var(--cyan);
608
+ filter: drop-shadow(0 0 4px var(--cyan));
609
+ }
610
+
611
+ .ptm-states {
612
+ display: flex;
613
+ justify-content: center;
614
+ gap: 16px;
615
+ margin: 16px 0 8px;
616
+ }
617
+
618
+ .ptm-state {
619
+ width: 52px; height: 52px;
620
+ border-radius: 50%;
621
+ border: 1px solid var(--border-primary);
622
+ display: flex;
623
+ align-items: center;
624
+ justify-content: center;
625
+ font-family: var(--mono);
626
+ font-size: 0.56rem;
627
+ color: var(--text-secondary);
628
+ transition: all 0.4s ease;
629
+ position: relative;
630
+ }
631
+
632
+ .ptm-state.current {
633
+ border-color: var(--cyan);
634
+ color: var(--cyan);
635
+ box-shadow: var(--glow-cyan);
636
+ }
637
+
638
+ .ptm-state.accept {
639
+ border-width: 2px;
640
+ }
641
+
642
+ .ptm-transition-table {
643
+ width: 100%;
644
+ border-collapse: collapse;
645
+ margin-top: 8px;
646
+ }
647
+
648
+ .ptm-transition-table th {
649
+ font-family: var(--mono);
650
+ font-size: 0.5rem;
651
+ letter-spacing: 0.12em;
652
+ text-transform: uppercase;
653
+ color: var(--text-label);
654
+ text-align: center;
655
+ padding: 3px 4px;
656
+ border-bottom: 1px solid var(--border-primary);
657
+ font-weight: 400;
658
+ }
659
+
660
+ .ptm-transition-table td {
661
+ font-family: var(--mono);
662
+ font-size: 0.56rem;
663
+ color: var(--text-secondary);
664
+ text-align: center;
665
+ padding: 2px 4px;
666
+ border-bottom: 1px solid rgba(0,200,255,0.04);
667
+ }
668
+
669
+ .ptm-transition-table tr.active-row td {
670
+ color: var(--cyan);
671
+ background: rgba(0,200,255,0.04);
672
+ }
673
+
674
+ .ptm-complexity {
675
+ font-family: var(--mono);
676
+ font-size: 0.56rem;
677
+ color: var(--text-label);
678
+ text-align: center;
679
+ margin-top: 8px;
680
+ line-height: 1.6;
681
+ }
682
+
683
+ .ptm-complexity span { color: var(--cyan); }
684
+
685
+ /* === BINARY PANEL === */
686
+ .binary-stream {
687
+ font-family: var(--mono);
688
+ font-size: 0.7rem;
689
+ color: var(--cyan-dim);
690
+ line-height: 1.6;
691
+ word-break: break-all;
692
+ margin: 6px 0;
693
+ letter-spacing: 0.05em;
694
+ }
695
+
696
+ .binary-stream .one { color: var(--cyan); }
697
+ .binary-stream .zero { color: var(--cyan-dim); opacity: 0.5; }
698
+
699
+ .binary-table {
700
+ width: 100%;
701
+ border-collapse: collapse;
702
+ margin-top: 6px;
703
+ }
704
+
705
+ .binary-table th {
706
+ font-family: var(--mono);
707
+ font-size: 0.5rem;
708
+ letter-spacing: 0.1em;
709
+ text-transform: uppercase;
710
+ color: var(--text-label);
711
+ text-align: left;
712
+ padding: 2px 6px;
713
+ border-bottom: 1px solid var(--border-primary);
714
+ font-weight: 400;
715
+ }
716
+
717
+ .binary-table td {
718
+ font-family: var(--mono);
719
+ font-size: 0.56rem;
720
+ color: var(--text-secondary);
721
+ padding: 2px 6px;
722
+ border-bottom: 1px solid rgba(0,200,255,0.04);
723
+ }
724
+
725
+ /* === QUIPPER PANEL === */
726
+ .quipper-circuit {
727
+ position: relative;
728
+ height: 140px;
729
+ margin: 8px 0;
730
+ }
731
+
732
+ .quipper-circuit canvas { width: 100%; height: 100%; }
733
+
734
+ .quipper-stats {
735
+ display: grid;
736
+ grid-template-columns: repeat(3, 1fr);
737
+ gap: 8px;
738
+ margin-top: 8px;
739
+ }
740
+
741
+ .quipper-stat {
742
+ text-align: center;
743
+ }
744
+
745
+ .quipper-stat .qs-val {
746
+ font-family: var(--mono);
747
+ font-size: 1.1rem;
748
+ font-weight: 500;
749
+ color: var(--violet);
750
+ text-shadow: var(--glow-violet);
751
+ }
752
+
753
+ .quipper-stat .qs-label {
754
+ font-family: var(--mono);
755
+ font-size: 0.48rem;
756
+ letter-spacing: 0.15em;
757
+ text-transform: uppercase;
758
+ color: var(--text-label);
759
+ margin-top: 2px;
760
+ }
761
+
762
+ /* === PROVENANCE PANEL === */
763
+ .prov-chain {
764
+ display: flex;
765
+ flex-direction: column;
766
+ gap: 0;
767
+ }
768
+
769
+ .prov-node {
770
+ display: grid;
771
+ grid-template-columns: auto 1fr auto;
772
+ gap: 8px;
773
+ padding: 4px 8px;
774
+ align-items: center;
775
+ border: 1px solid transparent;
776
+ border-radius: 2px;
777
+ cursor: pointer;
778
+ transition: all 0.3s ease;
779
+ }
780
+
781
+ .prov-node:hover {
782
+ border-color: var(--border-active);
783
+ background: rgba(0,200,255,0.03);
784
+ }
785
+
786
+ .prov-icon {
787
+ width: 8px; height: 8px;
788
+ border: 1px solid var(--cyan-dim);
789
+ border-radius: 50%;
790
+ }
791
+
792
+ .prov-node:hover .prov-icon {
793
+ background: var(--cyan);
794
+ border-color: var(--cyan);
795
+ box-shadow: 0 0 6px var(--cyan);
796
+ }
797
+
798
+ .prov-name {
799
+ font-family: var(--mono);
800
+ font-size: 0.58rem;
801
+ letter-spacing: 0.1em;
802
+ text-transform: uppercase;
803
+ color: var(--text-secondary);
804
+ }
805
+
806
+ .prov-hash {
807
+ font-family: var(--mono);
808
+ font-size: 0.5rem;
809
+ color: var(--text-label);
810
+ letter-spacing: 0.02em;
811
+ }
812
+
813
+ .prov-connector {
814
+ width: 1px;
815
+ height: 8px;
816
+ margin-left: 12px;
817
+ background: var(--border-primary);
818
+ position: relative;
819
+ }
820
+
821
+ .prov-detail {
822
+ display: none;
823
+ font-family: var(--mono);
824
+ font-size: 0.52rem;
825
+ color: var(--text-label);
826
+ padding: 6px 8px 6px 28px;
827
+ line-height: 1.6;
828
+ border-left: 1px solid var(--border-primary);
829
+ margin-left: 12px;
830
+ }
831
+
832
+ .prov-detail .pd-key { color: var(--text-label); text-transform: uppercase; letter-spacing: 0.1em; }
833
+ .prov-detail .pd-val { color: var(--text-secondary); }
834
+
835
+ /* === RPG/JCL PANEL === */
836
+ .rpg-block {
837
+ font-family: var(--mono);
838
+ font-size: 0.56rem;
839
+ line-height: 1.7;
840
+ color: var(--text-label);
841
+ background: rgba(0,0,0,0.3);
842
+ border: 1px solid rgba(0,200,255,0.06);
843
+ padding: 8px 10px;
844
+ margin: 6px 0;
845
+ border-radius: 2px;
846
+ position: relative;
847
+ }
848
+
849
+ .rpg-block .rpg-kw { color: var(--amber); }
850
+ .rpg-block .rpg-id { color: var(--cyan-dim); }
851
+ .rpg-block .rpg-st { color: var(--green-bio); }
852
+ .rpg-block .rpg-comment { color: var(--text-label); opacity: 0.5; }
853
+
854
+ .rpg-job-status {
855
+ display: grid;
856
+ grid-template-columns: auto 1fr auto;
857
+ gap: 4px 10px;
858
+ font-family: var(--mono);
859
+ font-size: 0.54rem;
860
+ margin-top: 6px;
861
+ }
862
+
863
+ .rpg-job-id { color: var(--text-label); }
864
+ .rpg-job-name { color: var(--text-secondary); text-transform: uppercase; letter-spacing: 0.08em; }
865
+ .rpg-job-state { font-weight: 500; }
866
+ .rpg-job-state.complete { color: var(--green-bio); }
867
+ .rpg-job-state.running { color: var(--amber); }
868
+ .rpg-job-state.queued { color: var(--text-label); }
869
+
870
+ /* === REPO PANEL === */
871
+ .repo-tree {
872
+ font-family: var(--mono);
873
+ font-size: 0.58rem;
874
+ line-height: 1.8;
875
+ color: var(--text-label);
876
+ }
877
+
878
+ .repo-tree .dir { color: var(--cyan-dim); }
879
+ .repo-tree .file { color: var(--text-secondary); }
880
+ .repo-tree .file-py { color: var(--green-bio); }
881
+ .repo-tree .file-ts { color: var(--blue-bright); }
882
+ .repo-tree .file-md { color: var(--text-secondary); }
883
+ .repo-tree .file-rpg { color: var(--amber); }
884
+ .repo-tree .file-jcl { color: var(--amber); opacity: 0.7; }
885
+ .repo-tree .file-html { color: var(--violet); }
886
+
887
+ .repo-indent { display: inline-block; }
888
+ .repo-indent-1 { width: 16px; }
889
+ .repo-indent-2 { width: 32px; }
890
+
891
+ /* === BOTTOM STRIP === */
892
+ .bottom-strip {
893
+ grid-column: 1 / -1;
894
+ display: grid;
895
+ grid-template-columns: 1fr 1fr 1fr;
896
+ gap: 12px;
897
+ }
898
+
899
+ /* === ARCHITECTURE PROPOSITION === */
900
+ .proposition {
901
+ grid-column: 1 / -1;
902
+ text-align: center;
903
+ padding: 20px;
904
+ border-top: 1px solid var(--border-primary);
905
+ margin-top: 4px;
906
+ }
907
+
908
+ .proposition-line {
909
+ font-family: var(--serif);
910
+ font-size: 0.72rem;
911
+ color: var(--text-label);
912
+ line-height: 2.2;
913
+ font-style: italic;
914
+ }
915
+
916
+ .proposition-line .prop-subject {
917
+ font-family: var(--mono);
918
+ font-style: normal;
919
+ font-size: 0.62rem;
920
+ letter-spacing: 0.12em;
921
+ text-transform: uppercase;
922
+ color: var(--cyan-dim);
923
+ }
924
+
925
+ .proposition-line .prop-verb {
926
+ color: var(--text-secondary);
927
+ }
928
+
929
+ /* === ANIMATIONS === */
930
+ @keyframes scan-line {
931
+ 0% { transform: translateY(-100%); }
932
+ 100% { transform: translateY(100%); }
933
+ }
934
+
935
+ @keyframes data-pulse {
936
+ 0%, 100% { opacity: 0.3; }
937
+ 50% { opacity: 0.8; }
938
+ }
939
+
940
+ @keyframes tape-scan {
941
+ 0% { left: 0; }
942
+ 100% { left: calc(100% - 28px); }
943
+ }
944
+
945
+ /* === SCROLLBAR === */
946
+ ::-webkit-scrollbar { width: 4px; }
947
+ ::-webkit-scrollbar-track { background: var(--bg-primary); }
948
+ ::-webkit-scrollbar-thumb { background: var(--cyan-dim); border-radius: 2px; }
949
+
950
+ /* === RESPONSIVE === */
951
+ @media (max-width: 1400px) {
952
+ .main {
953
+ grid-template-columns: 1fr 340px 1fr;
954
+ }
955
+ }
956
+
957
+ @media (max-width: 1100px) {
958
+ .main {
959
+ grid-template-columns: 1fr;
960
+ }
961
+ .helix-container { width: 100%; max-width: 380px; margin: 0 auto; }
962
+ .bottom-strip { grid-template-columns: 1fr; }
963
+ }
964
+
965
+ /* === MISC === */
966
+ .glow-text { text-shadow: var(--glow-cyan); }
967
+ .small-label {
968
+ font-family: var(--mono);
969
+ font-size: 0.48rem;
970
+ letter-spacing: 0.15em;
971
+ text-transform: uppercase;
972
+ color: var(--text-label);
973
+ }
974
+
975
+ .divider {
976
+ height: 1px;
977
+ background: linear-gradient(90deg, transparent, var(--border-primary), transparent);
978
+ margin: 8px 0;
979
+ }
980
+
981
+ .semantic-flow-label {
982
+ font-family: var(--mono);
983
+ font-size: 0.5rem;
984
+ letter-spacing: 0.15em;
985
+ text-transform: uppercase;
986
+ color: var(--text-label);
987
+ text-align: center;
988
+ margin: 4px 0 2px;
989
+ }
990
+
991
+ .semantic-flow-arrow {
992
+ text-align: center;
993
+ color: var(--cyan-dim);
994
+ font-size: 0.6rem;
995
+ line-height: 1;
996
+ }
997
  </style>
998
+ </head>
999
+ <body>
1000
+
1001
+ <!-- HEADER -->
1002
+ <header class="header">
1003
+ <div class="header-title">BIO-SEMANTICS</div>
1004
+ <div class="header-subtitle">Gene-Expression Semantic Modeling Pipeline</div>
1005
+ <div class="header-authors">
1006
+ <span>Ahmad Ali Parr</span>
1007
+ <span class="sep">·</span>
1008
+ <span>Jessica L. Williams</span>
1009
+ <span class="sep">·</span>
1010
+ <span>SNAPKITTYWEST</span>
1011
+ </div>
1012
  </header>
1013
+
1014
+ <!-- NAV -->
1015
+ <nav class="nav">
1016
+ <div class="nav-item active" data-target="observations">Observations</div>
1017
+ <div class="nav-item" data-target="semantics">Semantics</div>
1018
+ <div class="nav-item" data-target="bayesian">Bayesian</div>
1019
+ <div class="nav-item" data-target="constraints">Constraints</div>
1020
+ <div class="nav-item" data-target="binary">Binary</div>
1021
+ <div class="nav-item" data-target="ptm">PTM</div>
1022
+ <div class="nav-item" data-target="quipper">Quipper</div>
1023
+ <div class="nav-item" data-target="provenance">Provenance</div>
1024
+ <div class="nav-item" data-target="rpgjcl">RPG/JCL</div>
1025
+ </nav>
1026
+
1027
+ <!-- MAIN -->
1028
+ <main class="main">
1029
+
1030
+ <!-- LEFT COLUMN -->
1031
+ <div class="left-column">
1032
+
1033
+ <!-- cDNA SEQUENCING -->
1034
+ <div class="panel" id="panel-cdna">
1035
+ <div class="panel-title"><span class="indicator"></span>cDNA Sequencing Observations</div>
1036
+ <div class="seq-trace" id="seqTrace"></div>
1037
+ <div class="seq-reads" id="seqReads"></div>
1038
+ <div class="divider"></div>
1039
+ <div style="display:grid;grid-template-columns:1fr 1fr;gap:8px;font-family:var(--mono);font-size:0.54rem;">
1040
+ <div><span class="small-label">coverage</span><br><span style="color:var(--cyan)">42.7×</span></div>
1041
+ <div><span class="small-label">reads</span><br><span style="color:var(--cyan)">2.4M</span></div>
1042
+ <div><span class="small-label">quality</span><br><span style="color:var(--green-bio)">Q38.2</span></div>
1043
+ <div><span class="small-label">samples</span><br><span style="color:var(--cyan)">48</span></div>
1044
+ </div>
1045
+ </div>
1046
+
1047
+ <!-- GENE EXPRESSION MATRIX -->
1048
+ <div class="panel" id="panel-expression">
1049
+ <div class="panel-title"><span class="indicator"></span>Gene Expression Matrix</div>
1050
+ <div style="display:flex;justify-content:space-between;margin-bottom:4px;">
1051
+ <span class="expr-axis-label">← genes (n=2,847) →</span>
1052
+ <span class="expr-axis-label">samples ↓</span>
1053
+ </div>
1054
+ <div class="expr-matrix" id="exprMatrix"></div>
1055
+ <div class="divider"></div>
1056
+ <div style="display:flex;gap:12px;font-family:var(--mono);font-size:0.5rem;color:var(--text-label);">
1057
+ <span>CONDITION <span style="color:var(--cyan)">4</span></span>
1058
+ <span>TIMEPOINT <span style="color:var(--cyan)">6</span></span>
1059
+ <span>REPLICATE <span style="color:var(--cyan)">2</span></span>
1060
+ <span>CELL TYPE <span style="color:var(--cyan)">3</span></span>
1061
+ </div>
1062
+ </div>
1063
+
1064
+ <!-- DATA VALIDATION -->
1065
+ <div class="panel" id="panel-validation">
1066
+ <div class="panel-title"><span class="indicator"></span>Data Validation · semantics.py</div>
1067
+ <div style="font-family:var(--mono);font-size:0.54rem;color:var(--text-label);margin-bottom:6px;">10 VALIDATOR RULES</div>
1068
+ <div class="validation-grid">
1069
+ <div class="val-rule">version</div><div class="val-status pass">■ PASS</div>
1070
+ <div class="val-rule">discriminant</div><div class="val-status pass">■ PASS</div>
1071
+ <div class="val-rule">value/missing</div><div class="val-status pass">■ PASS</div>
1072
+ <div class="val-rule">raw count</div><div class="val-status pass">■ PASS</div>
1073
+ <div class="val-rule">decimal</div><div class="val-status pass">■ PASS</div>
1074
+ <div class="val-rule">reference</div><div class="val-status warn">■ WARN</div>
1075
+ <div class="val-rule">quantity kind</div><div class="val-status pass">■ PASS</div>
1076
+ <div class="val-rule">exclusion</div><div class="val-status pass">■ PASS</div>
1077
+ <div class="val-rule">source bytes</div><div class="val-status pass">■ PASS</div>
1078
+ <div class="val-rule">synthetic origin</div><div class="val-status pass">■ PASS</div>
1079
+ </div>
1080
+ </div>
1081
+
1082
+ <!-- QUANTITY KIND -->
1083
+ <div class="panel" id="panel-qtykind">
1084
+ <div class="panel-title"><span class="indicator"></span>Semantic Quantity Classification</div>
1085
+ <div class="qty-kinds">
1086
+ <div class="qty-kind" data-kind="observed">
1087
+ <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>
1088
+ <span class="qty-label">Observed</span>
1089
+ <span class="qty-source">measurement</span>
1090
+ </div>
1091
+ <div class="qty-kind" data-kind="inferred">
1092
+ <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>
1093
+ <span class="qty-label">Inferred</span>
1094
+ <span class="qty-source">posterior estimation</span>
1095
+ </div>
1096
+ <div class="qty-kind" data-kind="derived">
1097
+ <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>
1098
+ <span class="qty-label">Derived</span>
1099
+ <span class="qty-source">deterministic transform</span>
1100
+ </div>
1101
+ <div class="qty-kind" data-kind="predicted">
1102
+ <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>
1103
+ <span class="qty-label">Predicted</span>
1104
+ <span class="qty-source">model output</span>
1105
+ </div>
1106
+ <div class="qty-kind" data-kind="constrained">
1107
+ <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>
1108
+ <span class="qty-label">Constrained</span>
1109
+ <span class="qty-source">MiniZinc</span>
1110
+ </div>
1111
+ <div class="qty-kind" data-kind="encoded">
1112
+ <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>
1113
+ <span class="qty-label">Encoded</span>
1114
+ <span class="qty-source">representation</span>
1115
+ </div>
1116
+ <div class="qty-kind" data-kind="executed">
1117
+ <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>
1118
+ <span class="qty-label">Executed</span>
1119
+ <span class="qty-source">run evidence</span>
1120
+ </div>
1121
+ </div>
1122
+ </div>
1123
+
1124
+ </div>
1125
+
1126
+ <!-- CENTER COLUMN -->
1127
+ <div class="center-column">
1128
+
1129
+ <!-- HELIX -->
1130
+ <div class="helix-container">
1131
+ <canvas id="helixCanvas"></canvas>
1132
+ <div class="helix-labels">
1133
+ <div class="helix-label left" style="top:5%"><span class="hl-val">DNA</span></div>
1134
+ <div class="helix-label right" style="top:14%"><span class="hl-val">cDNA READS</span></div>
1135
+ <div class="helix-label left" style="top:24%"><span class="hl-val">EXPRESSION</span></div>
1136
+ <div class="helix-label right" style="top:34%"><span class="hl-val">SEMANTICS</span></div>
1137
+ <div class="helix-label left" style="top:44%"><span class="hl-val">BINARY</span></div>
1138
+ <div class="helix-label right" style="top:54%"><span class="hl-val">PTM STATES</span></div>
1139
+ <div class="helix-label left" style="top:64%"><span class="hl-val">POSTERIOR</span></div>
1140
+ <div class="helix-label right" style="top:74%"><span class="hl-val">CONSTRAINTS</span></div>
1141
+ <div class="helix-label left" style="top:86%"><span class="hl-val">MODEL</span></div>
1142
+ </div>
1143
+ </div>
1144
+
1145
+ <!-- PIPELINE -->
1146
+ <div class="pipeline-flow">
1147
+ <div class="pipeline-node" data-stage="bio">Biological Observations</div>
1148
+ <div class="pipeline-connector"></div>
1149
+ <div class="pipeline-node" data-stage="cdna">cDNA Expression Data</div>
1150
+ <div class="pipeline-connector"></div>
1151
+ <div class="pipeline-node" data-stage="validation">Data Validation</div>
1152
+ <div class="pipeline-connector"></div>
1153
+ <div class="pipeline-node" data-stage="expression">Gene Expression</div>
1154
+ <div class="pipeline-connector"></div>
1155
+ <div class="pipeline-node" data-stage="stan">Stan Model</div>
1156
+ <div class="pipeline-connector"></div>
1157
+ <div class="pipeline-node" data-stage="posterior">Posterior State</div>
1158
+ <div class="pipeline-connector"></div>
1159
+ <div class="pipeline-node" data-stage="minizinc">MiniZinc Model</div>
1160
+ <div class="pipeline-connector"></div>
1161
+ <div class="pipeline-node" data-stage="result">Computational Result</div>
1162
+ <div class="pipeline-connector"></div>
1163
+ <div class="pipeline-node" data-stage="viz">Visualizer</div>
1164
+ <div class="pipeline-connector"></div>
1165
+ <div class="pipeline-node" data-stage="model">Human-Readable Model</div>
1166
+ </div>
1167
+
1168
+ </div>
1169
+
1170
+ <!-- RIGHT COLUMN -->
1171
+ <div class="right-column">
1172
+
1173
+ <!-- STAN -->
1174
+ <div class="panel" id="panel-stan">
1175
+ <div class="panel-title"><span class="indicator"></span>Bayesian Inference · Stan</div>
1176
+ <div class="stan-flow">
1177
+ <span class="stan-flow-node">Prior</span>
1178
+ <span class="stan-flow-arrow">→</span>
1179
+ <span class="stan-flow-node">Likelihood</span>
1180
+ <span class="stan-flow-arrow">→</span>
1181
+ <span class="stan-flow-node">Posterior</span>
1182
+ </div>
1183
+ <div class="stan-chart"><canvas id="stanCanvas"></canvas></div>
1184
+ <table class="stan-table">
1185
+ <thead>
1186
+ <tr><th>Parameter</th><th>Mean</th><th>Median</th><th>σ</th><th>R̂</th><th>ESS</th></tr>
1187
+ </thead>
1188
+ <tbody>
1189
+ <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>
1190
+ <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>
1191
+ <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>
1192
+ <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>
1193
+ <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>
1194
+ </tbody>
1195
+ </table>
1196
+ <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>
1197
+ </div>
1198
+
1199
+ <!-- MINIZINC -->
1200
+ <div class="panel" id="panel-minizinc">
1201
+ <div class="panel-title"><span class="indicator"></span>Constraint Solver · MiniZinc</div>
1202
+ <div class="mzn-graph"><canvas id="mznCanvas"></canvas></div>
1203
+ <div class="mzn-notation">
1204
+ 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>
1205
+ </div>
1206
+ <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;">
1207
+ <span>Variables <span style="color:var(--cyan)">12</span></span>
1208
+ <span>Domains <span style="color:var(--cyan)">3</span></span>
1209
+ <span>Constraints <span style="color:var(--amber)">8</span></span>
1210
+ <span>Objective <span style="color:var(--violet)">1</span></span>
1211
+ </div>
1212
+ </div>
1213
+
1214
+ <!-- QUIPPER -->
1215
+ <div class="panel" id="panel-quipper">
1216
+ <div class="panel-title"><span class="indicator"></span>Quantum Circuit · Quipper</div>
1217
+ <div class="semantic-flow-label">semantic transformation → quantum circuit → quipper</div>
1218
+ <div class="quipper-circuit"><canvas id="quipperCanvas"></canvas></div>
1219
+ <div class="quipper-stats">
1220
+ <div class="quipper-stat">
1221
+ <div class="qs-val">5</div>
1222
+ <div class="qs-label">Qubits</div>
1223
+ </div>
1224
+ <div class="quipper-stat">
1225
+ <div class="qs-val">14</div>
1226
+ <div class="qs-label">Gate Count</div>
1227
+ </div>
1228
+ <div class="quipper-stat">
1229
+ <div class="qs-val">7</div>
1230
+ <div class="qs-label">Circuit Depth</div>
1231
+ </div>
1232
+ </div>
1233
+ </div>
1234
+
1235
+ <!-- PTM -->
1236
+ <div class="panel" id="panel-ptm">
1237
+ <div class="panel-title"><span class="indicator"></span>PTM-BIT-PARITY/1</div>
1238
+ <div class="ptm-states" id="ptmStates">
1239
+ <div class="ptm-state current" data-state="q_even">q<sub>even</sub></div>
1240
+ <div class="ptm-state" data-state="q_odd">q<sub>odd</sub></div>
1241
+ <div class="ptm-state accept" data-state="q_accept">q<sub>accept</sub></div>
1242
+ </div>
1243
+ <div class="ptm-tape" id="ptmTape"></div>
1244
+ <div class="divider"></div>
1245
+ <table class="ptm-transition-table">
1246
+ <thead><tr><th>State</th><th>Read</th><th>Write</th><th>Move</th><th>Next</th></tr></thead>
1247
+ <tbody id="ptmTransitions">
1248
+ <tr><td>q<sub>even</sub></td><td>0</td><td>0</td><td>R</td><td>q<sub>even</sub></td></tr>
1249
+ <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>
1250
+ <tr><td>q<sub>odd</sub></td><td>0</td><td>0</td><td>R</td><td>q<sub>odd</sub></td></tr>
1251
+ <tr><td>q<sub>odd</sub></td><td>1</td><td>1</td><td>R</td><td>q<sub>even</sub></td></tr>
1252
+ <tr><td>q<sub>even</sub></td><td>□</td><td>□</td><td>—</td><td>q<sub>accept</sub></td></tr>
1253
+ </tbody>
1254
+ </table>
1255
+ <div class="ptm-complexity">
1256
+ <span>8</span> input symbols · <span>9</span> transitions · <span>O(1)</span> auxiliary storage
1257
+ </div>
1258
+ </div>
1259
+
1260
+ </div>
1261
+
1262
+ <!-- BOTTOM STRIP -->
1263
+ <div class="bottom-strip">
1264
+
1265
+ <!-- BINARY SEMANTICS -->
1266
+ <div class="panel" id="panel-binary">
1267
+ <div class="panel-title"><span class="indicator"></span>Binary Semantics</div>
1268
+ <div class="semantic-flow-label">semantic value → encoding → bits → state → transition</div>
1269
+ <div class="binary-stream" id="binaryStream"></div>
1270
+ <table class="binary-table">
1271
+ <thead><tr><th>State ID</th><th>Tape Pos</th><th>Symbol</th><th>Transition</th><th>Next</th></tr></thead>
1272
+ <tbody>
1273
+ <tr><td>S₀</td><td>0x0000</td><td>0</td><td>δ(S₀,0)</td><td>S₀</td></tr>
1274
+ <tr><td>S₀</td><td>0x0001</td><td>1</td><td>δ(S₀,1)</td><td>S₁</td></tr>
1275
+ <tr><td>S₁</td><td>0x0002</td><td>0</td><td>δ(S₁,0)</td><td>S₁</td></tr>
1276
+ <tr><td>S₁</td><td>0x0003</td><td>1</td><td>δ(S₁,1)</td><td>S₀</td></tr>
1277
+ </tbody>
1278
+ </table>
1279
+ </div>
1280
+
1281
+ <!-- PROVENANCE -->
1282
+ <div class="panel" id="panel-provenance">
1283
+ <div class="panel-title"><span class="indicator"></span>Immutable Artifact Provenance</div>
1284
+ <div class="prov-chain" id="provChain"></div>
1285
+ </div>
1286
+
1287
+ <!-- RPG/JCL + REPO -->
1288
+ <div class="panel" id="panel-rpg">
1289
+ <div class="panel-title"><span class="indicator"></span>RPG/JCL · Legacy Integration</div>
1290
+ <div class="rpg-block">
1291
+ <span class="rpg-comment">//* BIO-SEMANTICS VAULT CONTROL</span>
1292
+ <span class="rpg-kw">//BSEM001</span> <span class="rpg-kw">JOB</span> <span class="rpg-id">BIOSEM,CLASS=A</span>
1293
+ <span class="rpg-kw">//STEP01</span> <span class="rpg-kw">EXEC</span> <span class="rpg-id">PGM=BSEMVLT</span>
1294
+ <span class="rpg-kw">//VAULT</span> <span class="rpg-kw">DD</span> <span class="rpg-id">DSN=BSEM.PROV.VAULT,DISP=SHR</span>
1295
+ <span class="rpg-kw">//ARCHIVE</span> <span class="rpg-kw">DD</span> <span class="rpg-id">DSN=BSEM.ARCH.V001,DISP=NEW</span>
1296
+ <span class="rpg-kw">//SYSPRINT</span> <span class="rpg-kw">DD</span> <span class="rpg-id">SYSOUT=*</span>
1297
+ </div>
1298
+ <div class="rpg-job-status">
1299
+ <span class="rpg-job-id">JOB0001</span><span class="rpg-job-name">BSEM Vault Seal</span><span class="rpg-job-state complete">COMPLETE</span>
1300
+ <span class="rpg-job-id">JOB0002</span><span class="rpg-job-name">Provenance Archive</span><span class="rpg-job-state complete">COMPLETE</span>
1301
+ <span class="rpg-job-id">JOB0003</span><span class="rpg-job-name">Artifact Verify</span><span class="rpg-job-state running">RUNNING</span>
1302
+ <span class="rpg-job-id">JOB0004</span><span class="rpg-job-name">Batch Export</span><span class="rpg-job-state queued">QUEUED</span>
1303
+ </div>
1304
+ <div class="divider"></div>
1305
+ <div class="panel-title" style="margin-top:6px;"><span class="indicator"></span>Repository Structure</div>
1306
+ <div class="repo-tree">
1307
+ <span class="dir">bio-semantics/</span><br>
1308
+ <span class="repo-indent repo-indent-1"></span><span class="dir">src/</span><br>
1309
+ <span class="repo-indent repo-indent-2"></span><span class="file file-py">semantics.py</span><br>
1310
+ <span class="repo-indent repo-indent-2"></span><span class="file file-py">test_semantics.py</span><br>
1311
+ <span class="repo-indent repo-indent-2"></span><span class="file file-html">dashboard.html</span><br>
1312
+ <span class="repo-indent repo-indent-1"></span><span class="dir">spec/</span><br>
1313
+ <span class="repo-indent repo-indent-2"></span><span class="file file-ts">types.ts</span><br>
1314
+ <span class="repo-indent repo-indent-2"></span><span class="file file-md">contract.md</span><br>
1315
+ <span class="repo-indent repo-indent-2"></span><span class="file file-md">system_prompt.md</span><br>
1316
+ <span class="repo-indent repo-indent-1"></span><span class="dir">templates/</span><br>
1317
+ <span class="repo-indent repo-indent-2"></span><span class="file file-rpg">workflow.rpgle</span><br>
1318
+ <span class="repo-indent repo-indent-2"></span><span class="file file-jcl">vault.jcl</span>
1319
+ </div>
1320
+ </div>
1321
+
1322
+ </div>
1323
+
1324
+ <!-- PROPOSITION -->
1325
+ <div class="proposition">
1326
+ <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>
1327
+ <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>
1328
+ <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>
1329
+ <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>
1330
+ <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>
1331
+ <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>
1332
+ <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>
1333
+ <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>
1334
+ </div>
1335
+
1336
+ </main>
1337
+
1338
  <script>
1339
+ // =====================================================
1340
+ // BIO-SEMANTICS COMPUTATIONAL FRONTEND
1341
+ // =====================================================
1342
+
1343
+ const C = {
1344
+ cyan: '#00c8ff',
1345
+ cyanDim: '#006688',
1346
+ blue: '#2266ff',
1347
+ blueBright: '#4488ff',
1348
+ violet: '#8844ff',
1349
+ violetDim: '#5522aa',
1350
+ greenBio: '#22cc66',
1351
+ greenDim: '#0a6630',
1352
+ amber: '#ffaa22',
1353
+ red: '#ff3344',
1354
+ white: '#e8eef6',
1355
+ whiteDim: '#8899aa',
1356
+ bg: '#030810',
1357
+ textLabel: '#4466888'
1358
+ };
1359
+
1360
+ // =====================================================
1361
+ // DNA HELIX
1362
+ // =====================================================
1363
+ (function initHelix() {
1364
+ const canvas = document.getElementById('helixCanvas');
1365
+ const ctx = canvas.getContext('2d');
1366
+ let W, H, dpr;
1367
+
1368
+ function resize() {
1369
+ const rect = canvas.parentElement.getBoundingClientRect();
1370
+ dpr = window.devicePixelRatio || 1;
1371
+ W = rect.width;
1372
+ H = rect.height;
1373
+ canvas.width = W * dpr;
1374
+ canvas.height = H * dpr;
1375
+ ctx.setTransform(dpr, 0, 0, dpr, 0, 0);
1376
  }
1377
+ resize();
1378
+ window.addEventListener('resize', resize);
1379
+
1380
+ const BASES = ['A','T','G','C'];
1381
+ const BASE_COLORS = { A: C.greenBio, T: C.red, G: C.amber, C: C.blueBright };
1382
+ const COMPLEMENT = { A: 'T', T: 'A', G: 'C', C: 'G' };
1383
+
1384
+ let time = 0;
1385
+ const particles = [];
1386
+ for (let i = 0; i < 80; i++) {
1387
+ particles.push({
1388
+ y: Math.random() * 1,
1389
+ x: (Math.random() - 0.5) * 0.4,
1390
+ speed: 0.0002 + Math.random() * 0.0003,
1391
+ size: 0.5 + Math.random() * 1.5,
1392
+ alpha: 0.1 + Math.random() * 0.3
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1393
  });
 
1394
  }
1395
+
1396
+ function draw() {
1397
+ ctx.clearRect(0, 0, W, H);
1398
+
1399
+ const cx = W / 2;
1400
+ const amplitude = 45;
1401
+ const rungs = 60;
1402
+ const twist = 0.12;
1403
+ const yStart = 20;
1404
+ const yEnd = H - 20;
1405
+ const totalH = yEnd - yStart;
1406
+
1407
+ // Transformation zones
1408
+ const zones = [
1409
+ { start: 0, end: 0.12, color1: C.greenBio, color2: C.greenBio, label: 'DNA', rungStyle: 'bio' },
1410
+ { start: 0.12, end: 0.22, color1: C.greenBio, color2: C.cyan, label: 'cDNA', rungStyle: 'reads' },
1411
+ { start: 0.22, end: 0.32, color1: C.cyan, color2: C.cyan, label: 'EXPR', rungStyle: 'expr' },
1412
+ { start: 0.32, end: 0.42, color1: C.cyan, color2: C.violet, label: 'SEM', rungStyle: 'semantic' },
1413
+ { start: 0.42, end: 0.52, color1: C.violet, color2: C.cyanDim, label: 'BIN', rungStyle: 'binary' },
1414
+ { start: 0.52, end: 0.62, color1: C.cyanDim, color2: C.cyan, label: 'PTM', rungStyle: 'state' },
1415
+ { start: 0.62, end: 0.72, color1: C.cyan, color2: C.violet, label: 'POST', rungStyle: 'posterior' },
1416
+ { start: 0.72, end: 0.84, color1: C.violet, color2: C.amber, label: 'CST', rungStyle: 'constraint' },
1417
+ { start: 0.84, end: 1.00, color1: C.amber, color2: C.white, label: 'MDL', rungStyle: 'model' },
1418
+ ];
1419
+
1420
+ function getZone(t) {
1421
+ for (const z of zones) {
1422
+ if (t >= z.start && t < z.end) return z;
1423
+ }
1424
+ return zones[zones.length - 1];
1425
+ }
1426
+
1427
+ function lerpColor(c1, c2, t) {
1428
+ const r1 = parseInt(c1.slice(1,3),16), g1 = parseInt(c1.slice(3,5),16), b1 = parseInt(c1.slice(5,7),16);
1429
+ const r2 = parseInt(c2.slice(1,3),16), g2 = parseInt(c2.slice(3,5),16), b2 = parseInt(c2.slice(5,7),16);
1430
+ const r = Math.round(r1 + (r2-r1)*t), g = Math.round(g1 + (g2-g1)*t), b = Math.round(b1 + (b2-b1)*t);
1431
+ return `rgb(${r},${g},${b})`;
1432
+ }
1433
+
1434
+ // Draw backbone strands
1435
+ ctx.lineWidth = 1.2;
1436
+ for (let strand = 0; strand < 2; strand++) {
1437
+ ctx.beginPath();
1438
+ for (let i = 0; i <= 200; i++) {
1439
+ const t = i / 200;
1440
+ const y = yStart + t * totalH;
1441
+ const phase = t * rungs * twist + time + (strand * Math.PI);
1442
+ const x = cx + Math.sin(phase) * amplitude;
1443
+ const zone = getZone(t);
1444
+ const zt = (t - zone.start) / (zone.end - zone.start);
1445
+ const color = lerpColor(zone.color1, zone.color2, zt);
1446
+
1447
+ if (i === 0) {
1448
+ ctx.moveTo(x, y);
1449
+ } else {
1450
+ ctx.lineTo(x, y);
1451
+ }
1452
+
1453
+ if (i > 0 && i % 2 === 0) {
1454
+ ctx.strokeStyle = color;
1455
+ ctx.globalAlpha = 0.7;
1456
+ ctx.stroke();
1457
+ ctx.beginPath();
1458
+ ctx.moveTo(x, y);
1459
+ }
1460
+ }
1461
+ ctx.stroke();
1462
+ }
1463
+ ctx.globalAlpha = 1;
1464
+
1465
+ // Draw rungs / connections
1466
+ for (let i = 0; i < rungs; i++) {
1467
+ const t = i / rungs;
1468
+ const y = yStart + t * totalH;
1469
+ const phase = t * rungs * twist + time;
1470
+ const x1 = cx + Math.sin(phase) * amplitude;
1471
+ const x2 = cx + Math.sin(phase + Math.PI) * amplitude;
1472
+ const depth = Math.cos(phase);
1473
+ const zone = getZone(t);
1474
+
1475
+ if (depth > -0.2) {
1476
+ const alpha = 0.15 + depth * 0.35;
1477
+ ctx.globalAlpha = alpha;
1478
+
1479
+ if (zone.rungStyle === 'bio') {
1480
+ const base = BASES[i % 4];
1481
+ const comp = COMPLEMENT[base];
1482
+ ctx.strokeStyle = BASE_COLORS[base];
1483
+ ctx.lineWidth = 1;
1484
+ ctx.beginPath();
1485
+ ctx.moveTo(x1, y);
1486
+ ctx.lineTo(cx, y);
1487
+ ctx.stroke();
1488
+ ctx.strokeStyle = BASE_COLORS[comp];
1489
+ ctx.beginPath();
1490
+ ctx.moveTo(cx, y);
1491
+ ctx.lineTo(x2, y);
1492
+ ctx.stroke();
1493
+ // Base pair dots
1494
+ ctx.fillStyle = BASE_COLORS[base];
1495
+ ctx.beginPath();
1496
+ ctx.arc(cx - 4, y, 1.5, 0, Math.PI*2);
1497
+ ctx.fill();
1498
+ ctx.fillStyle = BASE_COLORS[comp];
1499
+ ctx.beginPath();
1500
+ ctx.arc(cx + 4, y, 1.5, 0, Math.PI*2);
1501
+ ctx.fill();
1502
+ } else if (zone.rungStyle === 'binary') {
1503
+ ctx.strokeStyle = C.cyanDim;
1504
+ ctx.lineWidth = 0.5;
1505
+ ctx.beginPath();
1506
+ ctx.moveTo(x1, y);
1507
+ ctx.lineTo(x2, y);
1508
+ ctx.stroke();
1509
+ // Binary values
1510
+ ctx.fillStyle = C.cyan;
1511
+ ctx.font = '8px "IBM Plex Mono"';
1512
+ ctx.textAlign = 'center';
1513
+ const bit = (i + Math.floor(time * 2)) % 2;
1514
+ ctx.fillText(bit.toString(), cx, y + 3);
1515
+ } else if (zone.rungStyle === 'state') {
1516
+ ctx.strokeStyle = C.cyan;
1517
+ ctx.lineWidth = 0.5;
1518
+ ctx.setLineDash([2, 3]);
1519
+ ctx.beginPath();
1520
+ ctx.moveTo(x1, y);
1521
+ ctx.lineTo(x2, y);
1522
+ ctx.stroke();
1523
+ ctx.setLineDash([]);
1524
+ // State node
1525
+ ctx.strokeStyle = C.cyan;
1526
+ ctx.lineWidth = 0.8;
1527
+ ctx.beginPath();
1528
+ ctx.arc(cx, y, 4, 0, Math.PI*2);
1529
+ ctx.stroke();
1530
+ } else if (zone.rungStyle === 'posterior') {
1531
+ const zt = (t - zone.start) / (zone.end - zone.start);
1532
+ ctx.strokeStyle = lerpColor(C.cyan, C.violet, zt);
1533
+ ctx.lineWidth = 0.8;
1534
+ // Probability curve fragment
1535
+ const mid = (x1 + x2) / 2;
1536
+ ctx.beginPath();
1537
+ ctx.moveTo(x1, y);
1538
+ const cpx = mid;
1539
+ const cpy = y - 5 * Math.sin(i * 0.8);
1540
+ ctx.quadraticCurveTo(cpx, cpy, x2, y);
1541
+ ctx.stroke();
1542
+ } else if (zone.rungStyle === 'constraint') {
1543
+ ctx.strokeStyle = C.amber;
1544
+ ctx.lineWidth = 0.5;
1545
+ ctx.beginPath();
1546
+ ctx.moveTo(x1, y);
1547
+ ctx.lineTo(x2, y);
1548
+ ctx.stroke();
1549
+ // Constraint node
1550
+ ctx.strokeStyle = C.amber;
1551
+ ctx.lineWidth = 0.6;
1552
+ const s = 3;
1553
+ ctx.strokeRect(cx - s, y - s, s * 2, s * 2);
1554
+ } else {
1555
+ const zt = (t - zone.start) / (zone.end - zone.start);
1556
+ ctx.strokeStyle = lerpColor(zone.color1, zone.color2, zt);
1557
+ ctx.lineWidth = 0.6;
1558
+ ctx.beginPath();
1559
+ ctx.moveTo(x1, y);
1560
+ ctx.lineTo(x2, y);
1561
+ ctx.stroke();
1562
+ }
1563
+ }
1564
+ }
1565
+ ctx.globalAlpha = 1;
1566
+
1567
+ // Flowing particles
1568
+ for (const p of particles) {
1569
+ p.y += p.speed;
1570
+ if (p.y > 1) p.y -= 1;
1571
+ const y = yStart + p.y * totalH;
1572
+ const phase = p.y * rungs * twist + time;
1573
+ const x = cx + Math.sin(phase + p.x * 6) * (amplitude * 0.6 + p.x * amplitude);
1574
+ const zone = getZone(p.y);
1575
+ const zt = (p.y - zone.start) / (zone.end - zone.start);
1576
+ ctx.fillStyle = lerpColor(zone.color1, zone.color2, zt);
1577
+ ctx.globalAlpha = p.alpha;
1578
+ ctx.beginPath();
1579
+ ctx.arc(x, y, p.size, 0, Math.PI*2);
1580
+ ctx.fill();
1581
+ }
1582
+ ctx.globalAlpha = 1;
1583
+
1584
+ // Scanline
1585
+ const scanY = yStart + ((time * 40) % totalH);
1586
+ const scanGrad = ctx.createLinearGradient(0, scanY - 15, 0, scanY + 15);
1587
+ scanGrad.addColorStop(0, 'rgba(0,200,255,0)');
1588
+ scanGrad.addColorStop(0.5, 'rgba(0,200,255,0.05)');
1589
+ scanGrad.addColorStop(1, 'rgba(0,200,255,0)');
1590
+ ctx.fillStyle = scanGrad;
1591
+ ctx.fillRect(0, scanY - 15, W, 30);
1592
+
1593
+ time += 0.008;
1594
+ requestAnimationFrame(draw);
1595
  }
1596
+ draw();
1597
+ })();
1598
+
1599
+ // =====================================================
1600
+ // SEQUENCING TRACE
1601
+ // =====================================================
1602
+ (function initSeqTrace() {
1603
+ const container = document.getElementById('seqTrace');
1604
+ const bases = 'ATCGATCGATTGCCATGCAATCGGATCGATTGCCATGCAATCGGATCGATTGCCATGCAATCGGATCGATCGATCGATCGGA';
1605
+ const baseClass = { A: 'a', T: 't', G: 'g', C: 'c' };
1606
+ for (let i = 0; i < bases.length; i++) {
1607
+ const bar = document.createElement('div');
1608
+ bar.className = `seq-bar ${baseClass[bases[i]]}`;
1609
+ bar.style.height = (8 + Math.random() * 24) + 'px';
1610
+ container.appendChild(bar);
1611
+ }
1612
+
1613
+ const readsEl = document.getElementById('seqReads');
1614
+ const reads = [
1615
+ 'ATCGATCGATTGCCATGCAA',
1616
+ ' GATCGATTGCCATGCAATCGG',
1617
+ ' CGATTGCCATGCAATCGGATCG',
1618
+ ' TTGCCATGCAATCGGATCGATT'
1619
+ ];
1620
+ reads.forEach(read => {
1621
+ const line = document.createElement('div');
1622
+ let html = '';
1623
+ for (const ch of read) {
1624
+ if (ch === ' ') { html += '&nbsp;'; continue; }
1625
+ const cls = baseClass[ch] || '';
1626
+ html += `<span class="base ${cls}">${ch}</span>`;
1627
+ }
1628
+ line.innerHTML = html;
1629
+ readsEl.appendChild(line);
1630
  });
1631
+ })();
1632
+
1633
+ // =====================================================
1634
+ // EXPRESSION MATRIX
1635
+ // =====================================================
1636
+ (function initExprMatrix() {
1637
+ const container = document.getElementById('exprMatrix');
1638
+ const rows = 16;
1639
+ const cols = 24;
1640
+ container.style.gridTemplateColumns = `repeat(${cols}, 1fr)`;
1641
+
1642
+ for (let r = 0; r < rows; r++) {
1643
+ for (let c = 0; c < cols; c++) {
1644
+ const cell = document.createElement('div');
1645
+ cell.className = 'expr-cell';
1646
+ const v = Math.random();
1647
+ const hue = 200 + v * 80;
1648
+ const sat = 60 + v * 30;
1649
+ const light = 5 + v * 35;
1650
+ cell.style.background = `hsl(${hue}, ${sat}%, ${light}%)`;
1651
+ container.appendChild(cell);
1652
+ }
1653
+ }
1654
+ })();
1655
+
1656
+ // =====================================================
1657
+ // BINARY STREAM
1658
+ // =====================================================
1659
+ (function initBinaryStream() {
1660
+ const container = document.getElementById('binaryStream');
1661
+ let html = '';
1662
+ for (let i = 0; i < 256; i++) {
1663
+ const bit = Math.random() > 0.5 ? 1 : 0;
1664
+ html += `<span class="${bit ? 'one' : 'zero'}">${bit}</span>`;
1665
+ if ((i + 1) % 8 === 0) html += ' ';
1666
+ }
1667
+ container.innerHTML = html;
1668
+
1669
+ // Animate binary stream
1670
+ setInterval(() => {
1671
+ const spans = container.querySelectorAll('span');
1672
+ const idx = Math.floor(Math.random() * spans.length);
1673
+ if (spans[idx] && spans[idx].textContent.trim().length === 1) {
1674
+ const newBit = Math.random() > 0.5 ? 1 : 0;
1675
+ spans[idx].textContent = newBit.toString();
1676
+ spans[idx].className = newBit ? 'one' : 'zero';
1677
+ }
1678
+ }, 100);
1679
+ })();
1680
+
1681
+ // =====================================================
1682
+ // PTM TAPE
1683
+ // =====================================================
1684
+ (function initPTM() {
1685
+ const container = document.getElementById('ptmTape');
1686
+ const tape = [0, 1, 0, 1, 1, 0, 1, 0];
1687
+ let headPos = 0;
1688
+ let state = 'q_even';
1689
+
1690
+ function render() {
1691
+ container.innerHTML = '';
1692
+ tape.forEach((bit, i) => {
1693
+ const cell = document.createElement('div');
1694
+ cell.className = 'ptm-cell' + (i === headPos ? ' active' : '');
1695
+ cell.textContent = bit;
1696
+ if (i === headPos) {
1697
+ const head = document.createElement('div');
1698
+ head.className = 'ptm-head';
1699
+ cell.appendChild(head);
1700
+ }
1701
+ container.appendChild(cell);
1702
+ });
1703
+
1704
+ document.querySelectorAll('.ptm-state').forEach(el => {
1705
+ el.classList.toggle('current', el.dataset.state === state);
1706
+ });
1707
+ }
1708
+
1709
+ render();
1710
+
1711
+ setInterval(() => {
1712
+ if (headPos < tape.length) {
1713
+ const bit = tape[headPos];
1714
+ if (bit === 1) {
1715
+ state = state === 'q_even' ? 'q_odd' : 'q_even';
1716
+ }
1717
+ headPos++;
1718
+ if (headPos >= tape.length) {
1719
+ state = 'q_accept';
1720
+ setTimeout(() => { headPos = 0; state = 'q_even'; render(); }, 2000);
1721
+ }
1722
+ render();
1723
+ }
1724
+ }, 800);
1725
+ })();
1726
+
1727
+ // =====================================================
1728
+ // PROVENANCE CHAIN
1729
+ // =====================================================
1730
+ (function initProvenance() {
1731
+ const container = document.getElementById('provChain');
1732
+ const chain = [
1733
+ { name: 'Source', hash: 'a7f3e9c2d1b8' },
1734
+ { name: 'Hash', hash: '4e2a8f1c7d39' },
1735
+ { name: 'Semantics', hash: 'b8d4f2e6a1c5' },
1736
+ { name: 'Encoding', hash: 'c1e7a3f9d5b2' },
1737
+ { name: 'PTM', hash: '3f9b2e8c1d7a' },
1738
+ { name: 'Stan', hash: 'd5a1c8f3e7b4' },
1739
+ { name: 'MiniZinc', hash: 'e2c6b9d4f1a8' },
1740
+ { name: 'Quipper', hash: 'f8d3a7e1c2b5' },
1741
+ { name: 'Result', hash: '1b5e9a3f7d2c' },
1742
+ ];
1743
+
1744
+ chain.forEach((item, i) => {
1745
+ const node = document.createElement('div');
1746
+ node.className = 'prov-node';
1747
+ node.innerHTML = `
1748
+ <div class="prov-icon"></div>
1749
+ <div class="prov-name">${item.name}</div>
1750
+ <div class="prov-hash">${item.hash}…${(parseInt(item.hash.slice(0,4),16) % 9999).toString().padStart(4,'0')}</div>
1751
+ `;
1752
+
1753
+ node.addEventListener('click', () => {
1754
+ const existing = node.nextElementSibling;
1755
+ if (existing && existing.classList.contains('prov-detail')) {
1756
+ existing.style.display = existing.style.display === 'none' ? 'block' : 'none';
1757
+ return;
1758
+ }
1759
+ const detail = document.createElement('div');
1760
+ detail.className = 'prov-detail';
1761
+ detail.style.display = 'block';
1762
+ detail.innerHTML = `
1763
+ <span class="pd-key">artifact id</span> <span class="pd-val">BSM-${item.name.toUpperCase()}-${String(i+1).padStart(3,'0')}</span><br>
1764
+ <span class="pd-key">hash</span> <span class="pd-val">sha256:${item.hash}e4f8a2c1d7b3…</span><br>
1765
+ <span class="pd-key">version</span> <span class="pd-val">1.0.${i}</span><br>
1766
+ <span class="pd-key">parent</span> <span class="pd-val">${i > 0 ? chain[i-1].hash + '…' : '∅ (root)'}</span><br>
1767
+ <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>
1768
+ <span class="pd-key">status</span> <span class="pd-val" style="color:#22cc66">SEALED</span>
1769
+ `;
1770
+ node.after(detail);
1771
+ });
1772
+
1773
+ container.appendChild(node);
1774
+
1775
+ if (i < chain.length - 1) {
1776
+ const conn = document.createElement('div');
1777
+ conn.className = 'prov-connector';
1778
+ container.appendChild(conn);
1779
+ }
1780
+ });
1781
+ })();
1782
+
1783
+ // =====================================================
1784
+ // STAN POSTERIOR VISUALIZATION
1785
+ // =====================================================
1786
+ (function initStan() {
1787
+ const canvas = document.getElementById('stanCanvas');
1788
+ const ctx = canvas.getContext('2d');
1789
+ const rect = canvas.parentElement.getBoundingClientRect();
1790
+ const dpr = window.devicePixelRatio || 1;
1791
+ canvas.width = rect.width * dpr;
1792
+ canvas.height = rect.height * dpr;
1793
+ ctx.setTransform(dpr, 0, 0, dpr, 0, 0);
1794
+ const W = rect.width;
1795
+ const H = rect.height;
1796
+
1797
+ function gaussian(x, mu, sigma) {
1798
+ return Math.exp(-0.5 * ((x - mu) / sigma) ** 2) / (sigma * Math.sqrt(2 * Math.PI));
1799
+ }
1800
+
1801
+ // Posterior samples
1802
+ const samples = [];
1803
+ for (let i = 0; i < 200; i++) {
1804
+ samples.push({
1805
+ x: 0.15 + Math.random() * 0.7,
1806
+ y: Math.random(),
1807
+ alpha: 0.05 + Math.random() * 0.15,
1808
+ size: 0.8 + Math.random() * 1.2
1809
+ });
1810
+ }
1811
+
1812
+ let time = 0;
1813
+ function draw() {
1814
+ ctx.clearRect(0, 0, W, H);
1815
+
1816
+ // Posterior sample particles
1817
+ samples.forEach(s => {
1818
+ ctx.globalAlpha = s.alpha * (0.5 + 0.5 * Math.sin(time * 0.5 + s.x * 10));
1819
+ ctx.fillStyle = C.violet;
1820
+ ctx.beginPath();
1821
+ ctx.arc(s.x * W, (0.3 + s.y * 0.6) * H, s.size, 0, Math.PI * 2);
1822
+ ctx.fill();
1823
+ });
1824
+ ctx.globalAlpha = 1;
1825
+
1826
+ // Prior (wider, dimmer)
1827
+ ctx.beginPath();
1828
+ ctx.strokeStyle = C.cyanDim;
1829
+ ctx.lineWidth = 0.8;
1830
+ ctx.setLineDash([3, 3]);
1831
+ for (let i = 0; i <= 100; i++) {
1832
+ const x = i / 100;
1833
+ const val = gaussian(x, 0.5, 0.2);
1834
+ const px = x * W;
1835
+ const py = H - (val / 2.5) * H * 0.8 - 5;
1836
+ if (i === 0) ctx.moveTo(px, py); else ctx.lineTo(px, py);
1837
+ }
1838
+ ctx.stroke();
1839
+ ctx.setLineDash([]);
1840
+
1841
+ // Likelihood
1842
+ ctx.beginPath();
1843
+ ctx.strokeStyle = C.blue;
1844
+ ctx.lineWidth = 0.8;
1845
+ ctx.setLineDash([1, 2]);
1846
+ for (let i = 0; i <= 100; i++) {
1847
+ const x = i / 100;
1848
+ const val = gaussian(x, 0.55, 0.12);
1849
+ const px = x * W;
1850
+ const py = H - (val / 3.5) * H * 0.8 - 5;
1851
+ if (i === 0) ctx.moveTo(px, py); else ctx.lineTo(px, py);
1852
+ }
1853
+ ctx.stroke();
1854
+ ctx.setLineDash([]);
1855
+
1856
+ // Posterior (sharp, bright)
1857
+ ctx.beginPath();
1858
+ ctx.strokeStyle = C.cyan;
1859
+ ctx.lineWidth = 1.5;
1860
+ for (let i = 0; i <= 100; i++) {
1861
+ const x = i / 100;
1862
+ const val = gaussian(x, 0.53, 0.09);
1863
+ const px = x * W;
1864
+ const py = H - (val / 4.5) * H * 0.8 - 5;
1865
+ if (i === 0) ctx.moveTo(px, py); else ctx.lineTo(px, py);
1866
+ }
1867
+ ctx.stroke();
1868
+
1869
+ // Posterior fill
1870
+ ctx.beginPath();
1871
+ for (let i = 0; i <= 100; i++) {
1872
+ const x = i / 100;
1873
+ const val = gaussian(x, 0.53, 0.09);
1874
+ const px = x * W;
1875
+ const py = H - (val / 4.5) * H * 0.8 - 5;
1876
+ if (i === 0) ctx.moveTo(px, py); else ctx.lineTo(px, py);
1877
+ }
1878
+ ctx.lineTo(W, H);
1879
+ ctx.lineTo(0, H);
1880
+ ctx.closePath();
1881
+ ctx.fillStyle = 'rgba(0,200,255,0.06)';
1882
+ ctx.fill();
1883
+
1884
+ // Credible interval
1885
+ const ci_low = 0.53 - 0.09 * 1.96;
1886
+ const ci_high = 0.53 + 0.09 * 1.96;
1887
+ ctx.strokeStyle = C.violet;
1888
+ ctx.lineWidth = 0.8;
1889
+ ctx.beginPath();
1890
+ ctx.moveTo(ci_low * W, H - 5);
1891
+ ctx.lineTo(ci_low * W, H - 20);
1892
+ ctx.stroke();
1893
+ ctx.beginPath();
1894
+ ctx.moveTo(ci_high * W, H - 5);
1895
+ ctx.lineTo(ci_high * W, H - 20);
1896
+ ctx.stroke();
1897
+ ctx.strokeStyle = 'rgba(136,68,255,0.4)';
1898
+ ctx.setLineDash([2, 2]);
1899
+ ctx.beginPath();
1900
+ ctx.moveTo(ci_low * W, H - 12);
1901
+ ctx.lineTo(ci_high * W, H - 12);
1902
+ ctx.stroke();
1903
+ ctx.setLineDash([]);
1904
+
1905
+ // Labels
1906
+ ctx.font = '8px "IBM Plex Mono"';
1907
+ ctx.fillStyle = C.cyanDim;
1908
+ ctx.textAlign = 'right';
1909
+ ctx.fillText('PRIOR', W * 0.28, 16);
1910
+ ctx.fillStyle = C.blue;
1911
+ ctx.fillText('LIKELIHOOD', W * 0.78, 25);
1912
+ ctx.fillStyle = C.cyan;
1913
+ ctx.textAlign = 'center';
1914
+ ctx.fillText('POSTERIOR', W * 0.53, 12);
1915
+ ctx.fillStyle = C.violet;
1916
+ ctx.font = '7px "IBM Plex Mono"';
1917
+ ctx.fillText('95% CI', (ci_low + ci_high) / 2 * W, H - 16);
1918
+
1919
+ time += 0.02;
1920
+ requestAnimationFrame(draw);
1921
+ }
1922
+ draw();
1923
+ })();
1924
+
1925
+ // =====================================================
1926
+ // MINIZINC CONSTRAINT GRAPH
1927
+ // =====================================================
1928
+ (function initMzn() {
1929
+ const canvas = document.getElementById('mznCanvas');
1930
+ const ctx = canvas.getContext('2d');
1931
+ const rect = canvas.parentElement.getBoundingClientRect();
1932
+ const dpr = window.devicePixelRatio || 1;
1933
+ canvas.width = rect.width * dpr;
1934
+ canvas.height = rect.height * dpr;
1935
+ ctx.setTransform(dpr, 0, 0, dpr, 0, 0);
1936
+ const W = rect.width;
1937
+ const H = rect.height;
1938
+
1939
+ const nodes = [
1940
+ { x: 0.2, y: 0.25, label: 'x₁', type: 'var', color: C.cyan },
1941
+ { x: 0.4, y: 0.15, label: 'x₂', type: 'var', color: C.cyan },
1942
+ { x: 0.6, y: 0.2, label: 'x₃', type: 'var', color: C.cyan },
1943
+ { x: 0.8, y: 0.3, label: 'x₄', type: 'var', color: C.cyan },
1944
+ { x: 0.15, y: 0.6, label: 'C₁', type: 'con', color: C.amber },
1945
+ { x: 0.38, y: 0.55, label: 'C₂', type: 'con', color: C.amber },
1946
+ { x: 0.62, y: 0.6, label: 'C₃', type: 'con', color: C.amber },
1947
+ { x: 0.85, y: 0.55, label: 'C₄', type: 'con', color: C.amber },
1948
+ { x: 0.3, y: 0.85, label: 'D₁', type: 'dom', color: C.violet },
1949
+ { x: 0.7, y: 0.85, label: 'D₂', type: 'dom', color: C.violet },
1950
+ { x: 0.5, y: 0.42, label: 'obj', type: 'obj', color: C.greenBio },
1951
+ ];
1952
+
1953
+ const edges = [
1954
+ [0,4],[0,5],[1,5],[1,6],[2,6],[2,7],[3,7],[3,4],
1955
+ [4,8],[5,8],[6,9],[7,9],
1956
+ [0,10],[1,10],[2,10],[3,10],
1957
+ ];
1958
+
1959
+ let time = 0;
1960
+ function draw() {
1961
+ ctx.clearRect(0, 0, W, H);
1962
+
1963
+ // Feasible space
1964
+ ctx.beginPath();
1965
+ ctx.moveTo(nodes[0].x * W, nodes[0].y * H);
1966
+ ctx.lineTo(nodes[1].x * W, nodes[1].y * H);
1967
+ ctx.lineTo(nodes[2].x * W, nodes[2].y * H);
1968
+ ctx.lineTo(nodes[3].x * W, nodes[3].y * H);
1969
+ ctx.closePath();
1970
+ ctx.fillStyle = 'rgba(0,200,255,0.03)';
1971
+ ctx.fill();
1972
+ ctx.strokeStyle = 'rgba(0,200,255,0.08)';
1973
+ ctx.lineWidth = 0.5;
1974
+ ctx.stroke();
1975
+
1976
+ // Edges
1977
+ edges.forEach(([a, b], i) => {
1978
+ const na = nodes[a], nb = nodes[b];
1979
+ ctx.beginPath();
1980
+ ctx.moveTo(na.x * W, na.y * H);
1981
+ ctx.lineTo(nb.x * W, nb.y * H);
1982
+ ctx.strokeStyle = 'rgba(0,200,255,0.15)';
1983
+ ctx.lineWidth = 0.6;
1984
+ ctx.stroke();
1985
+
1986
+ // Animated pulse
1987
+ const t = ((time * 0.3 + i * 0.15) % 1);
1988
+ const px = na.x + (nb.x - na.x) * t;
1989
+ const py = na.y + (nb.y - na.y) * t;
1990
+ ctx.beginPath();
1991
+ ctx.arc(px * W, py * H, 1.5, 0, Math.PI * 2);
1992
+ ctx.fillStyle = `rgba(0,200,255,${0.5 - t * 0.5})`;
1993
+ ctx.fill();
1994
+ });
1995
+
1996
+ // Nodes
1997
+ nodes.forEach(n => {
1998
+ const x = n.x * W, y = n.y * H;
1999
+ const r = n.type === 'obj' ? 10 : n.type === 'con' ? 7 : 8;
2000
+
2001
+ if (n.type === 'con') {
2002
+ ctx.beginPath();
2003
+ ctx.rect(x - r, y - r, r * 2, r * 2);
2004
+ ctx.fillStyle = 'rgba(255,170,34,0.08)';
2005
+ ctx.fill();
2006
+ ctx.strokeStyle = n.color;
2007
+ ctx.lineWidth = 0.8;
2008
+ ctx.stroke();
2009
+ } else if (n.type === 'obj') {
2010
+ ctx.beginPath();
2011
+ ctx.moveTo(x, y - r);
2012
+ ctx.lineTo(x + r, y + r * 0.6);
2013
+ ctx.lineTo(x - r, y + r * 0.6);
2014
+ ctx.closePath();
2015
+ ctx.fillStyle = 'rgba(34,204,102,0.08)';
2016
+ ctx.fill();
2017
+ ctx.strokeStyle = n.color;
2018
+ ctx.lineWidth = 0.8;
2019
+ ctx.stroke();
2020
+ } else {
2021
+ ctx.beginPath();
2022
+ ctx.arc(x, y, r, 0, Math.PI * 2);
2023
+ ctx.fillStyle = 'rgba(0,200,255,0.06)';
2024
+ ctx.fill();
2025
+ ctx.strokeStyle = n.color;
2026
+ ctx.lineWidth = 0.8;
2027
+ ctx.stroke();
2028
+ }
2029
+
2030
+ ctx.fillStyle = n.color;
2031
+ ctx.font = '9px "IBM Plex Mono"';
2032
+ ctx.textAlign = 'center';
2033
+ ctx.textBaseline = 'middle';
2034
+ ctx.fillText(n.label, x, y);
2035
+ });
2036
+
2037
+ // Legend
2038
+ ctx.font = '7px "IBM Plex Mono"';
2039
+ ctx.textAlign = 'left';
2040
+ ctx.fillStyle = C.cyan;
2041
+ ctx.fillText('○ VARIABLE', 4, H - 24);
2042
+ ctx.fillStyle = C.amber;
2043
+ ctx.fillText('□ CONSTRAINT', 4, H - 14);
2044
+ ctx.fillStyle = C.violet;
2045
+ ctx.fillText('○ DOMAIN', 4, H - 4);
2046
+ ctx.fillStyle = C.greenBio;
2047
+ ctx.textAlign = 'right';
2048
+ ctx.fillText('△ OBJECTIVE', W - 4, H - 4);
2049
+
2050
+ time += 0.008;
2051
+ requestAnimationFrame(draw);
2052
  }
2053
+ draw();
2054
+ })();
2055
+
2056
+ // =====================================================
2057
+ // QUIPPER CIRCUIT
2058
+ // =====================================================
2059
+ (function initQuipper() {
2060
+ const canvas = document.getElementById('quipperCanvas');
2061
+ const ctx = canvas.getContext('2d');
2062
+ const rect = canvas.parentElement.getBoundingClientRect();
2063
+ const dpr = window.devicePixelRatio || 1;
2064
+ canvas.width = rect.width * dpr;
2065
+ canvas.height = rect.height * dpr;
2066
+ ctx.setTransform(dpr, 0, 0, dpr, 0, 0);
2067
+ const W = rect.width;
2068
+ const H = rect.height;
2069
+
2070
+ const qubits = 5;
2071
+ const wireY = (i) => 18 + i * (H - 36) / (qubits - 1);
2072
+ const gateX = (i) => 40 + i * ((W - 60) / 7);
2073
+
2074
+ const gates = [
2075
+ { type: 'H', qubit: 0, col: 0 },
2076
+ { type: 'H', qubit: 1, col: 0 },
2077
+ { type: 'CNOT', control: 0, target: 2, col: 1 },
2078
+ { type: 'T', qubit: 1, col: 2 },
2079
+ { type: 'CNOT', control: 2, target: 3, col: 2 },
2080
+ { type: 'S', qubit: 0, col: 3 },
2081
+ { type: 'H', qubit: 4, col: 3 },
2082
+ { type: 'CNOT', control: 4, target: 1, col: 4 },
2083
+ { type: 'T', qubit: 3, col: 4 },
2084
+ { type: 'CNOT', control: 1, target: 4, col: 5 },
2085
+ { type: 'H', qubit: 2, col: 5 },
2086
+ { type: 'M', qubit: 0, col: 6 },
2087
+ { type: 'M', qubit: 3, col: 6 },
2088
+ { type: 'M', qubit: 4, col: 7 },
2089
+ ];
2090
+
2091
+ let time = 0;
2092
+ function draw() {
2093
+ ctx.clearRect(0, 0, W, H);
2094
+
2095
+ // Qubit wires
2096
+ for (let i = 0; i < qubits; i++) {
2097
+ const y = wireY(i);
2098
+ ctx.beginPath();
2099
+ ctx.moveTo(10, y);
2100
+ ctx.lineTo(W - 10, y);
2101
+ ctx.strokeStyle = 'rgba(0,200,255,0.15)';
2102
+ ctx.lineWidth = 0.8;
2103
+ ctx.stroke();
2104
+
2105
+ // Qubit labels
2106
+ ctx.fillStyle = C.cyanDim;
2107
+ ctx.font = '8px "IBM Plex Mono"';
2108
+ ctx.textAlign = 'right';
2109
+ ctx.fillText(`q${i}`, 18, y + 3);
2110
+ }
2111
+
2112
+ // Gates
2113
+ gates.forEach((g, gi) => {
2114
+ const x = gateX(g.col);
2115
+ const pulse = 0.7 + 0.3 * Math.sin(time * 2 + gi * 0.5);
2116
+
2117
+ if (g.type === 'CNOT') {
2118
+ const cy = wireY(g.control);
2119
+ const ty = wireY(g.target);
2120
+ // Control-target line
2121
+ ctx.beginPath();
2122
+ ctx.moveTo(x, cy);
2123
+ ctx.lineTo(x, ty);
2124
+ ctx.strokeStyle = `rgba(136,68,255,${0.4 * pulse})`;
2125
+ ctx.lineWidth = 0.8;
2126
+ ctx.stroke();
2127
+ // Control dot
2128
+ ctx.beginPath();
2129
+ ctx.arc(x, cy, 3, 0, Math.PI * 2);
2130
+ ctx.fillStyle = C.violet;
2131
+ ctx.fill();
2132
+ // Target circle
2133
+ ctx.beginPath();
2134
+ ctx.arc(x, ty, 7, 0, Math.PI * 2);
2135
+ ctx.strokeStyle = C.violet;
2136
+ ctx.lineWidth = 1;
2137
+ ctx.stroke();
2138
+ ctx.beginPath();
2139
+ ctx.moveTo(x - 7, ty);
2140
+ ctx.lineTo(x + 7, ty);
2141
+ ctx.moveTo(x, ty - 7);
2142
+ ctx.lineTo(x, ty + 7);
2143
+ ctx.strokeStyle = `rgba(136,68,255,${0.5 * pulse})`;
2144
+ ctx.lineWidth = 0.5;
2145
+ ctx.stroke();
2146
+ } else if (g.type === 'M') {
2147
+ const y = wireY(g.qubit);
2148
+ ctx.strokeStyle = C.cyan;
2149
+ ctx.lineWidth = 0.8;
2150
+ ctx.strokeRect(x - 10, y - 8, 20, 16);
2151
+ // Meter arc
2152
+ ctx.beginPath();
2153
+ ctx.arc(x, y + 2, 5, Math.PI, 0);
2154
+ ctx.stroke();
2155
+ ctx.beginPath();
2156
+ ctx.moveTo(x, y + 2);
2157
+ ctx.lineTo(x + 4, y - 4);
2158
+ ctx.stroke();
2159
+ // Double line out
2160
+ ctx.beginPath();
2161
+ ctx.moveTo(x, y + 8);
2162
+ ctx.lineTo(x, y + 14);
2163
+ ctx.strokeStyle = 'rgba(0,200,255,0.3)';
2164
+ ctx.stroke();
2165
+ } else {
2166
+ const y = wireY(g.qubit);
2167
+ const boxW = 18, boxH = 16;
2168
+ ctx.fillStyle = `rgba(136,68,255,${0.08 * pulse})`;
2169
+ ctx.fillRect(x - boxW/2, y - boxH/2, boxW, boxH);
2170
+ ctx.strokeStyle = `rgba(136,68,255,${0.6 * pulse})`;
2171
+ ctx.lineWidth = 0.8;
2172
+ ctx.strokeRect(x - boxW/2, y - boxH/2, boxW, boxH);
2173
+ ctx.fillStyle = C.violet;
2174
+ ctx.font = '9px "IBM Plex Mono"';
2175
+ ctx.textAlign = 'center';
2176
+ ctx.textBaseline = 'middle';
2177
+ ctx.fillText(g.type, x, y);
2178
+ }
2179
+ });
2180
+
2181
+ // Animated photon
2182
+ const photonX = 10 + ((time * 60) % (W - 20));
2183
+ for (let i = 0; i < qubits; i++) {
2184
+ ctx.beginPath();
2185
+ ctx.arc(photonX, wireY(i), 2, 0, Math.PI * 2);
2186
+ ctx.fillStyle = `rgba(136,68,255,${0.3 + 0.2 * Math.sin(time * 3 + i)})`;
2187
+ ctx.fill();
2188
+ }
2189
+
2190
+ time += 0.016;
2191
+ requestAnimationFrame(draw);
2192
+ }
2193
+ draw();
2194
+ })();
2195
+
2196
+ // =====================================================
2197
+ // NAV INTERACTION
2198
+ // =====================================================
2199
+ document.querySelectorAll('.nav-item').forEach(item => {
2200
+ item.addEventListener('click', () => {
2201
+ document.querySelectorAll('.nav-item').forEach(n => n.classList.remove('active'));
2202
+ item.classList.add('active');
2203
+
2204
+ const target = item.dataset.target;
2205
+ const panelMap = {
2206
+ observations: ['panel-cdna', 'panel-expression'],
2207
+ semantics: ['panel-qtykind', 'panel-validation'],
2208
+ bayesian: ['panel-stan'],
2209
+ constraints: ['panel-minizinc'],
2210
+ binary: ['panel-binary'],
2211
+ ptm: ['panel-ptm'],
2212
+ quipper: ['panel-quipper'],
2213
+ provenance: ['panel-provenance'],
2214
+ rpgjcl: ['panel-rpg'],
2215
+ };
2216
+
2217
+ // Highlight relevant panels
2218
+ document.querySelectorAll('.panel').forEach(p => {
2219
+ p.style.borderColor = '';
2220
+ p.style.boxShadow = '';
2221
+ });
2222
+
2223
+ const targets = panelMap[target] || [];
2224
+ targets.forEach(id => {
2225
+ const el = document.getElementById(id);
2226
+ if (el) {
2227
+ el.style.borderColor = 'rgba(0,200,255,0.4)';
2228
+ el.style.boxShadow = '0 0 12px rgba(0,200,255,0.15), 0 0 30px rgba(0,200,255,0.05)';
2229
+ el.scrollIntoView({ behavior: 'smooth', block: 'nearest' });
2230
+ }
2231
+ });
2232
+ });
2233
  });
2234
+
2235
+ // =====================================================
2236
+ // PIPELINE NODE INTERACTION
2237
+ // =====================================================
2238
+ document.querySelectorAll('.pipeline-node').forEach(node => {
2239
+ node.addEventListener('click', () => {
2240
+ document.querySelectorAll('.pipeline-node').forEach(n => n.classList.remove('active'));
2241
+ node.classList.add('active');
2242
+ });
2243
+ });
2244
+
2245
+ // =====================================================
2246
+ // QUANTITY KIND INTERACTION
2247
+ // =====================================================
2248
+ document.querySelectorAll('.qty-kind').forEach(kind => {
2249
+ kind.addEventListener('click', () => {
2250
+ const wasActive = kind.classList.contains('active');
2251
+ document.querySelectorAll('.qty-kind').forEach(k => k.classList.remove('active'));
2252
+ if (!wasActive) kind.classList.add('active');
2253
+
2254
+ // Highlight related panel
2255
+ const kindName = kind.dataset.kind;
2256
+ document.querySelectorAll('.panel').forEach(p => {
2257
+ p.style.borderColor = '';
2258
+ p.style.boxShadow = '';
2259
+ });
2260
+
2261
+ const highlight = {
2262
+ observed: 'panel-cdna',
2263
+ inferred: 'panel-stan',
2264
+ derived: 'panel-expression',
2265
+ predicted: 'panel-stan',
2266
+ constrained: 'panel-minizinc',
2267
+ encoded: 'panel-binary',
2268
+ executed: 'panel-ptm',
2269
+ };
2270
+
2271
+ if (!wasActive && highlight[kindName]) {
2272
+ const el = document.getElementById(highlight[kindName]);
2273
+ if (el) {
2274
+ el.style.borderColor = 'rgba(0,200,255,0.4)';
2275
+ el.style.boxShadow = '0 0 12px rgba(0,200,255,0.15), 0 0 30px rgba(0,200,255,0.05)';
2276
+ }
2277
+ }
2278
+ });
2279
+ });
2280
+
2281
+ // =====================================================
2282
+ // AMBIENT GRID ANIMATION
2283
+ // =====================================================
2284
+ (function ambientScan() {
2285
+ let scanEl = document.createElement('div');
2286
+ scanEl.style.cssText = `
2287
+ position: fixed; top: 0; left: 0; right: 0; height: 2px;
2288
+ background: linear-gradient(90deg, transparent, rgba(0,200,255,0.08), transparent);
2289
+ pointer-events: none; z-index: 100;
2290
+ animation: ambient-scan 8s linear infinite;
2291
+ `;
2292
+ document.body.appendChild(scanEl);
2293
+
2294
+ const style = document.createElement('style');
2295
+ style.textContent = `
2296
+ @keyframes ambient-scan {
2297
+ 0% { transform: translateY(0); }
2298
+ 100% { transform: translateY(100vh); }
2299
+ }
2300
+ `;
2301
+ document.head.appendChild(style);
2302
+ })();
2303
+
2304
  </script>
2305
+ </body>
2306
  </html>