{"spec_id":"line-cycle-seasonal","library":"makie","language":"julia","code":"# anyplot.ai\n# line-cycle-seasonal: Cycle Plot (Seasonal Subseries)\n# Library: makie 0.22.10 | Julia 1.11.9\n# Quality: 87/100 | Created: 2026-06-15\n\nusing CairoMakie\nusing Colors\nusing Random\nusing Statistics\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 ELEVATED_BG = THEME == \"light\" ? colorant\"#FFFDF6\" : colorant\"#242420\"\nconst INK         = THEME == \"light\" ? colorant\"#1A1A17\" : colorant\"#F0EFE8\"\nconst INK_SOFT    = THEME == \"light\" ? colorant\"#4A4A44\" : colorant\"#B8B7B0\"\nconst INK_MUTED   = THEME == \"light\" ? colorant\"#6B6A63\" : colorant\"#A8A79F\"\nconst IMPRINT_PALETTE = [\n    colorant\"#009E73\",\n    colorant\"#C475FD\",\n    colorant\"#4467A3\",\n    colorant\"#BD8233\",\n    colorant\"#AE3030\",\n    colorant\"#2ABCCD\",\n    colorant\"#954477\",\n    colorant\"#99B314\",\n]\n\n# Data: Monthly average temperature (°C), mid-latitude city, 1994–2023\nconst N_YEARS      = 30\nconst N_MONTHS     = 12\nconst MONTH_NAMES  = [\"Jan\", \"Feb\", \"Mar\", \"Apr\", \"May\", \"Jun\",\n                      \"Jul\", \"Aug\", \"Sep\", \"Oct\", \"Nov\", \"Dec\"]\nconst BASE_TEMPS   = [-3.2, -1.8, 4.5, 11.2, 17.8, 22.5, 25.1, 24.3, 18.6, 12.1, 4.8, -1.5]\nconst WARMING_RATE = 0.04  # °C per year\n\ntemps = [BASE_TEMPS[mi] + WARMING_RATE * (yi - 1) + randn() * 1.2\n         for yi in 1:N_YEARS, mi in 1:N_MONTHS]\n\n# Title with length-scaled fontsize\nconst TITLE      = \"Monthly Temperature Cycles · line-cycle-seasonal · julia · makie · anyplot.ai\"\nconst TITLE_LEN  = length(TITLE)\nconst TITLE_SIZE = round(Int, 20 * (TITLE_LEN > 67 ? 67 / TITLE_LEN : 1.0))\n\n# Figure\nfig = Figure(\n    size            = (1600, 900),\n    fontsize        = 14,\n    backgroundcolor = PAGE_BG,\n)\n\nax = Axis(\n    fig[1, 1];\n    title              = TITLE,\n    titlesize          = TITLE_SIZE,\n    titlecolor         = INK,\n    xlabel             = \"Month\",\n    ylabel             = \"Average Temperature (°C)\",\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    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.12),\n    xticks             = (collect(1:N_MONTHS), MONTH_NAMES),\n    xminorgridvisible  = false,\n    yminorgridvisible  = false,\n)\n\nxlims!(ax, 0.5, 12.5)\n\n# Seasonal groups: each month centered at its integer position ±GROUP_HALF\n# Wider than original (0.42 vs 0.35) to reduce within-group line density\nconst GROUP_HALF    = 0.42\nconst SUBSERIES_COL = IMPRINT_PALETTE[1]  # #009E73 – brand green (annual subseries)\nconst MEAN_COL      = IMPRINT_PALETTE[3]  # #4467A3 – blue (monthly mean reference)\n\nmonthly_means = Float64[]\n\nfor mi in 1:N_MONTHS\n    center     = float(mi)\n    x_range    = range(center - GROUP_HALF, center + GROUP_HALF; length = N_YEARS)\n    y_vals     = temps[:, mi]\n    group_mean = mean(y_vals)\n    push!(monthly_means, group_mean)\n\n    # Chronological subseries line within this month group\n    lines!(ax, collect(x_range), y_vals;\n           color     = (SUBSERIES_COL, 0.72),\n           linewidth = 1.4)\n\n    # Horizontal mean reference line — the key seasonal comparison element\n    lines!(ax, [center - GROUP_HALF, center + GROUP_HALF], [group_mean, group_mean];\n           color     = MEAN_COL,\n           linewidth = 3.5)\n\n    # Subtle vertical divider between month groups\n    if mi < N_MONTHS\n        vlines!(ax, center + 0.5;\n                color     = RGBAf(INK.r, INK.g, INK.b, 0.10),\n                linewidth = 0.8)\n    end\nend\n\n# Seasonal arc: dashed curve connecting monthly means — highlights the annual cycle shape\narc_color = RGBAf(INK.r, INK.g, INK.b, 0.45)\nlines!(ax, collect(1.0:N_MONTHS), monthly_means;\n       color     = arc_color,\n       linewidth = 1.5,\n       linestyle = :dash)\n\n# Legend\nelem_sub  = LineElement(color = SUBSERIES_COL, linewidth = 1.4)\nelem_mean = LineElement(color = MEAN_COL,       linewidth = 3.5)\nelem_arc  = LineElement(color = arc_color,      linewidth = 1.5, linestyle = :dash)\nLegend(\n    fig[1, 2],\n    [elem_sub, elem_mean, elem_arc],\n    [\"Annual observations\\n(1994–2023)\", \"Monthly mean\", \"Seasonal arc\"];\n    framecolor      = RGBAf(INK_SOFT.r, INK_SOFT.g, INK_SOFT.b, 0.35f0),\n    backgroundcolor = ELEVATED_BG,\n    labelcolor      = INK_SOFT,\n    labelsize       = 12,\n    patchsize       = (25, 14),\n    padding         = (12, 12, 10, 10),\n)\n\ncolsize!(fig.layout, 1, Relative(0.84))\n\n# Warming trend annotation — makes the within-group slope insight explicit\nLabel(fig[2, 1:2];\n      text    = \"↑ Upward slope within each seasonal group reveals +0.04 °C yr⁻¹ warming signal (1994–2023)\",\n      fontsize = 11,\n      color    = INK_MUTED,\n      tellwidth = false,\n      halign   = :left,\n      padding  = (4, 0, 2, 4))\n\nrowgap!(fig.layout, 1, 4)\n\nsave(\"plot-$(THEME).png\", fig; px_per_unit = 2)\n"}