GitHub Blog kaynak akışındaki geliştirici içeriği aşağıdadır:
[QUOTE]
Providing developers the best model for the task at hand has always been our goal. Earlier this year, we made that easier by launching Auto model selection, which reviews your task and matches it to the best-suited model for that task.
Today, we’re introducing Project HydraFusion, a research preview that delivers frontier intelligence through runtime orchestration. It creates a full execution plan, choosing from models across multiple providers to draft, critique and revise, or cascade to more powerful models to complete your task.
HydraFusion fills a key role in our overall strategy to deliver automated semantic routing between local, cloud, and compound models. For developers, that complexity stays behind the scenes: you select HydraFusion like any other model, and it chooses a workflow that balances performance, cost, and latency for each task.
Now available as a research preview
HydraFusion is available to users on all GitHub Copilot plans through /experimental in GitHub Copilot CLI. Usage is based on the tokens consumed by the models HydraFusion uses, priced at each model’s standard rate.
To try HydraFusion in Copilot CLI:
• Run /update to install the latest version
• Run /experimental on
• Run /model, then select HydraFusion (Research Preview)
Please post feedback in the GitHub Community.
HydraFusion treats workflow selection as an optimization problem. It uses capability signals for reasoning, code generation, debugging, and tool use to select the most efficient execution pattern to meet the quality bar.
For each request, HydraFusion currently chooses one of three execution patterns:
• Single. One selected model solves the task directly.
• Cascade. An efficient model drafts a solution and a quality gate decides whether to accept it or escalate to a stronger model.
• Critique. One model drafts a result, an independent read-only critic from a different model family reviews it (following the same review pattern as Rubber Duck), and the drafting model revises once.

Figure 1. HydraFusion architecture
Each pattern addresses a different quality-to-cost trade-off. Single preserves speed and efficiency when one model can solve the task directly. Cascade gives an efficient model the first attempt while retaining a path to stronger inference when the candidate does not clear the acceptance gate. Critique adds an independent perspective for tasks where review is more useful than another unaided attempt.
In offline evaluations across three agentic coding benchmarks, HydraFusion consistently demonstrated frontier-level quality with substantial estimated cost savings. On TerminalBench 2.1, it improved verified task quality by 4.9 percentage points at 67% lower estimated cost compared with Claude Opus 5.
Let’s dive into the approach, the results, and the benchmarks.
Adaptive multi-model orchestration
Developers already coordinate models manually: choosing one for a task, asking another to review the work, or escalating a difficult problem to a more capable model. HydraFusion brings that familiar process into the runtime. You choose HydraFusion once and stay focused on your task while it manages the models and workflow behind the scenes.
The key is selectivity. Some coding tasks can be solved directly, while others benefit from review, revision, or escalation. HydraFusion evaluates each request and chooses the least complex workflow expected to meet its needs, using additional model calls only when they are likely to improve the result. This adaptive approach balances quality, cost, and latency across models.
As the model frontier advances, so does HydraFusion. When new models become available in GitHub Copilot, we can evaluate and incorporate them into its model pool, bringing their strengths to the tasks best suited to them.
Building HydraFusion
Turning adaptive multi-model orchestration into one dependable coding experience requires careful control of execution, review, cost, and repository state. HydraFusion is built around five operating principles:
• Complete accounting. Aggregate cost and usage across every workflow leg, including drafting, critique, revision, escalation, retry, and fallback.
• Bounded execution. Give each leg explicit timeout and cancellation behavior to keep execution and cost within defined limits.
• Isolated review. Run review steps in isolated, tool-less contexts, while solver steps use the shared workspace and normal permission-aware agent loop. This allows models to assess the work independently without modifying the repository.
• Fail-safe application. Apply no patch when the workflow is cancelled or fails validation, preventing incomplete changes from reaching the repository.
• Validated routing. Verify workflow definitions, model bindings, fallback behavior, and model availability before execution begins.
Together, these principles make multi-model orchestration practical for repository-level work. Internally, the runtime records the role, outcome, cost, latency, and diagnostics of each leg so the workflow can be understood after execution. Externally, the developer receives one coherent response and one permission-aware change set.
Showing progress without showing unfinished work
• Today: HydraFusion shows workflow stages but holds intermediate drafts until it returns one coherent result.
• Why: Those drafts may be reviewed, revised, or discarded, so showing them live could make unfinished work appear final.
• What we’re learning: Waiting without enough visibility is a real trade-off for developers.
• Next: We’re actively exploring better progress updates, guided by feedback from the research preview.
Benchmarking results
Fixed HydraFusion policies were evaluated across three agentic coding benchmarks — TerminalBench 2.1, DeepSWE, and CheckpointBench, our internal benchmark based on real GitHub Copilot sessions — using Claude Opus 5 and GPT-5.6 Sol as comparison baselines. Each policy used the same task inputs, tools, execution limits, pricing assumptions, grading conditions, and treatment of missing results. The evaluation measured verified task quality, which is the share of tasks confirmed as correctly answered, and the complete estimated workflow cost. Cost accounting included every invoked leg, such as drafting, critique, revision, escalation, retry, and fallback. The results below show the best tuned HydraFusion configuration.
Benchmarks Cost vs. Opus 5 Quality vs. Opus 5 TerminalBench 2.167% lower+4.9 points DeepSWE 36% lower -1.5 points CheckpointBench 65% lower-0.1 points Table 1. HydraFusion quality and cost across three agentic benchmarks, relative to Opus 5.
These controlled offline results are specific to the evaluated benchmark revisions, workflow configurations, model pool, and pricing assumptions, with all models evaluated at the same medium reasoning level. Through this research preview, we’ll validate how these results translate to real developer workloads and use the findings to further optimize HydraFusion for production quality, latency, reliability, caching efficiency, cost, and safety.
TerminalBench 2.1
TerminalBench 2.1 evaluates coding agents on complex, multi-step tasks in terminal environments.
Figure 2 compares HydraFusion and Opus 5 across verified task quality and estimated workflow cost.
.ghchart {
/* Theme tokens */
--ghc-text: #1f2328;
--ghc-muted: #656d76;
--ghc-grid: #d0d7de;
--ghc-axis: #59636e;
--ghc-card-bg: #ffffff;
--ghc-card-border: #d0d7de;
--ghc-divider: #b6bfb8;
--ghc-guide: #afb8c1;
/* GitHub brand font — matches the github.blog stack (Mona Sans, with
the blog's metric fallback) so it renders identically in-context. */
font-family: "Mona Sans", "Mona Sans Fallback", -apple-system, "system-ui",
"Segoe UI", Helvetica, Arial, sans-serif;
-webkit-font-smoothing: antialiased;
text-rendering: optimizeLegibility;
position: relative;
box-sizing: border-box;
width: 100%;
max-width: 699px;
margin: 0 auto;
color: var(--ghc-text);
line-height: 1.4;
}
.ghchart *,
.ghchart *::before,
.ghchart *::after { box-sizing: border-box; }
/* ------------------------------------------------------------------ */
/* Header + legend */
/* ------------------------------------------------------------------ */
.ghchart__header {
display: flex;
justify-content: flex-end;
align-items: center;
min-height: 24px;
margin-bottom: 4px;
}
.ghchart__legend {
display: flex;
align-items: center;
gap: 20px;
flex-wrap: wrap;
justify-content: flex-end;
}
.ghchart__legend-item {
display: inline-flex;
align-items: center;
gap: 8px;
font-size: clamp(14px, 1.9vw, 17px);
font-weight: 600;
color: var(--ghc-muted);
white-space: nowrap;
}
.ghchart__swatch {
width: 14px;
height: 14px;
border-radius: 4px;
flex: 0 0 auto;
}
/* ------------------------------------------------------------------ */
/* Plot (SVG) */
/* ------------------------------------------------------------------ */
.ghchart__plot {
display: block;
width: 100%;
height: auto;
overflow: visible;
touch-action: pan-y;
}
.ghchart__gridline {
stroke: var(--ghc-grid);
stroke-width: 1;
stroke-dasharray: 2 6;
stroke-linecap: round;
opacity: 0.9;
}
.ghchart__axislabel {
fill: var(--ghc-axis);
font-size: 22px;
font-weight: 500;
}
.ghchart__axislabel--x { font-weight: 600; }
.ghchart__line {
fill: none;
stroke-linecap: round;
stroke-linejoin: round;
}
.ghchart__dot { transition: r 0.12s ease; }
/* Dashed threshold line (e.g. a target/reference level) + floating label.
Stroke color is set inline per chart; dash + label styling live here. */
.ghchart__threshold {
stroke-width: 2;
stroke-dasharray: 7 6;
stroke-linecap: round;
opacity: 0.9;
}
.ghchart__threshold-label {
font-size: 20px;
font-weight: 700;
letter-spacing: -0.01em;
}
/* Hatched band marking a partial / projected trailing region. */
.ghchart__projection { pointer-events: none; }
.ghchart__guide {
stroke: var(--ghc-guide);
stroke-width: 1.5;
stroke-dasharray: 4 4;
opacity: 0;
transition: opacity 0.12s ease;
}
.ghchart__hit { fill: transparent; }
/* ------------------------------------------------------------------ */
/* Scatter plot */
/* ------------------------------------------------------------------ */
.ghchart__gridline--v { /* vertical gridlines reuse the dashed grid style */ }
.ghchart__axistitle {
fill: var(--ghc-muted);
font-size: 20px;
font-weight: 600;
letter-spacing: -0.01em;
}
/* Dotted reference crosshair through the "reference" point. Stroke color
is set inline (tracks the reference series color). */
.ghchart__crosshair {
stroke-width: 2;
stroke-dasharray: 2 7;
stroke-linecap: round;
opacity: 0.85;
pointer-events: none;
}
.ghchart__halo { opacity: 0.18; pointer-events: none; }
.ghchart__pt { transition: r 0.12s ease; }
.ghchart__pt-label {
fill: var(--ghc-muted);
font-size: 19px;
font-weight: 600;
letter-spacing: -0.01em;
pointer-events: none;
}
.ghchart__pt-label--strong { font-size: 21px; font-weight: 700; }
.ghchart__pthit { fill: transparent; cursor: default; outline: none; }
.ghchart__pthit:focus-visible {
outline: 2px solid var(--ghc-axis);
outline-offset: 1px;
}
.ghchart__swatch--dot { border-radius: 50%; }
/* ------------------------------------------------------------------ */
/* Trend chart (dated time-series with narrative headline) */
/* ------------------------------------------------------------------ */
.ghchart__headline {
font-size: clamp(17px, 2.3vw, 20px);
line-height: 1.32;
font-weight: 700;
letter-spacing: -0.01em;
color: var(--ghc-text);
margin: 0 0 8px;
}
/* Trend legend sits left-aligned under the headline/subtitle. */
.ghchart__legend--trend {
justify-content: flex-start;
margin: 2px 0 14px;
}
.ghchart__glyph { flex: 0 0 auto; }
.ghchart__glyph--square {
width: 13px; height: 13px; border-radius: 2px;
}
.ghchart__glyph--diamond {
width: 12px; height: 12px; border: 1.5px solid; transform: rotate(45deg);
}
.ghchart__glyph--line {
width: 20px; height: 0; border-top: 2px dashed; border-radius: 0;
}
/* Phase background columns + separators + top labels. */
.ghchart__phase { fill: #f6f8fa; opacity: 0.55; }
.ghchart__phase--alt { fill: #ffffff; opacity: 0; }
.ghchart__phase-sep {
stroke: var(--ghc-grid); stroke-width: 1; stroke-dasharray: 2 5; opacity: 0.8;
}
.ghchart__phase-label {
fill: var(--ghc-muted);
font-size: 15px;
font-weight: 700;
letter-spacing: 0.06em;
}
.ghchart__phase-avg {
font-size: 16px;
font-weight: 700;
letter-spacing: -0.01em;
}
/* Dashed trend line + ± noise band + slope label. */
.ghchart__trendband { opacity: 0.12; pointer-events: none; }
.ghchart__trendline {
stroke-width: 2.5;
stroke-dasharray: 8 6;
stroke-linecap: round;
fill: none;
}
.ghchart__slope {
font-size: 20px;
font-weight: 700;
letter-spacing: -0.01em;
}
/* Horizontal reference line + label. */
.ghchart__refline {
stroke-width: 2;
stroke-dasharray: 7 6;
stroke-linecap: round;
opacity: 0.85;
}
.ghchart__reflabel {
font-size: 16px;
font-weight: 700;
letter-spacing: -0.01em;
}
/* Data squares — values reveal on hover only. */
.ghchart__square { transition: width 0.12s ease, height 0.12s ease, x 0.12s ease, y 0.12s ease; }
.ghchart__squarehit { fill: transparent; cursor: default; outline: none; }
.ghchart__squarehit:focus-visible {
outline: 2px solid var(--ghc-axis);
outline-offset: 1px;
}
/* Invalid-run markers + leader-line annotations. */
.ghchart__invalid { stroke-width: 2; }
.ghchart__invalid-x { stroke-width: 2; stroke-linecap: round; }
.ghchart__leader { stroke-width: 1; stroke-dasharray: 2 3; opacity: 0.8; }
.ghchart__leader--callout { stroke-dasharray: none; opacity: 0.6; }
.ghchart__anno-title {
font-size: 15px;
font-weight: 700;
letter-spacing: -0.01em;
}
.ghchart__anno-sub {
fill: var(--ghc-muted);
font-size: 13px;
font-weight: 500;
}
/* Final-callout labels read a touch larger than the invalid-run notes. */
.ghchart__anno-title--callout { font-size: 17px; }
.ghchart__anno-sub--callout { font-size: 14px; }
/* Bracket span annotation. */
.ghchart__bracket { stroke: var(--ghc-axis); stroke-width: 1.5; opacity: 0.75; }
.ghchart__bracket-label {
fill: var(--ghc-muted);
font-size: 13px;
font-weight: 700;
letter-spacing: 0.04em;
}
/* Final callout ring. */
.ghchart__callout-ring { fill: none; stroke-width: 2; }
/* ------------------------------------------------------------------ */
/* Title + divider */
/* ------------------------------------------------------------------ */
.ghchart__title {
/* Matches the github.blog H2 (.wp-block-heading) exactly */
display: block;
font-size: 32px;
line-height: 40px;
font-weight: 700;
letter-spacing: -0.18px;
color: var(--ghc-text);
}
.ghchart__rule {
height: 1.5px;
border-radius: 1.5px;
background: var(--ghc-divider);
margin: 10px 0 18px;
}
/* ------------------------------------------------------------------ */
/* Bar chart: subtitle, panels, bars, caption */
/* ------------------------------------------------------------------ */
.ghchart__subtitle {
margin: -6px 0 20px;
font-size: clamp(13px, 1.6vw, 15px);
line-height: 1.5;
color: var(--ghc-muted);
}
.ghchart__panel { margin-top: 30px; }
.ghchart__panel:first-of-type { margin-top: 6px; }
.ghchart__panel-title {
margin: 0 0 6px;
font-size: clamp(15px, 1.9vw, 18px);
font-weight: 700;
letter-spacing: -0.01em;
color: var(--ghc-text);
}
.ghchart__plot--bar { touch-action: pan-y; }
.ghchart__axisline {
stroke: var(--ghc-axis);
stroke-width: 1.25;
opacity: 0.35;
}
.ghchart__bar { transition: opacity 0.12s ease; }
.ghchart__barhit { fill: transparent; }
/* Always-on value labels above selected bars. */
.ghchart__barvalue {
fill: var(--ghc-text);
font-size: 20px;
font-weight: 700;
font-variant-numeric: tabular-nums;
pointer-events: none;
}
/* Anchor annotation: dotted reference line + floating pill. */
.ghchart__anno-line {
stroke: #8b949e;
stroke-width: 1.5;
stroke-dasharray: 4 3;
stroke-linecap: round;
pointer-events: none;
}
.ghchart__anno-chip {
fill: #ffebe9;
stroke: #ffcecb;
stroke-width: 1;
pointer-events: none;
}
.ghchart__anno-chip-text {
fill: #a0111f;
font-weight: 700;
pointer-events: none;
}
.ghchart__caption {
margin-top: 22px;
padding-top: 12px;
border-top: 1px solid var(--ghc-card-border);
font-size: 12.5px;
line-height: 1.55;
color: var(--ghc-muted);
}
.ghchart__caption b { color: var(--ghc-text); font-weight: 700; }
.ghchart__caption code {
font-family: ui-monospace, SFMono-Regular, "SF Mono", Menlo, Consolas, monospace;
font-size: 0.92em;
padding: 1px 5px;
background: #f6f8fa;
border: 1px solid var(--ghc-card-border);
border-radius: 5px;
}
.ghchart__tag {
display: inline-block;
font-size: 10px;
font-weight: 650;
letter-spacing: 0.03em;
text-transform: uppercase;
padding: 2px 7px;
border-radius: 999px;
border: 1px solid #aceebb;
color: #0a5223;
background: #dafbe1;
white-space: nowrap;
vertical-align: middle;
}
/* ------------------------------------------------------------------ */
/* Distribution dashboard (segmented mix bar + ranked bar panels) */
/* ------------------------------------------------------------------ */
.ghchart__dist-section { margin-top: 28px; }
.ghchart__dist-section:first-of-type { margin-top: 12px; }
/* Segmented proportional "mix" bar: name inside, count/percent below.
Separators are 2px inside borders (box-sizing:border-box) rather than a
flex gap, so the three segments sum to exactly 100% with no overflow. */
.ghchart__seg {
display: flex;
width: 100%;
height: 42px;
margin-top: 2px;
border-radius: 7px;
overflow: hidden;
}
.ghchart__seg-cell {
display: flex;
align-items: center;
justify-content: center;
min-width: 0;
color: #ffffff;
font-size: 15px;
font-weight: 700;
letter-spacing: -0.01em;
white-space: nowrap;
overflow: hidden;
}
.ghchart__seg-cell + .ghchart__seg-cell { border-left: 2px solid var(--ghc-card-bg); }
.ghchart__seg-note-row {
display: flex;
width: 100%;
margin-top: 8px;
}
.ghchart__seg-note {
min-width: 0;
text-align: center;
font-size: 12.5px;
color: var(--ghc-muted);
font-variant-numeric: tabular-nums;
white-space: nowrap;
}
/* Keep the outer notes from spilling past the chart edges. */
.ghchart__seg-note:first-child { text-align: left; }
.ghchart__seg-note:last-child { text-align: right; }
.ghchart__seg-note b { color: var(--ghc-text); font-weight: 700; }
/* Ranked horizontal bar list: [category | bar track | value].
The list is the grid and rows use display:contents, so the category,
bar, and value columns line up across every row in a panel. */
.ghchart__dist-list {
display: grid;
grid-template-columns: max-content 1fr max-content;
align-items: center;
column-gap: 12px;
row-gap: 10px;
margin-top: 4px;
}
.ghchart__dist-row { display: contents; }
.ghchart__dist-cat {
text-align: right;
font-size: 13px;
line-height: 1.25;
color: var(--ghc-text);
white-space: nowrap;
}
.ghchart__dist-track { width: 100%; }
.ghchart__dist-bar {
height: 15px;
min-width: 2px;
border-radius: 3px;
}
.ghchart__dist-val {
font-size: 12.5px;
color: var(--ghc-muted);
font-variant-numeric: tabular-nums;
white-space: nowrap;
}
.ghchart__dist-val b { color: var(--ghc-text); font-weight: 700; }
/* Two-up panel grid (collapses to one column on narrow screens).
minmax(0,1fr) lets columns shrink so long labels never force overflow. */
.ghchart__dist-cols {
margin-top: 26px;
display: grid;
grid-template-columns: repeat(2, minmax(0, 1fr));
gap: 24px 30px;
}
.ghchart__dist-cols .ghchart__dist-section { margin-top: 0; }
.ghchart__dist-cols--stack { grid-template-columns: 1fr; }
.ghchart__dist-cols--stack .ghchart__dist-section { margin-top: 28px; }
.ghchart__dist-cols--stack .ghchart__dist-section:first-child { margin-top: 0; }
/* Tighten the label/value type a touch in the 2-up layout so the bars
keep room at the 699px blog width. */
.ghchart__dist-cols:not(.ghchart__dist-cols--stack) .ghchart__dist-cat { font-size: 12px; }
.ghchart__dist-cols:not(.ghchart__dist-cols--stack) .ghchart__dist-val { font-size: 11.5px; }
.ghchart__dist-cols:not(.ghchart__dist-cols--stack) .ghchart__dist-list { column-gap: 9px; }
@media (max-width: 600px) {
.ghchart__dist-cols { grid-template-columns: 1fr; gap: 26px; }
.ghchart__dist-cols .ghchart__dist-section ~ .ghchart__dist-section { margin-top: 2px; }
}
/* Let long category labels wrap on very narrow (mobile) widths. */
@media (max-width: 480px) {
.ghchart__dist-cat { white-space: normal; }
}
/* ------------------------------------------------------------------ */
/* Calendar heatmap (GitHub contribution-graph style) */
/* ------------------------------------------------------------------ */
.ghheat__eyebrow { display: flex; align-items: center; gap: 8px; margin: 0 0 12px; }
.ghheat__eyedot {
width: 9px; height: 9px; border-radius: 50%;
background: #cf222e; box-shadow: 0 0 0 3px rgba(207, 34, 46, 0.14);
flex: 0 0 auto;
}
.ghheat__eyetext {
font-size: 12px; font-weight: 700; letter-spacing: 0.08em;
text-transform: uppercase; color: #cf222e;
}
/* Subtitle here is a prominent lede, not the muted variant. */
.ghheat__subtitle {
color: var(--ghc-text);
font-size: clamp(14px, 1.7vw, 16px);
/* 24px top gap matches the github.blog H2 → paragraph block spacing. */
margin: 24px 0 8px;
}
.ghheat__subtitle b { font-weight: 700; }
.ghheat__panel { margin-top: 36px; }
.ghheat__phead {
display: flex; align-items: baseline; gap: 9px; flex-wrap: wrap;
margin-bottom: 16px;
}
.ghheat__badge {
display: inline-flex; align-items: center; justify-content: center;
width: 22px; height: 22px; border-radius: 50%;
color: #fff; font-size: 12px; font-weight: 700;
flex: 0 0 auto; align-self: center;
}
.ghheat__ptitle {
font-size: clamp(16px, 2vw, 19px); font-weight: 700;
letter-spacing: -0.01em; color: var(--ghc-text);
}
.ghheat__pdesc { font-size: 14px; color: var(--ghc-muted); }
.ghheat__body { display: flex; gap: 30px; align-items: flex-start; flex-wrap: wrap; }
.ghheat__gridwrap { flex: 1 1 340px; min-width: 260px; }
.ghheat__grid { display: block; width: 100%; height: auto; overflow: visible; }
.ghheat__cell {
stroke: rgba(27, 31, 36, 0.06);
stroke-width: 1;
transition: stroke 0.1s ease;
}
.ghheat__cell:hover { stroke: #24292f; stroke-width: 1.4; }
.ghheat__cell--pad { opacity: 0; pointer-events: none; }
.ghheat__wd, .ghheat__month {
fill: var(--ghc-muted); font-size: 11px; font-weight: 600;
}
.ghheat__side { flex: 1 1 250px; min-width: 230px; }
.ghheat__stat { display: flex; align-items: baseline; gap: 7px; }
.ghheat__statbig {
font-size: clamp(26px, 4vw, 34px); font-weight: 800; letter-spacing: -0.02em;
line-height: 1;
}
.ghheat__statsmall { font-size: 15px; font-weight: 600; color: var(--ghc-muted); }
.ghheat__note {
margin: 10px 0 16px; font-size: 14px; line-height: 1.55; color: var(--ghc-muted);
}
.ghheat__note b { color: var(--ghc-text); font-weight: 700; }
.ghheat__legend {
display: flex; align-items: center; gap: 5px;
font-size: 12.5px; color: var(--ghc-muted); flex-wrap: wrap;
}
.ghheat__sw { width: 12px; height: 12px; border-radius: 3px; display: inline-block; flex: 0 0 auto; }
.ghheat__sw--empty { border: 1px solid #d0d7de; }
.ghheat__legunit { margin-left: 2px; }
.ghheat__extra {
display: flex; align-items: flex-start; gap: 7px; margin-top: 14px;
font-size: 12px; line-height: 1.5; color: var(--ghc-muted); max-width: 330px;
}
.ghheat__extra .ghheat__sw { margin-top: 2px; }
/* ------------------------------------------------------------------ */
/* Tooltip */
/* ------------------------------------------------------------------ */
.ghchart__tooltip {
position: absolute;
z-index: 5;
top: 0;
left: 0;
min-width: 128px;
max-width: 260px;
padding: 10px 12px;
background: var(--ghc-card-bg);
border: 1px solid var(--ghc-card-border);
border-radius: 12px;
/* Layered drop shadow from Primer Brand's ActionMenu (.ActionMenu__menu) */
box-shadow:
0px 100px 80px rgba(0, 0, 0, 0.01),
0px 41px 33px rgba(0, 0, 0, 0.02),
0px 22px 17px rgba(0, 0, 0, 0.02),
0px 12px 10px rgba(0, 0, 0, 0.03),
0px 6px 5px rgba(0, 0, 0, 0.04),
0px 2px 2px rgba(0, 0, 0, 0.07);
pointer-events: none;
opacity: 0;
transform: translate(-50%, -100%);
/* Fade in on show and out on hide; quickly ease left/top when tracking
from one point to the next. The position ease is suppressed on the
first appearance (see chart.js) so the tooltip fades in place rather
than sliding in from its previous spot. */
transition: opacity 0.12s ease,
left 0.1s cubic-bezier(0.4, 0, 0.2, 1),
top 0.1s cubic-bezier(0.4, 0, 0.2, 1);
will-change: opacity, transform, left, top;
}
.ghchart__tooltip[data-visible="true"] { opacity: 1; }
.ghchart__tt-title {
font-size: 13px;
font-weight: 700;
color: var(--ghc-text);
margin-bottom: 6px;
}
.ghchart__tt-row {
display: flex;
align-items: center;
gap: 8px;
font-size: 13px;
line-height: 1.6;
color: var(--ghc-muted);
}
.ghchart__tt-swatch { width: 10px; height: 10px; border-radius: 3px; flex: 0 0 auto; }
.ghchart__tt-name { flex: 1 1 auto; }
.ghchart__tt-value { font-weight: 700; color: var(--ghc-text); font-variant-numeric: tabular-nums; }
/* Bar tooltip body: a title line + free-form text lines (no swatches). */
.ghchart__tt-line {
font-size: 13px;
line-height: 1.5;
color: var(--ghc-muted);
}
.ghchart__tt-line:first-child { color: var(--ghc-text); font-weight: 600; }
.ghchart__tt-line + .ghchart__tt-line { margin-top: 2px; }
@media (prefers-reduced-motion: reduce) {
.ghchart__dot,
.ghchart__guide,
.ghchart__bar,
.ghchart__pt,
.ghchart__square,
.ghheat__cell,
.ghchart__tooltip { transition: none; }
}
/*
Shared chart engine — reusable renderer for the blog-charts library.
------------------------------------------------------------------
Exposes window.GHChartEngine = { renderers, render(config, mountEl?) }.
Renderers are keyed by config.type (currently "line"); add a new viz type
by registering GHChartEngine.renderers[] = function (root, config) {…}.
This same file is embedded verbatim at the top of each exported WordPress
chart.js, followed by that chart's CONFIG + a small auto-boot. It has no
external dependencies and is safe to include more than once on a page.
*/
(function (global) {
"use strict";
var SVGNS = "http://www.w3.org/2000/svg";
function el(tag, attrs) {
var node = document.createElementNS(SVGNS, tag);
if (attrs) {
for (var k in attrs) {
if (Object.prototype.hasOwnProperty.call(attrs, k)) {
node.setAttribute(k, attrs[k]);
}
}
}
return node;
}
function html(tag, cls) {
var node = document.createElement(tag);
if (cls) node.className = cls;
return node;
}
function clamp(v, lo, hi) { return v < lo ? lo : v > hi ? hi : v; }
var renderers = {};
/* ================================================================
Line chart renderer
================================================================ */
renderers.line = function (root, config) {
// viewBox geometry (design units; scales fluidly to any width).
var VB = { w: 1000, h: 560 };
var M = { top: 30, right: 34, bottom: 56, left: 86 };
// Inset the plotted points slightly from the axis so the first/last
// months (e.g. "Jan") don't crowd the y-axis labels (e.g. "50").
var PAD_X = 18;
var innerW = VB.w - M.left - M.right;
var innerH = VB.h - M.top - M.bottom;
// Reset (in case of re-render) and use a unique id prefix so multiple
// charts on one page never share gradient ids.
root.textContent = "";
var uid = config.mountId;
var n = config.labels.length;
var yMin = config.yAxis.min;
var yMax = config.yAxis.max;
var step = config.yAxis.step;
// Y-axis tick labels can format differently from the tooltip values
// (e.g. a plain "40" on the axis but "40%" in the tooltip).
var fmtAxis = config.formatAxis || config.formatValue;
var plotW = innerW - 2 * PAD_X;
var stepX = n dark ramp is derived from a single tunable base color.
================================================================ */
renderers.heatmap = function (root, config) {
root.textContent = "";
var WHITE = [255, 255, 255], BLACK = [0, 0, 0];
function hexToRgb(h) {
h = String(h).replace("#", "");
if (h.length === 3) h = h[0] + h[0] + h[1] + h[1] + h[2] + h[2];
return [parseInt(h.slice(0, 2), 16), parseInt(h.slice(2, 4), 16), parseInt(h.slice(4, 6), 16)];
}
function rgbToHex(c) {
return "#" + c.map(function (x) {
x = Math.max(0, Math.min(255, Math.round(x)));
return (x < 16 ? "0" : "") + x.toString(16);
}).join("");
}
function mix(a, b, t) {
return [a[0] + (b[0] - a[0]) * t, a[1] + (b[1] - a[1]) * t, a[2] + (b[2] - a[2]) * t];
}
// 5-step contribution ramp (level 0 faint -> level 4 dark) from one hex.
function rampFrom(baseHex) {
var b = hexToRgb(baseHex);
return [
rgbToHex(mix(b, WHITE, 0.80)),
rgbToHex(mix(b, WHITE, 0.52)),
rgbToHex(mix(b, WHITE, 0.20)),
baseHex,
rgbToHex(mix(b, BLACK, 0.30))
];
}
function trim0(s) { return s.replace(/\.0$/, ""); }
function fmtM(v) {
if (v >= 1000) { var g = v / 1000; return (g >= 10 ? Math.round(g) : trim0(g.toFixed(1))) + "B"; }
if (v >= 1) return trim0(v.toFixed(1)) + "M";
return Math.round(v * 1000) + "K";
}
var MON = ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"];
var MON_FULL = ["January", "February", "March", "April", "May", "June",
"July", "August", "September", "October", "November", "December"];
var WD = ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"];
var year = config.year || 2026;
var startM = config.startMonth != null ? config.startMonth : 0;
var endM = config.endMonth != null ? config.endMonth : 5;
var emptyColor = config.emptyColor || "#ebedf0";
function daysInMonth(y, m) { return new Date(y, m + 1, 0).getDate(); }
function parseISO(s) { var p = s.split("-"); return new Date(+p[0], +p[1] - 1, +p[2]); }
function dayStart(dt) { return new Date(dt.getFullYear(), dt.getMonth(), dt.getDate()); }
/* ---- Eyebrow + title + subtitle ----------------------------- */
if (config.eyebrow) {
var eb = html("div", "ghheat__eyebrow");
eb.appendChild(html("span", "ghheat__eyedot"));
var ebt = html("span", "ghheat__eyetext");
ebt.textContent = config.eyebrow;
eb.appendChild(ebt);
root.appendChild(eb);
}
var title = html("span", "ghchart__title");
title.textContent = config.title;
root.appendChild(title);
if (config.subtitle) {
var sub = html("p", "ghchart__subtitle ghheat__subtitle");
sub.innerHTML = config.subtitle;
root.appendChild(sub);
}
/* ---- One shared tooltip -------------------------------------- */
var tip = html("div", "ghchart__tooltip");
tip.setAttribute("role", "status");
var tipTitle = html("div", "ghchart__tt-title");
var tipBody = html("div", "ghchart__tt-body");
tip.appendChild(tipTitle);
tip.appendChild(tipBody);
root.appendChild(tip);
var motionMQ = window.matchMedia ? window.matchMedia("(prefers-reduced-motion: reduce)") : null;
function reduceMotion() { return !!(motionMQ && motionMQ.matches); }
var activeCell = null;
function hideTip() { activeCell = null; tip.setAttribute("data-visible", "false"); }
function showTip(rectEl, titleText, lines) {
var reappearing = activeCell === null;
activeCell = rectEl;
tipTitle.textContent = titleText;
tipBody.textContent = "";
lines.forEach(function (ln) {
var r = html("div", "ghchart__tt-line");
r.innerHTML = ln;
tipBody.appendChild(r);
});
var a = rectEl.getBoundingClientRect(), rr = root.getBoundingClientRect();
var left = a.left + a.width / 2 - rr.left, top = a.top - rr.top - 12;
var suppress = reappearing && !reduceMotion();
if (suppress) tip.style.transition = "opacity 0.12s ease";
tip.setAttribute("data-visible", "true");
var halfW = tip.offsetWidth / 2;
left = clamp(left, halfW + 2, root.clientWidth - halfW - 2);
if (top - tip.offsetHeight < 0) {
top = a.bottom - rr.top + 12;
tip.style.transform = "translate(-50%, 0)";
} else {
tip.style.transform = "translate(-50%, -100%)";
}
tip.style.left = left + "px";
tip.style.top = top + "px";
if (suppress) { void tip.offsetWidth; tip.style.transition = ""; }
}
/* ---- Panels -------------------------------------------------- */
(config.panels || []).forEach(function (panel, pi) {
var base = (config.series && config.series[pi] && config.series[pi].color) || panel.base || "#3094ff";
var ramp = rampFrom(base);
var strong = ramp[4];
var totals = panel.months || [];
var activeVals = [];
for (var mi = startM; mi existing.version) {
/* This bundle is newer. Every export embeds the COMPLETE engine, so this
bundle's `renderers` is a self-contained, up-to-date set. Adopt it as
the shared registry (upgrading older same-named renderers in place),
while defensively preserving any renderer types only the older engine
happened to have. Then take over render(). */
for (var ek in existing.renderers) {
if (Object.prototype.hasOwnProperty.call(existing.renderers, ek) && !renderers[ek]) {
renderers[ek] = existing.renderers[ek];
}
}
existing.renderers = renderers;
existing.render = render;
existing.version = ENGINE_VERSION;
} else {
/* Same or older version already present: only contribute renderer types
the shared registry is missing (don't clobber an equal/newer engine). */
for (var rk in renderers) {
if (Object.prototype.hasOwnProperty.call(renderers, rk) && !existing.renderers[rk]) {
existing.renderers[rk] = renderers[rk];
}
}
}
} else {
/* No engine yet, or a pre-versioning (old-style) engine loaded first.
Take over, preserving any renderers the old-style engine registered. */
if (existing && existing.renderers) {
for (var ok in existing.renderers) {
if (Object.prototype.hasOwnProperty.call(existing.renderers, ok) && !renderers[ok]) {
renderers[ok] = existing.renderers[ok];
}
}
}
global.GHChartEngine = { renderers: renderers, render: render, version: ENGINE_VERSION };
}
})(typeof window !== "undefined" ? window : this);
;(function () {
"use strict";
var CONFIG = {
mountId: "gh-chart-hf-terminal",
type: "scatter",
title: "TerminalBench 2.1 - GitHub Copilot CLI",
xAxis: {
min: 0,
max: 1,
step: 0.2,
title: "Mean cost per task (USD)",
format: function (v) { return "$" + v.toFixed(2); }
},
yAxis: {
min: 50,
max: 90,
step: 10,
title: "Resolution rate (pass@1)",
format: function (v) { return v + "%"; }
},
tipSuffix: "/task",
series: [
{
name: "HydraFusion",
color: "#2da44f"
},
{
name: "Opus 5 (reference)",
color: "#8250df"
},
{
name: "Other solo models",
color: "#8c959f"
}
],
points: [
{
label: "HydraFusion",
x: 0.3,
y: 84.7,
series: 0,
highlight: true,
labelPos: "above"
},
{
label: "Opus 5",
x: 0.9,
y: 79.8,
series: 1,
reference: true,
labelPos: "above"
},
{
label: "Sonnet 5",
x: 0.55,
y: 76,
series: 2,
labelPos: "right"
},
{
label: "GPT-5.6-Sol",
x: 0.3,
y: 73.3,
series: 2,
labelPos: "right"
},
{
label: "GPT-5.6-Terra",
x: 0.25,
y: 72.5,
series: 2,
labelPos: "left"
},
{
label: "GPT-5.6-Luna",
x: 0.03,
y: 56,
series: 2,
labelPos: "right"
}
]
};
function boot() {
var root = document.getElementById(CONFIG.mountId);
if (!root || root.getAttribute("data-gh-rendered")) return;
root.setAttribute("data-gh-rendered", "1");
window.GHChartEngine.render(CONFIG);
}
if (document.readyState === "loading") {
document.addEventListener("DOMContentLoaded", boot);
} else {
boot();
}
})();
DeepSWE
DeepSWE evaluates challenging repository-level software engineering tasks that require navigating large codebases, understanding cross-file dependencies, and producing end-to-end fixes. On this benchmark, HydraFusion comes within 1.5 percentage points of Opus 5 while reducing cost by 36%, demonstrating a compelling quality-cost tradeoff for complex real-world engineering tasks.
.ghchart {
/* Theme tokens */
--ghc-text: #1f2328;
--ghc-muted: #656d76;
--ghc-grid: #d0d7de;
--ghc-axis: #59636e;
--ghc-card-bg: #ffffff;
--ghc-card-border: #d0d7de;
--ghc-divider: #b6bfb8;
--ghc-guide: #afb8c1;
/* GitHub brand font — matches the github.blog stack (Mona Sans, with
the blog's metric fallback) so it renders identically in-context. */
font-family: "Mona Sans", "Mona Sans Fallback", -apple-system, "system-ui",
"Segoe UI", Helvetica, Arial, sans-serif;
-webkit-font-smoothing: antialiased;
text-rendering: optimizeLegibility;
position: relative;
box-sizing: border-box;
width: 100%;
max-width: 699px;
margin: 0 auto;
color: var(--ghc-text);
line-height: 1.4;
}
.ghchart *,
.ghchart *::before,
.ghchart *::after { box-sizing: border-box; }
/* ------------------------------------------------------------------ */
/* Header + legend */
/* ------------------------------------------------------------------ */
.ghchart__header {
display: flex;
justify-content: flex-end;
align-items: center;
min-height: 24px;
margin-bottom: 4px;
}
.ghchart__legend {
display: flex;
align-items: center;
gap: 20px;
flex-wrap: wrap;
justify-content: flex-end;
}
.ghchart__legend-item {
display: inline-flex;
align-items: center;
gap: 8px;
font-size: clamp(14px, 1.9vw, 17px);
font-weight: 600;
color: var(--ghc-muted);
white-space: nowrap;
}
.ghchart__swatch {
width: 14px;
height: 14px;
border-radius: 4px;
flex: 0 0 auto;
}
/* ------------------------------------------------------------------ */
/* Plot (SVG) */
/* ------------------------------------------------------------------ */
.ghchart__plot {
display: block;
width: 100%;
height: auto;
overflow: visible;
touch-action: pan-y;
}
.ghchart__gridline {
stroke: var(--ghc-grid);
stroke-width: 1;
stroke-dasharray: 2 6;
stroke-linecap: round;
opacity: 0.9;
}
.ghchart__axislabel {
fill: var(--ghc-axis);
font-size: 22px;
font-weight: 500;
}
.ghchart__axislabel--x { font-weight: 600; }
.ghchart__line {
fill: none;
stroke-linecap: round;
stroke-linejoin: round;
}
.ghchart__dot { transition: r 0.12s ease; }
/* Dashed threshold line (e.g. a target/reference level) + floating label.
Stroke color is set inline per chart; dash + label styling live here. */
.ghchart__threshold {
stroke-width: 2;
stroke-dasharray: 7 6;
stroke-linecap: round;
opacity: 0.9;
}
.ghchart__threshold-label {
font-size: 20px;
font-weight: 700;
letter-spacing: -0.01em;
}
/* Hatched band marking a partial / projected trailing region. */
.ghchart__projection { pointer-events: none; }
.ghchart__guide {
stroke: var(--ghc-guide);
stroke-width: 1.5;
stroke-dasharray: 4 4;
opacity: 0;
transition: opacity 0.12s ease;
}
.ghchart__hit { fill: transparent; }
/* ------------------------------------------------------------------ */
/* Scatter plot */
/* ------------------------------------------------------------------ */
.ghchart__gridline--v { /* vertical gridlines reuse the dashed grid style */ }
.ghchart__axistitle {
fill: var(--ghc-muted);
font-size: 20px;
font-weight: 600;
letter-spacing: -0.01em;
}
/* Dotted reference crosshair through the "reference" point. Stroke color
is set inline (tracks the reference series color). */
.ghchart__crosshair {
stroke-width: 2;
stroke-dasharray: 2 7;
stroke-linecap: round;
opacity: 0.85;
pointer-events: none;
}
.ghchart__halo { opacity: 0.18; pointer-events: none; }
.ghchart__pt { transition: r 0.12s ease; }
.ghchart__pt-label {
fill: var(--ghc-muted);
font-size: 19px;
font-weight: 600;
letter-spacing: -0.01em;
pointer-events: none;
}
.ghchart__pt-label--strong { font-size: 21px; font-weight: 700; }
.ghchart__pthit { fill: transparent; cursor: default; outline: none; }
.ghchart__pthit:focus-visible {
outline: 2px solid var(--ghc-axis);
outline-offset: 1px;
}
.ghchart__swatch--dot { border-radius: 50%; }
/* ------------------------------------------------------------------ */
/* Trend chart (dated time-series with narrative headline) */
/* ------------------------------------------------------------------ */
.ghchart__headline {
font-size: clamp(17px, 2.3vw, 20px);
line-height: 1.32;
font-weight: 700;
letter-spacing: -0.01em;
color: var(--ghc-text);
margin: 0 0 8px;
}
/* Trend legend sits left-aligned under the headline/subtitle. */
.ghchart__legend--trend {
justify-content: flex-start;
margin: 2px 0 14px;
}
.ghchart__glyph { flex: 0 0 auto; }
.ghchart__glyph--square {
width: 13px; height: 13px; border-radius: 2px;
}
.ghchart__glyph--diamond {
width: 12px; height: 12px; border: 1.5px solid; transform: rotate(45deg);
}
.ghchart__glyph--line {
width: 20px; height: 0; border-top: 2px dashed; border-radius: 0;
}
/* Phase background columns + separators + top labels. */
.ghchart__phase { fill: #f6f8fa; opacity: 0.55; }
.ghchart__phase--alt { fill: #ffffff; opacity: 0; }
.ghchart__phase-sep {
stroke: var(--ghc-grid); stroke-width: 1; stroke-dasharray: 2 5; opacity: 0.8;
}
.ghchart__phase-label {
fill: var(--ghc-muted);
font-size: 15px;
font-weight: 700;
letter-spacing: 0.06em;
}
.ghchart__phase-avg {
font-size: 16px;
font-weight: 700;
letter-spacing: -0.01em;
}
/* Dashed trend line + ± noise band + slope label. */
.ghchart__trendband { opacity: 0.12; pointer-events: none; }
.ghchart__trendline {
stroke-width: 2.5;
stroke-dasharray: 8 6;
stroke-linecap: round;
fill: none;
}
.ghchart__slope {
font-size: 20px;
font-weight: 700;
letter-spacing: -0.01em;
}
/* Horizontal reference line + label. */
.ghchart__refline {
stroke-width: 2;
stroke-dasharray: 7 6;
stroke-linecap: round;
opacity: 0.85;
}
.ghchart__reflabel {
font-size: 16px;
font-weight: 700;
letter-spacing: -0.01em;
}
/* Data squares — values reveal on hover only. */
.ghchart__square { transition: width 0.12s ease, height 0.12s ease, x 0.12s ease, y 0.12s ease; }
.ghchart__squarehit { fill: transparent; cursor: default; outline: none; }
.ghchart__squarehit:focus-visible {
outline: 2px solid var(--ghc-axis);
outline-offset: 1px;
}
/* Invalid-run markers + leader-line annotations. */
.ghchart__invalid { stroke-width: 2; }
.ghchart__invalid-x { stroke-width: 2; stroke-linecap: round; }
.ghchart__leader { stroke-width: 1; stroke-dasharray: 2 3; opacity: 0.8; }
.ghchart__leader--callout { stroke-dasharray: none; opacity: 0.6; }
.ghchart__anno-title {
font-size: 15px;
font-weight: 700;
letter-spacing: -0.01em;
}
.ghchart__anno-sub {
fill: var(--ghc-muted);
font-size: 13px;
font-weight: 500;
}
/* Final-callout labels read a touch larger than the invalid-run notes. */
.ghchart__anno-title--callout { font-size: 17px; }
.ghchart__anno-sub--callout { font-size: 14px; }
/* Bracket span annotation. */
.ghchart__bracket { stroke: var(--ghc-axis); stroke-width: 1.5; opacity: 0.75; }
.ghchart__bracket-label {
fill: var(--ghc-muted);
font-size: 13px;
font-weight: 700;
letter-spacing: 0.04em;
}
/* Final callout ring. */
.ghchart__callout-ring { fill: none; stroke-width: 2; }
/* ------------------------------------------------------------------ */
/* Title + divider */
/* ------------------------------------------------------------------ */
.ghchart__title {
/* Matches the github.blog H2 (.wp-block-heading) exactly */
display: block;
font-size: 32px;
line-height: 40px;
font-weight: 700;
letter-spacing: -0.18px;
color: var(--ghc-text);
}
.ghchart__rule {
height: 1.5px;
border-radius: 1.5px;
background: var(--ghc-divider);
margin: 10px 0 18px;
}
/* ------------------------------------------------------------------ */
/* Bar chart: subtitle, panels, bars, caption */
/* ------------------------------------------------------------------ */
.ghchart__subtitle {
margin: -6px 0 20px;
font-size: clamp(13px, 1.6vw, 15px);
line-height: 1.5;
color: var(--ghc-muted);
}
.ghchart__panel { margin-top: 30px; }
.ghchart__panel:first-of-type { margin-top: 6px; }
.ghchart__panel-title {
margin: 0 0 6px;
font-size: clamp(15px, 1.9vw, 18px);
font-weight: 700;
letter-spacing: -0.01em;
color: var(--ghc-text);
}
.ghchart__plot--bar { touch-action: pan-y; }
.ghchart__axisline {
stroke: var(--ghc-axis);
stroke-width: 1.25;
opacity: 0.35;
}
.ghchart__bar { transition: opacity 0.12s ease; }
.ghchart__barhit { fill: transparent; }
/* Always-on value labels above selected bars. */
.ghchart__barvalue {
fill: var(--ghc-text);
font-size: 20px;
font-weight: 700;
font-variant-numeric: tabular-nums;
pointer-events: none;
}
/* Anchor annotation: dotted reference line + floating pill. */
.ghchart__anno-line {
stroke: #8b949e;
stroke-width: 1.5;
stroke-dasharray: 4 3;
stroke-linecap: round;
pointer-events: none;
}
.ghchart__anno-chip {
fill: #ffebe9;
stroke: #ffcecb;
stroke-width: 1;
pointer-events: none;
}
.ghchart__anno-chip-text {
fill: #a0111f;
font-weight: 700;
pointer-events: none;
}
.ghchart__caption {
margin-top: 22px;
padding-top: 12px;
border-top: 1px solid var(--ghc-card-border);
font-size: 12.5px;
line-height: 1.55;
color: var(--ghc-muted);
}
.ghchart__caption b { color: var(--ghc-text); font-weight: 700; }
.ghchart__caption code {
font-family: ui-monospace, SFMono-Regular, "SF Mono", Menlo, Consolas, monospace;
font-size: 0.92em;
padding: 1px 5px;
background: #f6f8fa;
border: 1px solid var(--ghc-card-border);
border-radius: 5px;
}
.ghchart__tag {
display: inline-block;
font-size: 10px;
font-weight: 650;
letter-spacing: 0.03em;
text-transform: uppercase;
padding: 2px 7px;
border-radius: 999px;
border: 1px solid #aceebb;
color: #0a5223;
background: #dafbe1;
white-space: nowrap;
vertical-align: middle;
}
/* ------------------------------------------------------------------ */
/* Distribution dashboard (segmented mix bar + ranked bar panels) */
/* ------------------------------------------------------------------ */
.ghchart__dist-section { margin-top: 28px; }
.ghchart__dist-section:first-of-type { margin-top: 12px; }
/* Segmented proportional "mix" bar: name inside, count/percent below.
Separators are 2px inside borders (box-sizing:border-box) rather than a
flex gap, so the three segments sum to exactly 100% with no overflow. */
.ghchart__seg {
display: flex;
width: 100%;
height: 42px;
margin-top: 2px;
border-radius: 7px;
overflow: hidden;
}
.ghchart__seg-cell {
display: flex;
align-items: center;
justify-content: center;
min-width: 0;
color: #ffffff;
font-size: 15px;
font-weight: 700;
letter-spacing: -0.01em;
white-space: nowrap;
overflow: hidden;
}
.ghchart__seg-cell + .ghchart__seg-cell { border-left: 2px solid var(--ghc-card-bg); }
.ghchart__seg-note-row {
display: flex;
width: 100%;
margin-top: 8px;
}
.ghchart__seg-note {
min-width: 0;
text-align: center;
font-size: 12.5px;
color: var(--ghc-muted);
font-variant-numeric: tabular-nums;
white-space: nowrap;
}
/* Keep the outer notes from spilling past the chart edges. */
.ghchart__seg-note:first-child { text-align: left; }
.ghchart__seg-note:last-child { text-align: right; }
.ghchart__seg-note b { color: var(--ghc-text); font-weight: 700; }
/* Ranked horizontal bar list: [category | bar track | value].
The list is the grid and rows use display:contents, so the category,
bar, and value columns line up across every row in a panel. */
.ghchart__dist-list {
display: grid;
grid-template-columns: max-content 1fr max-content;
align-items: center;
column-gap: 12px;
row-gap: 10px;
margin-top: 4px;
}
.ghchart__dist-row { display: contents; }
.ghchart__dist-cat {
text-align: right;
font-size: 13px;
line-height: 1.25;
color: var(--ghc-text);
white-space: nowrap;
}
.ghchart__dist-track { width: 100%; }
.ghchart__dist-bar {
height: 15px;
min-width: 2px;
border-radius: 3px;
}
.ghchart__dist-val {
font-size: 12.5px;
color: var(--ghc-muted);
font-variant-numeric: tabular-nums;
white-space: nowrap;
}
.ghchart__dist-val b { color: var(--ghc-text); font-weight: 700; }
/* Two-up panel grid (collapses to one column on narrow screens).
minmax(0,1fr) lets columns shrink so long labels never force overflow. */
.ghchart__dist-cols {
margin-top: 26px;
display: grid;
grid-template-columns: repeat(2, minmax(0, 1fr));
gap: 24px 30px;
}
.ghchart__dist-cols .ghchart__dist-section { margin-top: 0; }
.ghchart__dist-cols--stack { grid-template-columns: 1fr; }
.ghchart__dist-cols--stack .ghchart__dist-section { margin-top: 28px; }
.ghchart__dist-cols--stack .ghchart__dist-section:first-child { margin-top: 0; }
/* Tighten the label/value type a touch in the 2-up layout so the bars
keep room at the 699px blog width. */
.ghchart__dist-cols:not(.ghchart__dist-cols--stack) .ghchart__dist-cat { font-size: 12px; }
.ghchart__dist-cols:not(.ghchart__dist-cols--stack) .ghchart__dist-val { font-size: 11.5px; }
.ghchart__dist-cols:not(.ghchart__dist-cols--stack) .ghchart__dist-list { column-gap: 9px; }
@media (max-width: 600px) {
.ghchart__dist-cols { grid-template-columns: 1fr; gap: 26px; }
.ghchart__dist-cols .ghchart__dist-section ~ .ghchart__dist-section { margin-top: 2px; }
}
/* Let long category labels wrap on very narrow (mobile) widths. */
@media (max-width: 480px) {
.ghchart__dist-cat { white-space: normal; }
}
/* ------------------------------------------------------------------ */
/* Calendar heatmap (GitHub contribution-graph style) */
/* ------------------------------------------------------------------ */
.ghheat__eyebrow { display: flex; align-items: center; gap: 8px; margin: 0 0 12px; }
.ghheat__eyedot {
width: 9px; height: 9px; border-radius: 50%;
background: #cf222e; box-shadow: 0 0 0 3px rgba(207, 34, 46, 0.14);
flex: 0 0 auto;
}
.ghheat__eyetext {
font-size: 12px; font-weight: 700; letter-spacing: 0.08em;
text-transform: uppercase; color: #cf222e;
}
/* Subtitle here is a prominent lede, not the muted variant. */
.ghheat__subtitle {
color: var(--ghc-text);
font-size: clamp(14px, 1.7vw, 16px);
/* 24px top gap matches the github.blog H2 → paragraph block spacing. */
margin: 24px 0 8px;
}
.ghheat__subtitle b { font-weight: 700; }
.ghheat__panel { margin-top: 36px; }
.ghheat__phead {
display: flex; align-items: baseline; gap: 9px; flex-wrap: wrap;
margin-bottom: 16px;
}
.ghheat__badge {
display: inline-flex; align-items: center; justify-content: center;
width: 22px; height: 22px; border-radius: 50%;
color: #fff; font-size: 12px; font-weight: 700;
flex: 0 0 auto; align-self: center;
}
.ghheat__ptitle {
font-size: clamp(16px, 2vw, 19px); font-weight: 700;
letter-spacing: -0.01em; color: var(--ghc-text);
}
.ghheat__pdesc { font-size: 14px; color: var(--ghc-muted); }
.ghheat__body { display: flex; gap: 30px; align-items: flex-start; flex-wrap: wrap; }
.ghheat__gridwrap { flex: 1 1 340px; min-width: 260px; }
.ghheat__grid { display: block; width: 100%; height: auto; overflow: visible; }
.ghheat__cell {
stroke: rgba(27, 31, 36, 0.06);
stroke-width: 1;
transition: stroke 0.1s ease;
}
.ghheat__cell:hover { stroke: #24292f; stroke-width: 1.4; }
.ghheat__cell--pad { opacity: 0; pointer-events: none; }
.ghheat__wd, .ghheat__month {
fill: var(--ghc-muted); font-size: 11px; font-weight: 600;
}
.ghheat__side { flex: 1 1 250px; min-width: 230px; }
.ghheat__stat { display: flex; align-items: baseline; gap: 7px; }
.ghheat__statbig {
font-size: clamp(26px, 4vw, 34px); font-weight: 800; letter-spacing: -0.02em;
line-height: 1;
}
.ghheat__statsmall { font-size: 15px; font-weight: 600; color: var(--ghc-muted); }
.ghheat__note {
margin: 10px 0 16px; font-size: 14px; line-height: 1.55; color: var(--ghc-muted);
}
.ghheat__note b { color: var(--ghc-text); font-weight: 700; }
.ghheat__legend {
display: flex; align-items: center; gap: 5px;
font-size: 12.5px; color: var(--ghc-muted); flex-wrap: wrap;
}
.ghheat__sw { width: 12px; height: 12px; border-radius: 3px; display: inline-block; flex: 0 0 auto; }
.ghheat__sw--empty { border: 1px solid #d0d7de; }
.ghheat__legunit { margin-left: 2px; }
.ghheat__extra {
display: flex; align-items: flex-start; gap: 7px; margin-top: 14px;
font-size: 12px; line-height: 1.5; color: var(--ghc-muted); max-width: 330px;
}
.ghheat__extra .ghheat__sw { margin-top: 2px; }
/* ------------------------------------------------------------------ */
/* Tooltip */
/* ------------------------------------------------------------------ */
.ghchart__tooltip {
position: absolute;
z-index: 5;
top: 0;
left: 0;
min-width: 128px;
max-width: 260px;
padding: 10px 12px;
background: var(--ghc-card-bg);
border: 1px solid var(--ghc-card-border);
border-radius: 12px;
/* Layered drop shadow from Primer Brand's ActionMenu (.ActionMenu__menu) */
box-shadow:
0px 100px 80px rgba(0, 0, 0, 0.01),
0px 41px 33px rgba(0, 0, 0, 0.02),
0px 22px 17px rgba(0, 0, 0, 0.02),
0px 12px 10px rgba(0, 0, 0, 0.03),
0px 6px 5px rgba(0, 0, 0, 0.04),
0px 2px 2px rgba(0, 0, 0, 0.07);
pointer-events: none;
opacity: 0;
transform: translate(-50%, -100%);
/* Fade in on show and out on hide; quickly ease left/top when tracking
from one point to the next. The position ease is suppressed on the
first appearance (see chart.js) so the tooltip fades in place rather
than sliding in from its previous spot. */
transition: opacity 0.12s ease,
left 0.1s cubic-bezier(0.4, 0, 0.2, 1),
top 0.1s cubic-bezier(0.4, 0, 0.2, 1);
will-change: opacity, transform, left, top;
}
.ghchart__tooltip[data-visible="true"] { opacity: 1; }
.ghchart__tt-title {
font-size: 13px;
font-weight: 700;
color: var(--ghc-text);
margin-bottom: 6px;
}
.ghchart__tt-row {
display: flex;
align-items: center;
gap: 8px;
font-size: 13px;
line-height: 1.6;
color: var(--ghc-muted);
}
.ghchart__tt-swatch { width: 10px; height: 10px; border-radius: 3px; flex: 0 0 auto; }
.ghchart__tt-name { flex: 1 1 auto; }
.ghchart__tt-value { font-weight: 700; color: var(--ghc-text); font-variant-numeric: tabular-nums; }
/* Bar tooltip body: a title line + free-form text lines (no swatches). */
.ghchart__tt-line {
font-size: 13px;
line-height: 1.5;
color: var(--ghc-muted);
}
.ghchart__tt-line:first-child { color: var(--ghc-text); font-weight: 600; }
.ghchart__tt-line + .ghchart__tt-line { margin-top: 2px; }
@media (prefers-reduced-motion: reduce) {
.ghchart__dot,
.ghchart__guide,
.ghchart__bar,
.ghchart__pt,
.ghchart__square,
.ghheat__cell,
.ghchart__tooltip { transition: none; }
}
/*
Shared chart engine — reusable renderer for the blog-charts library.
------------------------------------------------------------------
Exposes window.GHChartEngine = { renderers, render(config, mountEl?) }.
Renderers are keyed by config.type (currently "line"); add a new viz type
by registering GHChartEngine.renderers[] = function (root, config) {…}.
This same file is embedded verbatim at the top of each exported WordPress
chart.js, followed by that chart's CONFIG + a small auto-boot. It has no
external dependencies and is safe to include more than once on a page.
*/
(function (global) {
"use strict";
var SVGNS = "http://www.w3.org/2000/svg";
function el(tag, attrs) {
var node = document.createElementNS(SVGNS, tag);
if (attrs) {
for (var k in attrs) {
if (Object.prototype.hasOwnProperty.call(attrs, k)) {
node.setAttribute(k, attrs[k]);
}
}
}
return node;
}
function html(tag, cls) {
var node = document.createElement(tag);
if (cls) node.className = cls;
return node;
}
function clamp(v, lo, hi) { return v < lo ? lo : v > hi ? hi : v; }
var renderers = {};
/* ================================================================
Line chart renderer
================================================================ */
renderers.line = function (root, config) {
// viewBox geometry (design units; scales fluidly to any width).
var VB = { w: 1000, h: 560 };
var M = { top: 30, right: 34, bottom: 56, left: 86 };
// Inset the plotted points slightly from the axis so the first/last
// months (e.g. "Jan") don't crowd the y-axis labels (e.g. "50").
var PAD_X = 18;
var innerW = VB.w - M.left - M.right;
var innerH = VB.h - M.top - M.bottom;
// Reset (in case of re-render) and use a unique id prefix so multiple
// charts on one page never share gradient ids.
root.textContent = "";
var uid = config.mountId;
var n = config.labels.length;
var yMin = config.yAxis.min;
var yMax = config.yAxis.max;
var step = config.yAxis.step;
// Y-axis tick labels can format differently from the tooltip values
// (e.g. a plain "40" on the axis but "40%" in the tooltip).
var fmtAxis = config.formatAxis || config.formatValue;
var plotW = innerW - 2 * PAD_X;
var stepX = n dark ramp is derived from a single tunable base color.
================================================================ */
renderers.heatmap = function (root, config) {
root.textContent = "";
var WHITE = [255, 255, 255], BLACK = [0, 0, 0];
function hexToRgb(h) {
h = String(h).replace("#", "");
if (h.length === 3) h = h[0] + h[0] + h[1] + h[1] + h[2] + h[2];
return [parseInt(h.slice(0, 2), 16), parseInt(h.slice(2, 4), 16), parseInt(h.slice(4, 6), 16)];
}
function rgbToHex(c) {
return "#" + c.map(function (x) {
x = Math.max(0, Math.min(255, Math.round(x)));
return (x < 16 ? "0" : "") + x.toString(16);
}).join("");
}
function mix(a, b, t) {
return [a[0] + (b[0] - a[0]) * t, a[1] + (b[1] - a[1]) * t, a[2] + (b[2] - a[2]) * t];
}
// 5-step contribution ramp (level 0 faint -> level 4 dark) from one hex.
function rampFrom(baseHex) {
var b = hexToRgb(baseHex);
return [
rgbToHex(mix(b, WHITE, 0.80)),
rgbToHex(mix(b, WHITE, 0.52)),
rgbToHex(mix(b, WHITE, 0.20)),
baseHex,
rgbToHex(mix(b, BLACK, 0.30))
];
}
function trim0(s) { return s.replace(/\.0$/, ""); }
function fmtM(v) {
if (v >= 1000) { var g = v / 1000; return (g >= 10 ? Math.round(g) : trim0(g.toFixed(1))) + "B"; }
if (v >= 1) return trim0(v.toFixed(1)) + "M";
return Math.round(v * 1000) + "K";
}
var MON = ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"];
var MON_FULL = ["January", "February", "March", "April", "May", "June",
"July", "August", "September", "October", "November", "December"];
var WD = ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"];
var year = config.year || 2026;
var startM = config.startMonth != null ? config.startMonth : 0;
var endM = config.endMonth != null ? config.endMonth : 5;
var emptyColor = config.emptyColor || "#ebedf0";
function daysInMonth(y, m) { return new Date(y, m + 1, 0).getDate(); }
function parseISO(s) { var p = s.split("-"); return new Date(+p[0], +p[1] - 1, +p[2]); }
function dayStart(dt) { return new Date(dt.getFullYear(), dt.getMonth(), dt.getDate()); }
/* ---- Eyebrow + title + subtitle ----------------------------- */
if (config.eyebrow) {
var eb = html("div", "ghheat__eyebrow");
eb.appendChild(html("span", "ghheat__eyedot"));
var ebt = html("span", "ghheat__eyetext");
ebt.textContent = config.eyebrow;
eb.appendChild(ebt);
root.appendChild(eb);
}
var title = html("span", "ghchart__title");
title.textContent = config.title;
root.appendChild(title);
if (config.subtitle) {
var sub = html("p", "ghchart__subtitle ghheat__subtitle");
sub.innerHTML = config.subtitle;
root.appendChild(sub);
}
/* ---- One shared tooltip -------------------------------------- */
var tip = html("div", "ghchart__tooltip");
tip.setAttribute("role", "status");
var tipTitle = html("div", "ghchart__tt-title");
var tipBody = html("div", "ghchart__tt-body");
tip.appendChild(tipTitle);
tip.appendChild(tipBody);
root.appendChild(tip);
var motionMQ = window.matchMedia ? window.matchMedia("(prefers-reduced-motion:
[QUOTE]
Providing developers the best model for the task at hand has always been our goal. Earlier this year, we made that easier by launching Auto model selection, which reviews your task and matches it to the best-suited model for that task.
Today, we’re introducing Project HydraFusion, a research preview that delivers frontier intelligence through runtime orchestration. It creates a full execution plan, choosing from models across multiple providers to draft, critique and revise, or cascade to more powerful models to complete your task.
HydraFusion fills a key role in our overall strategy to deliver automated semantic routing between local, cloud, and compound models. For developers, that complexity stays behind the scenes: you select HydraFusion like any other model, and it chooses a workflow that balances performance, cost, and latency for each task.
Now available as a research preview
HydraFusion is available to users on all GitHub Copilot plans through /experimental in GitHub Copilot CLI. Usage is based on the tokens consumed by the models HydraFusion uses, priced at each model’s standard rate.
To try HydraFusion in Copilot CLI:
• Run /update to install the latest version
• Run /experimental on
• Run /model, then select HydraFusion (Research Preview)
Please post feedback in the GitHub Community.
HydraFusion treats workflow selection as an optimization problem. It uses capability signals for reasoning, code generation, debugging, and tool use to select the most efficient execution pattern to meet the quality bar.
For each request, HydraFusion currently chooses one of three execution patterns:
• Single. One selected model solves the task directly.
• Cascade. An efficient model drafts a solution and a quality gate decides whether to accept it or escalate to a stronger model.
• Critique. One model drafts a result, an independent read-only critic from a different model family reviews it (following the same review pattern as Rubber Duck), and the drafting model revises once.

Figure 1. HydraFusion architecture
Each pattern addresses a different quality-to-cost trade-off. Single preserves speed and efficiency when one model can solve the task directly. Cascade gives an efficient model the first attempt while retaining a path to stronger inference when the candidate does not clear the acceptance gate. Critique adds an independent perspective for tasks where review is more useful than another unaided attempt.
In offline evaluations across three agentic coding benchmarks, HydraFusion consistently demonstrated frontier-level quality with substantial estimated cost savings. On TerminalBench 2.1, it improved verified task quality by 4.9 percentage points at 67% lower estimated cost compared with Claude Opus 5.
Let’s dive into the approach, the results, and the benchmarks.
Adaptive multi-model orchestration
Developers already coordinate models manually: choosing one for a task, asking another to review the work, or escalating a difficult problem to a more capable model. HydraFusion brings that familiar process into the runtime. You choose HydraFusion once and stay focused on your task while it manages the models and workflow behind the scenes.
The key is selectivity. Some coding tasks can be solved directly, while others benefit from review, revision, or escalation. HydraFusion evaluates each request and chooses the least complex workflow expected to meet its needs, using additional model calls only when they are likely to improve the result. This adaptive approach balances quality, cost, and latency across models.
As the model frontier advances, so does HydraFusion. When new models become available in GitHub Copilot, we can evaluate and incorporate them into its model pool, bringing their strengths to the tasks best suited to them.
Building HydraFusion
Turning adaptive multi-model orchestration into one dependable coding experience requires careful control of execution, review, cost, and repository state. HydraFusion is built around five operating principles:
• Complete accounting. Aggregate cost and usage across every workflow leg, including drafting, critique, revision, escalation, retry, and fallback.
• Bounded execution. Give each leg explicit timeout and cancellation behavior to keep execution and cost within defined limits.
• Isolated review. Run review steps in isolated, tool-less contexts, while solver steps use the shared workspace and normal permission-aware agent loop. This allows models to assess the work independently without modifying the repository.
• Fail-safe application. Apply no patch when the workflow is cancelled or fails validation, preventing incomplete changes from reaching the repository.
• Validated routing. Verify workflow definitions, model bindings, fallback behavior, and model availability before execution begins.
Together, these principles make multi-model orchestration practical for repository-level work. Internally, the runtime records the role, outcome, cost, latency, and diagnostics of each leg so the workflow can be understood after execution. Externally, the developer receives one coherent response and one permission-aware change set.
Showing progress without showing unfinished work
• Today: HydraFusion shows workflow stages but holds intermediate drafts until it returns one coherent result.
• Why: Those drafts may be reviewed, revised, or discarded, so showing them live could make unfinished work appear final.
• What we’re learning: Waiting without enough visibility is a real trade-off for developers.
• Next: We’re actively exploring better progress updates, guided by feedback from the research preview.
Benchmarking results
Fixed HydraFusion policies were evaluated across three agentic coding benchmarks — TerminalBench 2.1, DeepSWE, and CheckpointBench, our internal benchmark based on real GitHub Copilot sessions — using Claude Opus 5 and GPT-5.6 Sol as comparison baselines. Each policy used the same task inputs, tools, execution limits, pricing assumptions, grading conditions, and treatment of missing results. The evaluation measured verified task quality, which is the share of tasks confirmed as correctly answered, and the complete estimated workflow cost. Cost accounting included every invoked leg, such as drafting, critique, revision, escalation, retry, and fallback. The results below show the best tuned HydraFusion configuration.
Benchmarks Cost vs. Opus 5 Quality vs. Opus 5 TerminalBench 2.167% lower+4.9 points DeepSWE 36% lower -1.5 points CheckpointBench 65% lower-0.1 points Table 1. HydraFusion quality and cost across three agentic benchmarks, relative to Opus 5.
These controlled offline results are specific to the evaluated benchmark revisions, workflow configurations, model pool, and pricing assumptions, with all models evaluated at the same medium reasoning level. Through this research preview, we’ll validate how these results translate to real developer workloads and use the findings to further optimize HydraFusion for production quality, latency, reliability, caching efficiency, cost, and safety.
TerminalBench 2.1
TerminalBench 2.1 evaluates coding agents on complex, multi-step tasks in terminal environments.
Figure 2 compares HydraFusion and Opus 5 across verified task quality and estimated workflow cost.
.ghchart {
/* Theme tokens */
--ghc-text: #1f2328;
--ghc-muted: #656d76;
--ghc-grid: #d0d7de;
--ghc-axis: #59636e;
--ghc-card-bg: #ffffff;
--ghc-card-border: #d0d7de;
--ghc-divider: #b6bfb8;
--ghc-guide: #afb8c1;
/* GitHub brand font — matches the github.blog stack (Mona Sans, with
the blog's metric fallback) so it renders identically in-context. */
font-family: "Mona Sans", "Mona Sans Fallback", -apple-system, "system-ui",
"Segoe UI", Helvetica, Arial, sans-serif;
-webkit-font-smoothing: antialiased;
text-rendering: optimizeLegibility;
position: relative;
box-sizing: border-box;
width: 100%;
max-width: 699px;
margin: 0 auto;
color: var(--ghc-text);
line-height: 1.4;
}
.ghchart *,
.ghchart *::before,
.ghchart *::after { box-sizing: border-box; }
/* ------------------------------------------------------------------ */
/* Header + legend */
/* ------------------------------------------------------------------ */
.ghchart__header {
display: flex;
justify-content: flex-end;
align-items: center;
min-height: 24px;
margin-bottom: 4px;
}
.ghchart__legend {
display: flex;
align-items: center;
gap: 20px;
flex-wrap: wrap;
justify-content: flex-end;
}
.ghchart__legend-item {
display: inline-flex;
align-items: center;
gap: 8px;
font-size: clamp(14px, 1.9vw, 17px);
font-weight: 600;
color: var(--ghc-muted);
white-space: nowrap;
}
.ghchart__swatch {
width: 14px;
height: 14px;
border-radius: 4px;
flex: 0 0 auto;
}
/* ------------------------------------------------------------------ */
/* Plot (SVG) */
/* ------------------------------------------------------------------ */
.ghchart__plot {
display: block;
width: 100%;
height: auto;
overflow: visible;
touch-action: pan-y;
}
.ghchart__gridline {
stroke: var(--ghc-grid);
stroke-width: 1;
stroke-dasharray: 2 6;
stroke-linecap: round;
opacity: 0.9;
}
.ghchart__axislabel {
fill: var(--ghc-axis);
font-size: 22px;
font-weight: 500;
}
.ghchart__axislabel--x { font-weight: 600; }
.ghchart__line {
fill: none;
stroke-linecap: round;
stroke-linejoin: round;
}
.ghchart__dot { transition: r 0.12s ease; }
/* Dashed threshold line (e.g. a target/reference level) + floating label.
Stroke color is set inline per chart; dash + label styling live here. */
.ghchart__threshold {
stroke-width: 2;
stroke-dasharray: 7 6;
stroke-linecap: round;
opacity: 0.9;
}
.ghchart__threshold-label {
font-size: 20px;
font-weight: 700;
letter-spacing: -0.01em;
}
/* Hatched band marking a partial / projected trailing region. */
.ghchart__projection { pointer-events: none; }
.ghchart__guide {
stroke: var(--ghc-guide);
stroke-width: 1.5;
stroke-dasharray: 4 4;
opacity: 0;
transition: opacity 0.12s ease;
}
.ghchart__hit { fill: transparent; }
/* ------------------------------------------------------------------ */
/* Scatter plot */
/* ------------------------------------------------------------------ */
.ghchart__gridline--v { /* vertical gridlines reuse the dashed grid style */ }
.ghchart__axistitle {
fill: var(--ghc-muted);
font-size: 20px;
font-weight: 600;
letter-spacing: -0.01em;
}
/* Dotted reference crosshair through the "reference" point. Stroke color
is set inline (tracks the reference series color). */
.ghchart__crosshair {
stroke-width: 2;
stroke-dasharray: 2 7;
stroke-linecap: round;
opacity: 0.85;
pointer-events: none;
}
.ghchart__halo { opacity: 0.18; pointer-events: none; }
.ghchart__pt { transition: r 0.12s ease; }
.ghchart__pt-label {
fill: var(--ghc-muted);
font-size: 19px;
font-weight: 600;
letter-spacing: -0.01em;
pointer-events: none;
}
.ghchart__pt-label--strong { font-size: 21px; font-weight: 700; }
.ghchart__pthit { fill: transparent; cursor: default; outline: none; }
.ghchart__pthit:focus-visible {
outline: 2px solid var(--ghc-axis);
outline-offset: 1px;
}
.ghchart__swatch--dot { border-radius: 50%; }
/* ------------------------------------------------------------------ */
/* Trend chart (dated time-series with narrative headline) */
/* ------------------------------------------------------------------ */
.ghchart__headline {
font-size: clamp(17px, 2.3vw, 20px);
line-height: 1.32;
font-weight: 700;
letter-spacing: -0.01em;
color: var(--ghc-text);
margin: 0 0 8px;
}
/* Trend legend sits left-aligned under the headline/subtitle. */
.ghchart__legend--trend {
justify-content: flex-start;
margin: 2px 0 14px;
}
.ghchart__glyph { flex: 0 0 auto; }
.ghchart__glyph--square {
width: 13px; height: 13px; border-radius: 2px;
}
.ghchart__glyph--diamond {
width: 12px; height: 12px; border: 1.5px solid; transform: rotate(45deg);
}
.ghchart__glyph--line {
width: 20px; height: 0; border-top: 2px dashed; border-radius: 0;
}
/* Phase background columns + separators + top labels. */
.ghchart__phase { fill: #f6f8fa; opacity: 0.55; }
.ghchart__phase--alt { fill: #ffffff; opacity: 0; }
.ghchart__phase-sep {
stroke: var(--ghc-grid); stroke-width: 1; stroke-dasharray: 2 5; opacity: 0.8;
}
.ghchart__phase-label {
fill: var(--ghc-muted);
font-size: 15px;
font-weight: 700;
letter-spacing: 0.06em;
}
.ghchart__phase-avg {
font-size: 16px;
font-weight: 700;
letter-spacing: -0.01em;
}
/* Dashed trend line + ± noise band + slope label. */
.ghchart__trendband { opacity: 0.12; pointer-events: none; }
.ghchart__trendline {
stroke-width: 2.5;
stroke-dasharray: 8 6;
stroke-linecap: round;
fill: none;
}
.ghchart__slope {
font-size: 20px;
font-weight: 700;
letter-spacing: -0.01em;
}
/* Horizontal reference line + label. */
.ghchart__refline {
stroke-width: 2;
stroke-dasharray: 7 6;
stroke-linecap: round;
opacity: 0.85;
}
.ghchart__reflabel {
font-size: 16px;
font-weight: 700;
letter-spacing: -0.01em;
}
/* Data squares — values reveal on hover only. */
.ghchart__square { transition: width 0.12s ease, height 0.12s ease, x 0.12s ease, y 0.12s ease; }
.ghchart__squarehit { fill: transparent; cursor: default; outline: none; }
.ghchart__squarehit:focus-visible {
outline: 2px solid var(--ghc-axis);
outline-offset: 1px;
}
/* Invalid-run markers + leader-line annotations. */
.ghchart__invalid { stroke-width: 2; }
.ghchart__invalid-x { stroke-width: 2; stroke-linecap: round; }
.ghchart__leader { stroke-width: 1; stroke-dasharray: 2 3; opacity: 0.8; }
.ghchart__leader--callout { stroke-dasharray: none; opacity: 0.6; }
.ghchart__anno-title {
font-size: 15px;
font-weight: 700;
letter-spacing: -0.01em;
}
.ghchart__anno-sub {
fill: var(--ghc-muted);
font-size: 13px;
font-weight: 500;
}
/* Final-callout labels read a touch larger than the invalid-run notes. */
.ghchart__anno-title--callout { font-size: 17px; }
.ghchart__anno-sub--callout { font-size: 14px; }
/* Bracket span annotation. */
.ghchart__bracket { stroke: var(--ghc-axis); stroke-width: 1.5; opacity: 0.75; }
.ghchart__bracket-label {
fill: var(--ghc-muted);
font-size: 13px;
font-weight: 700;
letter-spacing: 0.04em;
}
/* Final callout ring. */
.ghchart__callout-ring { fill: none; stroke-width: 2; }
/* ------------------------------------------------------------------ */
/* Title + divider */
/* ------------------------------------------------------------------ */
.ghchart__title {
/* Matches the github.blog H2 (.wp-block-heading) exactly */
display: block;
font-size: 32px;
line-height: 40px;
font-weight: 700;
letter-spacing: -0.18px;
color: var(--ghc-text);
}
.ghchart__rule {
height: 1.5px;
border-radius: 1.5px;
background: var(--ghc-divider);
margin: 10px 0 18px;
}
/* ------------------------------------------------------------------ */
/* Bar chart: subtitle, panels, bars, caption */
/* ------------------------------------------------------------------ */
.ghchart__subtitle {
margin: -6px 0 20px;
font-size: clamp(13px, 1.6vw, 15px);
line-height: 1.5;
color: var(--ghc-muted);
}
.ghchart__panel { margin-top: 30px; }
.ghchart__panel:first-of-type { margin-top: 6px; }
.ghchart__panel-title {
margin: 0 0 6px;
font-size: clamp(15px, 1.9vw, 18px);
font-weight: 700;
letter-spacing: -0.01em;
color: var(--ghc-text);
}
.ghchart__plot--bar { touch-action: pan-y; }
.ghchart__axisline {
stroke: var(--ghc-axis);
stroke-width: 1.25;
opacity: 0.35;
}
.ghchart__bar { transition: opacity 0.12s ease; }
.ghchart__barhit { fill: transparent; }
/* Always-on value labels above selected bars. */
.ghchart__barvalue {
fill: var(--ghc-text);
font-size: 20px;
font-weight: 700;
font-variant-numeric: tabular-nums;
pointer-events: none;
}
/* Anchor annotation: dotted reference line + floating pill. */
.ghchart__anno-line {
stroke: #8b949e;
stroke-width: 1.5;
stroke-dasharray: 4 3;
stroke-linecap: round;
pointer-events: none;
}
.ghchart__anno-chip {
fill: #ffebe9;
stroke: #ffcecb;
stroke-width: 1;
pointer-events: none;
}
.ghchart__anno-chip-text {
fill: #a0111f;
font-weight: 700;
pointer-events: none;
}
.ghchart__caption {
margin-top: 22px;
padding-top: 12px;
border-top: 1px solid var(--ghc-card-border);
font-size: 12.5px;
line-height: 1.55;
color: var(--ghc-muted);
}
.ghchart__caption b { color: var(--ghc-text); font-weight: 700; }
.ghchart__caption code {
font-family: ui-monospace, SFMono-Regular, "SF Mono", Menlo, Consolas, monospace;
font-size: 0.92em;
padding: 1px 5px;
background: #f6f8fa;
border: 1px solid var(--ghc-card-border);
border-radius: 5px;
}
.ghchart__tag {
display: inline-block;
font-size: 10px;
font-weight: 650;
letter-spacing: 0.03em;
text-transform: uppercase;
padding: 2px 7px;
border-radius: 999px;
border: 1px solid #aceebb;
color: #0a5223;
background: #dafbe1;
white-space: nowrap;
vertical-align: middle;
}
/* ------------------------------------------------------------------ */
/* Distribution dashboard (segmented mix bar + ranked bar panels) */
/* ------------------------------------------------------------------ */
.ghchart__dist-section { margin-top: 28px; }
.ghchart__dist-section:first-of-type { margin-top: 12px; }
/* Segmented proportional "mix" bar: name inside, count/percent below.
Separators are 2px inside borders (box-sizing:border-box) rather than a
flex gap, so the three segments sum to exactly 100% with no overflow. */
.ghchart__seg {
display: flex;
width: 100%;
height: 42px;
margin-top: 2px;
border-radius: 7px;
overflow: hidden;
}
.ghchart__seg-cell {
display: flex;
align-items: center;
justify-content: center;
min-width: 0;
color: #ffffff;
font-size: 15px;
font-weight: 700;
letter-spacing: -0.01em;
white-space: nowrap;
overflow: hidden;
}
.ghchart__seg-cell + .ghchart__seg-cell { border-left: 2px solid var(--ghc-card-bg); }
.ghchart__seg-note-row {
display: flex;
width: 100%;
margin-top: 8px;
}
.ghchart__seg-note {
min-width: 0;
text-align: center;
font-size: 12.5px;
color: var(--ghc-muted);
font-variant-numeric: tabular-nums;
white-space: nowrap;
}
/* Keep the outer notes from spilling past the chart edges. */
.ghchart__seg-note:first-child { text-align: left; }
.ghchart__seg-note:last-child { text-align: right; }
.ghchart__seg-note b { color: var(--ghc-text); font-weight: 700; }
/* Ranked horizontal bar list: [category | bar track | value].
The list is the grid and rows use display:contents, so the category,
bar, and value columns line up across every row in a panel. */
.ghchart__dist-list {
display: grid;
grid-template-columns: max-content 1fr max-content;
align-items: center;
column-gap: 12px;
row-gap: 10px;
margin-top: 4px;
}
.ghchart__dist-row { display: contents; }
.ghchart__dist-cat {
text-align: right;
font-size: 13px;
line-height: 1.25;
color: var(--ghc-text);
white-space: nowrap;
}
.ghchart__dist-track { width: 100%; }
.ghchart__dist-bar {
height: 15px;
min-width: 2px;
border-radius: 3px;
}
.ghchart__dist-val {
font-size: 12.5px;
color: var(--ghc-muted);
font-variant-numeric: tabular-nums;
white-space: nowrap;
}
.ghchart__dist-val b { color: var(--ghc-text); font-weight: 700; }
/* Two-up panel grid (collapses to one column on narrow screens).
minmax(0,1fr) lets columns shrink so long labels never force overflow. */
.ghchart__dist-cols {
margin-top: 26px;
display: grid;
grid-template-columns: repeat(2, minmax(0, 1fr));
gap: 24px 30px;
}
.ghchart__dist-cols .ghchart__dist-section { margin-top: 0; }
.ghchart__dist-cols--stack { grid-template-columns: 1fr; }
.ghchart__dist-cols--stack .ghchart__dist-section { margin-top: 28px; }
.ghchart__dist-cols--stack .ghchart__dist-section:first-child { margin-top: 0; }
/* Tighten the label/value type a touch in the 2-up layout so the bars
keep room at the 699px blog width. */
.ghchart__dist-cols:not(.ghchart__dist-cols--stack) .ghchart__dist-cat { font-size: 12px; }
.ghchart__dist-cols:not(.ghchart__dist-cols--stack) .ghchart__dist-val { font-size: 11.5px; }
.ghchart__dist-cols:not(.ghchart__dist-cols--stack) .ghchart__dist-list { column-gap: 9px; }
@media (max-width: 600px) {
.ghchart__dist-cols { grid-template-columns: 1fr; gap: 26px; }
.ghchart__dist-cols .ghchart__dist-section ~ .ghchart__dist-section { margin-top: 2px; }
}
/* Let long category labels wrap on very narrow (mobile) widths. */
@media (max-width: 480px) {
.ghchart__dist-cat { white-space: normal; }
}
/* ------------------------------------------------------------------ */
/* Calendar heatmap (GitHub contribution-graph style) */
/* ------------------------------------------------------------------ */
.ghheat__eyebrow { display: flex; align-items: center; gap: 8px; margin: 0 0 12px; }
.ghheat__eyedot {
width: 9px; height: 9px; border-radius: 50%;
background: #cf222e; box-shadow: 0 0 0 3px rgba(207, 34, 46, 0.14);
flex: 0 0 auto;
}
.ghheat__eyetext {
font-size: 12px; font-weight: 700; letter-spacing: 0.08em;
text-transform: uppercase; color: #cf222e;
}
/* Subtitle here is a prominent lede, not the muted variant. */
.ghheat__subtitle {
color: var(--ghc-text);
font-size: clamp(14px, 1.7vw, 16px);
/* 24px top gap matches the github.blog H2 → paragraph block spacing. */
margin: 24px 0 8px;
}
.ghheat__subtitle b { font-weight: 700; }
.ghheat__panel { margin-top: 36px; }
.ghheat__phead {
display: flex; align-items: baseline; gap: 9px; flex-wrap: wrap;
margin-bottom: 16px;
}
.ghheat__badge {
display: inline-flex; align-items: center; justify-content: center;
width: 22px; height: 22px; border-radius: 50%;
color: #fff; font-size: 12px; font-weight: 700;
flex: 0 0 auto; align-self: center;
}
.ghheat__ptitle {
font-size: clamp(16px, 2vw, 19px); font-weight: 700;
letter-spacing: -0.01em; color: var(--ghc-text);
}
.ghheat__pdesc { font-size: 14px; color: var(--ghc-muted); }
.ghheat__body { display: flex; gap: 30px; align-items: flex-start; flex-wrap: wrap; }
.ghheat__gridwrap { flex: 1 1 340px; min-width: 260px; }
.ghheat__grid { display: block; width: 100%; height: auto; overflow: visible; }
.ghheat__cell {
stroke: rgba(27, 31, 36, 0.06);
stroke-width: 1;
transition: stroke 0.1s ease;
}
.ghheat__cell:hover { stroke: #24292f; stroke-width: 1.4; }
.ghheat__cell--pad { opacity: 0; pointer-events: none; }
.ghheat__wd, .ghheat__month {
fill: var(--ghc-muted); font-size: 11px; font-weight: 600;
}
.ghheat__side { flex: 1 1 250px; min-width: 230px; }
.ghheat__stat { display: flex; align-items: baseline; gap: 7px; }
.ghheat__statbig {
font-size: clamp(26px, 4vw, 34px); font-weight: 800; letter-spacing: -0.02em;
line-height: 1;
}
.ghheat__statsmall { font-size: 15px; font-weight: 600; color: var(--ghc-muted); }
.ghheat__note {
margin: 10px 0 16px; font-size: 14px; line-height: 1.55; color: var(--ghc-muted);
}
.ghheat__note b { color: var(--ghc-text); font-weight: 700; }
.ghheat__legend {
display: flex; align-items: center; gap: 5px;
font-size: 12.5px; color: var(--ghc-muted); flex-wrap: wrap;
}
.ghheat__sw { width: 12px; height: 12px; border-radius: 3px; display: inline-block; flex: 0 0 auto; }
.ghheat__sw--empty { border: 1px solid #d0d7de; }
.ghheat__legunit { margin-left: 2px; }
.ghheat__extra {
display: flex; align-items: flex-start; gap: 7px; margin-top: 14px;
font-size: 12px; line-height: 1.5; color: var(--ghc-muted); max-width: 330px;
}
.ghheat__extra .ghheat__sw { margin-top: 2px; }
/* ------------------------------------------------------------------ */
/* Tooltip */
/* ------------------------------------------------------------------ */
.ghchart__tooltip {
position: absolute;
z-index: 5;
top: 0;
left: 0;
min-width: 128px;
max-width: 260px;
padding: 10px 12px;
background: var(--ghc-card-bg);
border: 1px solid var(--ghc-card-border);
border-radius: 12px;
/* Layered drop shadow from Primer Brand's ActionMenu (.ActionMenu__menu) */
box-shadow:
0px 100px 80px rgba(0, 0, 0, 0.01),
0px 41px 33px rgba(0, 0, 0, 0.02),
0px 22px 17px rgba(0, 0, 0, 0.02),
0px 12px 10px rgba(0, 0, 0, 0.03),
0px 6px 5px rgba(0, 0, 0, 0.04),
0px 2px 2px rgba(0, 0, 0, 0.07);
pointer-events: none;
opacity: 0;
transform: translate(-50%, -100%);
/* Fade in on show and out on hide; quickly ease left/top when tracking
from one point to the next. The position ease is suppressed on the
first appearance (see chart.js) so the tooltip fades in place rather
than sliding in from its previous spot. */
transition: opacity 0.12s ease,
left 0.1s cubic-bezier(0.4, 0, 0.2, 1),
top 0.1s cubic-bezier(0.4, 0, 0.2, 1);
will-change: opacity, transform, left, top;
}
.ghchart__tooltip[data-visible="true"] { opacity: 1; }
.ghchart__tt-title {
font-size: 13px;
font-weight: 700;
color: var(--ghc-text);
margin-bottom: 6px;
}
.ghchart__tt-row {
display: flex;
align-items: center;
gap: 8px;
font-size: 13px;
line-height: 1.6;
color: var(--ghc-muted);
}
.ghchart__tt-swatch { width: 10px; height: 10px; border-radius: 3px; flex: 0 0 auto; }
.ghchart__tt-name { flex: 1 1 auto; }
.ghchart__tt-value { font-weight: 700; color: var(--ghc-text); font-variant-numeric: tabular-nums; }
/* Bar tooltip body: a title line + free-form text lines (no swatches). */
.ghchart__tt-line {
font-size: 13px;
line-height: 1.5;
color: var(--ghc-muted);
}
.ghchart__tt-line:first-child { color: var(--ghc-text); font-weight: 600; }
.ghchart__tt-line + .ghchart__tt-line { margin-top: 2px; }
@media (prefers-reduced-motion: reduce) {
.ghchart__dot,
.ghchart__guide,
.ghchart__bar,
.ghchart__pt,
.ghchart__square,
.ghheat__cell,
.ghchart__tooltip { transition: none; }
}
/*
Shared chart engine — reusable renderer for the blog-charts library.
------------------------------------------------------------------
Exposes window.GHChartEngine = { renderers, render(config, mountEl?) }.
Renderers are keyed by config.type (currently "line"); add a new viz type
by registering GHChartEngine.renderers[] = function (root, config) {…}.
This same file is embedded verbatim at the top of each exported WordPress
chart.js, followed by that chart's CONFIG + a small auto-boot. It has no
external dependencies and is safe to include more than once on a page.
*/
(function (global) {
"use strict";
var SVGNS = "http://www.w3.org/2000/svg";
function el(tag, attrs) {
var node = document.createElementNS(SVGNS, tag);
if (attrs) {
for (var k in attrs) {
if (Object.prototype.hasOwnProperty.call(attrs, k)) {
node.setAttribute(k, attrs[k]);
}
}
}
return node;
}
function html(tag, cls) {
var node = document.createElement(tag);
if (cls) node.className = cls;
return node;
}
function clamp(v, lo, hi) { return v < lo ? lo : v > hi ? hi : v; }
var renderers = {};
/* ================================================================
Line chart renderer
================================================================ */
renderers.line = function (root, config) {
// viewBox geometry (design units; scales fluidly to any width).
var VB = { w: 1000, h: 560 };
var M = { top: 30, right: 34, bottom: 56, left: 86 };
// Inset the plotted points slightly from the axis so the first/last
// months (e.g. "Jan") don't crowd the y-axis labels (e.g. "50").
var PAD_X = 18;
var innerW = VB.w - M.left - M.right;
var innerH = VB.h - M.top - M.bottom;
// Reset (in case of re-render) and use a unique id prefix so multiple
// charts on one page never share gradient ids.
root.textContent = "";
var uid = config.mountId;
var n = config.labels.length;
var yMin = config.yAxis.min;
var yMax = config.yAxis.max;
var step = config.yAxis.step;
// Y-axis tick labels can format differently from the tooltip values
// (e.g. a plain "40" on the axis but "40%" in the tooltip).
var fmtAxis = config.formatAxis || config.formatValue;
var plotW = innerW - 2 * PAD_X;
var stepX = n dark ramp is derived from a single tunable base color.
================================================================ */
renderers.heatmap = function (root, config) {
root.textContent = "";
var WHITE = [255, 255, 255], BLACK = [0, 0, 0];
function hexToRgb(h) {
h = String(h).replace("#", "");
if (h.length === 3) h = h[0] + h[0] + h[1] + h[1] + h[2] + h[2];
return [parseInt(h.slice(0, 2), 16), parseInt(h.slice(2, 4), 16), parseInt(h.slice(4, 6), 16)];
}
function rgbToHex(c) {
return "#" + c.map(function (x) {
x = Math.max(0, Math.min(255, Math.round(x)));
return (x < 16 ? "0" : "") + x.toString(16);
}).join("");
}
function mix(a, b, t) {
return [a[0] + (b[0] - a[0]) * t, a[1] + (b[1] - a[1]) * t, a[2] + (b[2] - a[2]) * t];
}
// 5-step contribution ramp (level 0 faint -> level 4 dark) from one hex.
function rampFrom(baseHex) {
var b = hexToRgb(baseHex);
return [
rgbToHex(mix(b, WHITE, 0.80)),
rgbToHex(mix(b, WHITE, 0.52)),
rgbToHex(mix(b, WHITE, 0.20)),
baseHex,
rgbToHex(mix(b, BLACK, 0.30))
];
}
function trim0(s) { return s.replace(/\.0$/, ""); }
function fmtM(v) {
if (v >= 1000) { var g = v / 1000; return (g >= 10 ? Math.round(g) : trim0(g.toFixed(1))) + "B"; }
if (v >= 1) return trim0(v.toFixed(1)) + "M";
return Math.round(v * 1000) + "K";
}
var MON = ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"];
var MON_FULL = ["January", "February", "March", "April", "May", "June",
"July", "August", "September", "October", "November", "December"];
var WD = ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"];
var year = config.year || 2026;
var startM = config.startMonth != null ? config.startMonth : 0;
var endM = config.endMonth != null ? config.endMonth : 5;
var emptyColor = config.emptyColor || "#ebedf0";
function daysInMonth(y, m) { return new Date(y, m + 1, 0).getDate(); }
function parseISO(s) { var p = s.split("-"); return new Date(+p[0], +p[1] - 1, +p[2]); }
function dayStart(dt) { return new Date(dt.getFullYear(), dt.getMonth(), dt.getDate()); }
/* ---- Eyebrow + title + subtitle ----------------------------- */
if (config.eyebrow) {
var eb = html("div", "ghheat__eyebrow");
eb.appendChild(html("span", "ghheat__eyedot"));
var ebt = html("span", "ghheat__eyetext");
ebt.textContent = config.eyebrow;
eb.appendChild(ebt);
root.appendChild(eb);
}
var title = html("span", "ghchart__title");
title.textContent = config.title;
root.appendChild(title);
if (config.subtitle) {
var sub = html("p", "ghchart__subtitle ghheat__subtitle");
sub.innerHTML = config.subtitle;
root.appendChild(sub);
}
/* ---- One shared tooltip -------------------------------------- */
var tip = html("div", "ghchart__tooltip");
tip.setAttribute("role", "status");
var tipTitle = html("div", "ghchart__tt-title");
var tipBody = html("div", "ghchart__tt-body");
tip.appendChild(tipTitle);
tip.appendChild(tipBody);
root.appendChild(tip);
var motionMQ = window.matchMedia ? window.matchMedia("(prefers-reduced-motion: reduce)") : null;
function reduceMotion() { return !!(motionMQ && motionMQ.matches); }
var activeCell = null;
function hideTip() { activeCell = null; tip.setAttribute("data-visible", "false"); }
function showTip(rectEl, titleText, lines) {
var reappearing = activeCell === null;
activeCell = rectEl;
tipTitle.textContent = titleText;
tipBody.textContent = "";
lines.forEach(function (ln) {
var r = html("div", "ghchart__tt-line");
r.innerHTML = ln;
tipBody.appendChild(r);
});
var a = rectEl.getBoundingClientRect(), rr = root.getBoundingClientRect();
var left = a.left + a.width / 2 - rr.left, top = a.top - rr.top - 12;
var suppress = reappearing && !reduceMotion();
if (suppress) tip.style.transition = "opacity 0.12s ease";
tip.setAttribute("data-visible", "true");
var halfW = tip.offsetWidth / 2;
left = clamp(left, halfW + 2, root.clientWidth - halfW - 2);
if (top - tip.offsetHeight < 0) {
top = a.bottom - rr.top + 12;
tip.style.transform = "translate(-50%, 0)";
} else {
tip.style.transform = "translate(-50%, -100%)";
}
tip.style.left = left + "px";
tip.style.top = top + "px";
if (suppress) { void tip.offsetWidth; tip.style.transition = ""; }
}
/* ---- Panels -------------------------------------------------- */
(config.panels || []).forEach(function (panel, pi) {
var base = (config.series && config.series[pi] && config.series[pi].color) || panel.base || "#3094ff";
var ramp = rampFrom(base);
var strong = ramp[4];
var totals = panel.months || [];
var activeVals = [];
for (var mi = startM; mi existing.version) {
/* This bundle is newer. Every export embeds the COMPLETE engine, so this
bundle's `renderers` is a self-contained, up-to-date set. Adopt it as
the shared registry (upgrading older same-named renderers in place),
while defensively preserving any renderer types only the older engine
happened to have. Then take over render(). */
for (var ek in existing.renderers) {
if (Object.prototype.hasOwnProperty.call(existing.renderers, ek) && !renderers[ek]) {
renderers[ek] = existing.renderers[ek];
}
}
existing.renderers = renderers;
existing.render = render;
existing.version = ENGINE_VERSION;
} else {
/* Same or older version already present: only contribute renderer types
the shared registry is missing (don't clobber an equal/newer engine). */
for (var rk in renderers) {
if (Object.prototype.hasOwnProperty.call(renderers, rk) && !existing.renderers[rk]) {
existing.renderers[rk] = renderers[rk];
}
}
}
} else {
/* No engine yet, or a pre-versioning (old-style) engine loaded first.
Take over, preserving any renderers the old-style engine registered. */
if (existing && existing.renderers) {
for (var ok in existing.renderers) {
if (Object.prototype.hasOwnProperty.call(existing.renderers, ok) && !renderers[ok]) {
renderers[ok] = existing.renderers[ok];
}
}
}
global.GHChartEngine = { renderers: renderers, render: render, version: ENGINE_VERSION };
}
})(typeof window !== "undefined" ? window : this);
;(function () {
"use strict";
var CONFIG = {
mountId: "gh-chart-hf-terminal",
type: "scatter",
title: "TerminalBench 2.1 - GitHub Copilot CLI",
xAxis: {
min: 0,
max: 1,
step: 0.2,
title: "Mean cost per task (USD)",
format: function (v) { return "$" + v.toFixed(2); }
},
yAxis: {
min: 50,
max: 90,
step: 10,
title: "Resolution rate (pass@1)",
format: function (v) { return v + "%"; }
},
tipSuffix: "/task",
series: [
{
name: "HydraFusion",
color: "#2da44f"
},
{
name: "Opus 5 (reference)",
color: "#8250df"
},
{
name: "Other solo models",
color: "#8c959f"
}
],
points: [
{
label: "HydraFusion",
x: 0.3,
y: 84.7,
series: 0,
highlight: true,
labelPos: "above"
},
{
label: "Opus 5",
x: 0.9,
y: 79.8,
series: 1,
reference: true,
labelPos: "above"
},
{
label: "Sonnet 5",
x: 0.55,
y: 76,
series: 2,
labelPos: "right"
},
{
label: "GPT-5.6-Sol",
x: 0.3,
y: 73.3,
series: 2,
labelPos: "right"
},
{
label: "GPT-5.6-Terra",
x: 0.25,
y: 72.5,
series: 2,
labelPos: "left"
},
{
label: "GPT-5.6-Luna",
x: 0.03,
y: 56,
series: 2,
labelPos: "right"
}
]
};
function boot() {
var root = document.getElementById(CONFIG.mountId);
if (!root || root.getAttribute("data-gh-rendered")) return;
root.setAttribute("data-gh-rendered", "1");
window.GHChartEngine.render(CONFIG);
}
if (document.readyState === "loading") {
document.addEventListener("DOMContentLoaded", boot);
} else {
boot();
}
})();
DeepSWE
DeepSWE evaluates challenging repository-level software engineering tasks that require navigating large codebases, understanding cross-file dependencies, and producing end-to-end fixes. On this benchmark, HydraFusion comes within 1.5 percentage points of Opus 5 while reducing cost by 36%, demonstrating a compelling quality-cost tradeoff for complex real-world engineering tasks.
.ghchart {
/* Theme tokens */
--ghc-text: #1f2328;
--ghc-muted: #656d76;
--ghc-grid: #d0d7de;
--ghc-axis: #59636e;
--ghc-card-bg: #ffffff;
--ghc-card-border: #d0d7de;
--ghc-divider: #b6bfb8;
--ghc-guide: #afb8c1;
/* GitHub brand font — matches the github.blog stack (Mona Sans, with
the blog's metric fallback) so it renders identically in-context. */
font-family: "Mona Sans", "Mona Sans Fallback", -apple-system, "system-ui",
"Segoe UI", Helvetica, Arial, sans-serif;
-webkit-font-smoothing: antialiased;
text-rendering: optimizeLegibility;
position: relative;
box-sizing: border-box;
width: 100%;
max-width: 699px;
margin: 0 auto;
color: var(--ghc-text);
line-height: 1.4;
}
.ghchart *,
.ghchart *::before,
.ghchart *::after { box-sizing: border-box; }
/* ------------------------------------------------------------------ */
/* Header + legend */
/* ------------------------------------------------------------------ */
.ghchart__header {
display: flex;
justify-content: flex-end;
align-items: center;
min-height: 24px;
margin-bottom: 4px;
}
.ghchart__legend {
display: flex;
align-items: center;
gap: 20px;
flex-wrap: wrap;
justify-content: flex-end;
}
.ghchart__legend-item {
display: inline-flex;
align-items: center;
gap: 8px;
font-size: clamp(14px, 1.9vw, 17px);
font-weight: 600;
color: var(--ghc-muted);
white-space: nowrap;
}
.ghchart__swatch {
width: 14px;
height: 14px;
border-radius: 4px;
flex: 0 0 auto;
}
/* ------------------------------------------------------------------ */
/* Plot (SVG) */
/* ------------------------------------------------------------------ */
.ghchart__plot {
display: block;
width: 100%;
height: auto;
overflow: visible;
touch-action: pan-y;
}
.ghchart__gridline {
stroke: var(--ghc-grid);
stroke-width: 1;
stroke-dasharray: 2 6;
stroke-linecap: round;
opacity: 0.9;
}
.ghchart__axislabel {
fill: var(--ghc-axis);
font-size: 22px;
font-weight: 500;
}
.ghchart__axislabel--x { font-weight: 600; }
.ghchart__line {
fill: none;
stroke-linecap: round;
stroke-linejoin: round;
}
.ghchart__dot { transition: r 0.12s ease; }
/* Dashed threshold line (e.g. a target/reference level) + floating label.
Stroke color is set inline per chart; dash + label styling live here. */
.ghchart__threshold {
stroke-width: 2;
stroke-dasharray: 7 6;
stroke-linecap: round;
opacity: 0.9;
}
.ghchart__threshold-label {
font-size: 20px;
font-weight: 700;
letter-spacing: -0.01em;
}
/* Hatched band marking a partial / projected trailing region. */
.ghchart__projection { pointer-events: none; }
.ghchart__guide {
stroke: var(--ghc-guide);
stroke-width: 1.5;
stroke-dasharray: 4 4;
opacity: 0;
transition: opacity 0.12s ease;
}
.ghchart__hit { fill: transparent; }
/* ------------------------------------------------------------------ */
/* Scatter plot */
/* ------------------------------------------------------------------ */
.ghchart__gridline--v { /* vertical gridlines reuse the dashed grid style */ }
.ghchart__axistitle {
fill: var(--ghc-muted);
font-size: 20px;
font-weight: 600;
letter-spacing: -0.01em;
}
/* Dotted reference crosshair through the "reference" point. Stroke color
is set inline (tracks the reference series color). */
.ghchart__crosshair {
stroke-width: 2;
stroke-dasharray: 2 7;
stroke-linecap: round;
opacity: 0.85;
pointer-events: none;
}
.ghchart__halo { opacity: 0.18; pointer-events: none; }
.ghchart__pt { transition: r 0.12s ease; }
.ghchart__pt-label {
fill: var(--ghc-muted);
font-size: 19px;
font-weight: 600;
letter-spacing: -0.01em;
pointer-events: none;
}
.ghchart__pt-label--strong { font-size: 21px; font-weight: 700; }
.ghchart__pthit { fill: transparent; cursor: default; outline: none; }
.ghchart__pthit:focus-visible {
outline: 2px solid var(--ghc-axis);
outline-offset: 1px;
}
.ghchart__swatch--dot { border-radius: 50%; }
/* ------------------------------------------------------------------ */
/* Trend chart (dated time-series with narrative headline) */
/* ------------------------------------------------------------------ */
.ghchart__headline {
font-size: clamp(17px, 2.3vw, 20px);
line-height: 1.32;
font-weight: 700;
letter-spacing: -0.01em;
color: var(--ghc-text);
margin: 0 0 8px;
}
/* Trend legend sits left-aligned under the headline/subtitle. */
.ghchart__legend--trend {
justify-content: flex-start;
margin: 2px 0 14px;
}
.ghchart__glyph { flex: 0 0 auto; }
.ghchart__glyph--square {
width: 13px; height: 13px; border-radius: 2px;
}
.ghchart__glyph--diamond {
width: 12px; height: 12px; border: 1.5px solid; transform: rotate(45deg);
}
.ghchart__glyph--line {
width: 20px; height: 0; border-top: 2px dashed; border-radius: 0;
}
/* Phase background columns + separators + top labels. */
.ghchart__phase { fill: #f6f8fa; opacity: 0.55; }
.ghchart__phase--alt { fill: #ffffff; opacity: 0; }
.ghchart__phase-sep {
stroke: var(--ghc-grid); stroke-width: 1; stroke-dasharray: 2 5; opacity: 0.8;
}
.ghchart__phase-label {
fill: var(--ghc-muted);
font-size: 15px;
font-weight: 700;
letter-spacing: 0.06em;
}
.ghchart__phase-avg {
font-size: 16px;
font-weight: 700;
letter-spacing: -0.01em;
}
/* Dashed trend line + ± noise band + slope label. */
.ghchart__trendband { opacity: 0.12; pointer-events: none; }
.ghchart__trendline {
stroke-width: 2.5;
stroke-dasharray: 8 6;
stroke-linecap: round;
fill: none;
}
.ghchart__slope {
font-size: 20px;
font-weight: 700;
letter-spacing: -0.01em;
}
/* Horizontal reference line + label. */
.ghchart__refline {
stroke-width: 2;
stroke-dasharray: 7 6;
stroke-linecap: round;
opacity: 0.85;
}
.ghchart__reflabel {
font-size: 16px;
font-weight: 700;
letter-spacing: -0.01em;
}
/* Data squares — values reveal on hover only. */
.ghchart__square { transition: width 0.12s ease, height 0.12s ease, x 0.12s ease, y 0.12s ease; }
.ghchart__squarehit { fill: transparent; cursor: default; outline: none; }
.ghchart__squarehit:focus-visible {
outline: 2px solid var(--ghc-axis);
outline-offset: 1px;
}
/* Invalid-run markers + leader-line annotations. */
.ghchart__invalid { stroke-width: 2; }
.ghchart__invalid-x { stroke-width: 2; stroke-linecap: round; }
.ghchart__leader { stroke-width: 1; stroke-dasharray: 2 3; opacity: 0.8; }
.ghchart__leader--callout { stroke-dasharray: none; opacity: 0.6; }
.ghchart__anno-title {
font-size: 15px;
font-weight: 700;
letter-spacing: -0.01em;
}
.ghchart__anno-sub {
fill: var(--ghc-muted);
font-size: 13px;
font-weight: 500;
}
/* Final-callout labels read a touch larger than the invalid-run notes. */
.ghchart__anno-title--callout { font-size: 17px; }
.ghchart__anno-sub--callout { font-size: 14px; }
/* Bracket span annotation. */
.ghchart__bracket { stroke: var(--ghc-axis); stroke-width: 1.5; opacity: 0.75; }
.ghchart__bracket-label {
fill: var(--ghc-muted);
font-size: 13px;
font-weight: 700;
letter-spacing: 0.04em;
}
/* Final callout ring. */
.ghchart__callout-ring { fill: none; stroke-width: 2; }
/* ------------------------------------------------------------------ */
/* Title + divider */
/* ------------------------------------------------------------------ */
.ghchart__title {
/* Matches the github.blog H2 (.wp-block-heading) exactly */
display: block;
font-size: 32px;
line-height: 40px;
font-weight: 700;
letter-spacing: -0.18px;
color: var(--ghc-text);
}
.ghchart__rule {
height: 1.5px;
border-radius: 1.5px;
background: var(--ghc-divider);
margin: 10px 0 18px;
}
/* ------------------------------------------------------------------ */
/* Bar chart: subtitle, panels, bars, caption */
/* ------------------------------------------------------------------ */
.ghchart__subtitle {
margin: -6px 0 20px;
font-size: clamp(13px, 1.6vw, 15px);
line-height: 1.5;
color: var(--ghc-muted);
}
.ghchart__panel { margin-top: 30px; }
.ghchart__panel:first-of-type { margin-top: 6px; }
.ghchart__panel-title {
margin: 0 0 6px;
font-size: clamp(15px, 1.9vw, 18px);
font-weight: 700;
letter-spacing: -0.01em;
color: var(--ghc-text);
}
.ghchart__plot--bar { touch-action: pan-y; }
.ghchart__axisline {
stroke: var(--ghc-axis);
stroke-width: 1.25;
opacity: 0.35;
}
.ghchart__bar { transition: opacity 0.12s ease; }
.ghchart__barhit { fill: transparent; }
/* Always-on value labels above selected bars. */
.ghchart__barvalue {
fill: var(--ghc-text);
font-size: 20px;
font-weight: 700;
font-variant-numeric: tabular-nums;
pointer-events: none;
}
/* Anchor annotation: dotted reference line + floating pill. */
.ghchart__anno-line {
stroke: #8b949e;
stroke-width: 1.5;
stroke-dasharray: 4 3;
stroke-linecap: round;
pointer-events: none;
}
.ghchart__anno-chip {
fill: #ffebe9;
stroke: #ffcecb;
stroke-width: 1;
pointer-events: none;
}
.ghchart__anno-chip-text {
fill: #a0111f;
font-weight: 700;
pointer-events: none;
}
.ghchart__caption {
margin-top: 22px;
padding-top: 12px;
border-top: 1px solid var(--ghc-card-border);
font-size: 12.5px;
line-height: 1.55;
color: var(--ghc-muted);
}
.ghchart__caption b { color: var(--ghc-text); font-weight: 700; }
.ghchart__caption code {
font-family: ui-monospace, SFMono-Regular, "SF Mono", Menlo, Consolas, monospace;
font-size: 0.92em;
padding: 1px 5px;
background: #f6f8fa;
border: 1px solid var(--ghc-card-border);
border-radius: 5px;
}
.ghchart__tag {
display: inline-block;
font-size: 10px;
font-weight: 650;
letter-spacing: 0.03em;
text-transform: uppercase;
padding: 2px 7px;
border-radius: 999px;
border: 1px solid #aceebb;
color: #0a5223;
background: #dafbe1;
white-space: nowrap;
vertical-align: middle;
}
/* ------------------------------------------------------------------ */
/* Distribution dashboard (segmented mix bar + ranked bar panels) */
/* ------------------------------------------------------------------ */
.ghchart__dist-section { margin-top: 28px; }
.ghchart__dist-section:first-of-type { margin-top: 12px; }
/* Segmented proportional "mix" bar: name inside, count/percent below.
Separators are 2px inside borders (box-sizing:border-box) rather than a
flex gap, so the three segments sum to exactly 100% with no overflow. */
.ghchart__seg {
display: flex;
width: 100%;
height: 42px;
margin-top: 2px;
border-radius: 7px;
overflow: hidden;
}
.ghchart__seg-cell {
display: flex;
align-items: center;
justify-content: center;
min-width: 0;
color: #ffffff;
font-size: 15px;
font-weight: 700;
letter-spacing: -0.01em;
white-space: nowrap;
overflow: hidden;
}
.ghchart__seg-cell + .ghchart__seg-cell { border-left: 2px solid var(--ghc-card-bg); }
.ghchart__seg-note-row {
display: flex;
width: 100%;
margin-top: 8px;
}
.ghchart__seg-note {
min-width: 0;
text-align: center;
font-size: 12.5px;
color: var(--ghc-muted);
font-variant-numeric: tabular-nums;
white-space: nowrap;
}
/* Keep the outer notes from spilling past the chart edges. */
.ghchart__seg-note:first-child { text-align: left; }
.ghchart__seg-note:last-child { text-align: right; }
.ghchart__seg-note b { color: var(--ghc-text); font-weight: 700; }
/* Ranked horizontal bar list: [category | bar track | value].
The list is the grid and rows use display:contents, so the category,
bar, and value columns line up across every row in a panel. */
.ghchart__dist-list {
display: grid;
grid-template-columns: max-content 1fr max-content;
align-items: center;
column-gap: 12px;
row-gap: 10px;
margin-top: 4px;
}
.ghchart__dist-row { display: contents; }
.ghchart__dist-cat {
text-align: right;
font-size: 13px;
line-height: 1.25;
color: var(--ghc-text);
white-space: nowrap;
}
.ghchart__dist-track { width: 100%; }
.ghchart__dist-bar {
height: 15px;
min-width: 2px;
border-radius: 3px;
}
.ghchart__dist-val {
font-size: 12.5px;
color: var(--ghc-muted);
font-variant-numeric: tabular-nums;
white-space: nowrap;
}
.ghchart__dist-val b { color: var(--ghc-text); font-weight: 700; }
/* Two-up panel grid (collapses to one column on narrow screens).
minmax(0,1fr) lets columns shrink so long labels never force overflow. */
.ghchart__dist-cols {
margin-top: 26px;
display: grid;
grid-template-columns: repeat(2, minmax(0, 1fr));
gap: 24px 30px;
}
.ghchart__dist-cols .ghchart__dist-section { margin-top: 0; }
.ghchart__dist-cols--stack { grid-template-columns: 1fr; }
.ghchart__dist-cols--stack .ghchart__dist-section { margin-top: 28px; }
.ghchart__dist-cols--stack .ghchart__dist-section:first-child { margin-top: 0; }
/* Tighten the label/value type a touch in the 2-up layout so the bars
keep room at the 699px blog width. */
.ghchart__dist-cols:not(.ghchart__dist-cols--stack) .ghchart__dist-cat { font-size: 12px; }
.ghchart__dist-cols:not(.ghchart__dist-cols--stack) .ghchart__dist-val { font-size: 11.5px; }
.ghchart__dist-cols:not(.ghchart__dist-cols--stack) .ghchart__dist-list { column-gap: 9px; }
@media (max-width: 600px) {
.ghchart__dist-cols { grid-template-columns: 1fr; gap: 26px; }
.ghchart__dist-cols .ghchart__dist-section ~ .ghchart__dist-section { margin-top: 2px; }
}
/* Let long category labels wrap on very narrow (mobile) widths. */
@media (max-width: 480px) {
.ghchart__dist-cat { white-space: normal; }
}
/* ------------------------------------------------------------------ */
/* Calendar heatmap (GitHub contribution-graph style) */
/* ------------------------------------------------------------------ */
.ghheat__eyebrow { display: flex; align-items: center; gap: 8px; margin: 0 0 12px; }
.ghheat__eyedot {
width: 9px; height: 9px; border-radius: 50%;
background: #cf222e; box-shadow: 0 0 0 3px rgba(207, 34, 46, 0.14);
flex: 0 0 auto;
}
.ghheat__eyetext {
font-size: 12px; font-weight: 700; letter-spacing: 0.08em;
text-transform: uppercase; color: #cf222e;
}
/* Subtitle here is a prominent lede, not the muted variant. */
.ghheat__subtitle {
color: var(--ghc-text);
font-size: clamp(14px, 1.7vw, 16px);
/* 24px top gap matches the github.blog H2 → paragraph block spacing. */
margin: 24px 0 8px;
}
.ghheat__subtitle b { font-weight: 700; }
.ghheat__panel { margin-top: 36px; }
.ghheat__phead {
display: flex; align-items: baseline; gap: 9px; flex-wrap: wrap;
margin-bottom: 16px;
}
.ghheat__badge {
display: inline-flex; align-items: center; justify-content: center;
width: 22px; height: 22px; border-radius: 50%;
color: #fff; font-size: 12px; font-weight: 700;
flex: 0 0 auto; align-self: center;
}
.ghheat__ptitle {
font-size: clamp(16px, 2vw, 19px); font-weight: 700;
letter-spacing: -0.01em; color: var(--ghc-text);
}
.ghheat__pdesc { font-size: 14px; color: var(--ghc-muted); }
.ghheat__body { display: flex; gap: 30px; align-items: flex-start; flex-wrap: wrap; }
.ghheat__gridwrap { flex: 1 1 340px; min-width: 260px; }
.ghheat__grid { display: block; width: 100%; height: auto; overflow: visible; }
.ghheat__cell {
stroke: rgba(27, 31, 36, 0.06);
stroke-width: 1;
transition: stroke 0.1s ease;
}
.ghheat__cell:hover { stroke: #24292f; stroke-width: 1.4; }
.ghheat__cell--pad { opacity: 0; pointer-events: none; }
.ghheat__wd, .ghheat__month {
fill: var(--ghc-muted); font-size: 11px; font-weight: 600;
}
.ghheat__side { flex: 1 1 250px; min-width: 230px; }
.ghheat__stat { display: flex; align-items: baseline; gap: 7px; }
.ghheat__statbig {
font-size: clamp(26px, 4vw, 34px); font-weight: 800; letter-spacing: -0.02em;
line-height: 1;
}
.ghheat__statsmall { font-size: 15px; font-weight: 600; color: var(--ghc-muted); }
.ghheat__note {
margin: 10px 0 16px; font-size: 14px; line-height: 1.55; color: var(--ghc-muted);
}
.ghheat__note b { color: var(--ghc-text); font-weight: 700; }
.ghheat__legend {
display: flex; align-items: center; gap: 5px;
font-size: 12.5px; color: var(--ghc-muted); flex-wrap: wrap;
}
.ghheat__sw { width: 12px; height: 12px; border-radius: 3px; display: inline-block; flex: 0 0 auto; }
.ghheat__sw--empty { border: 1px solid #d0d7de; }
.ghheat__legunit { margin-left: 2px; }
.ghheat__extra {
display: flex; align-items: flex-start; gap: 7px; margin-top: 14px;
font-size: 12px; line-height: 1.5; color: var(--ghc-muted); max-width: 330px;
}
.ghheat__extra .ghheat__sw { margin-top: 2px; }
/* ------------------------------------------------------------------ */
/* Tooltip */
/* ------------------------------------------------------------------ */
.ghchart__tooltip {
position: absolute;
z-index: 5;
top: 0;
left: 0;
min-width: 128px;
max-width: 260px;
padding: 10px 12px;
background: var(--ghc-card-bg);
border: 1px solid var(--ghc-card-border);
border-radius: 12px;
/* Layered drop shadow from Primer Brand's ActionMenu (.ActionMenu__menu) */
box-shadow:
0px 100px 80px rgba(0, 0, 0, 0.01),
0px 41px 33px rgba(0, 0, 0, 0.02),
0px 22px 17px rgba(0, 0, 0, 0.02),
0px 12px 10px rgba(0, 0, 0, 0.03),
0px 6px 5px rgba(0, 0, 0, 0.04),
0px 2px 2px rgba(0, 0, 0, 0.07);
pointer-events: none;
opacity: 0;
transform: translate(-50%, -100%);
/* Fade in on show and out on hide; quickly ease left/top when tracking
from one point to the next. The position ease is suppressed on the
first appearance (see chart.js) so the tooltip fades in place rather
than sliding in from its previous spot. */
transition: opacity 0.12s ease,
left 0.1s cubic-bezier(0.4, 0, 0.2, 1),
top 0.1s cubic-bezier(0.4, 0, 0.2, 1);
will-change: opacity, transform, left, top;
}
.ghchart__tooltip[data-visible="true"] { opacity: 1; }
.ghchart__tt-title {
font-size: 13px;
font-weight: 700;
color: var(--ghc-text);
margin-bottom: 6px;
}
.ghchart__tt-row {
display: flex;
align-items: center;
gap: 8px;
font-size: 13px;
line-height: 1.6;
color: var(--ghc-muted);
}
.ghchart__tt-swatch { width: 10px; height: 10px; border-radius: 3px; flex: 0 0 auto; }
.ghchart__tt-name { flex: 1 1 auto; }
.ghchart__tt-value { font-weight: 700; color: var(--ghc-text); font-variant-numeric: tabular-nums; }
/* Bar tooltip body: a title line + free-form text lines (no swatches). */
.ghchart__tt-line {
font-size: 13px;
line-height: 1.5;
color: var(--ghc-muted);
}
.ghchart__tt-line:first-child { color: var(--ghc-text); font-weight: 600; }
.ghchart__tt-line + .ghchart__tt-line { margin-top: 2px; }
@media (prefers-reduced-motion: reduce) {
.ghchart__dot,
.ghchart__guide,
.ghchart__bar,
.ghchart__pt,
.ghchart__square,
.ghheat__cell,
.ghchart__tooltip { transition: none; }
}
/*
Shared chart engine — reusable renderer for the blog-charts library.
------------------------------------------------------------------
Exposes window.GHChartEngine = { renderers, render(config, mountEl?) }.
Renderers are keyed by config.type (currently "line"); add a new viz type
by registering GHChartEngine.renderers[] = function (root, config) {…}.
This same file is embedded verbatim at the top of each exported WordPress
chart.js, followed by that chart's CONFIG + a small auto-boot. It has no
external dependencies and is safe to include more than once on a page.
*/
(function (global) {
"use strict";
var SVGNS = "http://www.w3.org/2000/svg";
function el(tag, attrs) {
var node = document.createElementNS(SVGNS, tag);
if (attrs) {
for (var k in attrs) {
if (Object.prototype.hasOwnProperty.call(attrs, k)) {
node.setAttribute(k, attrs[k]);
}
}
}
return node;
}
function html(tag, cls) {
var node = document.createElement(tag);
if (cls) node.className = cls;
return node;
}
function clamp(v, lo, hi) { return v < lo ? lo : v > hi ? hi : v; }
var renderers = {};
/* ================================================================
Line chart renderer
================================================================ */
renderers.line = function (root, config) {
// viewBox geometry (design units; scales fluidly to any width).
var VB = { w: 1000, h: 560 };
var M = { top: 30, right: 34, bottom: 56, left: 86 };
// Inset the plotted points slightly from the axis so the first/last
// months (e.g. "Jan") don't crowd the y-axis labels (e.g. "50").
var PAD_X = 18;
var innerW = VB.w - M.left - M.right;
var innerH = VB.h - M.top - M.bottom;
// Reset (in case of re-render) and use a unique id prefix so multiple
// charts on one page never share gradient ids.
root.textContent = "";
var uid = config.mountId;
var n = config.labels.length;
var yMin = config.yAxis.min;
var yMax = config.yAxis.max;
var step = config.yAxis.step;
// Y-axis tick labels can format differently from the tooltip values
// (e.g. a plain "40" on the axis but "40%" in the tooltip).
var fmtAxis = config.formatAxis || config.formatValue;
var plotW = innerW - 2 * PAD_X;
var stepX = n dark ramp is derived from a single tunable base color.
================================================================ */
renderers.heatmap = function (root, config) {
root.textContent = "";
var WHITE = [255, 255, 255], BLACK = [0, 0, 0];
function hexToRgb(h) {
h = String(h).replace("#", "");
if (h.length === 3) h = h[0] + h[0] + h[1] + h[1] + h[2] + h[2];
return [parseInt(h.slice(0, 2), 16), parseInt(h.slice(2, 4), 16), parseInt(h.slice(4, 6), 16)];
}
function rgbToHex(c) {
return "#" + c.map(function (x) {
x = Math.max(0, Math.min(255, Math.round(x)));
return (x < 16 ? "0" : "") + x.toString(16);
}).join("");
}
function mix(a, b, t) {
return [a[0] + (b[0] - a[0]) * t, a[1] + (b[1] - a[1]) * t, a[2] + (b[2] - a[2]) * t];
}
// 5-step contribution ramp (level 0 faint -> level 4 dark) from one hex.
function rampFrom(baseHex) {
var b = hexToRgb(baseHex);
return [
rgbToHex(mix(b, WHITE, 0.80)),
rgbToHex(mix(b, WHITE, 0.52)),
rgbToHex(mix(b, WHITE, 0.20)),
baseHex,
rgbToHex(mix(b, BLACK, 0.30))
];
}
function trim0(s) { return s.replace(/\.0$/, ""); }
function fmtM(v) {
if (v >= 1000) { var g = v / 1000; return (g >= 10 ? Math.round(g) : trim0(g.toFixed(1))) + "B"; }
if (v >= 1) return trim0(v.toFixed(1)) + "M";
return Math.round(v * 1000) + "K";
}
var MON = ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"];
var MON_FULL = ["January", "February", "March", "April", "May", "June",
"July", "August", "September", "October", "November", "December"];
var WD = ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"];
var year = config.year || 2026;
var startM = config.startMonth != null ? config.startMonth : 0;
var endM = config.endMonth != null ? config.endMonth : 5;
var emptyColor = config.emptyColor || "#ebedf0";
function daysInMonth(y, m) { return new Date(y, m + 1, 0).getDate(); }
function parseISO(s) { var p = s.split("-"); return new Date(+p[0], +p[1] - 1, +p[2]); }
function dayStart(dt) { return new Date(dt.getFullYear(), dt.getMonth(), dt.getDate()); }
/* ---- Eyebrow + title + subtitle ----------------------------- */
if (config.eyebrow) {
var eb = html("div", "ghheat__eyebrow");
eb.appendChild(html("span", "ghheat__eyedot"));
var ebt = html("span", "ghheat__eyetext");
ebt.textContent = config.eyebrow;
eb.appendChild(ebt);
root.appendChild(eb);
}
var title = html("span", "ghchart__title");
title.textContent = config.title;
root.appendChild(title);
if (config.subtitle) {
var sub = html("p", "ghchart__subtitle ghheat__subtitle");
sub.innerHTML = config.subtitle;
root.appendChild(sub);
}
/* ---- One shared tooltip -------------------------------------- */
var tip = html("div", "ghchart__tooltip");
tip.setAttribute("role", "status");
var tipTitle = html("div", "ghchart__tt-title");
var tipBody = html("div", "ghchart__tt-body");
tip.appendChild(tipTitle);
tip.appendChild(tipBody);
root.appendChild(tip);
var motionMQ = window.matchMedia ? window.matchMedia("(prefers-reduced-motion: