{"spec_id":"pyramid-basic","library":"ggplot2","language":"r","code":"#' anyplot.ai\n#' pyramid-basic: Basic Pyramid Chart\n#' Library: ggplot2 3.5.1 | R 4.4.1\n#' Quality: 87/100 | Created: 2026-09-05\n\nlibrary(ggplot2)\nlibrary(dplyr)\nlibrary(tidyr)\nlibrary(scales)\nlibrary(ragg)\n\nset.seed(42)\n\n# --- Theme tokens -----------------------------------------------------------\nTHEME       <- Sys.getenv(\"ANYPLOT_THEME\", \"light\")\nPAGE_BG     <- if (THEME == \"light\") \"#FAF8F1\" else \"#1A1A17\"\nELEVATED_BG <- if (THEME == \"light\") \"#FFFDF6\" else \"#242420\"\nINK         <- if (THEME == \"light\") \"#1A1A17\" else \"#F0EFE8\"\nINK_SOFT    <- if (THEME == \"light\") \"#4A4A44\" else \"#B8B7B0\"\n\nIMPRINT_MALE   <- \"#4467A3\"  # Imprint palette position 3 — blue (sky/male convention)\nIMPRINT_FEMALE <- \"#954477\"  # Imprint palette position 7 — rose (wellness/female convention)\n\n# --- Data --------------------------------------------------------------------\nage_groups <- c(\n  \"0-9\", \"10-19\", \"20-29\", \"30-39\", \"40-49\",\n  \"50-59\", \"60-69\", \"70-79\", \"80+\"\n)\n\ndf <- tibble::tibble(\n  category    = factor(age_groups, levels = age_groups),\n  value_left  = c(4.8, 5.1, 4.6, 4.2, 4.4, 4.0, 3.1, 1.9, 0.9),\n  value_right = c(4.6, 4.9, 4.5, 4.3, 4.6, 4.3, 3.4, 2.3, 1.3)\n) %>%\n  mutate(value_left = -value_left)\n\nplot_df <- df %>%\n  pivot_longer(\n    cols = c(value_left, value_right),\n    names_to = \"side\",\n    values_to = \"population\"\n  ) %>%\n  mutate(\n    side = factor(side,\n      levels = c(\"value_left\", \"value_right\"),\n      labels = c(\"Male\", \"Female\")\n    )\n  )\n\naxis_limit <- max(abs(df$value_left), df$value_right) * 1.15\n\n# --- Plot ---------------------------------------------------------------------\np <- ggplot(plot_df, aes(x = category, y = population, fill = side)) +\n  geom_col(width = 0.75) +\n  geom_text(\n    aes(\n      label = sprintf(\"%.1f\", abs(population)),\n      hjust = ifelse(population < 0, 1.15, -0.15)\n    ),\n    size = 2.6, color = INK_SOFT, show.legend = FALSE\n  ) +\n  coord_flip() +\n  scale_y_continuous(\n    limits = c(-axis_limit, axis_limit),\n    labels = function(x) label_number(accuracy = 1)(abs(x))\n  ) +\n  scale_fill_manual(values = c(\"Male\" = IMPRINT_MALE, \"Female\" = IMPRINT_FEMALE)) +\n  labs(\n    title = \"pyramid-basic · r · ggplot2 · anyplot.ai\",\n    x     = \"Age Group\",\n    y     = \"Population (millions)\",\n    fill  = NULL\n  ) +\n  theme_minimal(base_size = 8) +\n  theme(\n    plot.background    = element_rect(fill = PAGE_BG, color = PAGE_BG),\n    panel.background   = element_rect(fill = PAGE_BG, color = NA),\n    panel.grid.major.x = element_line(color = INK, linewidth = 0.3),\n    panel.grid.major.y = element_blank(),\n    panel.grid.minor   = element_blank(),\n    axis.title         = element_text(color = INK, size = 10),\n    axis.text          = element_text(color = INK_SOFT, size = 8),\n    axis.line.x.bottom = element_line(color = INK_SOFT, linewidth = 0.3),\n    axis.line.x.top    = element_blank(),\n    axis.line.y.left   = element_line(color = INK_SOFT, linewidth = 0.3),\n    axis.line.y.right  = element_blank(),\n    plot.title         = element_text(color = INK, size = 12),\n    legend.position     = \"top\",\n    legend.background   = element_rect(fill = ELEVATED_BG, color = NA),\n    legend.text         = element_text(color = INK_SOFT, size = 8),\n    legend.title        = element_text(color = INK)\n  )\n\n# --- Save -----------------------------------------------------------------\nggsave(\n  filename = sprintf(\"plot-%s.png\", THEME),\n  plot     = p,\n  device   = ragg::agg_png,\n  width    = 8,\n  height   = 4.5,\n  units    = \"in\",\n  dpi      = 400\n)\n"}