{"spec_id":"funnel-basic","library":"makie","language":"julia","code":"# anyplot.ai\n# funnel-basic: Basic Funnel Chart\n# Library: makie 0.21.9 | Julia 1.11.9\n# Quality: 87/100 | Created: 2026-09-05\n\nusing CairoMakie\nusing Colors\n\n# --- Theme tokens -----------------------------------------------------------\nconst THEME    = get(ENV, \"ANYPLOT_THEME\", \"light\")\nconst PAGE_BG  = THEME == \"light\" ? colorant\"#FAF8F1\" : colorant\"#1A1A17\"\nconst INK      = THEME == \"light\" ? colorant\"#1A1A17\" : colorant\"#F0EFE8\"\nconst INK_SOFT = THEME == \"light\" ? colorant\"#4A4A44\" : colorant\"#B8B7B0\"\nconst IMPRINT_PALETTE = [\n    colorant\"#009E73\", colorant\"#C475FD\", colorant\"#4467A3\", colorant\"#BD8233\",\n    colorant\"#AE3030\", colorant\"#2ABCCD\", colorant\"#954477\", colorant\"#99B314\",\n]\n\n# --- Data: e-commerce checkout funnel ---------------------------------------\nstages = [\n    \"Site Visitors\", \"Product Page Views\", \"Added to Cart\",\n    \"Started Checkout\", \"Entered Payment\", \"Completed Purchase\",\n]\nvalues = [48000, 29500, 15200, 8900, 6100, 4750]\nvalue_display = [\"48,000\", \"29,500\", \"15,200\", \"8,900\", \"6,100\", \"4,750\"]\nn = length(values)\npercentages = round.(Int, values ./ values[1] .* 100)\n\n# Each trapezoid tapers from the boundary above it to the boundary below it,\n# so adjacent stages share an edge and the whole shape narrows continuously.\n# A sqrt scale (rather than linear) keeps the taper visible through the tail\n# of the funnel, where later stages would otherwise compress into a near-flat\n# strip once their values are small relative to the first stage.\nnormalized = sqrt.(values ./ values[1])\nmax_half_width = 5.0\nboundaries = Vector{Float64}(undef, n + 1)\nboundaries[1] = normalized[1]\nfor i in 2:n\n    boundaries[i] = (normalized[i - 1] + normalized[i]) / 2\nend\nboundaries[n + 1] = normalized[n]\nhalf_widths = boundaries .* max_half_width\n\n# --- Plot ---------------------------------------------------------------\nfig = Figure(\n    size            = (1200, 1200),\n    fontsize        = 14,\n    backgroundcolor = PAGE_BG,\n)\n\nax = Axis(\n    fig[1, 1];\n    title           = \"funnel-basic · julia · makie · anyplot.ai\",\n    titlesize       = 20,\n    titlecolor      = INK,\n    backgroundcolor = PAGE_BG,\n)\nhidedecorations!(ax)\nhidespines!(ax)\n\nfor i in 1:n\n    y_top = n - (i - 1)\n    y_bottom = n - i\n    top_hw = half_widths[i]\n    bottom_hw = half_widths[i + 1]\n\n    points = Point2f[\n        (-top_hw, y_top), (top_hw, y_top),\n        (bottom_hw, y_bottom), (-bottom_hw, y_bottom),\n    ]\n    poly!(ax, points; color = IMPRINT_PALETTE[i], strokecolor = PAGE_BG, strokewidth = 3)\n\n    y_center = (y_top + y_bottom) / 2\n    text!(\n        ax, max_half_width + 1.0, y_center + 0.16;\n        text = stages[i], color = INK, fontsize = 22,\n        align = (:left, :center), font = :bold,\n    )\n    text!(\n        ax, max_half_width + 1.0, y_center - 0.22;\n        text = \"$(value_display[i]) · $(percentages[i])%\",\n        color = INK_SOFT, fontsize = 19, align = (:left, :center),\n    )\nend\n\nxlims!(ax, -max_half_width - 0.5, max_half_width + 9.0)\nylims!(ax, -0.5, n + 0.5)\n\n# --- Save -----------------------------------------------------------------\nsave(\"plot-$(THEME).png\", fig; px_per_unit = 2)\n"}