lvwerra HF Staff Claude Opus 4.7 (1M context) commited on
Commit
50571e5
·
1 Parent(s): 5b3ae9f

Integrate editorial banner as hero, adopt warm paper palette

Browse files

The new banner (animated DNA helix + CARBON-0 wordmark + spec line)
replaces the per-tab logo+title hero blocks. Page palette shifts from
cool gray (#fafafa / #1a1a1a / #1a8a3a / #d83a2a) to the banner's
warm paper tone (#f7f5ee / #1f1f1d / #317f3f / #bc2e25) — including
the corresponding RGB tuples used in the JS logprob coloring so the
sequence-rendering colors stay aligned with the page chrome.

Banner is scoped under .carbon-banner with cb-* prefixed selectors
and SVG IDs to avoid collisions; aspect-ratio 2048:620 preserved.
Double frame removed (kept the outer container border, dropped the
inner stroke rect). Per-tab heroes collapsed into a single lede
paragraph since the banner already provides identity.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

Files changed (1) hide show
  1. demo.html +438 -150
demo.html CHANGED
@@ -11,7 +11,7 @@
11
  body {
12
  font-family: "Inter", "Helvetica Neue", sans-serif;
13
  font-size: 13px; font-weight: 300; line-height: 1.7;
14
- color: #1a1a1a; background: #fafafa;
15
  padding: 0;
16
  }
17
  .container { max-width: 760px; margin: 0 auto; padding: 48px 32px 96px; }
@@ -22,7 +22,7 @@
22
  border-bottom: 1px solid #ccc; /* separator line under the tab strip */
23
  padding: 24px 32px 0; /* no bottom padding — tabs sit on the line */
24
  margin-bottom: 0;
25
- background: #fafafa;
26
  position: sticky; top: 0; z-index: 10;
27
  }
28
  .header-inner {
@@ -58,10 +58,10 @@
58
  cursor: pointer; transition: background 0.15s, color 0.15s;
59
  }
60
  nav#tab-nav .tab + .tab { margin-left: -1px; } /* shared border between adjacent tabs */
61
- nav#tab-nav .tab:hover { color: #1a1a1a; background: #f6f6f6; }
62
  nav#tab-nav .tab.active {
63
- color: #1a1a1a; background: #fafafa;
64
- border-bottom-color: #fafafa; /* hides bottom border so tab merges into content */
65
  z-index: 2;
66
  }
67
 
@@ -69,33 +69,89 @@
69
  .tab-panel { display: none; }
70
  .tab-panel.active { display: block; }
71
 
72
- /* --- Hero --- */
73
- .hero {
74
- padding: 64px 32px 48px;
75
- border-bottom: 1px solid #eee;
76
- }
77
- .hero-inner {
78
- max-width: 1080px; margin: 0 auto;
79
- display: flex; align-items: center; gap: 48px;
80
- flex-wrap: wrap;
81
- }
82
- .hero-logo {
83
  display: block;
84
- flex: 0 0 auto;
85
- max-width: 320px; width: 38%; min-width: 220px;
86
- height: auto;
87
- mix-blend-mode: multiply;
88
- }
89
- .hero-text { flex: 1 1 320px; min-width: 280px; }
90
- .hero h2 {
91
- font-family: "JetBrains Mono", monospace;
92
- font-size: 26px; font-weight: 300; letter-spacing: -0.5px;
93
- margin: 0 0 16px;
94
- line-height: 1.3;
95
- }
96
- .hero h2 em { font-style: normal; color: #1a8a3a; font-weight: 400; }
97
- .hero p {
98
- color: #666; font-size: 14px; margin: 0;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
99
  }
100
 
101
  /* --- Sections --- */
@@ -111,11 +167,11 @@
111
  padding: 80px 32px 48px;
112
  border-top: 1px solid #eee;
113
  border-bottom: 1px solid #eee;
114
- background: linear-gradient(to bottom, rgba(26,138,58,0.04), transparent);
115
  }
116
  .part-divider .part-eyebrow {
117
  font-family: "JetBrains Mono", monospace;
118
- font-size: 10px; color: #1a8a3a;
119
  letter-spacing: 4px; text-transform: uppercase;
120
  margin-bottom: 6px;
121
  }
@@ -130,7 +186,7 @@
130
 
131
  .section-num {
132
  font-family: "JetBrains Mono", monospace;
133
- font-size: 10px; color: #1a8a3a;
134
  letter-spacing: 2px; text-transform: uppercase;
135
  margin-bottom: 8px;
136
  }
@@ -138,7 +194,7 @@
138
  font-family: "JetBrains Mono", monospace;
139
  font-size: 22px; font-weight: 400; letter-spacing: -0.3px;
140
  margin-bottom: 24px;
141
- color: #1a1a1a;
142
  }
143
  .lede {
144
  color: #444; font-size: 14px; margin-bottom: 32px;
@@ -146,14 +202,14 @@
146
  }
147
  .takeaway {
148
  margin-top: 32px;
149
- padding: 16px 20px; border-left: 3px solid #1a8a3a;
150
  background: #f4f8f4; color: #333;
151
  font-size: 13px; max-width: 640px;
152
  }
153
  .takeaway strong {
154
  font-family: "JetBrains Mono", monospace;
155
  font-weight: 500; letter-spacing: 1px; text-transform: uppercase;
156
- font-size: 10px; color: #1a8a3a; display: block; margin-bottom: 4px;
157
  }
158
 
159
  /* --- Demo card (the interactive box inside each section) --- */
@@ -178,12 +234,12 @@
178
  text-transform: uppercase; letter-spacing: 1.5px;
179
  transition: all 0.15s;
180
  }
181
- button.action:hover, .pill:hover { border-color: #888; color: #1a1a1a; }
182
- button.action.primary { background: #1a1a1a; color: #fff; border-color: #1a1a1a; }
183
  button.action.primary:hover { background: #000; }
184
  button.action:disabled { opacity: 0.4; cursor: not-allowed; }
185
  button.action.primary:disabled { background: #888; border-color: #888; }
186
- .pill.active { background: #1a1a1a; color: #fff; border-color: #1a1a1a; }
187
  .pills { display: inline-flex; gap: 4px; }
188
  .pills .pill { font-size: 9px; padding: 4px 8px; }
189
 
@@ -207,15 +263,15 @@
207
  margin: 4px 0 12px;
208
  min-height: 14px;
209
  }
210
- .gene-info strong { color: #1a1a1a; font-weight: 500; }
211
  .gene-track {
212
  width: 100%; height: 28px; display: block;
213
  margin: 4px 0 8px;
214
  }
215
- .gene-track .exon { fill: #1a8a3a; }
216
  .gene-track .intron { stroke: #aaa; stroke-width: 1; }
217
- .gene-track .playhead { stroke: #d83a2a; stroke-width: 2; }
218
- .gene-track .gen-region { fill: #d83a2a; opacity: 0.10; }
219
  .gene-track text { font-family: "JetBrains Mono", monospace; font-size: 9px; fill: #888; }
220
  .track-axis-label {
221
  font-family: "JetBrains Mono", monospace; font-size: 9px;
@@ -238,11 +294,11 @@
238
  }
239
  .stat-pair { display: flex; flex-direction: column; gap: 2px; }
240
  .stat-pair-label { font-size: 9px; color: #999; text-transform: uppercase; letter-spacing: 1.2px; }
241
- .stat-pair-val { color: #1a1a1a; font-variant-numeric: tabular-nums; }
242
  .stat-pair-val.muted { color: #aaa; }
243
 
244
  /* Mismatch highlighting in reference row */
245
- .ref-mismatch { background: rgba(216, 58, 42, 0.18); color: #b00020; }
246
  .ref-match { color: #999; }
247
 
248
  /* Model section responsive overrides */
@@ -277,19 +333,19 @@
277
  background: #fff; color: #666; cursor: pointer;
278
  transition: all 0.15s;
279
  }
280
- #panel-sandbox .sb-ex-btn:hover { border-color: #888; color: #1a1a1a; }
281
  #panel-sandbox .sb-ex-btn .sb-ex-label {
282
  color: #aaa; font-size: 9px; margin-left: 6px; text-transform: uppercase;
283
  letter-spacing: 0.5px;
284
  }
285
  #panel-sandbox .sb-prompt-area, #panel-sandbox input[type=number] {
286
  font-family: "JetBrains Mono", monospace;
287
- font-size: 12px; font-weight: 300; color: #1a1a1a;
288
  background: #fff; border: 1px solid #ddd; border-radius: 3px;
289
  padding: 8px 12px; outline: none; transition: border 0.15s;
290
  }
291
  #panel-sandbox .sb-prompt-area:focus,
292
- #panel-sandbox input[type=number]:focus { border-color: #1a1a1a; }
293
  #panel-sandbox .sb-prompt-area {
294
  width: 100%; resize: none; overflow: hidden;
295
  letter-spacing: 1px; line-height: 1.7;
@@ -323,8 +379,8 @@
323
  }
324
  #panel-sandbox .sb-mode-btn:first-child { border-radius: 3px 0 0 3px; }
325
  #panel-sandbox .sb-mode-btn:last-child { border-right: 1px solid #ccc; border-radius: 0 3px 3px 0; }
326
- #panel-sandbox .sb-mode-btn:hover { color: #1a1a1a; }
327
- #panel-sandbox .sb-mode-btn.active { background: #1a1a1a; color: #fff; border-color: #1a1a1a; }
328
 
329
  #panel-sandbox .sb-button-row { margin-left: auto; display: flex; gap: 6px; }
330
 
@@ -339,7 +395,7 @@
339
  display: inline-block; width: 6px; height: 6px; border-radius: 50%;
340
  background: #888; margin-right: 6px; vertical-align: middle;
341
  }
342
- #panel-sandbox .sb-status.streaming .dot { background: #1a8a3a; animation: pulse 1.2s ease-in-out infinite; }
343
 
344
  #panel-sandbox .sb-output-row {
345
  display: grid;
@@ -361,9 +417,9 @@
361
  text-transform: uppercase; letter-spacing: 1px;
362
  transition: all 0.15s;
363
  }
364
- #panel-sandbox .sb-copy-btn:hover { border-color: #888; color: #1a1a1a; }
365
  #panel-sandbox .sb-copy-btn:disabled { opacity: 0; pointer-events: none; }
366
- #panel-sandbox .sb-copy-btn.copied { background: #1a8a3a; color: #fff; border-color: #1a8a3a; }
367
 
368
  #panel-sandbox .sb-seq-block {
369
  font-family: "JetBrains Mono", monospace;
@@ -379,7 +435,7 @@
379
  #panel-sandbox .sb-seq-line.tail::after {
380
  content: "";
381
  display: inline-block; width: 7px; height: 14px;
382
- background: #1a1a1a; vertical-align: text-bottom;
383
  margin-left: 2px;
384
  animation: blink 1s step-end infinite;
385
  }
@@ -401,7 +457,7 @@
401
  text-transform: uppercase; letter-spacing: 1.2px; font-weight: 300;
402
  }
403
  #panel-sandbox .sb-stat-value {
404
- font-size: 12px; font-weight: 400; color: #1a1a1a;
405
  font-variant-numeric: tabular-nums;
406
  }
407
  #panel-sandbox .sb-stat-value .sb-unit { font-size: 9px; color: #999; margin-left: 3px; font-weight: 300; }
@@ -418,7 +474,7 @@
418
  #panel-sandbox .sb-legend.show { display: block; }
419
  #panel-sandbox .sb-legend-bar {
420
  height: 6px; margin: 4px 0 3px;
421
- background: linear-gradient(to right, #d83a2a, #888, #1a1a1a);
422
  border-radius: 1px;
423
  }
424
  #panel-sandbox .sb-legend-row { display: flex; justify-content: space-between; }
@@ -437,8 +493,8 @@
437
  text-align: center;
438
  }
439
  .vep-window .ctx { color: #888; }
440
- .vep-window .var-ref { background: rgba(26, 138, 58, 0.18); color: #1a6a2a; padding: 0 4px; border-radius: 2px; font-weight: 500; }
441
- .vep-window .var-alt { background: rgba(216, 58, 42, 0.20); color: #b00020; padding: 0 4px; border-radius: 2px; font-weight: 500; }
442
  .vep-result {
443
  display: grid; grid-template-columns: 100px 1fr 80px;
444
  gap: 8px 12px; align-items: center;
@@ -447,14 +503,14 @@
447
  .vep-result .row-label { color: #666; text-transform: uppercase; letter-spacing: 1px; font-size: 10px; }
448
  .vep-result .row-bar { height: 14px; background: #f0f0f0; border-radius: 2px; position: relative; overflow: hidden; }
449
  .vep-result .row-bar .fill { position: absolute; top: 0; bottom: 0; left: 0; }
450
- .vep-result .row-bar.ref .fill { background: #1a8a3a; }
451
- .vep-result .row-bar.alt .fill { background: #d83a2a; }
452
- .vep-result .row-val { text-align: right; color: #1a1a1a; font-variant-numeric: tabular-nums; }
453
  .vep-result .row-delta { font-weight: 500; }
454
 
455
- .pill.sig-Pathogenic { border-left: 3px solid #d83a2a; }
456
  .pill.sig-Risk { border-left: 3px solid #e69500; }
457
- .pill.sig-Benign { border-left: 3px solid #1a8a3a; }
458
 
459
  /* --- Species rows (§4) --- */
460
  .species-row {
@@ -471,7 +527,7 @@
471
  font-size: 11px;
472
  }
473
  .species-name {
474
- font-weight: 500; color: #1a1a1a;
475
  text-transform: uppercase; letter-spacing: 1px; font-size: 11px;
476
  border-left: 4px solid;
477
  padding-left: 8px;
@@ -481,11 +537,11 @@
481
  text-align: right; font-family: "JetBrains Mono", monospace;
482
  font-size: 11px; color: #666;
483
  }
484
- .species-stats .stat-id { font-size: 16px; color: #1a1a1a; font-weight: 500; font-variant-numeric: tabular-nums; }
485
  .species-stats .stat-sub { font-size: 10px; color: #999; margin-top: 2px; }
486
  .species-seq {
487
  font-family: "JetBrains Mono", monospace;
488
- background: #fafafa; border: 1px solid #eee;
489
  padding: 8px 12px; overflow-x: auto;
490
  white-space: pre; font-size: 11px;
491
  line-height: 1.7; letter-spacing: 0.5px;
@@ -501,7 +557,7 @@
501
  border-top: 1px solid #eee;
502
  }
503
  footer a { color: #666; text-decoration: none; }
504
- footer a:hover { color: #1a1a1a; }
505
 
506
  /* --- Sequence display (shared with sandbox) --- */
507
  .seq-block {
@@ -526,7 +582,7 @@
526
  display: inline-block; width: 6px; height: 6px; border-radius: 50%;
527
  background: #888;
528
  }
529
- .status.streaming .dot { background: #1a8a3a; animation: pulse 1.2s ease-in-out infinite; }
530
  .status.error { color: #b00020; }
531
  @keyframes pulse { 50% { opacity: 0.3; } }
532
 
@@ -551,16 +607,73 @@
551
  </div>
552
  </header>
553
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
554
  <div class="tab-panel active" id="panel-demo" data-tab="demo">
555
 
556
- <div class="hero">
557
- <div class="hero-inner">
558
- <img src="/img/logo.png" alt="Carbon" class="hero-logo">
559
- <div class="hero-text">
560
- <h2>A model that learned <em>biology</em> from raw DNA — no labels, no annotations, just sequence.</h2>
561
- <p>Carbon is a 3B-parameter autoregressive model trained on a trillion bases of genomic DNA. We didn't tell it what an exon is. We didn't tell it which mutations are pathogenic. We didn't tell it how genes differ between species. Below, four ways to see what it picked up anyway.</p>
562
- </div>
563
- </div>
564
  </div>
565
 
566
  <div class="container wide">
@@ -604,10 +717,10 @@
604
  <div class="gene-info" id="d1-info">loading genes…</div>
605
  <svg class="gene-track" id="d1-track" viewBox="0 0 1000 28" preserveAspectRatio="none"></svg>
606
  <div class="track-axis-label">
607
- <span><span class="legend-swatch" style="background:#1a8a3a"></span>exon</span>
608
  <span><span class="legend-swatch" style="background:#aaa;height:2px;border-radius:0"></span>intron</span>
609
- <span><span class="legend-swatch" style="background:rgba(216,58,42,0.20)"></span>generated region</span>
610
- <span style="color:#d83a2a">│ prompt end</span>
611
  </div>
612
 
613
  <div class="seq-label">model output · <span style="color:#aaa">prompt in gray</span> · <span>generated colored by logprob (red=uncertain)</span></div>
@@ -666,9 +779,9 @@
666
  <svg id="d2-bars" style="display:block;width:100%;height:auto;background:#fff;border:1px solid #eee;margin-top:12px" preserveAspectRatio="xMinYMin meet"></svg>
667
 
668
  <div class="track-axis-label">
669
- <span><span class="legend-swatch" style="background:#d83a2a;border-radius:50%"></span>pathogenic (curated)</span>
670
  <span><span class="legend-swatch" style="background:#e69500;border-radius:50%"></span>risk</span>
671
- <span><span class="legend-swatch" style="background:#1a8a3a;border-radius:50%"></span>benign</span>
672
  <span style="color:#888">dot = ClinVar label · bar = model signal</span>
673
  </div>
674
  </div>
@@ -683,7 +796,7 @@
683
  likely culprit is allele frequency: alt alleles common enough in human populations look
684
  perfectly normal to a model trained on natural sequence. For sharper variant effect
685
  prediction, Carbon can be fine-tuned (see the
686
- <a href="https://huggingface.co/spaces/hf-carbon/dna-vep-explainer" style="color:#1a8a3a">dna-vep-explainer</a>).
687
  </div>
688
  </section>
689
 
@@ -713,7 +826,7 @@
713
  <svg class="gene-track" id="d3-track" viewBox="0 0 1000 28" preserveAspectRatio="none"></svg>
714
  <svg id="d3-chart" style="display:block;width:100%;height:140px;background:#fff;border:1px solid #eee;margin-top:6px" preserveAspectRatio="none" viewBox="0 0 1000 140"></svg>
715
  <div class="track-axis-label">
716
- <span><span class="legend-swatch" style="background:#1a8a3a"></span>exon (shaded)</span>
717
  <span style="color:#aaa">y-axis: log P per 6-bp token (higher = more confident)</span>
718
  <span id="d3-bp-label" style="color:#888">0 bp</span>
719
  </div>
@@ -773,7 +886,7 @@
773
 
774
  <div class="track-axis-label" style="margin-top:14px">
775
  <span style="color:#aaa">prompt in gray</span>
776
- <span style="color:#1a1a1a">generated colored by logprob</span>
777
  <span style="color:#b00020">mismatches in reference highlighted</span>
778
  </div>
779
  </div>
@@ -831,14 +944,8 @@
831
 
832
  <div class="tab-panel" id="panel-model" data-tab="model">
833
 
834
- <div class="hero">
835
- <div class="hero-inner">
836
- <img src="/img/logo.png" alt="Carbon" class="hero-logo">
837
- <div class="hero-text">
838
- <h2>How Carbon was <em>built</em> — what's specific to DNA, not the standard LLM playbook.</h2>
839
- <p>Three places where the recipe needed adjustment for biology: the way DNA gets tokenized, how the loss handles near-miss tokens, and which sequence ends up in the corpus. Plus a deliberately vanilla architecture so any improvement can be attributed to the recipe rather than custom blocks.</p>
840
- </div>
841
- </div>
842
  </div>
843
 
844
  <div class="container wide">
@@ -865,7 +972,7 @@
865
  value="ATGGCCAAGCTGACCAGCGAGCTGCTGGCC"
866
  style="font-family:'JetBrains Mono',monospace;font-size:12px;padding:6px 10px;border:1px solid #ccc;border-radius:3px;flex:1 1 280px;letter-spacing:1px;text-transform:uppercase">
867
  <span class="spacer"></span>
868
- <span class="status"><span class="dot" style="background:#1a8a3a"></span><span id="d7-len">30 bp</span></span>
869
  </div>
870
 
871
  <div id="d7-cols" style="display:grid;grid-template-columns:repeat(2,1fr);gap:16px;margin-top:8px">
@@ -893,8 +1000,8 @@
893
 
894
  <div class="track-axis-label">
895
  <span>same DNA span</span>
896
- <span style="color:#1a8a3a">▼ shorter token sequence = cheaper attention</span>
897
- <span id="d7-speedup" style="color:#1a8a3a;font-weight:500">36× cheaper</span>
898
  </div>
899
  </div>
900
 
@@ -982,7 +1089,7 @@
982
  <div class="seq-label">signal-to-noise · raw genome vs annotation-aware curation</div>
983
  <svg id="d9-snr" viewBox="0 0 1000 100" preserveAspectRatio="none" style="display:block;width:100%;height:90px;background:#fff;border:1px solid #eee"></svg>
984
  <div class="track-axis-label">
985
- <span><span class="legend-swatch" style="background:#1a8a3a"></span>functional / annotated</span>
986
  <span><span class="legend-swatch" style="background:#ddd"></span>background</span>
987
  <span style="color:#888">curating raises the density of biological signal in the gradient</span>
988
  </div>
@@ -1037,14 +1144,8 @@
1037
  <!-- ============================================================ -->
1038
  <div class="tab-panel" id="panel-sandbox" data-tab="sandbox">
1039
 
1040
- <div class="hero">
1041
- <div class="hero-inner">
1042
- <img src="/img/logo.png" alt="Carbon" class="hero-logo">
1043
- <div class="hero-text">
1044
- <h2>Run Carbon <em>yourself</em>.</h2>
1045
- <p>Open-ended DNA continuation. Type any prefix in {A, C, G, T}, watch the model continue token by token. Toggle base-coloring or per-token logprob coloring to see where the model is confident and where it's guessing. Track GC content, perplexity, and throughput live.</p>
1046
- </div>
1047
- </div>
1048
  </div>
1049
 
1050
  <div class="container" style="max-width:1280px">
@@ -1134,9 +1235,9 @@
1134
  // =========================================================================
1135
  // Shared helpers
1136
  // =========================================================================
1137
- const DARK_RGB = [26, 26, 26];
1138
  const MID_RGB = [136, 136, 136];
1139
- const RED_RGB = [216, 58, 42];
1140
  const PROMPT_RGB = [170, 170, 170];
1141
 
1142
  function lerp(a, b, t) { return Math.round(a + (b - a) * t); }
@@ -1346,7 +1447,7 @@ async function loadGenes() {
1346
  const matches = genText[genIdx] === base;
1347
  return matches
1348
  ? { style: "color:#bbb" }
1349
- : { style: "color:#b00020;background:rgba(216,58,42,0.18)" };
1350
  };
1351
  renderSeq(els.ref, refSeq, bpl, colorRef);
1352
  }
@@ -1557,7 +1658,7 @@ async function loadGenes() {
1557
  const wRef = (Math.abs(c.refSum) / range) * 100;
1558
  const wAlt = (Math.abs(c.altSum) / range) * 100;
1559
  const delta = c.altSum - c.refSum;
1560
- const dColor = delta < -0.5 ? "#d83a2a" : (delta > 0.5 ? "#1a8a3a" : "#888");
1561
  els.result.innerHTML = `
1562
  <div class="row-label">ref · ${v.ref}</div>
1563
  <div class="row-bar ref"><div class="fill" style="width:${wRef.toFixed(1)}%"></div></div>
@@ -1592,7 +1693,7 @@ async function loadGenes() {
1592
  const innerW = W - padL - padR;
1593
  const center = padL + innerW / 2;
1594
  const scale = (innerW / 2) / absMax;
1595
- const sigColor = (s) => s === "Pathogenic" ? "#d83a2a" : s === "Benign" ? "#1a8a3a" : "#e69500";
1596
 
1597
  // Bar color: encode the *model's* opinion of the alt allele
1598
  // - Δ < 0 : red (model finds alt unusual). Saturation ~ |Δ|.
@@ -1614,13 +1715,13 @@ async function loadGenes() {
1614
  let svg = "";
1615
 
1616
  // --- Top axis: directional caption ---
1617
- svg += `<text x="${padL.toFixed(1)}" y="14" font-family="JetBrains Mono" font-size="9" fill="#d83a2a" letter-spacing="1">← MODEL SURPRISED BY ALT</text>`;
1618
  svg += `<text x="${(W - padR).toFixed(1)}" y="14" font-family="JetBrains Mono" font-size="9" fill="#333" letter-spacing="1" text-anchor="end">MODEL FINE WITH ALT →</text>`;
1619
  svg += `<text x="${center.toFixed(1)}" y="14" font-family="JetBrains Mono" font-size="9" fill="#888" text-anchor="middle" letter-spacing="1">Δ (alt − ref)</text>`;
1620
  svg += `<text x="${(padL - 12).toFixed(1)}" y="14" font-family="JetBrains Mono" font-size="9" fill="#888" text-anchor="end" letter-spacing="1">VARIANT</text>`;
1621
 
1622
  // Faint shading: pathogenic-expected zone (left of 0)
1623
- svg += `<rect x="${padL.toFixed(1)}" y="${(padT - 4).toFixed(1)}" width="${(center - padL).toFixed(1)}" height="${(ordered.length * rowH + 8).toFixed(1)}" fill="#d83a2a" opacity="0.04"/>`;
1624
 
1625
  // Center line
1626
  svg += `<line x1="${center}" y1="${padT - 4}" x2="${center}" y2="${H - padB + 4}" stroke="#bbb" stroke-width="1"/>`;
@@ -1653,7 +1754,7 @@ async function loadGenes() {
1653
  const color = barColor(d);
1654
  const barX = Math.min(center, x);
1655
  const barW = Math.max(2, Math.abs(x - center));
1656
- svg += `<rect x="${barX.toFixed(1)}" y="${(y - 8).toFixed(1)}" width="${barW.toFixed(1)}" height="14" fill="${color}" stroke="${v === selected ? '#1a1a1a' : 'none'}" stroke-width="${v === selected ? 1 : 0}"/>`;
1657
 
1658
  const label = (d >= 0 ? "+" : "") + d.toFixed(2);
1659
  const insideOK = barW >= VALUE_INSIDE_MIN && Math.abs(d) >= 0.5; // color is dark enough only away from neutral
@@ -1876,7 +1977,7 @@ async function loadGenes() {
1876
  if (e.start > scoredLen) continue;
1877
  const x = xScale(e.start);
1878
  const w = xScale(Math.min(e.end, scoredLen)) - x;
1879
- svg += `<rect x="${x.toFixed(1)}" y="0" width="${Math.max(1, w).toFixed(1)}" height="${H}" fill="#1a8a3a" opacity="0.08"/>`;
1880
  }
1881
 
1882
  // Smoothed line: a moving average over the points (window=5)
@@ -1901,7 +2002,7 @@ async function loadGenes() {
1901
  smoothed.forEach((p, i) => {
1902
  d += (i === 0 ? "M" : "L") + xScale(p.pos).toFixed(1) + " " + yScale(p.lp).toFixed(1);
1903
  });
1904
- svg += `<path d="${d}" fill="none" stroke="#1a1a1a" stroke-width="1.2" stroke-linejoin="round"/>`;
1905
 
1906
  // Y-axis ticks
1907
  const tickLps = [yMin + (yMax - yMin) * 0.1, yMin + (yMax - yMin) * 0.5, yMin + (yMax - yMin) * 0.9];
@@ -2151,7 +2252,7 @@ async function loadGenes() {
2151
  const matches = genText[genIdx] === base;
2152
  return matches
2153
  ? { style: "color:#bbb" }
2154
- : { style: "color:#b00020;background:rgba(216,58,42,0.18)" };
2155
  };
2156
  const bpl2 = basesPerLine(refEl);
2157
  renderSeq(refEl, refSeq, bpl2, colorRef);
@@ -2297,7 +2398,7 @@ async function loadGenes() {
2297
  speedup: document.getElementById("d7-speedup"),
2298
  };
2299
  // 8-color palette for 6-mer tokens (cycle); 1-mer uses base coloring.
2300
- const TOKEN_PALETTE = ["#1a8a3a","#2c5aa0","#c08030","#7a4baa","#2a8a8a","#b03b6e","#5a6e30","#a87a30"];
2301
  const BASE_FILL = { A:"#3a8a3e", C:"#2e6bb8", G:"#b5891e", T:"#b53a3a", N:"#888" };
2302
 
2303
  function clean(s) { return (s || "").toUpperCase().replace(/[^ACGTN]/g, ""); }
@@ -2340,7 +2441,7 @@ async function loadGenes() {
2340
  let svg = "";
2341
  const rows = [
2342
  { label: "1-mer", n: n1, cost: n1 * n1, color: "#888" },
2343
- { label: "6-mer", n: n6, cost: n6 * n6, color: "#1a8a3a" },
2344
  ];
2345
  rows.forEach((r, i) => {
2346
  const y = padT + i * (rowH + 8);
@@ -2404,8 +2505,8 @@ async function loadGenes() {
2404
  html += `<div style="display:grid;grid-template-columns:140px 1fr 1fr;gap:8px 14px;align-items:center;font-family:'JetBrains Mono',monospace;font-size:11px">`;
2405
  // Header
2406
  html += `<div></div>`;
2407
- html += `<div style="font-size:9px;color:${mode==='fns'?'#aaa':'#1a1a1a'};text-transform:uppercase;letter-spacing:1.5px">${mode==='fns'?'cross-entropy':'cross-entropy'} <span style="font-weight:500">${mode==='ce'||mode==='both'?'· active':''}</span></div>`;
2408
- html += `<div style="font-size:9px;color:${mode==='ce'?'#aaa':'#1a1a1a'};text-transform:uppercase;letter-spacing:1.5px">FNS <span style="font-weight:500">${mode==='fns'||mode==='both'?'· active':''}</span></div>`;
2409
 
2410
  cands.forEach(c => {
2411
  const isTarget = c === t1Equal(c, target);
@@ -2413,25 +2514,25 @@ async function loadGenes() {
2413
  let badges = "";
2414
  for (let i = 0; i < 6; i++) {
2415
  const match = c[i] === target[i];
2416
- const color = match ? "#1a8a3a" : "#d83a2a";
2417
- const bg = match ? "rgba(26,138,58,0.10)" : "rgba(216,58,42,0.08)";
2418
  badges += `<span style="display:inline-block;background:${bg};color:${color};padding:2px 5px;margin:1px;border-radius:2px;font-weight:${match?500:400}">${c[i]}</span>`;
2419
  }
2420
  const isExact = c === target;
2421
  const labelText = isExact ? "exact target" : `${nMatches(c, target)}/6 match`;
2422
  html += `<div style="display:flex;flex-direction:column;gap:2px">
2423
  <div>${badges}</div>
2424
- <div style="font-size:9px;color:${isExact?'#1a8a3a':'#888'};letter-spacing:1px;text-transform:uppercase;padding-left:4px">${labelText}</div>
2425
  </div>`;
2426
 
2427
  // CE column
2428
  const ceVal = ceCredit(c, target);
2429
- html += creditCell(ceVal, mode === "fns", c === target ? "credit = 1" : "credit = 0", "#1a8a3a", "#d83a2a");
2430
 
2431
  // FNS column
2432
  const fnsVal = fnsCredit(c, target);
2433
  const fnsLabel = c === target ? "credit = 1" : `credit = ${fnsVal.toFixed(2)} (${nMatches(c, target)}/6)`;
2434
- html += creditCell(fnsVal, mode === "ce", fnsLabel, "#1a8a3a", "#d83a2a");
2435
  });
2436
  html += `</div>`;
2437
  canvas.innerHTML = html;
@@ -2462,8 +2563,8 @@ async function loadGenes() {
2462
  let svg = "";
2463
 
2464
  // Background two-tone (CE phase + FNS phase)
2465
- svg += `<rect x="${padL}" y="${padT}" width="${(switchX - padL).toFixed(1)}" height="${H - padT - padB}" fill="#1a1a1a" opacity="0.04"/>`;
2466
- svg += `<rect x="${switchX.toFixed(1)}" y="${padT}" width="${(W - padR - switchX).toFixed(1)}" height="${H - padT - padB}" fill="#1a8a3a" opacity="0.06"/>`;
2467
 
2468
  // Mock loss curve: smooth descent then a "staircase" that gets cleaned by the FNS switch
2469
  const points = [];
@@ -2486,20 +2587,20 @@ async function loadGenes() {
2486
  }
2487
  let d = "";
2488
  points.forEach(([x, y], i) => { d += (i === 0 ? "M" : "L") + x.toFixed(1) + " " + y.toFixed(1); });
2489
- svg += `<path d="${d}" fill="none" stroke="#1a1a1a" stroke-width="1.4"/>`;
2490
 
2491
  // Switch marker
2492
- svg += `<line x1="${switchX.toFixed(1)}" y1="${padT}" x2="${switchX.toFixed(1)}" y2="${H - padB}" stroke="#1a8a3a" stroke-width="1.5" stroke-dasharray="3,3"/>`;
2493
- svg += `<text x="${switchX.toFixed(1)}" y="${(padT - 4)}" font-family="JetBrains Mono" font-size="9" fill="#1a8a3a" text-anchor="middle" letter-spacing="1">CE → FNS</text>`;
2494
 
2495
  // Phase labels
2496
  svg += `<text x="${(padL + (switchX - padL) / 2).toFixed(1)}" y="${(H - padB + 14).toFixed(1)}" font-family="JetBrains Mono" font-size="10" fill="#666" text-anchor="middle" letter-spacing="1">CROSS-ENTROPY · learn joint structure</text>`;
2497
- svg += `<text x="${(switchX + (W - padR - switchX) / 2).toFixed(1)}" y="${(H - padB + 14).toFixed(1)}" font-family="JetBrains Mono" font-size="10" fill="#1a8a3a" text-anchor="middle" letter-spacing="1">FNS · smooth, BF16-stable refinement</text>`;
2498
  // y-axis label
2499
  svg += `<text x="${padL}" y="${(padT - 4)}" font-family="JetBrains Mono" font-size="9" fill="#aaa" letter-spacing="0.5">training loss</text>`;
2500
  // staircase callout
2501
  const staircaseX = padL + innerW * (switchAt - 0.04);
2502
- svg += `<text x="${staircaseX.toFixed(1)}" y="${(H - padB - 4).toFixed(1)}" font-family="JetBrains Mono" font-size="9" fill="#d83a2a" text-anchor="end">↑ staircase</text>`;
2503
 
2504
  schedule.innerHTML = svg;
2505
  }
@@ -2534,7 +2635,7 @@ async function loadGenes() {
2534
  const tplEl = document.getElementById("d9-templates");
2535
 
2536
  const COMPOSITION = [
2537
- { label: "GENERator-v2", pct: 70, color: "#1a8a3a", desc: "annotation-aware functional genomic backbone (eukaryotic, gene-centric)" },
2538
  { label: "mRNA", pct: 16, color: "#2c5aa0", desc: "OpenGenome2 mature transcripts · RNA-level functional context" },
2539
  { label: "GTDB", pct: 10, color: "#c08030", desc: "OpenGenome2 prokaryotic genomes · compact bacterial structure" },
2540
  { label: "mRNA-splice", pct: 4, color: "#7a4baa", desc: "OpenGenome2 transcript-derived · splice-related signal" },
@@ -2569,8 +2670,8 @@ async function loadGenes() {
2569
  }
2570
  }
2571
  // Inner hole for donut
2572
- svg += `<circle cx="${cx}" cy="${cy}" r="${r * 0.42}" fill="#fafafa"/>`;
2573
- svg += `<text x="${cx}" y="${cy - 2}" font-family="JetBrains Mono" font-size="11" fill="#1a1a1a" text-anchor="middle" font-weight="500">CARBON</text>`;
2574
  svg += `<text x="${cx}" y="${cy + 12}" font-family="JetBrains Mono" font-size="9" fill="#888" text-anchor="middle">corpus</text>`;
2575
  pieEl.innerHTML = svg;
2576
  }
@@ -2580,7 +2681,7 @@ async function loadGenes() {
2580
  <div style="display:flex;align-items:flex-start;gap:10px;padding:6px 0;border-bottom:1px solid #f0f0f0">
2581
  <span style="display:inline-block;flex:0 0 12px;height:12px;background:${s.color};border-radius:2px;margin-top:3px"></span>
2582
  <div style="flex:1">
2583
- <div><strong style="color:#1a1a1a">${s.label}</strong> <span style="color:#888">· ${s.pct}%</span></div>
2584
  <div style="color:#888;font-size:10px;margin-top:1px">${s.desc}</div>
2585
  </div>
2586
  </div>
@@ -2602,7 +2703,7 @@ async function loadGenes() {
2602
  const rowW = W - padL - 12;
2603
  for (const seg of segs) {
2604
  const w = (seg.frac * rowW);
2605
- svg += `<rect x="${cursor.toFixed(1)}" y="${y}" width="${Math.max(0.5, w).toFixed(1)}" height="${segH}" fill="${seg.func ? '#1a8a3a' : '#ddd'}"/>`;
2606
  cursor += w;
2607
  }
2608
  }
@@ -2627,8 +2728,8 @@ async function loadGenes() {
2627
  function renderTemplates() {
2628
  let html = "";
2629
  for (const t of TEMPLATES) {
2630
- html += `<div style="text-align:right;color:#1a8a3a;font-weight:500">${t.pct}</div>`;
2631
- html += `<div><span style="background:#f4f4f4;padding:2px 6px;border-radius:2px;color:#1a1a1a">${t.body.replace(/</g,"&lt;").replace(/>/g,"&gt;")}</span> <span style="color:#888;font-size:10px;margin-left:8px">${t.note}</span></div>`;
2632
  }
2633
  tplEl.innerHTML = html;
2634
  }
@@ -2660,16 +2761,16 @@ async function loadGenes() {
2660
  let html = `<thead>
2661
  <tr>
2662
  <th style="text-align:left;padding:10px 6px 8px;border-bottom:1px solid #ddd;font-size:10px;color:#888;text-transform:uppercase;letter-spacing:1.5px;font-weight:400"></th>
2663
- <th style="text-align:left;padding:10px 12px 8px;border-bottom:1px solid #ddd;font-size:11px;color:#1a1a1a;letter-spacing:1px">Carbon · 3B</th>
2664
- <th style="text-align:left;padding:10px 12px 8px;border-bottom:1px solid #ddd;font-size:11px;color:#1a1a1a;letter-spacing:1px">Carbon · 8B</th>
2665
  </tr>
2666
  </thead><tbody>`;
2667
  ROWS.forEach((r, i) => {
2668
- const bg = i % 2 === 0 ? "#fafafa" : "#fff";
2669
  html += `<tr style="background:${bg}">
2670
  <td style="padding:6px;color:#666;font-size:10px;text-transform:uppercase;letter-spacing:1px">${r[0]}</td>
2671
- <td style="padding:6px 12px;color:#1a1a1a">${r[1]}</td>
2672
- <td style="padding:6px 12px;color:#1a1a1a">${r[2]}</td>
2673
  </tr>`;
2674
  });
2675
  html += `</tbody>`;
@@ -2709,9 +2810,9 @@ async function loadGenes() {
2709
  A: [58, 138, 62], C: [46, 107, 184], G: [181, 137, 30], T: [181, 58, 58], N: [136, 136, 136],
2710
  };
2711
  const PROMPT_RGB_S = [170, 170, 170];
2712
- const DARK_RGB_S = [26, 26, 26];
2713
  const MID_RGB_S = [136, 136, 136];
2714
- const RED_RGB_S = [216, 58, 42];
2715
  const BG_ALPHA = 0.12;
2716
 
2717
  let promptBases = "";
@@ -2744,14 +2845,14 @@ async function loadGenes() {
2744
  const bar = document.getElementById("sb-legend-bar");
2745
  if (!lpRange) {
2746
  minEl.textContent = midEl.textContent = maxEl.textContent = "—";
2747
- bar.style.background = "linear-gradient(to right, #d83a2a, #888, #1a1a1a)";
2748
  } else {
2749
  const { min, mid, max } = lpRange;
2750
  minEl.textContent = min.toFixed(1);
2751
  midEl.textContent = mid.toFixed(1);
2752
  maxEl.textContent = max.toFixed(1);
2753
  const midPct = max > min ? ((mid - min) / (max - min)) * 100 : 50;
2754
- bar.style.background = `linear-gradient(to right, #d83a2a 0%, #888 ${midPct.toFixed(1)}%, #1a1a1a 100%)`;
2755
  }
2756
  updateLpChart();
2757
  }
@@ -2787,9 +2888,9 @@ async function loadGenes() {
2787
  svg.innerHTML = `
2788
  <defs>
2789
  <linearGradient id="sb-lp-grad" gradientUnits="userSpaceOnUse" x1="0" y1="${H - pad}" x2="0" y2="${pad}">
2790
- <stop offset="0%" stop-color="#d83a2a"/>
2791
  <stop offset="${midPct.toFixed(1)}%" stop-color="#888"/>
2792
- <stop offset="100%" stop-color="#1a1a1a"/>
2793
  </linearGradient>
2794
  </defs>
2795
  <path d="${d}" fill="none" stroke="url(#sb-lp-grad)" stroke-width="1.2" stroke-linejoin="round" stroke-linecap="round"/>
@@ -3140,6 +3241,193 @@ async function loadGenes() {
3140
  }
3141
  })();
3142
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
3143
  // =========================================================================
3144
  // Tab switching + hash routing
3145
  // =========================================================================
 
11
  body {
12
  font-family: "Inter", "Helvetica Neue", sans-serif;
13
  font-size: 13px; font-weight: 300; line-height: 1.7;
14
+ color: #1f1f1d; background: #f7f5ee;
15
  padding: 0;
16
  }
17
  .container { max-width: 760px; margin: 0 auto; padding: 48px 32px 96px; }
 
22
  border-bottom: 1px solid #ccc; /* separator line under the tab strip */
23
  padding: 24px 32px 0; /* no bottom padding — tabs sit on the line */
24
  margin-bottom: 0;
25
+ background: #f7f5ee;
26
  position: sticky; top: 0; z-index: 10;
27
  }
28
  .header-inner {
 
58
  cursor: pointer; transition: background 0.15s, color 0.15s;
59
  }
60
  nav#tab-nav .tab + .tab { margin-left: -1px; } /* shared border between adjacent tabs */
61
+ nav#tab-nav .tab:hover { color: #1f1f1d; background: #f6f6f6; }
62
  nav#tab-nav .tab.active {
63
+ color: #1f1f1d; background: #f7f5ee;
64
+ border-bottom-color: #f7f5ee; /* hides bottom border so tab merges into content */
65
  z-index: 2;
66
  }
67
 
 
69
  .tab-panel { display: none; }
70
  .tab-panel.active { display: block; }
71
 
72
+ /* ====================================================================== */
73
+ /* Carbon banner — editorial hero (scoped to .carbon-banner) */
74
+ /* ====================================================================== */
75
+ .carbon-banner {
76
+ --paper: #f7f5ee;
77
+ --ink: #1f1f1d;
78
+ --muted: #8c918b;
79
+ --hairline: #b9bcb7;
80
+ --green: #317f3f;
81
+ --red: #bc2e25;
82
+
83
  display: block;
84
+ margin: 28px auto;
85
+ width: min(1280px, calc(100vw - 32px));
86
+ aspect-ratio: 2048 / 620;
87
+ position: relative;
88
+ overflow: hidden;
89
+ border: 1px solid #8f928d;
90
+ background:
91
+ radial-gradient(circle at 22% 32%, rgba(0, 0, 0, 0.035), transparent 1px),
92
+ radial-gradient(circle at 78% 64%, rgba(0, 0, 0, 0.03), transparent 1px),
93
+ linear-gradient(90deg, rgba(49, 127, 63, 0.025), transparent 34%, transparent 66%, rgba(49, 127, 63, 0.025)),
94
+ var(--paper);
95
+ background-size: 7px 7px, 11px 11px, auto, auto;
96
+ font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace;
97
+ }
98
+ .carbon-banner .carbon-art { width: 100%; height: 100%; display: block; }
99
+ .carbon-banner .cb-mono {
100
+ font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace;
101
+ letter-spacing: 0.24em;
102
+ text-transform: uppercase;
103
+ }
104
+ .carbon-banner .cb-rule { stroke: var(--hairline); stroke-width: 1; vector-effect: non-scaling-stroke; }
105
+ .carbon-banner .cb-label { fill: var(--ink); font-size: 17px; letter-spacing: 0.24em; }
106
+ .carbon-banner .cb-label-green { fill: var(--green); }
107
+ .carbon-banner .cb-label-red { fill: var(--red); }
108
+ .carbon-banner .cb-carbon-word {
109
+ fill: var(--ink);
110
+ font-family: "Arial Narrow", "Helvetica Neue", Arial, sans-serif;
111
+ font-size: 255px;
112
+ font-stretch: condensed;
113
+ font-weight: 900;
114
+ letter-spacing: -0.02em;
115
+ }
116
+ .carbon-banner .cb-paper-grain { mix-blend-mode: multiply; opacity: 0.62; pointer-events: none; }
117
+ .carbon-banner .cb-helix-shadow { fill: #aeb5ad; opacity: 0.15; }
118
+ .carbon-banner .cb-helix-body {
119
+ fill: #e4e5dc;
120
+ stroke: rgba(49, 127, 63, 0.14);
121
+ stroke-width: 0.8;
122
+ }
123
+ .carbon-banner .cb-helix-edge {
124
+ fill: none; stroke: #2d332e; stroke-width: 1.15;
125
+ stroke-linecap: round; stroke-linejoin: round;
126
+ vector-effect: non-scaling-stroke;
127
+ }
128
+ .carbon-banner .cb-helix-texture { fill: url(#cb-helixGrain); opacity: 0.46; }
129
+ .carbon-banner .cb-base-pair {
130
+ fill: none; stroke: var(--green); stroke-width: 1.35;
131
+ stroke-linecap: round; vector-effect: non-scaling-stroke;
132
+ }
133
+ .carbon-banner .cb-base-letter-node {
134
+ will-change: transform, opacity;
135
+ transform-box: fill-box; transform-origin: 0 0;
136
+ }
137
+ .carbon-banner .cb-base-glyph {
138
+ fill: none; stroke: var(--green); stroke-width: 1.8;
139
+ stroke-linecap: square; stroke-linejoin: miter;
140
+ vector-effect: non-scaling-stroke;
141
+ }
142
+ .carbon-banner .cb-tiny-bars rect { fill: var(--green); }
143
+ @media (max-width: 760px) {
144
+ .carbon-banner { width: calc(100vw - 20px); margin: 16px auto; }
145
+ }
146
+
147
+ /* --- Tab lede (short narrative paragraph below the banner) --- */
148
+ .tab-lede {
149
+ max-width: 1080px; margin: 4px auto 0;
150
+ padding: 14px 32px 0;
151
+ }
152
+ .tab-lede p {
153
+ color: #555; font-size: 14px; line-height: 1.7;
154
+ max-width: 760px; margin: 0;
155
  }
156
 
157
  /* --- Sections --- */
 
167
  padding: 80px 32px 48px;
168
  border-top: 1px solid #eee;
169
  border-bottom: 1px solid #eee;
170
+ background: linear-gradient(to bottom, rgba(49,127,63,0.04), transparent);
171
  }
172
  .part-divider .part-eyebrow {
173
  font-family: "JetBrains Mono", monospace;
174
+ font-size: 10px; color: #317f3f;
175
  letter-spacing: 4px; text-transform: uppercase;
176
  margin-bottom: 6px;
177
  }
 
186
 
187
  .section-num {
188
  font-family: "JetBrains Mono", monospace;
189
+ font-size: 10px; color: #317f3f;
190
  letter-spacing: 2px; text-transform: uppercase;
191
  margin-bottom: 8px;
192
  }
 
194
  font-family: "JetBrains Mono", monospace;
195
  font-size: 22px; font-weight: 400; letter-spacing: -0.3px;
196
  margin-bottom: 24px;
197
+ color: #1f1f1d;
198
  }
199
  .lede {
200
  color: #444; font-size: 14px; margin-bottom: 32px;
 
202
  }
203
  .takeaway {
204
  margin-top: 32px;
205
+ padding: 16px 20px; border-left: 3px solid #317f3f;
206
  background: #f4f8f4; color: #333;
207
  font-size: 13px; max-width: 640px;
208
  }
209
  .takeaway strong {
210
  font-family: "JetBrains Mono", monospace;
211
  font-weight: 500; letter-spacing: 1px; text-transform: uppercase;
212
+ font-size: 10px; color: #317f3f; display: block; margin-bottom: 4px;
213
  }
214
 
215
  /* --- Demo card (the interactive box inside each section) --- */
 
234
  text-transform: uppercase; letter-spacing: 1.5px;
235
  transition: all 0.15s;
236
  }
237
+ button.action:hover, .pill:hover { border-color: #888; color: #1f1f1d; }
238
+ button.action.primary { background: #1f1f1d; color: #fff; border-color: #1f1f1d; }
239
  button.action.primary:hover { background: #000; }
240
  button.action:disabled { opacity: 0.4; cursor: not-allowed; }
241
  button.action.primary:disabled { background: #888; border-color: #888; }
242
+ .pill.active { background: #1f1f1d; color: #fff; border-color: #1f1f1d; }
243
  .pills { display: inline-flex; gap: 4px; }
244
  .pills .pill { font-size: 9px; padding: 4px 8px; }
245
 
 
263
  margin: 4px 0 12px;
264
  min-height: 14px;
265
  }
266
+ .gene-info strong { color: #1f1f1d; font-weight: 500; }
267
  .gene-track {
268
  width: 100%; height: 28px; display: block;
269
  margin: 4px 0 8px;
270
  }
271
+ .gene-track .exon { fill: #317f3f; }
272
  .gene-track .intron { stroke: #aaa; stroke-width: 1; }
273
+ .gene-track .playhead { stroke: #bc2e25; stroke-width: 2; }
274
+ .gene-track .gen-region { fill: #bc2e25; opacity: 0.10; }
275
  .gene-track text { font-family: "JetBrains Mono", monospace; font-size: 9px; fill: #888; }
276
  .track-axis-label {
277
  font-family: "JetBrains Mono", monospace; font-size: 9px;
 
294
  }
295
  .stat-pair { display: flex; flex-direction: column; gap: 2px; }
296
  .stat-pair-label { font-size: 9px; color: #999; text-transform: uppercase; letter-spacing: 1.2px; }
297
+ .stat-pair-val { color: #1f1f1d; font-variant-numeric: tabular-nums; }
298
  .stat-pair-val.muted { color: #aaa; }
299
 
300
  /* Mismatch highlighting in reference row */
301
+ .ref-mismatch { background: rgba(188, 46, 37, 0.18); color: #b00020; }
302
  .ref-match { color: #999; }
303
 
304
  /* Model section responsive overrides */
 
333
  background: #fff; color: #666; cursor: pointer;
334
  transition: all 0.15s;
335
  }
336
+ #panel-sandbox .sb-ex-btn:hover { border-color: #888; color: #1f1f1d; }
337
  #panel-sandbox .sb-ex-btn .sb-ex-label {
338
  color: #aaa; font-size: 9px; margin-left: 6px; text-transform: uppercase;
339
  letter-spacing: 0.5px;
340
  }
341
  #panel-sandbox .sb-prompt-area, #panel-sandbox input[type=number] {
342
  font-family: "JetBrains Mono", monospace;
343
+ font-size: 12px; font-weight: 300; color: #1f1f1d;
344
  background: #fff; border: 1px solid #ddd; border-radius: 3px;
345
  padding: 8px 12px; outline: none; transition: border 0.15s;
346
  }
347
  #panel-sandbox .sb-prompt-area:focus,
348
+ #panel-sandbox input[type=number]:focus { border-color: #1f1f1d; }
349
  #panel-sandbox .sb-prompt-area {
350
  width: 100%; resize: none; overflow: hidden;
351
  letter-spacing: 1px; line-height: 1.7;
 
379
  }
380
  #panel-sandbox .sb-mode-btn:first-child { border-radius: 3px 0 0 3px; }
381
  #panel-sandbox .sb-mode-btn:last-child { border-right: 1px solid #ccc; border-radius: 0 3px 3px 0; }
382
+ #panel-sandbox .sb-mode-btn:hover { color: #1f1f1d; }
383
+ #panel-sandbox .sb-mode-btn.active { background: #1f1f1d; color: #fff; border-color: #1f1f1d; }
384
 
385
  #panel-sandbox .sb-button-row { margin-left: auto; display: flex; gap: 6px; }
386
 
 
395
  display: inline-block; width: 6px; height: 6px; border-radius: 50%;
396
  background: #888; margin-right: 6px; vertical-align: middle;
397
  }
398
+ #panel-sandbox .sb-status.streaming .dot { background: #317f3f; animation: pulse 1.2s ease-in-out infinite; }
399
 
400
  #panel-sandbox .sb-output-row {
401
  display: grid;
 
417
  text-transform: uppercase; letter-spacing: 1px;
418
  transition: all 0.15s;
419
  }
420
+ #panel-sandbox .sb-copy-btn:hover { border-color: #888; color: #1f1f1d; }
421
  #panel-sandbox .sb-copy-btn:disabled { opacity: 0; pointer-events: none; }
422
+ #panel-sandbox .sb-copy-btn.copied { background: #317f3f; color: #fff; border-color: #317f3f; }
423
 
424
  #panel-sandbox .sb-seq-block {
425
  font-family: "JetBrains Mono", monospace;
 
435
  #panel-sandbox .sb-seq-line.tail::after {
436
  content: "";
437
  display: inline-block; width: 7px; height: 14px;
438
+ background: #1f1f1d; vertical-align: text-bottom;
439
  margin-left: 2px;
440
  animation: blink 1s step-end infinite;
441
  }
 
457
  text-transform: uppercase; letter-spacing: 1.2px; font-weight: 300;
458
  }
459
  #panel-sandbox .sb-stat-value {
460
+ font-size: 12px; font-weight: 400; color: #1f1f1d;
461
  font-variant-numeric: tabular-nums;
462
  }
463
  #panel-sandbox .sb-stat-value .sb-unit { font-size: 9px; color: #999; margin-left: 3px; font-weight: 300; }
 
474
  #panel-sandbox .sb-legend.show { display: block; }
475
  #panel-sandbox .sb-legend-bar {
476
  height: 6px; margin: 4px 0 3px;
477
+ background: linear-gradient(to right, #bc2e25, #888, #1f1f1d);
478
  border-radius: 1px;
479
  }
480
  #panel-sandbox .sb-legend-row { display: flex; justify-content: space-between; }
 
493
  text-align: center;
494
  }
495
  .vep-window .ctx { color: #888; }
496
+ .vep-window .var-ref { background: rgba(49, 127, 63, 0.18); color: #215a2a; padding: 0 4px; border-radius: 2px; font-weight: 500; }
497
+ .vep-window .var-alt { background: rgba(188, 46, 37, 0.20); color: #b00020; padding: 0 4px; border-radius: 2px; font-weight: 500; }
498
  .vep-result {
499
  display: grid; grid-template-columns: 100px 1fr 80px;
500
  gap: 8px 12px; align-items: center;
 
503
  .vep-result .row-label { color: #666; text-transform: uppercase; letter-spacing: 1px; font-size: 10px; }
504
  .vep-result .row-bar { height: 14px; background: #f0f0f0; border-radius: 2px; position: relative; overflow: hidden; }
505
  .vep-result .row-bar .fill { position: absolute; top: 0; bottom: 0; left: 0; }
506
+ .vep-result .row-bar.ref .fill { background: #317f3f; }
507
+ .vep-result .row-bar.alt .fill { background: #bc2e25; }
508
+ .vep-result .row-val { text-align: right; color: #1f1f1d; font-variant-numeric: tabular-nums; }
509
  .vep-result .row-delta { font-weight: 500; }
510
 
511
+ .pill.sig-Pathogenic { border-left: 3px solid #bc2e25; }
512
  .pill.sig-Risk { border-left: 3px solid #e69500; }
513
+ .pill.sig-Benign { border-left: 3px solid #317f3f; }
514
 
515
  /* --- Species rows (§4) --- */
516
  .species-row {
 
527
  font-size: 11px;
528
  }
529
  .species-name {
530
+ font-weight: 500; color: #1f1f1d;
531
  text-transform: uppercase; letter-spacing: 1px; font-size: 11px;
532
  border-left: 4px solid;
533
  padding-left: 8px;
 
537
  text-align: right; font-family: "JetBrains Mono", monospace;
538
  font-size: 11px; color: #666;
539
  }
540
+ .species-stats .stat-id { font-size: 16px; color: #1f1f1d; font-weight: 500; font-variant-numeric: tabular-nums; }
541
  .species-stats .stat-sub { font-size: 10px; color: #999; margin-top: 2px; }
542
  .species-seq {
543
  font-family: "JetBrains Mono", monospace;
544
+ background: #f7f5ee; border: 1px solid #eee;
545
  padding: 8px 12px; overflow-x: auto;
546
  white-space: pre; font-size: 11px;
547
  line-height: 1.7; letter-spacing: 0.5px;
 
557
  border-top: 1px solid #eee;
558
  }
559
  footer a { color: #666; text-decoration: none; }
560
+ footer a:hover { color: #1f1f1d; }
561
 
562
  /* --- Sequence display (shared with sandbox) --- */
563
  .seq-block {
 
582
  display: inline-block; width: 6px; height: 6px; border-radius: 50%;
583
  background: #888;
584
  }
585
+ .status.streaming .dot { background: #317f3f; animation: pulse 1.2s ease-in-out infinite; }
586
  .status.error { color: #b00020; }
587
  @keyframes pulse { 50% { opacity: 0.3; } }
588
 
 
607
  </div>
608
  </header>
609
 
610
+ <section class="carbon-banner" aria-label="Carbon DNA model banner">
611
+ <svg class="carbon-art" viewBox="0 0 2048 620" role="img" aria-labelledby="cb-carbonTitle cb-carbonDesc">
612
+ <title id="cb-carbonTitle">Carbon genomic language model banner</title>
613
+ <desc id="cb-carbonDesc">A technical editorial banner with the CARBON-0 model name and an adjacent animated DNA strand.</desc>
614
+
615
+ <defs>
616
+ <pattern id="cb-paperDot" width="12" height="12" patternUnits="userSpaceOnUse">
617
+ <circle cx="2" cy="2" r="0.8" fill="#1f1f1d" opacity="0.045" />
618
+ <circle cx="9" cy="8" r="0.55" fill="#317f3f" opacity="0.035" />
619
+ </pattern>
620
+ <pattern id="cb-helixGrain" width="9" height="9" patternUnits="userSpaceOnUse">
621
+ <rect width="9" height="9" fill="transparent" />
622
+ <circle cx="1.4" cy="1.7" r="0.7" fill="#263128" opacity="0.34" />
623
+ <circle cx="6.2" cy="5.5" r="0.45" fill="#263128" opacity="0.24" />
624
+ <line x1="3" y1="8" x2="7" y2="8" stroke="#263128" stroke-width="0.45" opacity="0.18" />
625
+ </pattern>
626
+ </defs>
627
+
628
+ <rect class="cb-paper-grain" x="25" y="25" width="1998" height="570" fill="url(#cb-paperDot)" />
629
+
630
+ <g class="cb-mono">
631
+ <text class="cb-label" x="63" y="76">CARBON-0</text>
632
+ <text class="cb-label cb-label-green" x="240" y="76">3B Autoregressive Genomic Foundation Model</text>
633
+ <text class="cb-label" x="1162" y="76">49,152 BP Context</text>
634
+ <circle cx="1456" cy="70" r="4" fill="#317f3f" />
635
+ <text class="cb-label" x="1490" y="76">6-Mer Tokenizer</text>
636
+ <circle cx="1760" cy="70" r="4" fill="#317f3f" />
637
+ <text class="cb-label" x="1795" y="76">1T Train Tokens</text>
638
+ </g>
639
+
640
+ <line class="cb-rule" x1="40" y1="110" x2="2010" y2="110" />
641
+
642
+ <g class="cb-mono">
643
+ <text class="cb-carbon-word" x="60" y="405" textLength="795" lengthAdjust="spacingAndGlyphs">CARBON-0</text>
644
+ </g>
645
+
646
+ <g id="cb-helix" aria-hidden="true">
647
+ <g id="cb-strandBackLayer"></g>
648
+ <g id="cb-rungs"></g>
649
+ <g id="cb-baseLetters" class="cb-mono"></g>
650
+ <g id="cb-strandFrontLayer"></g>
651
+ </g>
652
+
653
+ <line class="cb-rule" x1="40" y1="545" x2="2010" y2="545" />
654
+ <g class="cb-mono">
655
+ <text class="cb-label" x="64" y="578">Carbon Labs</text>
656
+ <line x1="240" y1="570" x2="266" y2="570" stroke="#317f3f" stroke-width="2" />
657
+ <text class="cb-label" x="294" y="578">Building Foundational Models For Genomic Sequences</text>
658
+ <text class="cb-label" x="1378" y="578">CLB-2026-05-11</text>
659
+ <text class="cb-label" x="1638" y="578">DNA-LM-49K</text>
660
+ <text class="cb-label" x="1784" y="578">CARBON-0 v0</text>
661
+ <g class="cb-tiny-bars" transform="translate(1956 562)">
662
+ <rect x="0" y="0" width="3" height="20" />
663
+ <rect x="8" y="0" width="3" height="20" />
664
+ <rect x="16" y="0" width="3" height="20" />
665
+ <rect x="24" y="0" width="3" height="20" />
666
+ <rect x="32" y="0" width="3" height="20" />
667
+ <rect x="40" y="0" width="3" height="20" />
668
+ </g>
669
+ </g>
670
+ </svg>
671
+ </section>
672
+
673
  <div class="tab-panel active" id="panel-demo" data-tab="demo">
674
 
675
+ <div class="tab-lede">
676
+ <p>We didn't tell Carbon what an exon is. We didn't tell it which mutations are pathogenic. We didn't tell it how genes differ between species. Four ways to see what it picked up anyway.</p>
 
 
 
 
 
 
677
  </div>
678
 
679
  <div class="container wide">
 
717
  <div class="gene-info" id="d1-info">loading genes…</div>
718
  <svg class="gene-track" id="d1-track" viewBox="0 0 1000 28" preserveAspectRatio="none"></svg>
719
  <div class="track-axis-label">
720
+ <span><span class="legend-swatch" style="background:#317f3f"></span>exon</span>
721
  <span><span class="legend-swatch" style="background:#aaa;height:2px;border-radius:0"></span>intron</span>
722
+ <span><span class="legend-swatch" style="background:rgba(188,46,37,0.20)"></span>generated region</span>
723
+ <span style="color:#bc2e25">│ prompt end</span>
724
  </div>
725
 
726
  <div class="seq-label">model output · <span style="color:#aaa">prompt in gray</span> · <span>generated colored by logprob (red=uncertain)</span></div>
 
779
  <svg id="d2-bars" style="display:block;width:100%;height:auto;background:#fff;border:1px solid #eee;margin-top:12px" preserveAspectRatio="xMinYMin meet"></svg>
780
 
781
  <div class="track-axis-label">
782
+ <span><span class="legend-swatch" style="background:#bc2e25;border-radius:50%"></span>pathogenic (curated)</span>
783
  <span><span class="legend-swatch" style="background:#e69500;border-radius:50%"></span>risk</span>
784
+ <span><span class="legend-swatch" style="background:#317f3f;border-radius:50%"></span>benign</span>
785
  <span style="color:#888">dot = ClinVar label · bar = model signal</span>
786
  </div>
787
  </div>
 
796
  likely culprit is allele frequency: alt alleles common enough in human populations look
797
  perfectly normal to a model trained on natural sequence. For sharper variant effect
798
  prediction, Carbon can be fine-tuned (see the
799
+ <a href="https://huggingface.co/spaces/hf-carbon/dna-vep-explainer" style="color:#317f3f">dna-vep-explainer</a>).
800
  </div>
801
  </section>
802
 
 
826
  <svg class="gene-track" id="d3-track" viewBox="0 0 1000 28" preserveAspectRatio="none"></svg>
827
  <svg id="d3-chart" style="display:block;width:100%;height:140px;background:#fff;border:1px solid #eee;margin-top:6px" preserveAspectRatio="none" viewBox="0 0 1000 140"></svg>
828
  <div class="track-axis-label">
829
+ <span><span class="legend-swatch" style="background:#317f3f"></span>exon (shaded)</span>
830
  <span style="color:#aaa">y-axis: log P per 6-bp token (higher = more confident)</span>
831
  <span id="d3-bp-label" style="color:#888">0 bp</span>
832
  </div>
 
886
 
887
  <div class="track-axis-label" style="margin-top:14px">
888
  <span style="color:#aaa">prompt in gray</span>
889
+ <span style="color:#1f1f1d">generated colored by logprob</span>
890
  <span style="color:#b00020">mismatches in reference highlighted</span>
891
  </div>
892
  </div>
 
944
 
945
  <div class="tab-panel" id="panel-model" data-tab="model">
946
 
947
+ <div class="tab-lede">
948
+ <p>Three places where the recipe needed adjustment for biology: the way DNA gets tokenized, how the loss handles near-miss tokens, and which sequence ends up in the corpus. Plus a deliberately vanilla architecture so any improvement can be attributed to the recipe rather than custom blocks.</p>
 
 
 
 
 
 
949
  </div>
950
 
951
  <div class="container wide">
 
972
  value="ATGGCCAAGCTGACCAGCGAGCTGCTGGCC"
973
  style="font-family:'JetBrains Mono',monospace;font-size:12px;padding:6px 10px;border:1px solid #ccc;border-radius:3px;flex:1 1 280px;letter-spacing:1px;text-transform:uppercase">
974
  <span class="spacer"></span>
975
+ <span class="status"><span class="dot" style="background:#317f3f"></span><span id="d7-len">30 bp</span></span>
976
  </div>
977
 
978
  <div id="d7-cols" style="display:grid;grid-template-columns:repeat(2,1fr);gap:16px;margin-top:8px">
 
1000
 
1001
  <div class="track-axis-label">
1002
  <span>same DNA span</span>
1003
+ <span style="color:#317f3f">▼ shorter token sequence = cheaper attention</span>
1004
+ <span id="d7-speedup" style="color:#317f3f;font-weight:500">36× cheaper</span>
1005
  </div>
1006
  </div>
1007
 
 
1089
  <div class="seq-label">signal-to-noise · raw genome vs annotation-aware curation</div>
1090
  <svg id="d9-snr" viewBox="0 0 1000 100" preserveAspectRatio="none" style="display:block;width:100%;height:90px;background:#fff;border:1px solid #eee"></svg>
1091
  <div class="track-axis-label">
1092
+ <span><span class="legend-swatch" style="background:#317f3f"></span>functional / annotated</span>
1093
  <span><span class="legend-swatch" style="background:#ddd"></span>background</span>
1094
  <span style="color:#888">curating raises the density of biological signal in the gradient</span>
1095
  </div>
 
1144
  <!-- ============================================================ -->
1145
  <div class="tab-panel" id="panel-sandbox" data-tab="sandbox">
1146
 
1147
+ <div class="tab-lede">
1148
+ <p>Open-ended DNA continuation. Type any prefix in {A, C, G, T}, watch the model continue token by token. Toggle base-coloring or per-token logprob coloring to see where Carbon is confident and where it's guessing. Track GC content, perplexity, and throughput live.</p>
 
 
 
 
 
 
1149
  </div>
1150
 
1151
  <div class="container" style="max-width:1280px">
 
1235
  // =========================================================================
1236
  // Shared helpers
1237
  // =========================================================================
1238
+ const DARK_RGB = [31, 31, 29];
1239
  const MID_RGB = [136, 136, 136];
1240
+ const RED_RGB = [188, 46, 37];
1241
  const PROMPT_RGB = [170, 170, 170];
1242
 
1243
  function lerp(a, b, t) { return Math.round(a + (b - a) * t); }
 
1447
  const matches = genText[genIdx] === base;
1448
  return matches
1449
  ? { style: "color:#bbb" }
1450
+ : { style: "color:#b00020;background:rgba(188,46,37,0.18)" };
1451
  };
1452
  renderSeq(els.ref, refSeq, bpl, colorRef);
1453
  }
 
1658
  const wRef = (Math.abs(c.refSum) / range) * 100;
1659
  const wAlt = (Math.abs(c.altSum) / range) * 100;
1660
  const delta = c.altSum - c.refSum;
1661
+ const dColor = delta < -0.5 ? "#bc2e25" : (delta > 0.5 ? "#317f3f" : "#888");
1662
  els.result.innerHTML = `
1663
  <div class="row-label">ref · ${v.ref}</div>
1664
  <div class="row-bar ref"><div class="fill" style="width:${wRef.toFixed(1)}%"></div></div>
 
1693
  const innerW = W - padL - padR;
1694
  const center = padL + innerW / 2;
1695
  const scale = (innerW / 2) / absMax;
1696
+ const sigColor = (s) => s === "Pathogenic" ? "#bc2e25" : s === "Benign" ? "#317f3f" : "#e69500";
1697
 
1698
  // Bar color: encode the *model's* opinion of the alt allele
1699
  // - Δ < 0 : red (model finds alt unusual). Saturation ~ |Δ|.
 
1715
  let svg = "";
1716
 
1717
  // --- Top axis: directional caption ---
1718
+ svg += `<text x="${padL.toFixed(1)}" y="14" font-family="JetBrains Mono" font-size="9" fill="#bc2e25" letter-spacing="1">← MODEL SURPRISED BY ALT</text>`;
1719
  svg += `<text x="${(W - padR).toFixed(1)}" y="14" font-family="JetBrains Mono" font-size="9" fill="#333" letter-spacing="1" text-anchor="end">MODEL FINE WITH ALT →</text>`;
1720
  svg += `<text x="${center.toFixed(1)}" y="14" font-family="JetBrains Mono" font-size="9" fill="#888" text-anchor="middle" letter-spacing="1">Δ (alt − ref)</text>`;
1721
  svg += `<text x="${(padL - 12).toFixed(1)}" y="14" font-family="JetBrains Mono" font-size="9" fill="#888" text-anchor="end" letter-spacing="1">VARIANT</text>`;
1722
 
1723
  // Faint shading: pathogenic-expected zone (left of 0)
1724
+ svg += `<rect x="${padL.toFixed(1)}" y="${(padT - 4).toFixed(1)}" width="${(center - padL).toFixed(1)}" height="${(ordered.length * rowH + 8).toFixed(1)}" fill="#bc2e25" opacity="0.04"/>`;
1725
 
1726
  // Center line
1727
  svg += `<line x1="${center}" y1="${padT - 4}" x2="${center}" y2="${H - padB + 4}" stroke="#bbb" stroke-width="1"/>`;
 
1754
  const color = barColor(d);
1755
  const barX = Math.min(center, x);
1756
  const barW = Math.max(2, Math.abs(x - center));
1757
+ svg += `<rect x="${barX.toFixed(1)}" y="${(y - 8).toFixed(1)}" width="${barW.toFixed(1)}" height="14" fill="${color}" stroke="${v === selected ? '#1f1f1d' : 'none'}" stroke-width="${v === selected ? 1 : 0}"/>`;
1758
 
1759
  const label = (d >= 0 ? "+" : "") + d.toFixed(2);
1760
  const insideOK = barW >= VALUE_INSIDE_MIN && Math.abs(d) >= 0.5; // color is dark enough only away from neutral
 
1977
  if (e.start > scoredLen) continue;
1978
  const x = xScale(e.start);
1979
  const w = xScale(Math.min(e.end, scoredLen)) - x;
1980
+ svg += `<rect x="${x.toFixed(1)}" y="0" width="${Math.max(1, w).toFixed(1)}" height="${H}" fill="#317f3f" opacity="0.08"/>`;
1981
  }
1982
 
1983
  // Smoothed line: a moving average over the points (window=5)
 
2002
  smoothed.forEach((p, i) => {
2003
  d += (i === 0 ? "M" : "L") + xScale(p.pos).toFixed(1) + " " + yScale(p.lp).toFixed(1);
2004
  });
2005
+ svg += `<path d="${d}" fill="none" stroke="#1f1f1d" stroke-width="1.2" stroke-linejoin="round"/>`;
2006
 
2007
  // Y-axis ticks
2008
  const tickLps = [yMin + (yMax - yMin) * 0.1, yMin + (yMax - yMin) * 0.5, yMin + (yMax - yMin) * 0.9];
 
2252
  const matches = genText[genIdx] === base;
2253
  return matches
2254
  ? { style: "color:#bbb" }
2255
+ : { style: "color:#b00020;background:rgba(188,46,37,0.18)" };
2256
  };
2257
  const bpl2 = basesPerLine(refEl);
2258
  renderSeq(refEl, refSeq, bpl2, colorRef);
 
2398
  speedup: document.getElementById("d7-speedup"),
2399
  };
2400
  // 8-color palette for 6-mer tokens (cycle); 1-mer uses base coloring.
2401
+ const TOKEN_PALETTE = ["#317f3f","#2c5aa0","#c08030","#7a4baa","#2a8a8a","#b03b6e","#5a6e30","#a87a30"];
2402
  const BASE_FILL = { A:"#3a8a3e", C:"#2e6bb8", G:"#b5891e", T:"#b53a3a", N:"#888" };
2403
 
2404
  function clean(s) { return (s || "").toUpperCase().replace(/[^ACGTN]/g, ""); }
 
2441
  let svg = "";
2442
  const rows = [
2443
  { label: "1-mer", n: n1, cost: n1 * n1, color: "#888" },
2444
+ { label: "6-mer", n: n6, cost: n6 * n6, color: "#317f3f" },
2445
  ];
2446
  rows.forEach((r, i) => {
2447
  const y = padT + i * (rowH + 8);
 
2505
  html += `<div style="display:grid;grid-template-columns:140px 1fr 1fr;gap:8px 14px;align-items:center;font-family:'JetBrains Mono',monospace;font-size:11px">`;
2506
  // Header
2507
  html += `<div></div>`;
2508
+ html += `<div style="font-size:9px;color:${mode==='fns'?'#aaa':'#1f1f1d'};text-transform:uppercase;letter-spacing:1.5px">${mode==='fns'?'cross-entropy':'cross-entropy'} <span style="font-weight:500">${mode==='ce'||mode==='both'?'· active':''}</span></div>`;
2509
+ html += `<div style="font-size:9px;color:${mode==='ce'?'#aaa':'#1f1f1d'};text-transform:uppercase;letter-spacing:1.5px">FNS <span style="font-weight:500">${mode==='fns'||mode==='both'?'· active':''}</span></div>`;
2510
 
2511
  cands.forEach(c => {
2512
  const isTarget = c === t1Equal(c, target);
 
2514
  let badges = "";
2515
  for (let i = 0; i < 6; i++) {
2516
  const match = c[i] === target[i];
2517
+ const color = match ? "#317f3f" : "#bc2e25";
2518
+ const bg = match ? "rgba(49,127,63,0.10)" : "rgba(188,46,37,0.08)";
2519
  badges += `<span style="display:inline-block;background:${bg};color:${color};padding:2px 5px;margin:1px;border-radius:2px;font-weight:${match?500:400}">${c[i]}</span>`;
2520
  }
2521
  const isExact = c === target;
2522
  const labelText = isExact ? "exact target" : `${nMatches(c, target)}/6 match`;
2523
  html += `<div style="display:flex;flex-direction:column;gap:2px">
2524
  <div>${badges}</div>
2525
+ <div style="font-size:9px;color:${isExact?'#317f3f':'#888'};letter-spacing:1px;text-transform:uppercase;padding-left:4px">${labelText}</div>
2526
  </div>`;
2527
 
2528
  // CE column
2529
  const ceVal = ceCredit(c, target);
2530
+ html += creditCell(ceVal, mode === "fns", c === target ? "credit = 1" : "credit = 0", "#317f3f", "#bc2e25");
2531
 
2532
  // FNS column
2533
  const fnsVal = fnsCredit(c, target);
2534
  const fnsLabel = c === target ? "credit = 1" : `credit = ${fnsVal.toFixed(2)} (${nMatches(c, target)}/6)`;
2535
+ html += creditCell(fnsVal, mode === "ce", fnsLabel, "#317f3f", "#bc2e25");
2536
  });
2537
  html += `</div>`;
2538
  canvas.innerHTML = html;
 
2563
  let svg = "";
2564
 
2565
  // Background two-tone (CE phase + FNS phase)
2566
+ svg += `<rect x="${padL}" y="${padT}" width="${(switchX - padL).toFixed(1)}" height="${H - padT - padB}" fill="#1f1f1d" opacity="0.04"/>`;
2567
+ svg += `<rect x="${switchX.toFixed(1)}" y="${padT}" width="${(W - padR - switchX).toFixed(1)}" height="${H - padT - padB}" fill="#317f3f" opacity="0.06"/>`;
2568
 
2569
  // Mock loss curve: smooth descent then a "staircase" that gets cleaned by the FNS switch
2570
  const points = [];
 
2587
  }
2588
  let d = "";
2589
  points.forEach(([x, y], i) => { d += (i === 0 ? "M" : "L") + x.toFixed(1) + " " + y.toFixed(1); });
2590
+ svg += `<path d="${d}" fill="none" stroke="#1f1f1d" stroke-width="1.4"/>`;
2591
 
2592
  // Switch marker
2593
+ svg += `<line x1="${switchX.toFixed(1)}" y1="${padT}" x2="${switchX.toFixed(1)}" y2="${H - padB}" stroke="#317f3f" stroke-width="1.5" stroke-dasharray="3,3"/>`;
2594
+ svg += `<text x="${switchX.toFixed(1)}" y="${(padT - 4)}" font-family="JetBrains Mono" font-size="9" fill="#317f3f" text-anchor="middle" letter-spacing="1">CE → FNS</text>`;
2595
 
2596
  // Phase labels
2597
  svg += `<text x="${(padL + (switchX - padL) / 2).toFixed(1)}" y="${(H - padB + 14).toFixed(1)}" font-family="JetBrains Mono" font-size="10" fill="#666" text-anchor="middle" letter-spacing="1">CROSS-ENTROPY · learn joint structure</text>`;
2598
+ svg += `<text x="${(switchX + (W - padR - switchX) / 2).toFixed(1)}" y="${(H - padB + 14).toFixed(1)}" font-family="JetBrains Mono" font-size="10" fill="#317f3f" text-anchor="middle" letter-spacing="1">FNS · smooth, BF16-stable refinement</text>`;
2599
  // y-axis label
2600
  svg += `<text x="${padL}" y="${(padT - 4)}" font-family="JetBrains Mono" font-size="9" fill="#aaa" letter-spacing="0.5">training loss</text>`;
2601
  // staircase callout
2602
  const staircaseX = padL + innerW * (switchAt - 0.04);
2603
+ svg += `<text x="${staircaseX.toFixed(1)}" y="${(H - padB - 4).toFixed(1)}" font-family="JetBrains Mono" font-size="9" fill="#bc2e25" text-anchor="end">↑ staircase</text>`;
2604
 
2605
  schedule.innerHTML = svg;
2606
  }
 
2635
  const tplEl = document.getElementById("d9-templates");
2636
 
2637
  const COMPOSITION = [
2638
+ { label: "GENERator-v2", pct: 70, color: "#317f3f", desc: "annotation-aware functional genomic backbone (eukaryotic, gene-centric)" },
2639
  { label: "mRNA", pct: 16, color: "#2c5aa0", desc: "OpenGenome2 mature transcripts · RNA-level functional context" },
2640
  { label: "GTDB", pct: 10, color: "#c08030", desc: "OpenGenome2 prokaryotic genomes · compact bacterial structure" },
2641
  { label: "mRNA-splice", pct: 4, color: "#7a4baa", desc: "OpenGenome2 transcript-derived · splice-related signal" },
 
2670
  }
2671
  }
2672
  // Inner hole for donut
2673
+ svg += `<circle cx="${cx}" cy="${cy}" r="${r * 0.42}" fill="#f7f5ee"/>`;
2674
+ svg += `<text x="${cx}" y="${cy - 2}" font-family="JetBrains Mono" font-size="11" fill="#1f1f1d" text-anchor="middle" font-weight="500">CARBON</text>`;
2675
  svg += `<text x="${cx}" y="${cy + 12}" font-family="JetBrains Mono" font-size="9" fill="#888" text-anchor="middle">corpus</text>`;
2676
  pieEl.innerHTML = svg;
2677
  }
 
2681
  <div style="display:flex;align-items:flex-start;gap:10px;padding:6px 0;border-bottom:1px solid #f0f0f0">
2682
  <span style="display:inline-block;flex:0 0 12px;height:12px;background:${s.color};border-radius:2px;margin-top:3px"></span>
2683
  <div style="flex:1">
2684
+ <div><strong style="color:#1f1f1d">${s.label}</strong> <span style="color:#888">· ${s.pct}%</span></div>
2685
  <div style="color:#888;font-size:10px;margin-top:1px">${s.desc}</div>
2686
  </div>
2687
  </div>
 
2703
  const rowW = W - padL - 12;
2704
  for (const seg of segs) {
2705
  const w = (seg.frac * rowW);
2706
+ svg += `<rect x="${cursor.toFixed(1)}" y="${y}" width="${Math.max(0.5, w).toFixed(1)}" height="${segH}" fill="${seg.func ? '#317f3f' : '#ddd'}"/>`;
2707
  cursor += w;
2708
  }
2709
  }
 
2728
  function renderTemplates() {
2729
  let html = "";
2730
  for (const t of TEMPLATES) {
2731
+ html += `<div style="text-align:right;color:#317f3f;font-weight:500">${t.pct}</div>`;
2732
+ html += `<div><span style="background:#f4f4f4;padding:2px 6px;border-radius:2px;color:#1f1f1d">${t.body.replace(/</g,"&lt;").replace(/>/g,"&gt;")}</span> <span style="color:#888;font-size:10px;margin-left:8px">${t.note}</span></div>`;
2733
  }
2734
  tplEl.innerHTML = html;
2735
  }
 
2761
  let html = `<thead>
2762
  <tr>
2763
  <th style="text-align:left;padding:10px 6px 8px;border-bottom:1px solid #ddd;font-size:10px;color:#888;text-transform:uppercase;letter-spacing:1.5px;font-weight:400"></th>
2764
+ <th style="text-align:left;padding:10px 12px 8px;border-bottom:1px solid #ddd;font-size:11px;color:#1f1f1d;letter-spacing:1px">Carbon · 3B</th>
2765
+ <th style="text-align:left;padding:10px 12px 8px;border-bottom:1px solid #ddd;font-size:11px;color:#1f1f1d;letter-spacing:1px">Carbon · 8B</th>
2766
  </tr>
2767
  </thead><tbody>`;
2768
  ROWS.forEach((r, i) => {
2769
+ const bg = i % 2 === 0 ? "#f7f5ee" : "#fff";
2770
  html += `<tr style="background:${bg}">
2771
  <td style="padding:6px;color:#666;font-size:10px;text-transform:uppercase;letter-spacing:1px">${r[0]}</td>
2772
+ <td style="padding:6px 12px;color:#1f1f1d">${r[1]}</td>
2773
+ <td style="padding:6px 12px;color:#1f1f1d">${r[2]}</td>
2774
  </tr>`;
2775
  });
2776
  html += `</tbody>`;
 
2810
  A: [58, 138, 62], C: [46, 107, 184], G: [181, 137, 30], T: [181, 58, 58], N: [136, 136, 136],
2811
  };
2812
  const PROMPT_RGB_S = [170, 170, 170];
2813
+ const DARK_RGB_S = [31, 31, 29];
2814
  const MID_RGB_S = [136, 136, 136];
2815
+ const RED_RGB_S = [188, 46, 37];
2816
  const BG_ALPHA = 0.12;
2817
 
2818
  let promptBases = "";
 
2845
  const bar = document.getElementById("sb-legend-bar");
2846
  if (!lpRange) {
2847
  minEl.textContent = midEl.textContent = maxEl.textContent = "—";
2848
+ bar.style.background = "linear-gradient(to right, #bc2e25, #888, #1f1f1d)";
2849
  } else {
2850
  const { min, mid, max } = lpRange;
2851
  minEl.textContent = min.toFixed(1);
2852
  midEl.textContent = mid.toFixed(1);
2853
  maxEl.textContent = max.toFixed(1);
2854
  const midPct = max > min ? ((mid - min) / (max - min)) * 100 : 50;
2855
+ bar.style.background = `linear-gradient(to right, #bc2e25 0%, #888 ${midPct.toFixed(1)}%, #1f1f1d 100%)`;
2856
  }
2857
  updateLpChart();
2858
  }
 
2888
  svg.innerHTML = `
2889
  <defs>
2890
  <linearGradient id="sb-lp-grad" gradientUnits="userSpaceOnUse" x1="0" y1="${H - pad}" x2="0" y2="${pad}">
2891
+ <stop offset="0%" stop-color="#bc2e25"/>
2892
  <stop offset="${midPct.toFixed(1)}%" stop-color="#888"/>
2893
+ <stop offset="100%" stop-color="#1f1f1d"/>
2894
  </linearGradient>
2895
  </defs>
2896
  <path d="${d}" fill="none" stroke="url(#sb-lp-grad)" stroke-width="1.2" stroke-linejoin="round" stroke-linecap="round"/>
 
3241
  }
3242
  })();
3243
 
3244
+ // =========================================================================
3245
+ // Carbon banner — animated DNA helix
3246
+ // =========================================================================
3247
+ (function initCarbonBanner() {
3248
+ const banner = document.querySelector(".carbon-banner");
3249
+ if (!banner) return;
3250
+ const svgNS = "http://www.w3.org/2000/svg";
3251
+ const prefersReduced = window.matchMedia("(prefers-reduced-motion: reduce)").matches;
3252
+ const sequence = ["A","T","A","A","C","G","A","C","T","T","C","C","C","T","A","T","T","G"];
3253
+ const complement = { A: "T", T: "A", C: "G", G: "C" };
3254
+
3255
+ const helix = {
3256
+ startX: 868, endX: 1988, centerY: 318, amplitude: 88,
3257
+ cycles: 3.62, speed: 0.00015,
3258
+ rungCount: 27, segmentCount: 96,
3259
+ bodyRadius: 6.4, shadowRadius: 8.4,
3260
+ rungInset: 9.2, glyphGap: 13.5,
3261
+ };
3262
+
3263
+ const $ = (id) => banner.querySelector("#" + id);
3264
+ const els = {
3265
+ strandBackLayer: $("cb-strandBackLayer"),
3266
+ strandFrontLayer: $("cb-strandFrontLayer"),
3267
+ rungs: $("cb-rungs"),
3268
+ baseLetters: $("cb-baseLetters"),
3269
+ };
3270
+
3271
+ function makeEl(name, attrs = {}) {
3272
+ const node = document.createElementNS(svgNS, name);
3273
+ for (const [k, v] of Object.entries(attrs)) node.setAttribute(k, v);
3274
+ return node;
3275
+ }
3276
+ function makeBaseGlyph(letter) {
3277
+ const glyph = makeEl("g", { class: "cb-base-glyph" });
3278
+ const paths = {
3279
+ A: "M -5 8 L 0 -8 L 5 8 M -3.1 2 L 3.1 2",
3280
+ C: "M 5 -6 C 1 -9 -6 -7 -6 0 C -6 7 1 9 5 6",
3281
+ G: "M 5 -6 C 1 -9 -6 -7 -6 0 C -6 7 1 9 5 6 M 5 1 L 1 1 M 5 1 L 5 6",
3282
+ T: "M -6 -7 L 6 -7 M 0 -7 L 0 8",
3283
+ };
3284
+ glyph.appendChild(makeEl("path", { d: paths[letter] }));
3285
+ return glyph;
3286
+ }
3287
+
3288
+ const segmentNodes = [];
3289
+ const rungNodes = [];
3290
+ const letterNodes = [];
3291
+
3292
+ function makeSegmentNode(strandOffset, index) {
3293
+ const group = makeEl("g");
3294
+ const shadow = makeEl("path", { class: "cb-helix-shadow" });
3295
+ const body = makeEl("path", { class: "cb-helix-body" });
3296
+ const texture= makeEl("path", { class: "cb-helix-texture" });
3297
+ const edgeA = makeEl("path", { class: "cb-helix-edge" });
3298
+ const edgeB = makeEl("path", { class: "cb-helix-edge" });
3299
+ group.append(shadow, body, texture, edgeA, edgeB);
3300
+ return { group, shadow, body, texture, edgeA, edgeB, strandOffset, index, z: 0 };
3301
+ }
3302
+
3303
+ for (let i = 0; i < helix.segmentCount; i++) {
3304
+ segmentNodes.push(makeSegmentNode(0, i));
3305
+ segmentNodes.push(makeSegmentNode(Math.PI, i));
3306
+ }
3307
+ for (let i = 0; i < helix.rungCount; i++) {
3308
+ const path = makeEl("path", { class: "cb-base-pair" });
3309
+ els.rungs.appendChild(path);
3310
+ rungNodes.push(path);
3311
+ const pair = sequence[i % sequence.length];
3312
+ const mate = complement[pair];
3313
+ const groupA = makeEl("g", { class: "cb-base-letter-node" });
3314
+ const groupB = makeEl("g", { class: "cb-base-letter-node" });
3315
+ groupA.appendChild(makeBaseGlyph(pair));
3316
+ groupB.appendChild(makeBaseGlyph(mate));
3317
+ els.baseLetters.append(groupA, groupB);
3318
+ letterNodes.push([groupA, groupB]);
3319
+ }
3320
+
3321
+ function pointAt(x, offset, phase) {
3322
+ const t = (x - helix.startX) / (helix.endX - helix.startX);
3323
+ const theta = t * helix.cycles * Math.PI * 2 + phase + offset;
3324
+ const slope = Math.cos(theta) * helix.amplitude * helix.cycles * Math.PI * 2 / (helix.endX - helix.startX);
3325
+ const normalLength = Math.hypot(slope, 1);
3326
+ return {
3327
+ x,
3328
+ y: helix.centerY + Math.sin(theta) * helix.amplitude,
3329
+ z: Math.cos(theta),
3330
+ nx: -slope / normalLength,
3331
+ ny: 1 / normalLength,
3332
+ };
3333
+ }
3334
+ function samplesFor(offset, phase) {
3335
+ const points = [];
3336
+ for (let i = 0; i <= helix.segmentCount; i++) {
3337
+ const x = helix.startX + ((helix.endX - helix.startX) * i) / helix.segmentCount;
3338
+ points.push(pointAt(x, offset, phase));
3339
+ }
3340
+ return points;
3341
+ }
3342
+ const shiftedPoint = (p, r) => ({ x: p.x + p.nx * r, y: p.y + p.ny * r });
3343
+ function lineSegmentPath(a, b, radius) {
3344
+ const p0 = shiftedPoint(a, radius), p1 = shiftedPoint(b, radius);
3345
+ return `M ${p0.x.toFixed(2)} ${p0.y.toFixed(2)} L ${p1.x.toFixed(2)} ${p1.y.toFixed(2)}`;
3346
+ }
3347
+ function ribbonSegmentPath(a, b, radius) {
3348
+ const a0 = shiftedPoint(a, radius);
3349
+ const b0 = shiftedPoint(b, radius);
3350
+ const b1 = shiftedPoint(b, -radius);
3351
+ const a1 = shiftedPoint(a, -radius);
3352
+ return `M ${a0.x.toFixed(2)} ${a0.y.toFixed(2)} L ${b0.x.toFixed(2)} ${b0.y.toFixed(2)} L ${b1.x.toFixed(2)} ${b1.y.toFixed(2)} L ${a1.x.toFixed(2)} ${a1.y.toFixed(2)} Z`;
3353
+ }
3354
+ function setPath(el, d, opacity = 1) {
3355
+ el.setAttribute("d", d);
3356
+ el.style.opacity = opacity;
3357
+ }
3358
+ function updateSegment(node, points) {
3359
+ const a = points[node.index];
3360
+ const b = points[node.index + 1];
3361
+ const z = (a.z + b.z) / 2;
3362
+ const front = Math.max(0, Math.min(1, (z + 1) / 2));
3363
+ node.z = z;
3364
+ setPath(node.shadow, ribbonSegmentPath(a, b, helix.shadowRadius), 0.05 + front * 0.12);
3365
+ setPath(node.body, ribbonSegmentPath(a, b, helix.bodyRadius), 0.5 + front * 0.42);
3366
+ setPath(node.texture, ribbonSegmentPath(a, b, helix.bodyRadius - 0.8), 0.14 + front * 0.22);
3367
+ setPath(node.edgeA, lineSegmentPath(a, b, helix.bodyRadius + 0.35), 0.2 + front * 0.68);
3368
+ setPath(node.edgeB, lineSegmentPath(a, b, -helix.bodyRadius - 0.35), 0.2 + front * 0.68);
3369
+ }
3370
+ function brokenRungPath(x, yStart, letterYs, yEnd, gap) {
3371
+ const start = Math.min(yStart, yEnd);
3372
+ const end = Math.max(yStart, yEnd);
3373
+ const gaps = letterYs
3374
+ .map(y => [Math.max(start, y - gap), Math.min(end, y + gap)])
3375
+ .filter(([f, t]) => t > f)
3376
+ .sort((a, b) => a[0] - b[0]);
3377
+ const merged = [];
3378
+ for (const r of gaps) {
3379
+ const last = merged[merged.length - 1];
3380
+ if (!last || r[0] > last[1]) merged.push(r);
3381
+ else last[1] = Math.max(last[1], r[1]);
3382
+ }
3383
+ const parts = [];
3384
+ const addPart = (f, t) => { if (t - f > 0.7) parts.push(`M ${x.toFixed(2)} ${f.toFixed(2)} L ${x.toFixed(2)} ${t.toFixed(2)}`); };
3385
+ let cursor = start;
3386
+ for (const [f, t] of merged) { addPart(cursor, f); cursor = t; }
3387
+ addPart(cursor, end);
3388
+ return parts.join(" ");
3389
+ }
3390
+ function draw(ts = 0) {
3391
+ const phase = prefersReduced ? 0.6 : ts * helix.speed;
3392
+ const pointsByOffset = new Map([
3393
+ [0, samplesFor(0, phase)],
3394
+ [Math.PI, samplesFor(Math.PI, phase)],
3395
+ ]);
3396
+ for (const node of segmentNodes) {
3397
+ updateSegment(node, pointsByOffset.get(node.strandOffset));
3398
+ }
3399
+ els.strandBackLayer.replaceChildren();
3400
+ els.strandFrontLayer.replaceChildren();
3401
+ for (const node of segmentNodes.slice().sort((a, b) => a.z - b.z)) {
3402
+ const target = node.z >= 0 ? els.strandFrontLayer : els.strandBackLayer;
3403
+ target.appendChild(node.group);
3404
+ }
3405
+ for (let i = 0; i < helix.rungCount; i++) {
3406
+ const t = i / (helix.rungCount - 1);
3407
+ const x = helix.startX + (helix.endX - helix.startX) * t;
3408
+ const a = pointAt(x, 0, phase);
3409
+ const b = pointAt(x, Math.PI, phase);
3410
+ const yTop = Math.min(a.y, b.y);
3411
+ const yBottom = Math.max(a.y, b.y);
3412
+ const span = yBottom - yTop;
3413
+ const inset = Math.min(helix.rungInset, Math.max(0, span * 0.5 - 3));
3414
+ const visible = Math.max(0, Math.min(1, (span - 34) / 70));
3415
+ const aLetterY = a.y + (b.y - a.y) * 0.34;
3416
+ const bLetterY = b.y + (a.y - b.y) * 0.34;
3417
+ const letterGap = Math.min(helix.glyphGap, Math.max(7, span * 0.13));
3418
+ rungNodes[i].setAttribute("d", brokenRungPath(x, yTop + inset, [aLetterY, bLetterY], yBottom - inset, letterGap));
3419
+ rungNodes[i].style.opacity = 0.18 + visible * 0.72;
3420
+ const [sA, sB] = letterNodes[i];
3421
+ sA.setAttribute("transform", `translate(${x} ${aLetterY})`);
3422
+ sB.setAttribute("transform", `translate(${x} ${bLetterY})`);
3423
+ sA.style.opacity = 0.16 + visible * 0.84;
3424
+ sB.style.opacity = 0.16 + visible * 0.84;
3425
+ }
3426
+ if (!prefersReduced) requestAnimationFrame(draw);
3427
+ }
3428
+ draw();
3429
+ })();
3430
+
3431
  // =========================================================================
3432
  // Tab switching + hash routing
3433
  // =========================================================================