{"spec_id":"waterfall-basic","library":"makie","language":"julia","code":"# anyplot.ai\n# waterfall-basic: Basic Waterfall Chart\n# Library: makie 0.21.9 | Julia 1.11.9\n# Quality: 93/100 | Created: 2026-08-04\n\nusing CairoMakie\nusing Colors\nusing Printf\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\"\n\nconst IMPRINT_PALETTE = [\n    colorant\"#009E73\", colorant\"#C475FD\", colorant\"#4467A3\", colorant\"#BD8233\",\n    colorant\"#AE3030\", colorant\"#2ABCCD\", colorant\"#954477\", colorant\"#99B314\",\n]\nconst BRAND   = IMPRINT_PALETTE[1]  # Imprint palette position 1 — increases\nconst LOSS    = IMPRINT_PALETTE[5]  # Imprint semantic anchor for loss — decreases\nconst NEUTRAL = INK                 # theme-adaptive neutral — start/end totals\n\n# --- Data: quarterly profit bridge, $ millions --------------------------------\ncategories = [\"Starting\\nBalance\", \"Product\\nSales\", \"Service\\nRevenue\",\n              \"Operating\\nCosts\", \"Marketing\\nSpend\", \"Taxes\", \"Net\\nProfit\"]\ndeltas   = [12.4, 8.5, 4.3, -5.4, -3.1, -1.85, 0.0]\nis_total = [true, false, false, false, false, false, true]\nn = length(categories)\n\nrunning = zeros(Float64, n)\nrunning[1] = deltas[1]\nfor i in 2:(n - 1)\n    running[i] = running[i - 1] + deltas[i]\nend\nrunning[n] = running[n - 1]\n\nbottoms = zeros(Float64, n)\ntops = zeros(Float64, n)\nbar_colors = Vector{typeof(BRAND)}(undef, n)\nfor i in 1:n\n    if is_total[i]\n        bottoms[i] = 0.0\n        tops[i] = running[i]\n        bar_colors[i] = NEUTRAL\n    else\n        lo, hi = extrema((running[i - 1], running[i]))\n        bottoms[i] = lo\n        tops[i] = hi\n        bar_colors[i] = deltas[i] >= 0 ? BRAND : LOSS\n    end\nend\n\n# --- Title (fontsize scaled to length — see plot-generator.md) ----------------\ntitle_str = \"Quarterly Profit Bridge · waterfall-basic · julia · makie · anyplot.ai\"\ntitle_fontsize = round(Int, 20 * min(1.0, 67 / length(title_str)))\n\n# --- Plot -----------------------------------------------------------------------\nfig = Figure(\n    resolution      = (1600, 900),\n    fontsize        = 14,\n    backgroundcolor = PAGE_BG,\n)\n\nax = Axis(\n    fig[1, 1];\n    title              = title_str,\n    titlesize          = title_fontsize,\n    titlecolor         = INK,\n    ylabel             = \"Amount (\\$M)\",\n    ylabelsize         = 14,\n    ylabelcolor        = INK,\n    xticklabelcolor    = INK_SOFT,\n    yticklabelcolor    = INK_SOFT,\n    xticklabelsize     = 12,\n    yticklabelsize     = 12,\n    backgroundcolor    = PAGE_BG,\n    topspinevisible    = false,\n    rightspinevisible  = false,\n    leftspinecolor     = INK_SOFT,\n    bottomspinecolor   = INK_SOFT,\n    xgridvisible       = false,\n    ygridcolor         = RGBAf(INK.r, INK.g, INK.b, 0.15),\n    xticks             = (1:n, categories),\n    xticksvisible      = false,\n)\n\nbar_width = 0.62\nbarplot!(ax, 1:n, tops; fillto = bottoms, color = bar_colors, width = bar_width, strokewidth = 0)\n\n# Connecting lines linking each bar's cumulative level to the next bar's start\nhalf = bar_width / 2\nfor i in 1:(n - 1)\n    level = running[i]\n    lines!(ax, [i + half, i + 1 - half], [level, level];\n        color = INK_SOFT, linewidth = 1.8, linestyle = :dot)\nend\n\n# The largest single decrease gets a bolder, slightly larger label to call\n# out the focal point of the bridge.\ndecrease_idxs = findall(i -> !is_total[i] && deltas[i] < 0, 1:n)\nbiggest_idx = decrease_idxs[argmax(abs.(deltas[decrease_idxs]))]\nbiggest_label = replace(categories[biggest_idx], \"\\n\" => \" \")\n\n# Running total labels — above the bar for increases/totals, below for decreases\nlabel_offset = maximum(tops) * 0.035\nfor i in 1:n\n    label = @sprintf(\"\\$%.2fM\", running[i])\n    emphasize = i == biggest_idx\n    lbl_fontsize = emphasize ? 17 : 15\n    lbl_font = emphasize ? :bold : :regular\n    if is_total[i] || deltas[i] >= 0\n        text!(ax, i, tops[i] + label_offset; text = label,\n            align = (:center, :bottom), color = INK, fontsize = lbl_fontsize, font = lbl_font)\n    else\n        text!(ax, i, bottoms[i] - label_offset; text = label,\n            align = (:center, :top), color = INK, fontsize = lbl_fontsize, font = lbl_font)\n    end\nend\n\n# Subtitle annotation summarizing the bridge's focal point, set in the axis's\n# relative-space headroom above the tallest bar — a Makie `rich()` touch that\n# mixes weight/color within a single text object to call out the two key figures.\nsubtitle_rich = rich(\n    \"Net profit reaches \",\n    rich(@sprintf(\"\\$%.2fM\", running[end]); color = BRAND, font = :bold),\n    \" despite a \",\n    rich(@sprintf(\"\\$%.2fM\", abs(deltas[biggest_idx])); color = LOSS, font = :bold),\n    \" $biggest_label pullback — the bridge's largest single decrease\",\n)\ntext!(ax, 0.5, 0.97; space = :relative, text = subtitle_rich,\n    align = (:center, :top), fontsize = 13, color = INK_SOFT)\n\nylims!(ax, -maximum(tops) * 0.08, maximum(tops) * 1.18)\n\nelements = [PolyElement(color = BRAND), PolyElement(color = LOSS), PolyElement(color = NEUTRAL)]\nLegend(fig[1, 1], elements, [\"Increase\", \"Decrease\", \"Total\"];\n    tellwidth = false, tellheight = false,\n    halign = :left, valign = :top,\n    orientation = :horizontal,\n    framevisible = false,\n    labelcolor = INK_SOFT,\n    labelsize = 12,\n    backgroundcolor = :transparent,\n)\n\n# --- Save -----------------------------------------------------------------------\nsave(\"plot-$(THEME).png\", fig; px_per_unit = 2)\n"}