diff --git a/plots/radar-basic/implementations/javascript/highcharts.js b/plots/radar-basic/implementations/javascript/highcharts.js
new file mode 100644
index 0000000000..a5a27ea502
--- /dev/null
+++ b/plots/radar-basic/implementations/javascript/highcharts.js
@@ -0,0 +1,205 @@
+// anyplot.ai
+// radar-basic: Basic Radar Chart
+// Library: highcharts 12.6.0 | JavaScript 22.23.1
+// Quality: 91/100 | Created: 2026-07-24
+//# anyplot-orientation: square
+
+// Only the core `highcharts` bundle is loaded (no highcharts-more), so the
+// native polar/radar series type isn't available. Instead we project each
+// axis's polar coordinate to Cartesian ourselves and draw the grid, spokes,
+// labels, and data polygons with the core SVG renderer — same technique the
+// gauge-basic implementation uses for its needle overlay.
+const t = window.ANYPLOT_TOKENS;
+
+// --- Data (Employee performance review, two review cycles) -----------------
+const categories = [
+ "Communication",
+ "Technical Skills",
+ "Teamwork",
+ "Leadership",
+ "Problem Solving",
+ "Adaptability",
+];
+const series = [
+ { name: "Q3 Review", color: t.palette[0], values: [78, 85, 90, 65, 72, 80] },
+ { name: "Q4 Review", color: t.palette[1], values: [88, 82, 92, 75, 85, 78] },
+];
+const MAX_VALUE = 100;
+const RING_LEVELS = [20, 40, 60, 80, 100];
+const n = categories.length;
+
+const TITLE = "radar-basic · javascript · highcharts · anyplot.ai";
+const titleFs = Math.max(15, Math.round(22 * Math.min(1, 67 / TITLE.length))) + "px";
+
+// --- Chart (empty core chart used as a canvas for the renderer overlay) ----
+const chart = Highcharts.chart("container", {
+ chart: {
+ backgroundColor: "transparent",
+ animation: false,
+ style: { fontFamily: "inherit" },
+ margin: [110, 80, 130, 80],
+ },
+ credits: { enabled: false },
+ title: {
+ text: TITLE,
+ style: { color: t.ink, fontSize: titleFs, fontWeight: "600" },
+ },
+ xAxis: { visible: false, gridLineWidth: 0, lineWidth: 0, tickLength: 0 },
+ yAxis: { visible: false, gridLineWidth: 0, lineWidth: 0, tickLength: 0 },
+ legend: { enabled: false },
+ tooltip: {
+ enabled: true,
+ backgroundColor: t.elevatedBg,
+ borderColor: t.grid,
+ style: { color: t.ink },
+ formatter() {
+ return `${this.series.name}
${this.point.name}: ${this.point.custom.actualValue}`;
+ },
+ },
+ plotOptions: { series: { animation: false } },
+ series: [],
+});
+
+// Fix the (visible: false) axes to a known pixel-space extent so a real
+// scatter series can be data-bound at the same polar-projected coordinates
+// the renderer overlay uses below — this keeps the PNG output identical
+// while giving the interactive HTML genuine Highcharts series/tooltip usage.
+chart.xAxis[0].setExtremes(0, chart.plotWidth, false);
+chart.yAxis[0].setExtremes(0, chart.plotHeight, false);
+
+// --- Geometry ----------------------------------------------------------------
+const cx = chart.plotLeft + chart.plotWidth / 2;
+const cy = chart.plotTop + chart.plotHeight / 2;
+const outerR = Math.min(chart.plotWidth, chart.plotHeight) / 2 - 70;
+
+// angle 0 points straight up, axes proceed clockwise
+const angleOf = (i) => -Math.PI / 2 + i * ((2 * Math.PI) / n);
+const pointAt = (i, radiusFrac) => {
+ const angle = angleOf(i);
+ return [cx + outerR * radiusFrac * Math.cos(angle), cy + outerR * radiusFrac * Math.sin(angle)];
+};
+
+// --- Grid rings --------------------------------------------------------------
+RING_LEVELS.forEach((level) => {
+ const radiusFrac = level / MAX_VALUE;
+ const path = ["M"];
+ for (let i = 0; i < n; i += 1) {
+ const [x, y] = pointAt(i, radiusFrac);
+ path.push(...(i === 0 ? [x, y] : ["L", x, y]));
+ }
+ path.push("Z");
+ chart.renderer
+ .path(path)
+ .attr({ stroke: t.grid, "stroke-width": 1, fill: "none", zIndex: 1 })
+ .add();
+});
+
+// --- Spokes + axis labels -----------------------------------------------------
+categories.forEach((label, i) => {
+ const [ox, oy] = pointAt(i, 1);
+ chart.renderer
+ .path(["M", cx, cy, "L", ox, oy])
+ .attr({ stroke: t.inkSoft, "stroke-width": 1, zIndex: 1 })
+ .add();
+
+ const angle = angleOf(i);
+ const cos = Math.cos(angle);
+ const sin = Math.sin(angle);
+ const [lx, ly] = pointAt(i, 1 + 26 / outerR);
+ const align = cos > 0.3 ? "left" : cos < -0.3 ? "right" : "center";
+ chart.renderer
+ .text(label, lx, ly + sin * 6 + 5)
+ .attr({ align, zIndex: 5 })
+ .css({ fontSize: "15px", fontWeight: "600", color: t.ink, fontFamily: "inherit" })
+ .add();
+});
+
+// --- Ring scale labels (along the top spoke) ---------------------------------
+RING_LEVELS.forEach((level) => {
+ const [, ly] = pointAt(0, level / MAX_VALUE);
+ chart.renderer
+ .text(String(level), cx + 8, ly + 4)
+ .attr({ align: "left", zIndex: 4 })
+ .css({ fontSize: "12px", color: t.inkSoft, fontFamily: "inherit" })
+ .add();
+});
+
+// --- Data polygons -------------------------------------------------------------
+series.forEach((s) => {
+ const vertices = s.values.map((value, i) => pointAt(i, value / MAX_VALUE));
+ const path = ["M"];
+ vertices.forEach(([x, y], i) => path.push(...(i === 0 ? [x, y] : ["L", x, y])));
+ path.push("Z");
+
+ chart.renderer
+ .path(path)
+ .attr({
+ fill: s.color,
+ "fill-opacity": 0.22,
+ stroke: s.color,
+ "stroke-width": 2.5,
+ "stroke-linejoin": "round",
+ zIndex: 3,
+ })
+ .add();
+
+ vertices.forEach(([x, y]) => {
+ chart.renderer
+ .circle(x, y, 6)
+ .attr({ fill: s.color, stroke: t.pageBg, "stroke-width": 1.5, zIndex: 4 })
+ .add();
+ });
+
+ // Real Highcharts scatter series at the same vertex pixels (mapped through
+ // the fixed-extent axes above) — markers stay hidden so the PNG is
+ // untouched, but each point is genuinely data-bound and hoverable in the
+ // interactive HTML, with the tooltip reporting the actual category/value.
+ chart.addSeries(
+ {
+ type: "scatter",
+ name: s.name,
+ color: s.color,
+ enableMouseTracking: true,
+ stickyTracking: false,
+ animation: false,
+ marker: {
+ enabled: false,
+ radius: 7,
+ states: { hover: { enabled: true, radius: 7, lineWidth: 1.5, lineColor: t.pageBg } },
+ },
+ data: vertices.map(([x, y], i) => ({
+ x: x - chart.plotLeft,
+ y: chart.plotTop + chart.plotHeight - y,
+ name: categories[i],
+ custom: { actualValue: s.values[i] },
+ })),
+ },
+ false
+ );
+});
+chart.redraw();
+
+// --- Legend ---------------------------------------------------------------
+const chipSize = 14;
+const gap = 28;
+const legendFs = 15;
+const chipTextWidths = series.map((s) => s.name.length * 8.2);
+const legendWidth = series.reduce(
+ (sum, _s, i) => sum + chipSize + 10 + chipTextWidths[i] + (i < series.length - 1 ? gap : 0),
+ 0
+);
+let legendX = cx - legendWidth / 2;
+const legendY = chart.plotTop + chart.plotHeight + 60;
+
+series.forEach((s, i) => {
+ chart.renderer
+ .rect(legendX, legendY - chipSize / 2, chipSize, chipSize, 3)
+ .attr({ fill: s.color, zIndex: 5 })
+ .add();
+ chart.renderer
+ .text(s.name, legendX + chipSize + 10, legendY + chipSize / 2 - 2)
+ .attr({ align: "left", zIndex: 5 })
+ .css({ fontSize: `${legendFs}px`, color: t.ink, fontFamily: "inherit" })
+ .add();
+ legendX += chipSize + 10 + chipTextWidths[i] + gap;
+});
diff --git a/plots/radar-basic/metadata/javascript/highcharts.yaml b/plots/radar-basic/metadata/javascript/highcharts.yaml
new file mode 100644
index 0000000000..e5009d3274
--- /dev/null
+++ b/plots/radar-basic/metadata/javascript/highcharts.yaml
@@ -0,0 +1,254 @@
+library: highcharts
+language: javascript
+specification_id: radar-basic
+created: '2026-07-24T16:17:43Z'
+updated: '2026-07-24T20:30:28Z'
+generated_by: claude-sonnet
+workflow_run: 30108052134
+issue: 744
+language_version: 22.23.1
+library_version: 12.6.0
+preview_url_light: https://storage.googleapis.com/anyplot-images/plots/radar-basic/javascript/highcharts/plot-light.png
+preview_url_dark: https://storage.googleapis.com/anyplot-images/plots/radar-basic/javascript/highcharts/plot-dark.png
+preview_html_light: https://storage.googleapis.com/anyplot-images/plots/radar-basic/javascript/highcharts/plot-light.html
+preview_html_dark: https://storage.googleapis.com/anyplot-images/plots/radar-basic/javascript/highcharts/plot-dark.html
+quality_score: 91
+review:
+ strengths:
+ - Clean hand-built polar geometry (angleOf/pointAt helpers) produces a well-proportioned
+ hexagonal radar with balanced margins and no text overlap or edge clipping in
+ either theme.
+ - 'Correct Imprint palette usage — first series #009E73, second series canonical
+ position 2 (#C475FD) — identical across light and dark renders; theme-adaptive
+ chrome correctly threads through every drawn element.'
+ - Repair adds a genuine data-bound Highcharts scatter series at the same vertex
+ coordinates, with enableMouseTracking and a tooltip formatter reporting the real
+ category/value — the interactive HTML now provides real hover value beyond the
+ static PNG, while the PNG output is pixel-identical to attempt 1.
+ - 'Realistic, well-differentiated data: Q3-to-Q4 employee review scores rise in
+ most competencies but dip slightly in Adaptability, giving the two overlapping
+ polygons genuine comparative meaning.'
+ - Ring scale labels, spokes, and outer category labels are all legible and non-overlapping
+ in both themes, including at the crowded top-spoke intersection near the highest-value
+ data points.
+ weaknesses:
+ - 'The core visual (grid rings, spokes, category/ring labels, data polygons, markers,
+ legend) is still drawn entirely via chart.renderer primitives rather than Highcharts''
+ series/data-binding model — the newly added scatter series is a thin, invisible
+ (marker.enabled: false) layer bolted on purely for tooltip purposes, so Library
+ Mastery is improved but not maximized.'
+ - No explicit callout highlighting the single biggest Q3→Q4 change (Leadership 65→75)
+ — optional polish for Data Storytelling, not required.
+ image_description: |-
+ Light render (plot-light.png):
+ Background: Warm off-white, consistent with #FAF8F1 (not pure white).
+ Chrome: Bold dark-ink title "radar-basic · javascript · highcharts · anyplot.ai" centered at top; six bold dark category labels (Communication, Technical Skills, Teamwork, Leadership, Adaptability, Problem Solving) at the outer edge of each spoke; thin gray grid rings at 20/40/60/80/100 with small gray ring-scale numbers along the top spoke; bottom-center legend with two rounded color chips ("Q3 Review" green, "Q4 Review" purple). All chrome text renders in dark ink against the light background.
+ Data: Two overlapping filled hexagons — brand green #009E73 (Q3 Review) and lavender #C475FD (Q4 Review) — with ~22% fill opacity so the overlap region reads as a blended tone, plus stroked circular vertex markers on each of the 6 axes per series.
+ Legibility verdict: PASS — all text (title, axis/category labels, ring-scale numbers, legend) is clearly readable against the warm off-white background; no light-on-light issues.
+
+ Dark render (plot-dark.png):
+ Background: Warm near-black, consistent with #1A1A17 (not pure black).
+ Chrome: Same title, category labels, ring-scale numbers, and legend now render in light/cream text (theme-adaptive ink token) and remain fully legible; grid rings and spokes render in a subtle lighter gray appropriate for the dark surface.
+ Data: Colors are confirmed identical to the light render — #009E73 (Q3 Review) and #C475FD (Q4 Review); only chrome (background, text, grid) flipped as expected, per the Imprint palette rule that data colors are constant across themes.
+ Legibility verdict: PASS — no dark-on-dark failures; every chrome element (title, category labels, ring-scale numbers, legend text) is clearly visible against the near-black background.
+
+ Both renders pass the theme-readability checklist. No missing/clipped pixels at either canvas edge (AR-09 clear). Canvas dimensions confirmed 2400x2400 (square format, matches //# anyplot-orientation: square directive).
+ criteria_checklist:
+ visual_quality:
+ score: 30
+ max: 30
+ items:
+ - id: VQ-01
+ name: Text Legibility
+ score: 8
+ max: 8
+ passed: true
+ comment: All font sizes explicitly set (titleFs scales with title length,
+ 15px category labels, 12px ring labels); readable at full size in both themes
+ - id: VQ-02
+ name: No Overlap
+ score: 6
+ max: 6
+ passed: true
+ comment: No collisions between labels, ring numbers, spokes, or legend
+ - id: VQ-03
+ name: Element Visibility
+ score: 6
+ max: 6
+ passed: true
+ comment: 6px vertex markers and 2.5px strokes well-sized for sparse (6-axis,
+ 2-series) data
+ - id: VQ-04
+ name: Color Accessibility
+ score: 2
+ max: 2
+ passed: true
+ comment: Green/lavender pairing with alpha-blended overlap gives good contrast
+ and CVD-safe distinction
+ - id: VQ-05
+ name: Layout & Canvas
+ score: 4
+ max: 4
+ passed: true
+ comment: Balanced margins, plot fills majority of the 2400x2400 canvas, legend
+ sits close to the plot
+ - id: VQ-06
+ name: Axis Labels & Title
+ score: 2
+ max: 2
+ passed: true
+ comment: Descriptive category labels plus explicit 20/40/60/80/100 ring scale
+ - id: VQ-07
+ name: Palette Compliance
+ score: 2
+ max: 2
+ passed: true
+ comment: 'First series #009E73, second series canonical position 2 (#C475FD),
+ identical across themes; backgrounds theme-correct'
+ design_excellence:
+ score: 15
+ max: 20
+ items:
+ - id: DE-01
+ name: Aesthetic Sophistication
+ score: 6
+ max: 8
+ passed: true
+ comment: Thoughtful typography hierarchy and hand-built polar geometry, clearly
+ above a configured default
+ - id: DE-02
+ name: Visual Refinement
+ score: 5
+ max: 6
+ passed: true
+ comment: Subtle grid rings, no frame clutter, generous whitespace
+ - id: DE-03
+ name: Data Storytelling
+ score: 4
+ max: 6
+ passed: true
+ comment: Overlap communicates the Q3-to-Q4 shift; no explicit callout on the
+ single biggest change (optional polish)
+ spec_compliance:
+ score: 15
+ max: 15
+ items:
+ - id: SC-01
+ name: Plot Type
+ score: 5
+ max: 5
+ passed: true
+ comment: Correct radar/spider chart
+ - id: SC-02
+ name: Required Features
+ score: 4
+ max: 4
+ passed: true
+ comment: Filled polygons with alpha (~0.22), gridlines at 20/40/60/80/100,
+ outer-edge axis labels, distinct colors + legend, closed polygons
+ - id: SC-03
+ name: Data Mapping
+ score: 3
+ max: 3
+ passed: true
+ comment: All 6 category axes correctly mapped for both series
+ - id: SC-04
+ name: Title & Legend
+ score: 3
+ max: 3
+ passed: true
+ comment: Title format correct; legend labels match series names (Q3 Review
+ / Q4 Review)
+ data_quality:
+ score: 15
+ max: 15
+ items:
+ - id: DQ-01
+ name: Feature Coverage
+ score: 6
+ max: 6
+ passed: true
+ comment: Two review cycles with realistic per-axis variation (some up, one
+ down)
+ - id: DQ-02
+ name: Realistic Context
+ score: 5
+ max: 5
+ passed: true
+ comment: Employee performance review scenario — real, comprehensible, neutral
+ - id: DQ-03
+ name: Appropriate Scale
+ score: 4
+ max: 4
+ passed: true
+ comment: Scores in 65-92 range on a 0-100 scale are plausible for performance
+ review data
+ code_quality:
+ score: 10
+ max: 10
+ items:
+ - id: CQ-01
+ name: KISS Structure
+ score: 3
+ max: 3
+ passed: true
+ comment: Linear data → chart → geometry → draw structure, no classes
+ - id: CQ-02
+ name: Reproducibility
+ score: 2
+ max: 2
+ passed: true
+ comment: Fully hard-coded, deterministic data
+ - id: CQ-03
+ name: Clean Imports
+ score: 2
+ max: 2
+ passed: true
+ comment: No imports beyond the provided Highcharts global
+ - id: CQ-04
+ name: Code Elegance
+ score: 2
+ max: 2
+ passed: true
+ comment: No fake UI or fake-interactivity comments; the added scatter series
+ is genuinely data-bound, not simulated
+ - id: CQ-05
+ name: Output & API
+ score: 1
+ max: 1
+ passed: true
+ comment: Uses Highcharts.chart("container", ...) mount-node contract correctly,
+ animation disabled
+ library_mastery:
+ score: 6
+ max: 10
+ items:
+ - id: LM-01
+ name: Idiomatic Usage
+ score: 3
+ max: 5
+ passed: true
+ comment: Correct usage of a real Highcharts series (scatter, addSeries, fixed-extent
+ axes) added in this repair, but the chart's core visual body still bypasses
+ series/data-binding via chart.renderer primitives
+ - id: LM-02
+ name: Distinctive Features
+ score: 3
+ max: 5
+ passed: true
+ comment: Tooltip formatter with custom point data (this.point.custom.actualValue)
+ and hover marker states are genuine Highcharts-specific features, addressing
+ the attempt-1 gap
+ verdict: APPROVED
+impl_tags:
+ dependencies: []
+ techniques:
+ - polar-projection
+ - custom-legend
+ - hover-tooltips
+ patterns:
+ - iteration-over-groups
+ dataprep: []
+ styling:
+ - alpha-blending
+ - minimal-chrome