{"spec_id":"lollipop-grouped","library":"highcharts","language":"javascript","code":"// anyplot.ai\n// lollipop-grouped: Grouped Lollipop Chart\n// Library: highcharts 12.6.0 | JavaScript 22.23.2\n// Quality: 89/100 | Created: 2026-09-02\n\nconst t = window.ANYPLOT_TOKENS;\n\n// --- Data (in-memory, deterministic) ----------------------------------------\n// Quarterly product-line revenue ($M) by region, sorted by total descending.\nconst categories = [\"South\", \"North\", \"West\", \"East\"];\nconst seriesNames = [\"Solar\", \"Wind\", \"Storage\"];\nconst revenueByProduct = {\n  Solar: [58, 42, 61, 35],\n  Wind: [33, 51, 29, 47],\n  Storage: [24, 18, 15, 21],\n};\n\n// Side-by-side offset for lollipops within a group.\nconst offsetStep = 0.22;\nconst seriesOffsets = seriesNames.map((_, i) => (i - (seriesNames.length - 1) / 2) * offsetStep);\n\n// Standout data point (single highest value) gets a subtle focal-point\n// emphasis — a larger radius and a bolder ink ring — to give the chart a\n// point of visual hierarchy beyond the raw category comparison.\nconst globalMax = Math.max(...seriesNames.flatMap((name) => revenueByProduct[name]));\n\n// Each lollipop is a 2-point segment (baseline -> value) followed by a null\n// gap, so a single \"line\" series draws every stem for that product line\n// without connecting across categories.\nconst series = seriesNames.map((name, si) => {\n  const x0 = seriesOffsets[si];\n  const data = [];\n  categories.forEach((_, ci) => {\n    const x = ci + x0;\n    const value = revenueByProduct[name][ci];\n    const isFocal = value === globalMax;\n    data.push({ x, y: 0, marker: { enabled: false } });\n    data.push({\n      x,\n      y: value,\n      marker: isFocal ? { enabled: true, radius: 10, lineWidth: 3, lineColor: t.ink } : { enabled: true },\n    });\n    data.push({ x, y: null });\n  });\n  return {\n    name,\n    type: \"line\",\n    data,\n    color: t.palette[si],\n    lineWidth: 2.5,\n    marker: { enabled: true, radius: 7, symbol: \"circle\", lineColor: t.pageBg, lineWidth: 1.5 },\n  };\n});\n\n// --- Chart -------------------------------------------------------------------\nHighcharts.chart(\"container\", {\n  chart: { backgroundColor: \"transparent\", animation: false, style: { fontFamily: \"inherit\" } },\n  credits: { enabled: false },\n  colors: t.palette,\n  title: {\n    text: \"lollipop-grouped · javascript · highcharts · anyplot.ai\",\n    style: { color: t.ink, fontSize: \"26px\", fontWeight: \"700\" },\n  },\n  xAxis: {\n    categories,\n    min: -0.5,\n    max: categories.length - 0.5,\n    startOnTick: false,\n    endOnTick: false,\n    tickPositions: categories.map((_, ci) => ci),\n    lineColor: t.inkSoft,\n    tickColor: t.inkSoft,\n    gridLineColor: t.grid,\n    labels: { style: { color: t.inkSoft, fontSize: \"14px\" } },\n    title: { text: \"Region\", style: { color: t.inkSoft, fontSize: \"16px\" } },\n  },\n  yAxis: {\n    min: 0,\n    title: { text: \"Revenue ($M)\", style: { color: t.inkSoft, fontSize: \"16px\" } },\n    gridLineColor: t.grid,\n    labels: { style: { color: t.inkSoft, fontSize: \"14px\" } },\n  },\n  legend: {\n    itemStyle: { color: t.inkSoft, fontSize: \"14px\" },\n    itemHoverStyle: { color: t.ink },\n  },\n  tooltip: {\n    formatter: function () {\n      if (this.y === 0 || this.y === null) return false;\n      const category = categories[Math.round(this.x)];\n      return `<b>${this.series.name}</b><br/>${category}: $${this.y}M`;\n    },\n  },\n  plotOptions: {\n    series: { animation: false, enableMouseTracking: true },\n  },\n  series,\n});\n"}