tfrere HF Staff Cursor commited on
Commit
c30d8ed
·
1 Parent(s): 3486829

Intro: split hero rail/figure + new site-map band

Browse files

- .tab-lede--split: sticky left rail (title + lede + figure caption)
with the Pareto SVG promoted to a sibling figure on the right, so the
message stays in view while the chart reads as adjacent evidence.
- Pareto chart: tighter viewBox cropped to visible content, L-shape axis
lines replace the full frame, 275× speedup shaft split around a
centred on-axis label, italic units subtitle dropped.
- Replace .intro-guide-list with .intro-sitemap: full-width banded nav
of four numbered cards (Intro / DNA Lab / Carbon Recipe / Sandbox)
feeding the existing #primer/#dna-lab/#recipe/#sandbox routing.

Co-authored-by: Cursor <cursoragent@cursor.com>

Files changed (2) hide show
  1. assets/styles/section-intro.css +368 -55
  2. demo.html +122 -37
assets/styles/section-intro.css CHANGED
@@ -25,50 +25,337 @@
25
  --promoter-soft: rgba(184, 134, 44, 0.18);
26
  }
27
 
28
- /* --- Tab-navigation list (Demo / Model / Sandbox). Styled to mirror the
29
- .banner-links pattern at the top of the page: mono / uppercase /
30
- underline anchor with a small arrow on the right, green hover. The
31
- <li> body uses normal prose styling so the link reads as a label
32
- in front of an explanation. */
33
- .intro-guide-list {
34
- list-style: disc;
35
- padding-left: 22px;
36
- margin: 32px 0 56px;
37
- max-width: 720px;
38
- }
39
- .intro-guide-list li {
40
- font-size: 14px;
41
- line-height: 1.7;
42
- color: var(--ink-soft);
43
- margin-bottom: 6px;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
44
  }
45
- .intro-guide-list li:last-child { margin-bottom: 0; }
46
- .intro-guide-list a {
47
- font-family: "JetBrains Mono", monospace;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
48
  font-size: 11px;
49
  font-weight: 500;
50
- letter-spacing: 0.12em;
51
  text-transform: uppercase;
 
 
 
 
 
 
 
 
 
 
52
  color: var(--ink);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
53
  text-decoration: none;
54
- border-bottom: 1px solid rgba(31, 31, 29, 0.25);
55
- padding-bottom: 1px;
56
- margin-right: 8px;
57
- white-space: nowrap;
58
- transition: color 0.15s, border-color 0.15s;
59
  }
60
- .intro-guide-list a:hover,
61
- .intro-guide-list a:focus-visible {
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
62
  color: var(--green);
63
- border-bottom-color: var(--green);
64
- outline: none;
65
  }
66
- .intro-guide-list a .arrow {
67
- margin-left: 4px;
68
- display: inline-block;
69
- font-size: 10px;
70
- letter-spacing: 0;
71
- transform: translateY(-1px);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
72
  }
73
 
74
  /* --- Primer header: visually delimits where the optional background
@@ -76,9 +363,10 @@
76
  section-title so it sits at the same typographic weight as the
77
  headings inside each sub-row. */
78
  .intro-primer-heading {
 
 
 
79
  margin: 24px 0 28px;
80
- padding-top: 32px;
81
- border-top: 1px solid #eee;
82
  }
83
  .intro-primer-heading .section-num { margin-bottom: 8px; }
84
  .intro-primer-heading h2 {
@@ -527,23 +815,43 @@
527
  max-width: 760px;
528
  }
529
  .pareto-chart {
 
 
 
 
 
 
530
  display: block;
531
  width: 100%;
532
  height: auto;
533
- background: #ffffff;
534
- border: 1px solid #cfcdbf;
535
- }
536
- .pareto-bg {
537
- fill: #ffffff;
538
  }
 
 
539
 
540
- /* Hairline frame at the same weight as the rest of the demo's
541
- section borders the chart reads as another paper card rather
542
- than a heavy matplotlib export. */
543
- .pareto-frame {
544
- fill: none;
545
  stroke: #cfcdbf;
546
  stroke-width: 1;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
547
  }
548
 
549
  /* Tick marks at the same hairline weight. Tick labels in JetBrains
@@ -556,7 +864,7 @@
556
  }
557
  .pareto-axis text {
558
  font-family: "JetBrains Mono", ui-monospace, monospace;
559
- font-size: 13px;
560
  fill: var(--ink-soft);
561
  font-feature-settings: "tnum";
562
  }
@@ -573,7 +881,7 @@
573
  "Throughput" carries the units in the muted ink-soft tone. */
574
  .pareto-axis-title {
575
  font-family: "Inter", "Helvetica Neue", sans-serif;
576
- font-size: 18px;
577
  font-weight: 600;
578
  fill: var(--ink);
579
  text-anchor: middle;
@@ -622,15 +930,20 @@
622
  fill: var(--ink);
623
  }
624
  .pareto-speedup-label {
625
- font-family: "Inter", "Helvetica Neue", sans-serif;
626
- font-size: 26px;
 
 
 
 
 
 
 
627
  font-weight: 700;
628
  fill: var(--ink);
629
  text-anchor: middle;
630
- paint-order: stroke;
631
- stroke: #ffffff;
632
- stroke-width: 6px;
633
- stroke-linejoin: round;
634
  }
635
 
636
  /* Data labels: plain text, no pill box. The paint-order stroke acts
 
25
  --promoter-soft: rgba(184, 134, 44, 0.18);
26
  }
27
 
28
+ /* --- Hero · split layout -----------------------------------------------
29
+ Two-column variant of .tab-lede used by the Intro tab only. Editorial
30
+ composition: a centered text block on the left (eyebrow + big title +
31
+ short paragraph) parked next to the Pareto figure on the right. The
32
+ rail is vertically centered against the chart (align-items: center)
33
+ so neither side reads as anchored to the top edge — the two halves
34
+ share visual weight rather than fighting over it. The green left
35
+ rail accent inherited from .tab-lede__rail is dropped here: the title
36
+ already carries the editorial heft on its own.
37
+ Below 960px we collapse to one column; the chart needs the full
38
+ viewport width on narrow screens to stay readable. */
39
+ .tab-lede--split {
40
+ display: grid;
41
+ /* 45 / 55 split between text rail and chart figure. The chart gets the
42
+ larger share so the Pareto plot keeps its decade ticks readable
43
+ without crushing the right-edge model labels. */
44
+ grid-template-columns: minmax(0, 40fr) minmax(0, 60fr);
45
+ column-gap: 56px;
46
+ align-items: center;
47
+ /* Hero claims at least half of the viewport so the announcement +
48
+ headline chart reads as a proper landing block, not a header that
49
+ visitors scroll past on the first pixel. The columns share this
50
+ height via align-items: center, so neither side anchors to the top
51
+ edge of the band. */
52
+ min-height: 50vh;
53
+ }
54
+ .tab-lede--split .tab-lede__rail {
55
+ /* Drop the sticky behaviour and the green left rail accent — the rail
56
+ centers itself vertically in its column (via the grid's
57
+ align-items: center on the parent) but the prose stays left-aligned
58
+ so the long title doesn't read as a centred slogan. */
59
+ position: static;
60
+ border-left: none;
61
+ padding: 0;
62
+ margin: 0 auto;
63
+ max-width: 520px;
64
+ text-align: left;
65
+ }
66
+ .tab-lede__title {
67
+ /* Editorial headline in JetBrains Mono — matches the technical-mono
68
+ register used elsewhere on the page (CARBON wordmark subtitle, the
69
+ §-section labels, the chart axis ticks). Mono is wider per glyph
70
+ than Inter so we drop one size step (36 → 32) to keep the line
71
+ length comparable. Weight 500 lands between the page body (400)
72
+ and the wordmark (700), giving the deck enough heft to read as
73
+ the headline of the tab without overpowering the chart on the right. */
74
+ margin: 0 0 22px;
75
+ font-family: "JetBrains Mono", ui-monospace, monospace;
76
+ font-size: 32px;
77
+ font-weight: 500;
78
+ letter-spacing: -0.01em;
79
+ line-height: 1.15;
80
+ color: var(--ink);
81
  }
82
+ .tab-lede--split .tab-lede__rail p {
83
+ margin: 0;
84
+ max-width: 460px;
85
+ font-size: 16px;
86
+ line-height: 1.55;
87
+ }
88
+ /* Figure caption that lives in the left-column rail rather than
89
+ under the chart on the right. Same size + colour as the original
90
+ under-chart figcaption (13px, #5b5b56) so it reads as a secondary,
91
+ descriptive note about the chart, a clear step down from the
92
+ 16px announcement paragraph above it. No top divider — the size
93
+ and saturation drop alone are enough to separate it from the
94
+ paragraph above.
95
+ Selector includes p.tab-lede__figcaption to outrank the more general
96
+ .tab-lede--split .tab-lede__rail p rule above (which would otherwise
97
+ force this caption back up to 16px). */
98
+ .tab-lede--split .tab-lede__rail p.tab-lede__figcaption {
99
+ margin-top: 18px;
100
+ max-width: 460px;
101
+ font-size: 13px;
102
+ line-height: 1.55;
103
+ color: #5b5b56;
104
+ }
105
+ .tab-lede--split .tab-lede__figure {
106
+ margin: 0;
107
+ max-width: none;
108
+ /* Keep the chart from ballooning past its readable size on very wide
109
+ viewports. The container.wide cap is 1200px, the rail eats ~520px
110
+ of that, leaving ~580px for the chart — well under 760px so the cap
111
+ is mostly a safety net. */
112
+ width: 100%;
113
+ }
114
+ .tab-lede--split .tab-lede__figure--pareto {
115
+ /* Drop the inherited .tab-lede__figure--pareto cap; the column already
116
+ sets the chart width. The chart's own viewBox handles aspect. */
117
+ max-width: none;
118
+ /* White card framing to match the .demo modules used everywhere else
119
+ on the page (see controls.css → .demo). Same background/border/padding
120
+ so the hero chart reads as part of the same family of figures rather
121
+ than a stray transparent SVG sitting on the paper background. */
122
+ background: #fff;
123
+ border: 1px solid #ddd;
124
+ padding: 24px;
125
+ }
126
+ @media (max-width: 960px) {
127
+ .tab-lede--split {
128
+ grid-template-columns: 1fr;
129
+ row-gap: 36px;
130
+ /* Stacked layout is naturally tall — drop the half-viewport floor
131
+ so the hero doesn't add extra dead space on top. */
132
+ min-height: auto;
133
+ }
134
+ .tab-lede--split .tab-lede__rail {
135
+ max-width: 720px;
136
+ }
137
+ .tab-lede--split .tab-lede__figure--pareto {
138
+ /* Tighter card padding on narrow viewports so the chart keeps its
139
+ readable inner width. */
140
+ padding: 16px;
141
+ }
142
+ .tab-lede__title { font-size: 30px; }
143
+ }
144
+ @media (max-width: 720px) {
145
+ /* The Pareto SVG sizes its text in viewBox user units (viewBox is
146
+ 0 0 1000 600). When the chart renders at viewport width on a phone
147
+ (~360px), viewBox units map to ~0.36 screen pixels each, so 15-unit
148
+ ticks would render at ~5px — illegible. Bump every text element up
149
+ in viewBox units so the on-screen size lands ~10-13px on mobile,
150
+ comparable to the desktop rendering. */
151
+ .pareto-axis text { font-size: 24px; }
152
+ .pareto-axis-title { font-size: 28px; }
153
+ .pareto-label { font-size: 20px; }
154
+ .pareto-point--highlight .pareto-label { font-size: 24px; }
155
+ .pareto-indicator-text { font-size: 16px; }
156
+ .pareto-speedup-label { font-size: 44px; }
157
+ }
158
+ @media (max-width: 600px) {
159
+ .tab-lede__title { font-size: 24px; }
160
+ .tab-lede--split .tab-lede__rail p { font-size: 15px; }
161
+ }
162
+ @media (max-width: 480px) {
163
+ /* Very-narrow phones: bump chart labels one more notch + tighten the
164
+ hero rail's max-width so the prose doesn't kiss the viewport edge. */
165
+ .pareto-axis text { font-size: 28px; }
166
+ .pareto-label { font-size: 24px; }
167
+ .pareto-point--highlight .pareto-label { font-size: 28px; }
168
+ .pareto-speedup-label { font-size: 52px; }
169
+ }
170
+
171
+ /* --- Site map · full-width independent band ----------------------------
172
+ Hand-off block between the release hero and the bio primer. Sits
173
+ edge-to-edge in its own paper tone (slightly recessed vs the page) so
174
+ the visitor reads it as a deliberate "where to go next" signpost
175
+ rather than another prose paragraph. Four numbered cards (01..04)
176
+ sit on a 4-column grid, each with a mono uppercase title, an arrow
177
+ (↓ for the in-page primer anchor, → for the cross-tab links), and a
178
+ one-line gloss. Hover lifts the card colour to the green accent and
179
+ nudges the arrow.
180
+ Anchors still feed tabs.js's hashchange listener, identical to the
181
+ previous .intro-guide-list anchors — only the styling and structure
182
+ change. */
183
+ .intro-sitemap {
184
+ margin: 64px 0 16px;
185
+ padding: 56px 32px 60px;
186
+ border-top: 1px solid var(--rule);
187
+ border-bottom: 1px solid var(--rule);
188
+ }
189
+ .intro-sitemap__inner {
190
+ max-width: 1200px;
191
+ margin: 0 auto;
192
+ }
193
+ /* Three-tier centered heading: small green mono eyebrow "Site map" above
194
+ a large Inter title "What's inside" above a one-line gloss. Reads as
195
+ an editorial signpost (FT/distill.pub style) rather than a flat label.
196
+ The stack is centered to mirror the symmetric four-card grid below. */
197
+ .intro-sitemap__heading {
198
+ text-align: center;
199
+ margin-bottom: 52px;
200
+ }
201
+ .intro-sitemap__eyebrow {
202
+ display: inline-block;
203
+ font-family: "JetBrains Mono", ui-monospace, monospace;
204
  font-size: 11px;
205
  font-weight: 500;
206
+ letter-spacing: 0.22em;
207
  text-transform: uppercase;
208
+ color: var(--green);
209
+ margin-bottom: 16px;
210
+ }
211
+ .intro-sitemap__title {
212
+ margin: 0;
213
+ font-family: "Inter", "Helvetica Neue", sans-serif;
214
+ font-size: 40px;
215
+ font-weight: 400;
216
+ letter-spacing: -0.02em;
217
+ line-height: 1.1;
218
  color: var(--ink);
219
+ }
220
+ .intro-sitemap__subtitle {
221
+ margin: 14px auto 0;
222
+ max-width: 520px;
223
+ font-family: "Inter", "Helvetica Neue", sans-serif;
224
+ font-size: 14px;
225
+ font-weight: 300;
226
+ line-height: 1.55;
227
+ color: var(--ink-soft);
228
+ }
229
+ @media (max-width: 720px) {
230
+ .intro-sitemap__heading { margin-bottom: 36px; }
231
+ .intro-sitemap__title { font-size: 30px; }
232
+ }
233
+ .intro-sitemap__steps {
234
+ list-style: none;
235
+ padding: 0;
236
+ margin: 0;
237
+ display: grid;
238
+ grid-template-columns: repeat(4, 1fr);
239
+ gap: 0;
240
+ }
241
+ .intro-sitemap__step { display: flex; }
242
+ .intro-sitemap__link {
243
+ display: flex;
244
+ flex-direction: column;
245
+ width: 100%;
246
+ padding: 8px 28px 8px 0;
247
+ margin-right: 28px;
248
+ border-right: 1px solid var(--rule);
249
  text-decoration: none;
250
+ color: inherit;
251
+ transition: color 0.18s ease;
 
 
 
252
  }
253
+ .intro-sitemap__step:last-child .intro-sitemap__link {
254
+ border-right: none;
255
+ margin-right: 0;
256
+ padding-right: 0;
257
+ }
258
+ .intro-sitemap__num {
259
+ font-family: "JetBrains Mono", ui-monospace, monospace;
260
+ font-size: 11px;
261
+ font-weight: 500;
262
+ letter-spacing: 0.18em;
263
+ color: var(--ink-faint);
264
+ margin-bottom: 28px;
265
+ transition: color 0.18s ease;
266
+ }
267
+ .intro-sitemap__label {
268
+ display: flex;
269
+ align-items: baseline;
270
+ justify-content: space-between;
271
+ gap: 12px;
272
+ margin-bottom: 14px;
273
+ font-family: "JetBrains Mono", ui-monospace, monospace;
274
+ font-size: 18px;
275
+ font-weight: 500;
276
+ letter-spacing: 0.02em;
277
+ line-height: 1.1;
278
+ color: var(--ink);
279
+ transition: color 0.18s ease;
280
+ }
281
+ .intro-sitemap__title { white-space: nowrap; }
282
+ .intro-sitemap__arrow {
283
+ font-size: 18px;
284
+ font-weight: 400;
285
  color: var(--green);
286
+ transition: transform 0.18s ease, color 0.18s ease;
287
+ flex-shrink: 0;
288
  }
289
+ .intro-sitemap__desc {
290
+ margin: 0;
291
+ font-family: "Inter", "Helvetica Neue", sans-serif;
292
+ font-size: 13px;
293
+ font-weight: 300;
294
+ line-height: 1.55;
295
+ color: var(--ink-soft);
296
+ }
297
+ .intro-sitemap__link:hover,
298
+ .intro-sitemap__link:focus-visible { outline: none; }
299
+ .intro-sitemap__link:hover .intro-sitemap__num,
300
+ .intro-sitemap__link:focus-visible .intro-sitemap__num,
301
+ .intro-sitemap__link:hover .intro-sitemap__label,
302
+ .intro-sitemap__link:focus-visible .intro-sitemap__label,
303
+ .intro-sitemap__link:hover .intro-sitemap__arrow,
304
+ .intro-sitemap__link:focus-visible .intro-sitemap__arrow {
305
+ color: var(--green);
306
+ }
307
+ /* Move the arrow on the directional axis: → slides right, ↓ slides down.
308
+ Each step's nth-child sets which one applies — the first card has the
309
+ ↓ (in-page primer), the rest are cross-tab → links. */
310
+ .intro-sitemap__step:first-child .intro-sitemap__link:hover .intro-sitemap__arrow,
311
+ .intro-sitemap__step:first-child .intro-sitemap__link:focus-visible .intro-sitemap__arrow {
312
+ transform: translateY(3px);
313
+ }
314
+ .intro-sitemap__step:not(:first-child) .intro-sitemap__link:hover .intro-sitemap__arrow,
315
+ .intro-sitemap__step:not(:first-child) .intro-sitemap__link:focus-visible .intro-sitemap__arrow {
316
+ transform: translateX(4px);
317
+ }
318
+ @media (max-width: 960px) {
319
+ .intro-sitemap { padding: 40px 24px 44px; }
320
+ .intro-sitemap__steps {
321
+ grid-template-columns: repeat(2, 1fr);
322
+ row-gap: 28px;
323
+ }
324
+ .intro-sitemap__link {
325
+ padding: 0 20px 0 0;
326
+ margin-right: 20px;
327
+ }
328
+ /* Right column items lose the right border (they're the row-end). */
329
+ .intro-sitemap__step:nth-child(2n) .intro-sitemap__link {
330
+ border-right: none;
331
+ margin-right: 0;
332
+ padding-right: 0;
333
+ }
334
+ .intro-sitemap__num { margin-bottom: 18px; }
335
+ }
336
+ @media (max-width: 600px) {
337
+ .intro-sitemap__steps {
338
+ grid-template-columns: 1fr;
339
+ row-gap: 0;
340
+ }
341
+ .intro-sitemap__link {
342
+ padding: 22px 0;
343
+ margin-right: 0;
344
+ border-right: none;
345
+ border-bottom: 1px solid var(--rule);
346
+ }
347
+ .intro-sitemap__step:nth-child(2n) .intro-sitemap__link {
348
+ /* override the 2-col mobile rule above */
349
+ border-right: none;
350
+ }
351
+ .intro-sitemap__step:last-child .intro-sitemap__link {
352
+ border-bottom: none;
353
+ padding-bottom: 4px;
354
+ }
355
+ .intro-sitemap__step:first-child .intro-sitemap__link {
356
+ padding-top: 4px;
357
+ }
358
+ .intro-sitemap__num { margin-bottom: 12px; }
359
  }
360
 
361
  /* --- Primer header: visually delimits where the optional background
 
363
  section-title so it sits at the same typographic weight as the
364
  headings inside each sub-row. */
365
  .intro-primer-heading {
366
+ /* No top border / padding here: the .intro-sitemap band above already
367
+ provides a hairline bottom border + breathing room as the visual
368
+ separator between the site map and the primer below. */
369
  margin: 24px 0 28px;
 
 
370
  }
371
  .intro-primer-heading .section-num { margin-bottom: 8px; }
372
  .intro-primer-heading h2 {
 
815
  max-width: 760px;
816
  }
817
  .pareto-chart {
818
+ /* The white card frame is now provided by the parent
819
+ .tab-lede__figure--pareto (matching the .demo modules used on the
820
+ rest of the page), so the SVG itself stays transparent — the inner
821
+ plot fill (.pareto-bg) and frame stroke (.pareto-frame) below are
822
+ left as no-ops to avoid double-drawing a second nested box inside
823
+ the figure card. */
824
  display: block;
825
  width: 100%;
826
  height: auto;
 
 
 
 
 
827
  }
828
+ .pareto-bg { fill: none; }
829
+ .pareto-frame { display: none; }
830
 
831
+ /* Axis lines · the L-shape (left + bottom) that replaces the dropped
832
+ rectangular frame. Same hairline weight as the old frame so the chart
833
+ keeps its editorial-paper register. */
834
+ .pareto-axis-lines line {
 
835
  stroke: #cfcdbf;
836
  stroke-width: 1;
837
+ stroke-linecap: square;
838
+ }
839
+
840
+ /* Figcaption tag · small mono-uppercase eyebrow that prefixes the
841
+ descriptive sentence below the chart. Signals "this caption refers
842
+ to the figure above" without bloating the prose itself, and ties
843
+ the caption back to the technical-mono register used by the chart's
844
+ axis ticks and the page's section labels. */
845
+ .pareto-figcaption-tag {
846
+ display: inline;
847
+ margin-right: 10px;
848
+ font-family: "JetBrains Mono", ui-monospace, monospace;
849
+ font-size: 10px;
850
+ font-weight: 500;
851
+ letter-spacing: 0.18em;
852
+ text-transform: uppercase;
853
+ color: var(--green);
854
+ white-space: nowrap;
855
  }
856
 
857
  /* Tick marks at the same hairline weight. Tick labels in JetBrains
 
864
  }
865
  .pareto-axis text {
866
  font-family: "JetBrains Mono", ui-monospace, monospace;
867
+ font-size: 15px;
868
  fill: var(--ink-soft);
869
  font-feature-settings: "tnum";
870
  }
 
881
  "Throughput" carries the units in the muted ink-soft tone. */
882
  .pareto-axis-title {
883
  font-family: "Inter", "Helvetica Neue", sans-serif;
884
+ font-size: 21px;
885
  font-weight: 600;
886
  fill: var(--ink);
887
  text-anchor: middle;
 
930
  fill: var(--ink);
931
  }
932
  .pareto-speedup-label {
933
+ /* Mono + bold, sized so the "275×" reads as the chart's editorial
934
+ headline. The label sits on-axis (y matches the arrow shaft) and
935
+ the shaft is split in two segments around it (see demo.html), so
936
+ no paint-order halo is needed: the number occupies the gap
937
+ between the two arrow halves rather than overlaying a continuous
938
+ line. dominant-baseline: middle vertically centres the glyphs
939
+ on the same y-coord as the shaft endpoints. */
940
+ font-family: "JetBrains Mono", ui-monospace, monospace;
941
+ font-size: 32px;
942
  font-weight: 700;
943
  fill: var(--ink);
944
  text-anchor: middle;
945
+ dominant-baseline: middle;
946
+ letter-spacing: -0.02em;
 
 
947
  }
948
 
949
  /* Data labels: plain text, no pill box. The paint-order stroke acts
demo.html CHANGED
@@ -269,16 +269,32 @@
269
  section--two-col / .demo for visual parity with §1-§7. -->
270
  <div class="tab-panel active section--intro" id="panel-intro" data-tab="intro">
271
 
272
- <div class="tab-lede">
 
 
 
 
 
273
  <div class="tab-lede__rail">
274
- <span class="tab-lede__eyebrow">Release</span>
275
  <p>
276
- Today we're releasing <strong>Carbon</strong>, the fastest open-source foundation model
277
- for DNA. Three model sizes (<em>500M</em>, <em>3B</em>, and <em>8B</em> parameters),
278
- shipping with the full training code, the data pipeline, and the model weights.
279
- Everything is open source on the Hugging Face Hub.
280
  </p>
281
- <!-- Pareto chart, drawn natively as inline SVG so the figure scales
 
 
 
 
 
 
 
 
 
 
 
282
  sharply, picks up the page's typography, and can be tuned in
283
  CSS without a matplotlib re-export. Source data lives in
284
  pareto/pareto_data.csv; geometry mirrors the matplotlib
@@ -293,9 +309,20 @@
293
  Carbon points scale up + use a heavier label per the source
294
  script's HIGHLIGHT_LOGO_SCALE so the eye lands on them. -->
295
  <figure class="tab-lede__figure tab-lede__figure--pareto">
 
 
 
 
 
 
 
 
 
 
 
296
  <svg
297
  class="pareto-chart"
298
- viewBox="0 0 1000 600"
299
  xmlns="http://www.w3.org/2000/svg"
300
  role="img"
301
  aria-labelledby="pareto-title pareto-desc"
@@ -306,6 +333,15 @@
306
  <!-- Plot interior. -->
307
  <rect class="pareto-bg" x="100" y="30" width="870" height="470"/>
308
 
 
 
 
 
 
 
 
 
 
309
  <!-- Y axis: linear win-rate %, ticks at 0/20/40/60/80/100. The
310
  plot range runs −12..108 (matches matplotlib padding) so
311
  the data points have headroom above 100 and below 0 for
@@ -369,14 +405,18 @@
369
  <text class="pareto-indicator-text" x="35" y="20">faster</text>
370
  </g>
371
 
372
- <!-- 275× speedup arrow: starts just right of the Evo2 7B label
373
- pill and lands just left of the Carbon 3B logo. y placed
374
- between the two points (Evo2 7B at 64.3%, Carbon 3B at
375
- 59.5%) so it reads as level with both. -->
 
 
 
376
  <g class="pareto-speedup">
377
- <line x1="290" y1="215" x2="822" y2="215"/>
 
378
  <polygon points="836,215 820,206 820,224"/>
379
- <text class="pareto-speedup-label" x="556" y="200">275×</text>
380
  </g>
381
 
382
  <!-- Data points. Coordinates baked in from pareto_data.csv:
@@ -427,37 +467,82 @@
427
  <text class="pareto-label" x="871.0" y="255">Carbon 3B</text>
428
  </g>
429
 
430
- <!-- Axis titles. Y title rotated -90 along the left margin, X
431
- title + italic "Base pairs per second" subtitle below. -->
 
 
 
432
  <text class="pareto-axis-title" transform="translate(34 265) rotate(-90)">Win rate (%)</text>
433
- <text class="pareto-axis-title" x="535" y="558">Throughput</text>
434
- <text class="pareto-axis-subtitle" x="535" y="582">Base pairs per second</text>
435
  </svg>
436
- <figcaption>Throughput (base pairs per second, log scale) vs win rate across open DNA foundation models. Carbon 3B matches Evo2 7B's win rate at roughly 275× the throughput.</figcaption>
437
  </figure>
438
- </div>
439
  </div>
440
 
441
- <div class="container wide">
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
442
 
443
- <!-- Tab-navigation list. Anchor hrefs feed into tabs.js's hashchange
444
- listener, which calls setTab on the matched tab name, same path
445
- the banner buttons take. Mono / uppercase styling mirrors the
446
- banner-links pattern up in the hero. -->
447
- <ul class="intro-guide-list">
448
- <li><a href="#primer">Intro<span class="arrow" aria-hidden="true">↓</span></a>
449
- continue reading to learn the basics of genetics.</li>
450
- <li><a href="#dna-lab">DNA Lab<span class="arrow" aria-hidden="true">→</span></a>
451
- explore what the model can do, with live interactions against the 3B checkpoint.</li>
452
- <li><a href="#recipe">Carbon Recipe<span class="arrow" aria-hidden="true">→</span></a>
453
- learn how Carbon was trained: tokenizer, loss, dataset, results.</li>
454
- <li><a href="#sandbox">Sandbox<span class="arrow" aria-hidden="true">→</span></a>
455
- run Carbon on your own DNA sequences.</li>
456
- </ul>
457
 
458
  <!-- Optional bio primer below. Subsections are §1-§5 within this tab.
459
- id="primer" so the "Intro → continue reading…" link in the guide
460
- list above can scroll-anchor here via tabs.js's SECTION_TO_TAB
461
  routing. -->
462
  <div class="intro-primer-heading" id="primer">
463
  <div class="section-num">Background</div>
 
269
  section--two-col / .demo for visual parity with §1-§7. -->
270
  <div class="tab-panel active section--intro" id="panel-intro" data-tab="intro">
271
 
272
+ <!-- Hero: two-column split. Left rail (eyebrow + announcement) is sticky
273
+ so the message stays in view while the visitor scrolls past the
274
+ Pareto figure on the right. The figure was previously stacked
275
+ beneath the text inside .tab-lede__rail; the split layout pulls
276
+ it out as a sibling so the two read as anchor + evidence. -->
277
+ <div class="tab-lede tab-lede--split">
278
  <div class="tab-lede__rail">
279
+ <h2 class="tab-lede__title">The fastest open-source foundation model for DNA.</h2>
280
  <p>
281
+ Today we're releasing <strong>Carbon</strong> three model sizes
282
+ (<em>500M</em>, <em>3B</em>, and <em>8B</em> parameters), shipping with the full
283
+ training code, the data pipeline, and the model weights.
284
+ All open-source on the Hugging Face Hub.
285
  </p>
286
+ <!-- Figure caption pulled out of the right-column .tab-lede__figure
287
+ so the descriptive sentence sits under the announcement prose
288
+ instead of dangling below the chart. The visual flow is
289
+ lede → context paragraph → figure caption, all in the same
290
+ column; the chart on the right reads as the visual evidence
291
+ the prose is referring to. -->
292
+ <p class="tab-lede__figcaption">
293
+ <span class="pareto-figcaption-tag">Fig · Benchmark</span>
294
+ Throughput (base pairs per second, log scale) vs win rate across open DNA foundation models. Carbon 3B matches Evo2 7B's win rate at roughly 275× the throughput.
295
+ </p>
296
+ </div>
297
+ <!-- Pareto chart, drawn natively as inline SVG so the figure scales
298
  sharply, picks up the page's typography, and can be tuned in
299
  CSS without a matplotlib re-export. Source data lives in
300
  pareto/pareto_data.csv; geometry mirrors the matplotlib
 
309
  Carbon points scale up + use a heavier label per the source
310
  script's HIGHLIGHT_LOGO_SCALE so the eye lands on them. -->
311
  <figure class="tab-lede__figure tab-lede__figure--pareto">
312
+ <!-- viewBox tightly cropped around the actual visible content
313
+ (rotated "Win rate (%)" Y title, "100" Y tick label, rightmost
314
+ data label "GENERator-v2 1.2B", and "Throughput" X title
315
+ descender). No internal margin is left inside the SVG itself —
316
+ the visual breathing around the chart is provided entirely by
317
+ the parent .tab-lede__figure--pareto's 24px card padding (see
318
+ section-intro.css), otherwise we'd be stacking SVG margins
319
+ onto CSS padding and the chart would read as floating inside
320
+ an oversized frame. The data coordinates further down still
321
+ use the original 1000×600 reference grid; only the visible
322
+ window is shifted/shrunk. -->
323
  <svg
324
  class="pareto-chart"
325
+ viewBox="20 50 910 530"
326
  xmlns="http://www.w3.org/2000/svg"
327
  role="img"
328
  aria-labelledby="pareto-title pareto-desc"
 
333
  <!-- Plot interior. -->
334
  <rect class="pareto-bg" x="100" y="30" width="870" height="470"/>
335
 
336
+ <!-- Axis lines · L-shape (left + bottom) bordering the data
337
+ area. The full rectangular frame is dropped so the chart
338
+ sits transparent on the page; just the two lines that
339
+ anchor the ticks remain, the editorial chart minimum. -->
340
+ <g class="pareto-axis-lines">
341
+ <line x1="100" y1="30" x2="100" y2="500"/>
342
+ <line x1="100" y1="500" x2="970" y2="500"/>
343
+ </g>
344
+
345
  <!-- Y axis: linear win-rate %, ticks at 0/20/40/60/80/100. The
346
  plot range runs −12..108 (matches matplotlib padding) so
347
  the data points have headroom above 100 and below 0 for
 
405
  <text class="pareto-indicator-text" x="35" y="20">faster</text>
406
  </g>
407
 
408
+ <!-- 275× speedup callout: a single horizontal arrow from
409
+ just-right-of Evo2 7B to just-left-of Carbon 3B, split in
410
+ two segments around a centred "275×" label that sits
411
+ on-axis. The label cuts the shaft instead of floating
412
+ above it, so the number reads as part of the arrow
413
+ itself. y=215 lands between Evo2 7B (64.3%) and Carbon
414
+ 3B (59.5%) so the arrow reads level with both endpoints. -->
415
  <g class="pareto-speedup">
416
+ <line x1="290" y1="215" x2="508" y2="215"/>
417
+ <line x1="618" y1="215" x2="822" y2="215"/>
418
  <polygon points="836,215 820,206 820,224"/>
419
+ <text class="pareto-speedup-label" x="563" y="218">275×</text>
420
  </g>
421
 
422
  <!-- Data points. Coordinates baked in from pareto_data.csv:
 
467
  <text class="pareto-label" x="871.0" y="255">Carbon 3B</text>
468
  </g>
469
 
470
+ <!-- Axis titles. Y title rotated -90 along the left margin,
471
+ X title centred under the X axis. The italic "Base pairs
472
+ per second" subtitle that used to sit under "Throughput"
473
+ was removed: the units carry less weight than the
474
+ headline measure, and the chart reads cleaner without it. -->
475
  <text class="pareto-axis-title" transform="translate(34 265) rotate(-90)">Win rate (%)</text>
476
+ <text class="pareto-axis-title" x="535" y="572">Throughput</text>
 
477
  </svg>
 
478
  </figure>
 
479
  </div>
480
 
481
+ <!-- Site map · full-width independent band that signposts the four
482
+ destinations of the page (Intro primer / DNA Lab / Carbon Recipe /
483
+ Sandbox). Pulled out of .container.wide so the band can extend
484
+ edge-to-edge with its own paper tone, reading as the deliberate
485
+ hand-off between the release lede above and the bio primer below.
486
+ Each step is a numbered card with a mono uppercase label and a
487
+ short gloss; the anchors still feed tabs.js's hashchange listener
488
+ (#primer scroll-anchors here, #dna-lab/#recipe/#sandbox switch tab). -->
489
+ <nav class="intro-sitemap" aria-label="Site map">
490
+ <div class="intro-sitemap__inner">
491
+ <header class="intro-sitemap__heading">
492
+ <span class="intro-sitemap__eyebrow">Site map</span>
493
+ <h2 class="intro-sitemap__title">What's inside</h2>
494
+ <p class="intro-sitemap__subtitle">Four ways to explore Carbon, from background to hands-on.</p>
495
+ </header>
496
+ <ol class="intro-sitemap__steps">
497
+ <li class="intro-sitemap__step">
498
+ <a class="intro-sitemap__link" href="#primer">
499
+ <span class="intro-sitemap__num">01</span>
500
+ <span class="intro-sitemap__label">
501
+ <span class="intro-sitemap__title">Intro</span>
502
+ <span class="intro-sitemap__arrow" aria-hidden="true">↓</span>
503
+ </span>
504
+ <span class="intro-sitemap__desc">A short primer on the basics of genetics — the alphabet Carbon reads.</span>
505
+ </a>
506
+ </li>
507
+ <li class="intro-sitemap__step">
508
+ <a class="intro-sitemap__link" href="#dna-lab">
509
+ <span class="intro-sitemap__num">02</span>
510
+ <span class="intro-sitemap__label">
511
+ <span class="intro-sitemap__title">DNA Lab</span>
512
+ <span class="intro-sitemap__arrow" aria-hidden="true">→</span>
513
+ </span>
514
+ <span class="intro-sitemap__desc">Live interactions with the 3B checkpoint: explore what the model can do.</span>
515
+ </a>
516
+ </li>
517
+ <li class="intro-sitemap__step">
518
+ <a class="intro-sitemap__link" href="#recipe">
519
+ <span class="intro-sitemap__num">03</span>
520
+ <span class="intro-sitemap__label">
521
+ <span class="intro-sitemap__title">Carbon Recipe</span>
522
+ <span class="intro-sitemap__arrow" aria-hidden="true">→</span>
523
+ </span>
524
+ <span class="intro-sitemap__desc">How Carbon was trained: tokenizer, loss, dataset, and results.</span>
525
+ </a>
526
+ </li>
527
+ <li class="intro-sitemap__step">
528
+ <a class="intro-sitemap__link" href="#sandbox">
529
+ <span class="intro-sitemap__num">04</span>
530
+ <span class="intro-sitemap__label">
531
+ <span class="intro-sitemap__title">Sandbox</span>
532
+ <span class="intro-sitemap__arrow" aria-hidden="true">→</span>
533
+ </span>
534
+ <span class="intro-sitemap__desc">Run Carbon on your own DNA sequences, end-to-end.</span>
535
+ </a>
536
+ </li>
537
+ </ol>
538
+ </div>
539
+ </nav>
540
 
541
+ <div class="container wide">
 
 
 
 
 
 
 
 
 
 
 
 
 
542
 
543
  <!-- Optional bio primer below. Subsections are §1-§5 within this tab.
544
+ id="primer" so the "Intro → continue reading…" link in the
545
+ site map above can scroll-anchor here via tabs.js's SECTION_TO_TAB
546
  routing. -->
547
  <div class="intro-primer-heading" id="primer">
548
  <div class="section-num">Background</div>