{"spec_id":"funnel-basic","library":"altair","language":"python","code":"\"\"\" anyplot.ai\nfunnel-basic: Basic Funnel Chart\nLibrary: altair 6.1.0 | Python 3.14.4\nQuality: 87/100 | Updated: 2026-04-26\n\"\"\"\n\nimport os\n\nimport altair as alt\nimport pandas as pd\n\n\n# Theme-adaptive chrome\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\"\n\n# Okabe-Ito categorical palette — first stage is brand green (#009E73)\nIMPRINT = [\"#009E73\", \"#C475FD\", \"#4467A3\", \"#BD8233\", \"#AE3030\"]\n\n# Data — sales funnel example from the specification\nstages = [\"Awareness\", \"Interest\", \"Consideration\", \"Intent\", \"Purchase\"]\nvalues = [1000, 600, 400, 200, 100]\n\ndf = pd.DataFrame({\"stage\": stages, \"value\": values})\ndf[\"percentage\"] = (df[\"value\"] / df[\"value\"].iloc[0] * 100).round(1)\ndf[\"label\"] = df[\"value\"].astype(str) + \"  ·  \" + df[\"percentage\"].astype(str) + \"%\"\n\n# Centered funnel: bars span [-value/2, +value/2]; mid is always 0\ndf[\"x_start\"] = -df[\"value\"] / 2\ndf[\"x_end\"] = df[\"value\"] / 2\ndf[\"x_mid\"] = 0\n\nx_scale = alt.Scale(domain=[-620, 620])\n\n# Funnel bars (centered, narrowing)\nbars = (\n    alt.Chart(df)\n    .mark_bar(cornerRadius=6, height=110, stroke=PAGE_BG, strokeWidth=2)\n    .encode(\n        x=alt.X(\"x_start:Q\", axis=None, scale=x_scale),\n        x2=\"x_end:Q\",\n        y=alt.Y(\"stage:N\", sort=stages, axis=alt.Axis(title=None, labelPadding=18, ticks=False, domain=False)),\n        color=alt.Color(\"stage:N\", scale=alt.Scale(domain=stages, range=IMPRINT), legend=None),\n        tooltip=[\n            alt.Tooltip(\"stage:N\", title=\"Stage\"),\n            alt.Tooltip(\"value:Q\", title=\"Count\", format=\",\"),\n            alt.Tooltip(\"percentage:Q\", title=\"% of top\", format=\".1f\"),\n        ],\n    )\n)\n\n# Value + percentage labels positioned to the right of each bar\nlabels = (\n    alt.Chart(df)\n    .mark_text(align=\"left\", baseline=\"middle\", dx=14, fontSize=20, fontWeight=\"bold\", color=INK)\n    .encode(x=alt.X(\"x_end:Q\", scale=x_scale, axis=None), y=alt.Y(\"stage:N\", sort=stages), text=\"label:N\")\n)\n\nchart = (\n    (bars + labels)\n    .properties(\n        width=1400,\n        height=900,\n        background=PAGE_BG,\n        title=alt.Title(\n            \"Sales Funnel · funnel-basic · altair · anyplot.ai\", fontSize=28, color=INK, anchor=\"middle\", offset=20\n        ),\n    )\n    .configure_view(fill=PAGE_BG, stroke=None)\n    .configure_axis(\n        labelFontSize=20,\n        labelFontWeight=\"bold\",\n        labelColor=INK,\n        titleColor=INK,\n        domainColor=INK_SOFT,\n        tickColor=INK_SOFT,\n        grid=False,\n    )\n    .configure_title(color=INK)\n    .configure_legend(fillColor=ELEVATED_BG, strokeColor=INK_SOFT, labelColor=INK_SOFT, titleColor=INK)\n)\n\nchart.save(f\"plot-{THEME}.png\", scale_factor=3.0)\nchart.save(f\"plot-{THEME}.html\")\n"}