{"spec_id":"bump-basic","library":"chartjs","language":"javascript","code":"// anyplot.ai\n// bump-basic: Basic Bump Chart\n// Library: chartjs 4.4.7 | JavaScript 22.23.2\n// Quality: 95/100 | Created: 2026-08-24\n\nconst t = window.ANYPLOT_TOKENS;\n\n// --- Data (in-memory, deterministic) ----------------------------------------\n// Streaming platform weekly top-6 popularity ranking (lower rank = more popular).\n// Apple TV+ launches a hit series mid-season and climbs the full range from\n// 6th to 1st, overtaking Netflix (the incumbent leader) by week 7.\nconst weeks = [\"Wk 1\", \"Wk 2\", \"Wk 3\", \"Wk 4\", \"Wk 5\", \"Wk 6\", \"Wk 7\", \"Wk 8\"];\nconst platforms = [\n  { name: \"Netflix\", ranks: [1, 1, 2, 1, 1, 1, 2, 2] },\n  { name: \"Disney+\", ranks: [2, 3, 1, 2, 2, 3, 3, 3] },\n  { name: \"Max\", ranks: [3, 2, 3, 3, 4, 4, 4, 4] },\n  { name: \"Prime Video\", ranks: [4, 4, 4, 5, 5, 5, 5, 5] },\n  { name: \"Apple TV+\", ranks: [6, 6, 5, 4, 3, 2, 1, 1] },\n  { name: \"Paramount+\", ranks: [5, 5, 6, 6, 6, 6, 6, 6] },\n];\nconst leadIndex = 4; // Apple TV+ — the season's storyline, bottom tier to #1\nconst overtakeWeekIndex = 6; // \"Wk 7\", first week Apple TV+ reaches rank #1\n\n// --- Mount -------------------------------------------------------------------\nconst canvas = document.createElement(\"canvas\");\ndocument.getElementById(\"container\").appendChild(canvas);\n\n// --- Plugins (native Chart.js plugin API, no external annotation package) ----\n// Subtle band across the #1 row so the \"race to the top\" reads at a glance.\nconst rankOneHighlight = {\n  id: \"rankOneHighlight\",\n  beforeDatasetsDraw(chart) {\n    const { ctx, chartArea, scales } = chart;\n    const halfStep = Math.abs(scales.y.getPixelForValue(2) - scales.y.getPixelForValue(1)) / 2;\n    const yCenter = scales.y.getPixelForValue(1);\n    ctx.save();\n    ctx.globalAlpha = 0.07;\n    ctx.fillStyle = t.ink;\n    ctx.fillRect(chartArea.left, yCenter - halfStep, chartArea.width, halfStep * 2);\n    ctx.restore();\n  },\n};\n\n// Callout marking the overtake, computed from the same scale the lines use.\nconst overtakeAnnotation = {\n  id: \"overtakeAnnotation\",\n  afterDatasetsDraw(chart) {\n    const { ctx, scales } = chart;\n    const x = scales.x.getPixelForValue(overtakeWeekIndex);\n    const y = scales.y.getPixelForValue(1);\n    ctx.save();\n    ctx.font = \"600 13px sans-serif\";\n    ctx.fillStyle = t.palette[leadIndex % t.palette.length];\n    ctx.textAlign = \"right\";\n    ctx.textBaseline = \"bottom\";\n    ctx.fillText(\"Apple TV+ overtakes for #1\", x + 4, y - 14);\n    ctx.restore();\n  },\n};\n\n// --- Chart ---------------------------------------------------------------\nnew Chart(canvas, {\n  type: \"line\",\n  data: {\n    labels: weeks,\n    datasets: platforms.map((platform, i) => ({\n      label: platform.name,\n      data: platform.ranks,\n      borderColor: t.palette[i % t.palette.length],\n      backgroundColor: t.palette[i % t.palette.length],\n      borderWidth: i === leadIndex ? 5 : 3,\n      pointRadius: i === leadIndex ? 8 : 6,\n      pointHoverRadius: i === leadIndex ? 8 : 6,\n      pointBackgroundColor: t.palette[i % t.palette.length],\n      pointBorderColor: t.pageBg,\n      pointBorderWidth: 2,\n      tension: 0,\n      fill: false,\n    })),\n  },\n  options: {\n    responsive: true,\n    maintainAspectRatio: false,\n    animation: false,\n    layout: { padding: { top: 8, right: 24, bottom: 8, left: 8 } },\n    plugins: {\n      title: {\n        display: true,\n        text: \"bump-basic · javascript · chartjs · anyplot.ai\",\n        color: t.ink,\n        font: { size: 22, weight: \"500\" },\n        padding: { bottom: 20 },\n      },\n      legend: {\n        position: \"right\",\n        labels: {\n          color: t.ink,\n          font: { size: 14 },\n          usePointStyle: true,\n          pointStyle: \"circle\",\n          boxWidth: 8,\n          boxHeight: 8,\n        },\n      },\n    },\n    scales: {\n      x: {\n        title: { display: true, text: \"Week\", color: t.ink, font: { size: 16 } },\n        ticks: { color: t.inkSoft, font: { size: 14 } },\n        grid: { display: false },\n      },\n      y: {\n        reverse: true,\n        min: 1,\n        max: platforms.length,\n        ticks: {\n          stepSize: 1,\n          color: t.inkSoft,\n          font: { size: 14 },\n          callback: (value) => `#${value}`,\n        },\n        title: { display: true, text: \"Rank\", color: t.ink, font: { size: 16 } },\n        grid: { color: t.grid },\n      },\n    },\n  },\n  plugins: [rankOneHighlight, overtakeAnnotation],\n});\n"}