{"spec_id":"bar-heart-rate-zones","library":"plotly","language":"python","code":"\"\"\" anyplot.ai\nbar-heart-rate-zones: Time in Heart Rate Zones Bar Chart\nLibrary: plotly 6.8.0 | Python 3.13.13\nQuality: 91/100 | Created: 2026-06-14\n\"\"\"\n\nimport os\n\nimport plotly.graph_objects as go\n\n\n# Theme tokens\nTHEME = os.getenv(\"ANYPLOT_THEME\", \"light\")\nPAGE_BG = \"#FAF8F1\" if THEME == \"light\" else \"#1A1A17\"\nELEVATED_BG = \"#FFFDF6\" if THEME == \"light\" else \"#242420\"\nINK = \"#1A1A17\" if THEME == \"light\" else \"#F0EFE8\"\nINK_SOFT = \"#4A4A44\" if THEME == \"light\" else \"#B8B7B0\"\nINK_MUTED = \"#6B6A63\" if THEME == \"light\" else \"#A8A79F\"\nGRID = \"rgba(26,26,23,0.15)\" if THEME == \"light\" else \"rgba(240,239,232,0.15)\"\n\n# Zone colors — Imprint palette, semantically mapped to fitness zone conventions:\n# Z1 grey → theme-adaptive muted; Z2 blue → Imprint #3; Z3 green → Imprint #1 (brand);\n# Z4 orange → Imprint #4 ochre; Z5 red → Imprint #5 matte red\nZONE_COLORS = [INK_MUTED, \"#4467A3\", \"#009E73\", \"#BD8233\", \"#AE3030\"]\n\n# Data — 60-min tempo run\nzones = [\"Z1\", \"Z2\", \"Z3\", \"Z4\", \"Z5\"]\nzone_names = [\"Recovery\", \"Endurance\", \"Aerobic\", \"Threshold\", \"Maximum\"]\nminutes = [8, 22, 15, 12, 3]\nhr_ranges = [\"< 115 bpm\", \"115–133 bpm\", \"133–152 bpm\", \"152–171 bpm\", \"> 171 bpm\"]\n\ntitle = \"60-min Tempo Run · bar-heart-rate-zones · python · plotly · anyplot.ai\"\ntitle_size = max(11, round(16 * 67 / len(title)))\n\ntick_labels = [f\"<b>{z}</b> {name}<br>{hr}\" for z, name, hr in zip(zones, zone_names, hr_ranges, strict=False)]\n\n# Plot\nfig = go.Figure(\n    go.Bar(\n        x=zones,\n        y=minutes,\n        marker_color=ZONE_COLORS,\n        marker_line={\"width\": 1, \"color\": INK_SOFT},\n        text=[f\"{m} min\" for m in minutes],\n        textposition=\"outside\",\n        textfont={\"size\": 14, \"color\": INK},\n        constraintext=\"none\",\n        hovertemplate=\"<b>%{x}</b><br>Time in zone: %{y} min<extra></extra>\",\n    )\n)\n\nfig.add_hline(\n    y=12,\n    line_color=INK_MUTED,\n    line_dash=\"dot\",\n    line_width=2,\n    annotation_text=\"12 min / zone avg\",\n    annotation_font={\"size\": 10, \"color\": INK_MUTED},\n    annotation_position=\"top right\",\n)\n\nfig.update_layout(\n    autosize=False,\n    bargap=0.4,\n    margin={\"l\": 80, \"r\": 40, \"t\": 80, \"b\": 65},\n    paper_bgcolor=PAGE_BG,\n    plot_bgcolor=PAGE_BG,\n    title={\"text\": title, \"font\": {\"size\": title_size, \"color\": INK}, \"x\": 0.5, \"xanchor\": \"center\"},\n    xaxis={\n        \"tickmode\": \"array\",\n        \"tickvals\": zones,\n        \"ticktext\": tick_labels,\n        \"tickfont\": {\"size\": 11, \"color\": INK_SOFT},\n        \"showline\": False,\n        \"showgrid\": False,\n        \"zeroline\": False,\n    },\n    yaxis={\n        \"title\": {\"text\": \"Time (min)\", \"font\": {\"size\": 12, \"color\": INK}},\n        \"tickfont\": {\"size\": 10, \"color\": INK_SOFT},\n        \"gridcolor\": GRID,\n        \"showline\": False,\n        \"zeroline\": False,\n        \"range\": [0, max(minutes) * 1.4],\n    },\n    showlegend=False,\n)\n\nfig.write_image(f\"plot-{THEME}.png\", width=800, height=450, scale=4)\nfig.write_html(f\"plot-{THEME}.html\", include_plotlyjs=\"cdn\")\n"}