{"spec_id":"area-mountain-panorama","library":"echarts","language":"javascript","code":"// anyplot.ai\n// area-mountain-panorama: Mountain Panorama Profile with Labeled Peaks\n// Library: echarts 5.5.1 | JavaScript 22.23.0\n// Quality: 91/100 | Created: 2026-06-30\n\nconst t = window.ANYPLOT_TOKENS;\nconst THEME = window.ANYPLOT_THEME || 'light';\n\n// Wallis (Valais) panorama from Zermatt — Weisshorn to Rimpfischhorn\n// peaks: [bearing_deg, elevation_m, name]\nconst peaks = [\n  [6,   4506, \"Weisshorn\"],\n  [16,  4221, \"Zinalrothorn\"],\n  [24,  4063, \"Ober Gabelhorn\"],\n  [31,  4358, \"Dent Blanche\"],\n  [43,  4478, \"Matterhorn\"],\n  [56,  4164, \"Breithorn\"],\n  [63,  4092, \"Pollux\"],\n  [68,  4223, \"Castor\"],\n  [76,  4527, \"Liskamm\"],\n  [86,  4634, \"Dufourspitze\"],\n  [93,  4190, \"Strahlhorn\"],\n  [98,  4199, \"Rimpfischhorn\"],\n];\nconst MATTERHORN_IDX = 4;\n\n// Piecewise-linear ridgeline — sharp triangular peaks with inter-summit jaggedness\nconst ridgePoints = [\n  [0, 3050], [1, 3150], [2, 3250], [2.5, 3420], [3.5, 3600], [4, 3800], [5, 4100],\n  [6, 4506],\n  [7, 4150], [7.5, 4000], [8, 3800], [8.8, 3650], [9.5, 3420], [10, 3480], [11, 3520],\n  [11.8, 3620], [12.5, 3700], [13, 3750], [13.8, 3900], [14.5, 4000], [15, 4080], [15.5, 4150],\n  [16, 4221],\n  [16.8, 4050], [17, 3900], [17.8, 3700], [18.5, 3420], [19, 3350], [19.5, 3280],\n  [20, 3320], [20.8, 3420], [21, 3550], [21.5, 3650], [22.5, 3820], [23, 3940],\n  [24, 4063],\n  [24.8, 3900], [25, 3720], [25.8, 3580], [26, 3300], [26.8, 3180], [27, 3080],\n  [27.5, 3200], [28, 3320], [28.5, 3450], [29, 3700], [29.5, 3900], [30, 4000], [30.5, 4150],\n  [31, 4358],\n  [31.5, 4200], [32, 4050], [32.8, 3800], [33.5, 3450], [34, 3280], [35, 2980],\n  [35.8, 2900], [36.5, 2850], [37, 2870], [37.8, 2920], [38.5, 3000], [39, 3220], [39.5, 3380],\n  [40, 3500], [40.8, 3650], [41.5, 3950], [42, 4150], [42.5, 4350],\n  [43, 4478],\n  [43.5, 4200], [44, 3900], [44.8, 3600], [45, 3700], [45.8, 3500], [46.5, 3150],\n  [47, 3080], [47.8, 2980], [48, 2950],\n  [48.8, 2980], [49.5, 3000], [50, 3000], [50.8, 3120], [51.5, 3200], [52, 3300],\n  [52.8, 3500], [53.5, 3650], [54, 3800], [54.8, 3920], [55.5, 4050],\n  [56, 4164],\n  [56.8, 4050], [57.5, 3900], [58, 3800], [58.8, 3700], [59, 3500],\n  [59.8, 3480], [60.5, 3480], [61, 3550], [61.8, 3650], [62, 3780],\n  [63, 4092],\n  [63.5, 3960], [64, 3850], [64.8, 3780], [65, 3750],\n  [65.8, 3800], [66.5, 3900], [67, 3980], [67.5, 4050],\n  [68, 4223],\n  [68.5, 4010], [69, 3850], [69.8, 3750], [70, 3650], [70.8, 3700], [71.5, 3750],\n  [72, 3800], [72.8, 3920], [73.5, 4050], [74, 4180], [74.8, 4300], [75, 4350],\n  [76, 4527],\n  [76.8, 4380], [77, 4280], [77.8, 4150], [78.5, 3900], [79, 3780], [79.8, 3700],\n  [80, 3650], [80.8, 3800], [81.5, 3950], [82, 4000], [82.8, 4150], [83.5, 4200], [84.5, 4300],\n  [85, 4420], [85.5, 4500],\n  [86, 4634],\n  [86.8, 4480], [87, 4380], [87.8, 4250], [88.5, 4100], [89, 3980], [89.8, 3880],\n  [90, 3750], [90.8, 3800], [91.5, 3850], [92, 3920], [92.5, 4000],\n  [93, 4190],\n  [93.8, 4050], [94, 3950], [94.8, 3850], [95.5, 3720], [96, 3760], [96.5, 3800],\n  [97, 3900], [97.5, 4030],\n  [98, 4199],\n  [98.8, 4000], [99, 3880],\n  [99.8, 3750], [100.5, 3600], [101, 3450], [101.8, 3300], [102.5, 3200], [103, 3100],\n  [103.8, 3020], [104.5, 2960], [105, 2900],\n];\n\n// Sky gradient — Imprint blue (#4467A3, palette[2]) layered as background series\nconst skyTopColor = THEME === 'dark' ? 'rgba(68,103,163,0.75)' : 'rgba(68,103,163,0.32)';\nconst skyMidColor = THEME === 'dark' ? 'rgba(68,103,163,0.30)' : 'rgba(68,103,163,0.08)';\n\n// Mountain silhouette — theme-adaptive ink tokens (no custom hex values)\nconst fillColor = t.ink;\nconst ridgeColor = t.inkSoft;\n\n// Imprint palette position 1 — highlight the focal Matterhorn peak\nconst BRAND = t.palette[0]; // #009E73\n\nconst titleText = \"Alpine Panorama · area-mountain-panorama · javascript · echarts · anyplot.ai\";\nconst titleFontSize = Math.max(14, Math.round(22 * Math.min(1.0, 67 / titleText.length)));\n\nconst chart = echarts.init(document.getElementById(\"container\"));\n\nchart.setOption({\n  animation: false,\n  color: t.palette,\n  backgroundColor: t.pageBg,\n  title: {\n    text: titleText,\n    left: 'center',\n    top: 18,\n    textStyle: { color: t.ink, fontSize: titleFontSize, fontWeight: 'normal' },\n  },\n  grid: {\n    left: 95,\n    right: 50,\n    top: 230,\n    bottom: 65,\n  },\n  xAxis: {\n    type: 'value',\n    min: 0,\n    max: 105,\n    show: false,\n  },\n  yAxis: {\n    type: 'value',\n    min: 2600,\n    max: 4900,\n    name: 'Elevation (m)',\n    nameLocation: 'middle',\n    nameGap: 60,\n    nameTextStyle: { color: t.inkSoft, fontSize: 14 },\n    axisLabel: {\n      color: t.inkSoft,\n      fontSize: 13,\n      formatter: (v) => `${v} m`,\n    },\n    axisLine: { show: false },\n    axisTick: { show: false },\n    splitLine: { lineStyle: { color: t.grid, width: 1 } },\n  },\n  series: [\n    {\n      // Sky gradient — Imprint blue layered as background series behind mountain\n      type: 'line',\n      data: [[0, 4900], [105, 4900]],\n      smooth: false,\n      symbol: 'none',\n      lineStyle: { width: 0, opacity: 0 },\n      areaStyle: {\n        color: {\n          type: 'linear', x: 0, y: 0, x2: 0, y2: 1,\n          colorStops: [\n            { offset: 0,    color: skyTopColor },\n            { offset: 0.55, color: skyMidColor },\n            { offset: 1,    color: 'rgba(68,103,163,0)' },\n          ],\n        },\n        opacity: 1,\n      },\n      z: -1,\n    },\n    {\n      // Mountain silhouette\n      type: 'line',\n      data: ridgePoints,\n      smooth: false,\n      symbol: 'none',\n      lineStyle: { color: ridgeColor, width: 1.5 },\n      areaStyle: {\n        color: fillColor,\n        opacity: 0.95,\n      },\n      z: 0,\n    },\n  ],\n});\n\n// Peak annotations: staggered leader lines + two-line labels (name + elevation)\n// Wider xShifts for dense clusters (Pollux/Castor, Strahlhorn/Rimpfischhorn)\nconst yOffsets = [110,  75, 115,  80, 145,  75,  95,  60, 105, 130,  65,  92];\nconst xShifts  = [  0,   0,   0,   0,   0,   0, -18, +18,   0,   0, -18, +18];\n\nconst graphics = [];\npeaks.forEach(([angle, elev, name], i) => {\n  const result = chart.convertToPixel('grid', [angle, elev]);\n  if (!result) return;\n  const [px, py] = result;\n\n  const ly = py - yOffsets[i];\n  const lx = px + xShifts[i];\n  const isMH = (i === MATTERHORN_IDX);\n\n  const dotColor     = isMH ? BRAND : t.inkSoft;\n  const lineStroke   = isMH ? BRAND : t.inkSoft;\n  const dotR         = isMH ? 5 : 3;\n  const nameFill     = isMH ? BRAND : t.ink;\n  const nameFontSize = isMH ? 15 : 13;\n  const lineWidth    = isMH ? 1.5 : 1;\n\n  // Leader line: from just above summit dot to just below elevation label\n  graphics.push({\n    type: 'line',\n    z: 20,\n    shape: { x1: px, y1: py - dotR - 1, x2: lx, y2: ly + 16 },\n    style: { stroke: lineStroke, lineWidth, opacity: 0.7 },\n  });\n\n  // Summit dot\n  graphics.push({\n    type: 'circle',\n    z: 20,\n    shape: { cx: px, cy: py - dotR, r: dotR },\n    style: { fill: dotColor, stroke: 'none' },\n  });\n\n  // Peak name (bold, baseline at ly)\n  graphics.push({\n    type: 'text',\n    z: 20,\n    x: lx,\n    y: ly,\n    style: {\n      text: name,\n      textAlign: 'center',\n      textBaseline: 'bottom',\n      fill: nameFill,\n      fontSize: nameFontSize,\n      fontWeight: 'bold',\n    },\n  });\n\n  // Elevation — 14px gap below name baseline for breathing room\n  graphics.push({\n    type: 'text',\n    z: 20,\n    x: lx,\n    y: ly + 14,\n    style: {\n      text: `${elev} m`,\n      textAlign: 'center',\n      textBaseline: 'top',\n      fill: t.inkSoft,\n      fontSize: 11,\n    },\n  });\n});\n\nchart.setOption({ graphic: graphics });\n"}