{"spec_id":"range-interval","library":"makie","language":"julia","code":"# anyplot.ai\n# range-interval: Range Interval Chart\n# Library: makie 0.21.9 | Julia 1.11.9\n# Quality: 94/100 | Created: 2026-09-02\n\nusing CairoMakie\nusing Colors\nusing Random\n\nRandom.seed!(42)\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]\nconst BRAND = IMPRINT_PALETTE[1]\n\n# --- Data ---------------------------------------------------------------\n# Annual base salary ranges by job title at a mid-size tech company (USD thousands),\n# sorted by midpoint so the chart reads as a low-to-high career ladder.\njob_titles = [\n    \"Support Specialist\", \"QA Analyst\", \"Data Analyst\", \"Frontend Developer\",\n    \"Backend Developer\", \"DevOps Engineer\", \"Product Manager\", \"Data Scientist\",\n    \"Senior Engineer\", \"Engineering Manager\", \"Principal Engineer\", \"VP of Engineering\",\n]\nmin_salary = [42, 48, 55, 62, 68, 75, 82, 90, 105, 120, 145, 175]\nmax_salary = [58, 66, 76, 85, 94, 102, 118, 130, 150, 175, 205, 260]\n\norder = sortperm(min_salary .+ max_salary)\njob_titles = job_titles[order]\nmin_salary = Float64.(min_salary[order])\nmax_salary = Float64.(max_salary[order])\npositions = 1:length(job_titles)\n\nspread = max_salary .- min_salary\nwidest_idx = argmax(spread)\n\n# --- Title (scales fontsize to length; 67 chars is the style-guide baseline) --\ntitle_text = \"Salary Ranges by Job Title · range-interval · julia · makie · anyplot.ai\"\nratio = length(title_text) > 67 ? 67 / length(title_text) : 1.0\ntitlesize = max(round(Int, 20 * ratio), 13)\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_text,\n    titlesize          = titlesize,\n    titlecolor         = INK,\n    xlabel             = \"Annual Base Salary (USD thousands)\",\n    ylabel             = \"Job Title\",\n    xlabelsize         = 14,\n    ylabelsize         = 14,\n    xlabelcolor        = INK,\n    ylabelcolor        = INK,\n    xticklabelsize     = 12,\n    yticklabelsize     = 12,\n    xticklabelcolor    = INK_SOFT,\n    yticklabelcolor    = INK_SOFT,\n    xtickcolor         = INK_SOFT,\n    ytickcolor         = INK_SOFT,\n    yticks             = (positions, job_titles),\n    backgroundcolor    = PAGE_BG,\n    topspinevisible    = false,\n    rightspinevisible  = false,\n    leftspinecolor     = INK_SOFT,\n    bottomspinecolor   = INK_SOFT,\n    xgridcolor         = RGBAf(INK.r, INK.g, INK.b, 0.15),\n    ygridvisible       = false,\n    xautolimitmargin   = (0.03, 0.22),\n)\n\n# Alternating row bands (every other category) for scan-ability across 12 rows.\nband_rows = collect(positions)[2:2:end]\nhspan!(ax, band_rows .- 0.5, band_rows .+ 0.5; color = (INK_SOFT, 0.07))\n\nrangebars!(\n    ax, positions, min_salary, max_salary;\n    direction = :x,\n    color = (BRAND, 0.55),\n    whiskerwidth = 14,\n    linewidth = 16,\n)\n\nscatter!(ax, min_salary, positions; color = BRAND, markersize = 14, strokewidth = 1.5, strokecolor = PAGE_BG)\nscatter!(ax, max_salary, positions; color = BRAND, markersize = 14, strokewidth = 1.5, strokecolor = PAGE_BG)\n\n# Callout on the widest-range category to give readers an immediate takeaway\n# beyond the sort order alone.\ntext!(\n    ax, max_salary[widest_idx] + 6, positions[widest_idx];\n    text = \"Widest range: \\$$(round(Int, spread[widest_idx]))K spread\",\n    color = INK, fontsize = 12, align = (:left, :center),\n)\n\n# --- Save -----------------------------------------------------------------\nsave(\"plot-$(THEME).png\", fig; px_per_unit = 2)\n"}