A Tale of Two Toilets

ECON2120G-M05 — Principles of Microeconomics | Episode 5: Elasticity, Complements, and Substitutes

Dr. Meghan Downes

2026-02-25

“A Tale of Two Toilets”

Note🏴‍☠️ Captain’s Log — Pirate Pete’s Tales of Misadventure

The year is 1625. The Invisible Hand rocks gently in a Caribbean port.

Captain Pirate Pete haggles furiously with a spice merchant over a barrel of cinnamon. First Mate Orpheus is inspecting rum barrels — tapping them, sniffing them, swapping one vendor for another. Socks the Goat is eating a rival merchant’s price sign.

“Orpheus! Why are you switching vendors?” Pete calls out.

“Because this one’s cheaper, Captain. Rum is rum. If it’s the same quality, I’ll take the lower price every time.”

Pete nods. In 1625, everything is elastic. At port, there are a dozen merchants selling the same spices, the same rum, the same sailcloth. Substitutes everywhere. You shop on price.

Fast forward to 2025. Pete materializes on a modern street. He watches a woman sprint into a pharmacy, eyes desperate, clutching a prescription. Insulin. She doesn’t even look at the price. She can’t — she’ll die without it. There is no substitute.

Pete scratches his head. “Orpheus… in my day, everything had a substitute. What happened?”

Fast forward again to 2425. Socks the GOAT — now several centuries old and inexplicably sentient — floats aboard a sleek vessel orbiting a dust-brown planet. Clean water has become the scarcest resource in the galaxy. There is no alternative. No substitute. Demand is perfectly inelastic — a vertical line. Socks watches the price climb every cycle and thinks: “Baaaa.”

Here’s the economics: From 1625 to 2425, the question is always the same — how responsive is demand to a change in price? When substitutes are plentiful, demand is elastic. When there are no substitutes, demand is inelastic. And when the good is life itself? The demand curve goes vertical.

Today, we learn to tell the difference. Welcome to the tale of two toilets. 🏴‍☠️

Complements: Everywhere a Human Goes…

…A Toilet Is Sure to Follow

Here’s something no other professor is likely to tell you, but it is a truth of the world: everywhere a human goes, a toilet is sure to follow. There is no more truth in the world than this. This is a truth bomb. And it stinks.

This is a way of introducing our first key economic term: complements (Cowen and Tabarrok (2023), Ch. 5).

Tip🧻 The Toilet & Toilet Paper

What do we see? A toilet. And toilet paper. Just like humans and toilets are complements, everywhere you have a toilet, you really are much happier when there’s toilet paper. They are two things you always use together.

Complement: a good that is typically consumed alongside another good. When the price of one rises, demand for the other falls.

  • Toilets & toilet paper
  • Cars & gasoline
  • Hot dogs & buns
  • Printers & ink cartridges

The economic definition is precise: two goods are complements if an increase in the price of one leads to a decrease in the demand for the other. Formally:

\[ \frac{\partial Q_B}{\partial P_A} < 0 \quad \text{(A and B are complements)} \]

If the price of toilets goes through the roof, fewer people install toilets, and therefore fewer people buy toilet paper. The goods move together — they complement each other (Cowen and Tabarrok (2023), Ch. 5).

Substitutes: Beef, Chicken, and the Pandemic

Close Your Eyes — It’s March 2020

Here’s what I want you to do. Close your eyes. Picture yourself in December 2019. Life is good. Christmas break. Middle school, maybe high school. Everything is rocking.

Now fast-forward to March 2020. What changed? What happened? Do you have people you love who were essential workers? What did school look like? How were you doing psychologically? It was pretty rough.

And the thing that many of us remember — despite all the crazy things, the bad things, the long COVID, the death, the fear — the thing we all remember is:

Warning🧻 The Great Toilet Paper Shortage of 2020

Fight Club in the grocery stores. Empty shelves everywhere. People were paying $125 for a six-pack of toilet paper on Amazon at the height of the panic. People had garages full of the stuff — some of whom are still working through that supply.

Nobody knew how long it was going to last. And if you went to the store and saw the shelf was empty — oh no, I better get whatever I can get. Irrational? Maybe. Terrifying? Absolutely.

Two Supply Chains, Two Markets

Now here’s the key insight. Think about the toilet paper you find in public restrooms — campus, Starbucks, the gas station (don’t advise that one). The cheapest, the scratchiest, the thinnest. It doesn’t even accomplish its job as toilet paper. It comes in huge rolls, big boxes, no extra wrapping.

Compare that to what we keep at home. The good stuff. Charmin, soft and strong. Quilted Northern with little patterns. The new kind where someone spent two years of their life designing an easy-tear edge. Thank you, specialization, division of labor, and trade — we have easy-tear toilet paper. That’s what we learned from the pandemic.

These are two completely different products with two completely different supply chains:

Show R code
tp_compare <- tibble(
  Feature = c("Wood / pulp type", "Chemical processing", "Roll size", "Packaging",
              "Manufacturing equipment", "Shipping & distribution", "End user",
              "Softness level"),
  `Public (Commercial) TP` = c(
    "Cheaper hardwood pulp", "Minimal processing", "Huge industrial rolls",
    "Bulk boxes, no wrapping", "Industrial-scale machines", "Commercial distributors",
    "Offices, schools, restaurants", "Sandpaper-adjacent"
  ),
  `Private (Consumer) TP` = c(
    "Higher-grade softwood pulp", "Multi-step softening", "Standard consumer rolls",
    "Individual wrapping, branding", "Different specialized machines", "Retail supply chain",
    "Homes and households", "Cloud-like (ideally)"
  )
)

tp_compare %>%
  kable(align = "lll",
        caption = "Two Toilets, Two Supply Chains — What We Think Is One Market Is Actually Two") %>%
  kable_styling(bootstrap_options = c("striped", "hover"), full_width = FALSE) %>%
  column_spec(1, bold = TRUE)
Two Toilets, Two Supply Chains — What We Think Is One Market Is Actually Two
Feature Public (Commercial) TP Private (Consumer) TP
Wood / pulp type Cheaper hardwood pulp Higher-grade softwood pulp
Chemical processing Minimal processing Multi-step softening
Roll size Huge industrial rolls Standard consumer rolls
Packaging Bulk boxes, no wrapping Individual wrapping, branding
Manufacturing equipment Industrial-scale machines Different specialized machines
Shipping & distribution Commercial distributors Retail supply chain
End user Offices, schools, restaurants Homes and households
Softness level Sandpaper-adjacent Cloud-like (ideally)
Note🔑 The Key Insight

What we think of as “just toilet paper” is actually two different goods produced by two entirely separate supply chains — different wood, different pulp, different chemicals, different machines, different boxes. During the pandemic, the commercial supply chain could not help the consumer supply chain. They’re broadly substitutes, but the machinery to switch between them doesn’t exist on short notice.

This is why the great toilet paper shortage of 2020 happened. Demand shifted massively to homes, but the private supply chain couldn’t absorb the surge.

Substitutes: The Formal Definition

Two goods are substitutes if an increase in the price of one leads to an increase in the demand for the other (Cowen and Tabarrok (2023), Ch. 5):

\[ \frac{\partial Q_B}{\partial P_A} > 0 \quad \text{(A and B are substitutes)} \]

When the price of beef goes up, people buy more chicken. When private TP becomes scarce and expensive, people (reluctantly) grab the commercial stuff if they can. The goods replace each other — they substitute.

The Beef-Chicken-Pork Substitution

Have any of you gone to the grocery store recently and attempted to purchase beef? It is crazy. We’re talking $8 to $10 a pound for ground hamburger. Even at Sam’s Club, a pack of two steaks is pushing fifty dollars — that’s $20 a pound for chuck steak. What is going on?

So what do we do? We substitute. My family’s beef consumption has fallen quite a bit. And what have I substituted it with? Chicken, right? Because chicken is a lot cheaper than beef. And I’m even considering pork as an option — and I don’t usually consider pork, because pigs are very smart and quite a bit like humans. But the price is like $1.10 a pound, so at some point, economic necessity means you need to compromise your values. Maybe those are sad days, but that’s economics.

Show R code
meat_long <- meat_data %>%
  pivot_longer(cols = c(beef_price, chicken_price),
               names_to = "meat", values_to = "price") %>%
  mutate(meat = ifelse(meat == "beef_price",
                       "Ground Beef (APU0000FC1101)",
                       "Whole Chicken (APU0000FF1101)"))

ggplot(meat_long, aes(x = year, y = price, color = meat)) +
  geom_line(linewidth = 1.5) +
  geom_point(size = 3) +
  scale_color_manual(values = c("Ground Beef (APU0000FC1101)" = pal$red,
                                 "Whole Chicken (APU0000FF1101)" = pal$teal)) +
  scale_y_continuous(labels = dollar_format()) +
  scale_x_continuous(breaks = meat_data$year) +
  labs(
    title = "Beef vs. Chicken: The Substitution Story",
    subtitle = "As beef prices climb, consumers substitute toward chicken and pork",
    x = NULL, y = "Average Price ($/lb)",
    color = NULL,
    caption = meat_note
  ) +
  theme_minimal(base_size = 14) +
  theme(
    plot.title = element_text(face = "bold"),
    legend.position = "bottom",
    axis.text.x = element_text(angle = 45, hjust = 1)
  )

Tip🐔 The Chicken Digression

Your professor keeps chickens. In Georgia, the chickens escaped and became literally free-range — they roosted in trees and bred and bred until there were about 125 of them. The hawks and coyotes eventually took care of most of them. But the good news? No chiggers, no ticks, no crazy nasty snakes near the house. Chickens are awesome.

We did kill a rooster once, but only because Speckles kept attacking the children and would sneak up behind me when I had groceries in my hands and spur me to heck. So — chickens, it’s okay to eat roosters.

Elasticity: How Stretchy Is Demand?

The Definition

Now we introduce elasticity — and unlike many things economists talk about, this one actually sounds like what it means. It describes stretchiness: how much does the quantity demanded change when we change the price? (Cowen and Tabarrok (2023), Ch. 5)

\[ E_d = \left| \frac{\%\Delta Q_d}{\%\Delta P} \right| \tag{1} \]

Or, using the midpoint formula for more precision:

\[ E_d = \left| \frac{\dfrac{Q_2 - Q_1}{(Q_2 + Q_1)/2}}{\dfrac{P_2 - P_1}{(P_2 + P_1)/2}} \right| \tag{2} \]

The interpretation is straightforward:

Show R code
elast_ranges <- tibble(
  Condition = c("$E_d > 1$", "$E_d < 1$", "$E_d = 1$", "$E_d = 0$", "$E_d = \\infty$"),
  Classification = c("Elastic", "Inelastic", "Unit Elastic",
                      "Perfectly Inelastic", "Perfectly Elastic"),
  Meaning = c(
    "Quantity is VERY responsive to price — small ΔP → big ΔQ",
    "Quantity is NOT very responsive to price — big ΔP → small ΔQ",
    "Quantity changes proportionally to price",
    "Quantity does NOT change regardless of price (vertical demand)",
    "Any price increase → quantity drops to zero (horizontal demand)"
  ),
  Example = c(
    "Corn, tortilla chips, generic goods",
    "Coffee, insulin, tiger eyeballs",
    "Theoretical benchmark",
    "Insulin for a Type 1 diabetic",
    "Perfectly competitive commodity in theory"
  )
)

elast_ranges %>%
  kable(align = "llll", escape = FALSE,
        caption = "Elasticity Ranges — How Responsive Is Demand? (Cowen & Tabarrok, Ch. 5)") %>%
  kable_styling(bootstrap_options = c("striped", "hover"), full_width = FALSE) %>%
  column_spec(1, bold = TRUE, monospace = TRUE) %>%
  column_spec(2, bold = TRUE)
Elasticity Ranges — How Responsive Is Demand? (Cowen & Tabarrok, Ch. 5)
Condition Classification Meaning Example
$E_d > 1$ Elastic Quantity is VERY responsive to price — small ΔP → big ΔQ Corn, tortilla chips, generic goods
$E_d < 1$ Inelastic Quantity is NOT very responsive to price — big ΔP → small ΔQ Coffee, insulin, tiger eyeballs
$E_d = 1$ Unit Elastic Quantity changes proportionally to price Theoretical benchmark
$E_d = 0$ Perfectly Inelastic Quantity does NOT change regardless of price (vertical demand) Insulin for a Type 1 diabetic
$E_d = \infty$ Perfectly Elastic Any price increase → quantity drops to zero (horizontal demand) Perfectly competitive commodity in theory

Elastic Goods: Corn and Tortilla Chips

Think about corn. Is one corn cob kind of the same as the next one? Pretty much. You don’t care whether you bought it from Farmer John or Farmer Joe as long as it meets your basic corn quality. These are commodity goods — highly interchangeable, lots of substitutes.

Now imagine you’re standing in the grocery store. Two bags of corn tortilla chips. They taste about the same. One is $5 and one is $7. Which are you going to choose? The $5 one, right? You’re making your decision based on price. That’s elastic demand — you’re very price-sensitive because substitutes are readily available (Cowen and Tabarrok (2023), Ch. 5).

Inelastic Goods: Caffeine, Insulin, and Tiger Eyeballs

Now think about coffee. I personally run on coffee. I might have been guilty of stockpiling quite a bit of coffee during the pandemic — like, two cases of two-pound coffee beans. I don’t function without coffee. It is a cold day in hell before I will go without my caffeine.

And I discovered with my first pregnancy, when they said “no caffeine starting day one” — worst month of my life. Never doing that again.

What is it about caffeine? It’s addictive. If the price triples overnight, is that going to cut your consumption? For those of us deep down the caffeine addiction path — no. I would give up most everything in my life to be able to have coffee. That’s inelastic demand (Cowen and Tabarrok (2023), Ch. 5).

Warning💉 Insulin: Life or Death

Now imagine this were insulin. One month, your supplies cost $200. Next month, someone bought the patent and now it’s $1,000. Are you going to pay it?

You’ll die if you don’t have it.

A student shared an article about someone who couldn’t afford their Type 1 diabetes insulin anymore. They ended up dying because they wanted to pay the rent instead. Those are some terrible choices. But if you need insulin to live, your choice is literally life or death. They can raise the price and raise the price — you’re going to sell plasma and do whatever it takes, because you don’t have a choice.

That’s what we call inelastic. It’s the very opposite of the chicken. Zero substitutes. Vertical demand curve. The darkest corner of economics.

Tiger Eyeballs: The Dark Side of Inelastic Demand

Tiger, Tiger, Burning Bright

“Tiger, tiger, burning bright, / In the forests of the night, / What immortal hand or eye / Dare frame thy fearful symmetry?”

— William Blake, “The Tyger” (1794) (Blake (1794))

Tigers are an incredibly endangered species. It’s estimated that there are only about 700 to 750 wild tigers left in the world. There are vastly more in zoos and private keeping, because some people think tigers are pets. This almost always ends badly for them.

So why are there so few wild tigers left? Deforestation and habitat conflict, yes. But the primary driver is poaching. Tigers are one of the most poached animals on earth, alongside rhinos and elephants.

Inelastic Demand Drives Poaching

Tigers are considered powerful in traditional medicines of some cultures. Tiger eyeballs, whiskers, and hearts have tremendous value on the black market. And that demand is very inelastic — if you believe it will save the person you love the most or restore your vitality, you’re willing to pay whatever it takes. There is no substitute (Cowen and Tabarrok (2023), Ch. 5).

And as tigers become more scarce, they become more valuable — which increases the incentive to poach. It’s a scarcity spiral:

\[ \text{Scarcity} \uparrow \;\; \Rightarrow \;\; \text{Price} \uparrow \;\; \Rightarrow \;\; \text{Poaching incentive} \uparrow \;\; \Rightarrow \;\; \text{Scarcity} \uparrow \uparrow \]

Note🐯 The Poacher’s Insulin Choice

It’s easy to hate the poachers — violent, terrible people who kill these beautiful creatures. But many of them live in countries where the poverty is so deep, where the fight for survival is so real, that if you could kill a tiger and sell those parts, you could feed your family for a lifetime and ensure you could get them out of poverty.

It’s the same logic as the insulin choice. When survival is at stake, moral calculations change. Tiger reserves are now protected by full-on combat forces, trying to keep the last few alive. And zoos work to maintain biological diversity to maybe someday stabilize the population.

This is the sad way that elasticity, substitutes, and complements intersect with the real world: for some goods, there are no substitutes, and that makes the demand curve terrifyingly steep.

The Tale of Two Toilets: S&D Graphs

Graph 1: The Public Toilet Paper Market

During COVID, people stayed home. Public restrooms were shut down. Nobody was on campus, at the office, at restaurants. Demand for commercial toilet paper collapsed.

Show R code
ggplot(pub_sd) +
  # Supply curve
  geom_line(aes(x = Q, y = P_supply), color = pal$green, linewidth = 1.8) +
  # Original demand
  geom_line(aes(x = Q, y = P_demand1), color = pal$navy, linewidth = 1.5) +
  # Shifted demand (leftward)
  geom_line(aes(x = Q, y = P_demand2), color = pal$red, linewidth = 1.5, linetype = "dashed") +
  # Equilibrium A (original)
  annotate("point", x = pub_eq1_Q, y = pub_eq1_P, size = 5, color = pal$coral, shape = 18) +
  annotate("segment", x = 0, xend = pub_eq1_Q, y = pub_eq1_P, yend = pub_eq1_P,
           linetype = "dotted", color = pal$slate) +
  annotate("segment", x = pub_eq1_Q, xend = pub_eq1_Q, y = 0, yend = pub_eq1_P,
           linetype = "dotted", color = pal$slate) +
  annotate("text", x = pub_eq1_Q + 3, y = pub_eq1_P + 0.3, label = "A (Pre-COVID)",
           fontface = "bold", size = 4.5, color = pal$coral) +
  # Equilibrium B (shifted)
  annotate("point", x = pub_eq2_Q, y = pub_eq2_P, size = 5, color = pal$red, shape = 18) +
  annotate("segment", x = 0, xend = pub_eq2_Q, y = pub_eq2_P, yend = pub_eq2_P,
           linetype = "dotted", color = pal$slate) +
  annotate("segment", x = pub_eq2_Q, xend = pub_eq2_Q, y = 0, yend = pub_eq2_P,
           linetype = "dotted", color = pal$slate) +
  annotate("text", x = pub_eq2_Q + 3, y = pub_eq2_P - 0.3, label = "B (COVID)",
           fontface = "bold", size = 4.5, color = pal$red) +
  # Arrow from A to B
  annotate("segment", x = pub_eq1_Q - 2, y = pub_eq1_P - 0.1,
           xend = pub_eq2_Q + 2, yend = pub_eq2_P + 0.1,
           arrow = arrow(length = unit(0.3, "cm")),
           color = pal$red, linewidth = 1.2) +
  # Shift arrow for demand curve
  annotate("segment", x = 80, y = 3.5, xend = 55, yend = 3.5,
           arrow = arrow(length = unit(0.4, "cm")),
           color = pal$red, linewidth = 1.5) +
  annotate("text", x = 67, y = 3.9, label = "Demand\nshifts LEFT",
           fontface = "bold", size = 4, color = pal$red) +
  # Curve labels
  annotate("text", x = 90, y = 6, label = "Supply",
           fontface = "bold.italic", size = 4.5, color = pal$green) +
  annotate("text", x = 95, y = 2.5, label = expression(D[1]),
           fontface = "bold", size = 5, color = pal$navy) +
  annotate("text", x = 70, y = 1.5, label = expression(D[2]),
           fontface = "bold", size = 5, color = pal$red) +
  # Result box
  annotate("label", x = 15, y = 8,
           label = "P↓  Q↓\nA → B",
           fill = pal$cream, color = pal$red,
           fontface = "bold", size = 5, label.padding = unit(0.6, "lines")) +
  scale_x_continuous(limits = c(-5, 110), expand = c(0, 0)) +
  scale_y_continuous(limits = c(-0.5, 10), expand = c(0, 0)) +
  labs(
    title = "The PUBLIC Toilet Paper Market — Pandemic Demand Collapse",
    subtitle = "People stay home → demand for commercial TP falls → P↓, Q↓ (A → B)",
    x = "Quantity of Public Toilet Paper (Q)",
    y = "Price ($)",
    caption = "Illustrative S&D model | Determinant of demand that changed: tastes & preferences (stay-at-home mandates) | Cowen & Tabarrok, Ch. 5"
  ) +
  theme_minimal(base_size = 14) +
  theme(
    plot.title = element_text(face = "bold"),
    panel.grid.minor = element_blank()
  )

Note📐 Reading the Graph — Public TP

Before COVID (Point A): Normal demand. People use public restrooms at work, school, restaurants. Price and quantity at their pre-pandemic equilibrium.

During COVID (Point B): The pandemic sends everyone home. Fewer people need the public bog. Demand shifts left (from \(D_1\) to \(D_2\)). We move from A to B along the existing supply curve. Both price and quantity fall.

  • What changed? Demand (the curve shifted)
  • What moved along? Supply (change in quantity supplied)
  • Which determinant? Tastes & preferences (mandated stay-at-home orders)

Graph 2: The Private Toilet Paper Market

Meanwhile, at home, every human still needs their toilet paper. The demand for private, consumer-grade TP skyrocketed.

Show R code
ggplot(priv_sd) +
  # Supply curve
  geom_line(aes(x = Q, y = P_supply), color = pal$green, linewidth = 1.8) +
  # Original demand
  geom_line(aes(x = Q, y = P_demand1), color = pal$navy, linewidth = 1.5) +
  # Shifted demand (rightward)
  geom_line(aes(x = Q, y = P_demand2), color = pal$teal, linewidth = 1.5, linetype = "dashed") +
  # Equilibrium C (original)
  annotate("point", x = priv_eq1_Q, y = priv_eq1_P, size = 5, color = pal$coral, shape = 18) +
  annotate("segment", x = 0, xend = priv_eq1_Q, y = priv_eq1_P, yend = priv_eq1_P,
           linetype = "dotted", color = pal$slate) +
  annotate("segment", x = priv_eq1_Q, xend = priv_eq1_Q, y = 0, yend = priv_eq1_P,
           linetype = "dotted", color = pal$slate) +
  annotate("text", x = priv_eq1_Q - 8, y = priv_eq1_P + 0.5, label = "C (Pre-COVID)",
           fontface = "bold", size = 4.5, color = pal$coral) +
  # Equilibrium D (shifted)
  annotate("point", x = priv_eq2_Q, y = priv_eq2_P, size = 5, color = pal$teal, shape = 18) +
  annotate("segment", x = 0, xend = priv_eq2_Q, y = priv_eq2_P, yend = priv_eq2_P,
           linetype = "dotted", color = pal$slate) +
  annotate("segment", x = priv_eq2_Q, xend = priv_eq2_Q, y = 0, yend = priv_eq2_P,
           linetype = "dotted", color = pal$slate) +
  annotate("text", x = priv_eq2_Q + 3, y = priv_eq2_P + 0.5, label = "D (COVID)",
           fontface = "bold", size = 4.5, color = pal$teal) +
  # Arrow from C to D
  annotate("segment", x = priv_eq1_Q + 2, y = priv_eq1_P + 0.1,
           xend = priv_eq2_Q - 2, yend = priv_eq2_P - 0.1,
           arrow = arrow(length = unit(0.3, "cm")),
           color = pal$teal, linewidth = 1.2) +
  # Shift arrow for demand curve
  annotate("segment", x = 100, y = 8, xend = 140, yend = 8,
           arrow = arrow(length = unit(0.4, "cm")),
           color = pal$teal, linewidth = 1.5) +
  annotate("text", x = 120, y = 8.8, label = "Demand\nshifts RIGHT",
           fontface = "bold", size = 4, color = pal$teal) +
  # Curve labels
  annotate("text", x = 180, y = 12.5, label = "Supply",
           fontface = "bold.italic", size = 4.5, color = pal$green) +
  annotate("text", x = 175, y = 2, label = expression(D[1]),
           fontface = "bold", size = 5, color = pal$navy) +
  annotate("text", x = 190, y = 7, label = expression(D[2]),
           fontface = "bold", size = 5, color = pal$teal) +
  # Result box
  annotate("label", x = 30, y = 14,
           label = "P↑  Q↑\nC → D",
           fill = pal$cream, color = pal$teal,
           fontface = "bold", size = 5, label.padding = unit(0.6, "lines")) +
  scale_x_continuous(limits = c(-5, 200), expand = c(0, 0)) +
  scale_y_continuous(limits = c(-0.5, 16), expand = c(0, 0)) +
  labs(
    title = "The PRIVATE Toilet Paper Market — Pandemic Demand Surge",
    subtitle = "Everyone stays home → demand for consumer TP skyrockets → P↑, Q↑ (C → D)",
    x = "Quantity of Private Toilet Paper (Q)",
    y = "Price ($)",
    caption = "Illustrative S&D model | Determinant of demand that changed: tastes & preferences (stay-at-home mandates) | Cowen & Tabarrok, Ch. 5"
  ) +
  theme_minimal(base_size = 14) +
  theme(
    plot.title = element_text(face = "bold"),
    panel.grid.minor = element_blank()
  )

Note📐 Reading the Graph — Private TP

Before COVID (Point C): Normal demand for household TP. Most people are out of the house during the day. Supply and demand intersect at C.

During COVID (Point D): The pandemic forces everyone home. People need their private bog a lot more. Demand shifts right (from \(D_1\) to \(D_2\)). We move from C to D along the existing supply curve. Both price and quantity increase — and we get the Great Toilet Paper Shortage.

  • What changed? Demand (the curve shifted)
  • What moved along? Supply (change in quantity supplied)
  • Which determinant? Tastes & preferences (mandated stay-at-home orders)

Side by Side: The Tale of Two Toilets

Show R code
tale_summary <- tibble(
  Market = c("Public (Commercial) TP", "Private (Consumer) TP"),
  `What Happened` = c(
    "People stay home → fewer public restrooms used",
    "People stay home → massively more home use"
  ),
  `Demand Shift` = c("LEFT (decrease)", "RIGHT (increase)"),
  `Price Effect` = c("P ↓", "P ↑"),
  `Quantity Effect` = c("Q ↓", "Q ↑"),
  `Equilibrium Move` = c("A → B", "C → D"),
  `Determinant` = c("Tastes & preferences", "Tastes & preferences")
)

tale_summary %>%
  kable(align = "lllllll",
        caption = "A Tale of Two Toilets — Summary (Cowen & Tabarrok, Ch. 5)") %>%
  kable_styling(bootstrap_options = c("striped", "hover"), full_width = FALSE) %>%
  column_spec(1, bold = TRUE) %>%
  row_spec(1, background = "#fce4ec") %>%
  row_spec(2, background = "#e8f8f5")
A Tale of Two Toilets — Summary (Cowen & Tabarrok, Ch. 5)
Market What Happened Demand Shift Price Effect Quantity Effect Equilibrium Move Determinant
Public (Commercial) TP People stay home → fewer public restrooms used LEFT (decrease) P ↓ Q ↓ A → B Tastes & preferences
Private (Consumer) TP People stay home → massively more home use RIGHT (increase) P ↑ Q ↑ C → D Tastes & preferences
Important🔑 The Big Takeaway

What looks like one market (toilet paper) is actually two separate markets with completely different supply chains. When demand shifted massively toward homes, the commercial supply chain couldn’t pivot. Two markets. Two supply chains. Two completely different outcomes. And the Great Toilet Paper Shortage of 2020 was born.

That’s the tale of two toilets. And I’m pretty sure that nowhere else on campus will you get a lecture about toilets. 🧻

Elastic vs. Inelastic: The Visual Guide

Demand Curve Shapes Tell the Story

The slope of the demand curve reveals whether a good is elastic or inelastic. Flat curves = elastic (stretchy). Steep curves = inelastic (stiff). Vertical = perfectly inelastic (no response at all).

Show R code
# Build data for elastic, inelastic, and perfectly inelastic
elast_data <- tibble(Q = seq(0, 100, 0.5)) %>%
  mutate(
    P_elastic    = pmax(10 - 0.05 * Q, 0),    # Flat: elastic
    P_inelastic  = pmax(10 - 0.5 * Q, 0),     # Steep: inelastic
    P_supply     = 2 + 0.04 * Q
  )

# Elastic panel
p_elastic <- ggplot(elast_data) +
  geom_line(aes(x = Q, y = P_elastic), color = pal$teal, linewidth = 2) +
  geom_line(aes(x = Q, y = P_supply), color = pal$green, linewidth = 1.2, alpha = 0.5) +
  annotate("segment", x = 50, xend = 50, y = 0, yend = 7.5,
           linetype = "dashed", color = pal$slate, alpha = 0.5) +
  annotate("segment", x = 80, xend = 80, y = 0, yend = 6,
           linetype = "dashed", color = pal$slate, alpha = 0.5) +
  annotate("text", x = 65, y = 8.5, label = "Small ΔP",
           fontface = "bold", size = 4, color = pal$coral) +
  annotate("segment", x = 50, y = 7.5, xend = 80, yend = 6,
           arrow = arrow(length = unit(0.3, "cm")),
           color = pal$coral, linewidth = 1) +
  annotate("text", x = 65, y = 1.5, label = "BIG ΔQ",
           fontface = "bold", size = 4, color = pal$coral) +
  annotate("text", x = 85, y = 3, label = "Demand\n(elastic)", fontface = "bold.italic",
           size = 4, color = pal$teal) +
  scale_x_continuous(limits = c(0, 100)) +
  scale_y_continuous(limits = c(0, 11)) +
  labs(title = "ELASTIC (Ed > 1)", subtitle = "Flat curve: small ΔP → big ΔQ",
       x = "Q", y = "P ($)",
       caption = "Examples: corn, tortilla chips, beef → chicken") +
  theme_minimal(base_size = 13) +
  theme(plot.title = element_text(face = "bold", color = pal$teal),
        panel.grid.minor = element_blank())

# Inelastic panel
p_inelastic <- ggplot(elast_data %>% filter(Q <= 20)) +
  geom_line(aes(x = Q, y = P_inelastic), color = pal$red, linewidth = 2) +
  annotate("segment", x = 5, xend = 5, y = 0, yend = 7.5,
           linetype = "dashed", color = pal$slate, alpha = 0.5) +
  annotate("segment", x = 8, xend = 8, y = 0, yend = 6,
           linetype = "dashed", color = pal$slate, alpha = 0.5) +
  annotate("text", x = 3, y = 5, label = "BIG ΔP",
           fontface = "bold", size = 4, color = pal$coral) +
  annotate("segment", x = 5, y = 7.5, xend = 8, yend = 6,
           arrow = arrow(length = unit(0.3, "cm")),
           color = pal$coral, linewidth = 1) +
  annotate("text", x = 6.5, y = 1.5, label = "small ΔQ",
           fontface = "bold", size = 4, color = pal$coral) +
  annotate("text", x = 15, y = 3, label = "Demand\n(inelastic)", fontface = "bold.italic",
           size = 4, color = pal$red) +
  scale_x_continuous(limits = c(0, 20)) +
  scale_y_continuous(limits = c(0, 11)) +
  labs(title = "INELASTIC (Ed < 1)", subtitle = "Steep curve: big ΔP → small ΔQ",
       x = "Q", y = "P ($)",
       caption = "Examples: coffee, heroin, tiger eyeballs") +
  theme_minimal(base_size = 13) +
  theme(plot.title = element_text(face = "bold", color = pal$red),
        panel.grid.minor = element_blank())

# Perfectly inelastic panel
p_perfect <- ggplot() +
  geom_vline(xintercept = 10, color = pal$purple, linewidth = 2.5) +
  annotate("text", x = 10, y = 9.5, label = "Demand\n(perfectly\ninelastic)",
           fontface = "bold.italic", size = 4, color = pal$purple) +
  annotate("segment", x = 10, xend = 10, y = 3, yend = 7,
           arrow = arrow(length = unit(0.3, "cm"), ends = "both"),
           color = pal$coral, linewidth = 1) +
  annotate("text", x = 7, y = 5, label = "ANY ΔP →\nZERO ΔQ",
           fontface = "bold", size = 4, color = pal$coral) +
  scale_x_continuous(limits = c(0, 20)) +
  scale_y_continuous(limits = c(0, 11)) +
  labs(title = "PERFECTLY INELASTIC (Ed = 0)", subtitle = "Vertical: price doesn't matter",
       x = "Q", y = "P ($)",
       caption = "Example: insulin (you'll die without it)") +
  theme_minimal(base_size = 13) +
  theme(plot.title = element_text(face = "bold", color = pal$purple),
        panel.grid.minor = element_blank())

# Combine panels
library(patchwork)
p_elastic + p_inelastic + p_perfect +
  plot_annotation(
    title = "Elastic vs. Inelastic vs. Perfectly Inelastic Demand",
    subtitle = "The shape of the demand curve tells you everything about how responsive consumers are to price changes",
    caption = "Cowen & Tabarrok, Ch. 5 | Illustrative curves",
    theme = theme(
      plot.title = element_text(face = "bold", size = 16),
      plot.subtitle = element_text(size = 12)
    )
  )

Examples: From Elastic to Inelastic

Show R code
examples <- tibble(
  Good = c("Corn / tortilla chips", "Beef (with chicken as substitute)",
           "Brand-name vs. generic medicine", "Coffee / caffeine",
           "Heroin / nicotine / alcohol", "Insulin (Type 1 diabetes)",
           "Tiger eyeballs (traditional medicine)"),
  `Elasticity` = c("Very elastic", "Elastic", "Elastic", "Inelastic",
                     "Very inelastic", "Perfectly inelastic", "Very inelastic"),
  `Why?` = c(
    "Commodity good — many substitutes, shop on price",
    "Chicken, pork, and other proteins substitute easily",
    "Generic is chemically identical but cheaper → highly substitutable",
    "Addictive, few real substitutes, withdrawal is miserable",
    "Powerfully addictive — habit + chemical dependency",
    "Zero substitutes — you die without it",
    "Cultural belief in medicinal power, no perceived substitute"
  ),
  `Substitutes Available?` = c("Many", "Several", "Yes (generic)", "Few (tea, yaupon?)",
                                "Very few", "None", "None")
)

examples %>%
  kable(align = "llll",
        caption = "The Elasticity Spectrum — From Stretchy to Stiff (Cowen & Tabarrok, Ch. 5)") %>%
  kable_styling(bootstrap_options = c("striped", "hover"), full_width = FALSE) %>%
  column_spec(1, bold = TRUE) %>%
  row_spec(6:7, bold = TRUE, background = "#fce4ec")
The Elasticity Spectrum — From Stretchy to Stiff (Cowen & Tabarrok, Ch. 5)
Good Elasticity Why? Substitutes Available?
Corn / tortilla chips Very elastic Commodity good — many substitutes, shop on price Many
Beef (with chicken as substitute) Elastic Chicken, pork, and other proteins substitute easily Several
Brand-name vs. generic medicine Elastic Generic is chemically identical but cheaper → highly substitutable Yes (generic)
Coffee / caffeine Inelastic Addictive, few real substitutes, withdrawal is miserable Few (tea, yaupon?)
Heroin / nicotine / alcohol Very inelastic Powerfully addictive — habit + chemical dependency Very few
Insulin (Type 1 diabetes) Perfectly inelastic Zero substitutes — you die without it None
Tiger eyeballs (traditional medicine) Very inelastic Cultural belief in medicinal power, no perceived substitute None
Tip🔑 The Pattern

The single most important determinant of elasticity is the availability of substitutes. If you can easily switch to another product when the price goes up, demand is elastic. If you’re stuck — addicted, dependent, or there simply isn’t an alternative — demand is inelastic. Substitutes → elastic. No substitutes → inelastic. That’s the rule of thumb that will carry you through this entire chapter (Cowen and Tabarrok (2023), Ch. 5).

Real-World Data: Pandemic Prices in Motion

CPI — The Price Level During COVID

The Consumer Price Index captures how prices move in the real economy. During COVID, we saw something dramatic: prices barely budged in 2020 (demand collapsed across sectors), then exploded in 2021–2022 as demand roared back while supply chains remained broken (U.S. Bureau of Labor Statistics (2026)).

Show R code
ggplot(cpi_data %>% filter(!is.na(pct_change)),
       aes(x = year, y = pct_change)) +
  geom_col(aes(fill = pct_change > 4), width = 0.6, show.legend = FALSE) +
  scale_fill_manual(values = c("FALSE" = pal$teal, "TRUE" = pal$red)) +
  geom_hline(yintercept = 2, linetype = "dashed", color = pal$gold, linewidth = 0.8) +
  annotate("text", x = 2019, y = 2.4, label = "Fed's 2% Target",
           size = 3.5, color = pal$gold, fontface = "italic") +
  annotate("label", x = 2020, y = 5,
           label = "COVID hits:\nDemand collapses →\nInflation drops to 1.2%",
           fill = pal$cream, color = pal$navy, size = 3, fontface = "italic",
           label.padding = unit(0.4, "lines")) +
  annotate("label", x = 2022, y = 6.5,
           label = "Supply chains broken +\nstimulus checks +\nreopening =\n8% inflation",
           fill = pal$cream, color = pal$red, size = 3, fontface = "italic",
           label.padding = unit(0.4, "lines")) +
  scale_y_continuous(labels = percent_format(scale = 1)) +
  scale_x_continuous(breaks = cpi_data$year[!is.na(cpi_data$pct_change)]) +
  labs(
    title = "U.S. Inflation Rate (CPI-U, Annual % Change)",
    subtitle = "Red bars = inflation above 4% — supply-demand imbalances in action",
    x = NULL, y = "Annual Inflation (%)",
    caption = fred_note
  ) +
  theme_minimal(base_size = 14) +
  theme(
    plot.title = element_text(face = "bold"),
    axis.text.x = element_text(angle = 45, hjust = 1)
  )

Show R code
cpi_data %>%
  rename(Year = year, `CPI Index` = cpi, `Annual % Change` = pct_change) %>%
  kable(align = "rrr",
        caption = paste0(
          "U.S. Consumer Price Index (CPI-U), 2018–2024\n",
          "FRED Series: CPIAUCSL | BLS | Index 1982–84 = 100"
        )) %>%
  kable_styling(bootstrap_options = c("striped", "hover", "condensed"),
                full_width = FALSE) %>%
  row_spec(which(cpi_data$year %in% c(2021, 2022)), bold = TRUE, background = "#fce4ec")
U.S. Consumer Price Index (CPI-U), 2018–2024 FRED Series: CPIAUCSL | BLS | Index 1982–84 = 100
Year CPI Index Annual % Change
2018 251.1 2.4
2019 255.7 1.8
2020 258.8 1.2
2021 271.0 4.7
2022 292.7 8.0
2023 304.7 4.1
2024 314.2 3.1

Key Equations Summary

Show R code
equations <- tibble(
  `#` = 1:5,
  Concept = c(
    "Price Elasticity of Demand",
    "Midpoint Formula",
    "Elastic threshold",
    "Inelastic threshold",
    "Complements / Substitutes (cross-price)"
  ),
  Equation = c(
    "Ed = |(%ΔQd) / (%ΔP)|",
    "Ed = |((Q₂−Q₁)/((Q₂+Q₁)/2)) / ((P₂−P₁)/((P₂+P₁)/2))|",
    "Ed > 1 → elastic",
    "Ed < 1 → inelastic",
    "∂QB/∂PA < 0 (complements) or > 0 (substitutes)"
  ),
  Source = c(
    "Cowen & Tabarrok, Ch. 5",
    "Cowen & Tabarrok, Ch. 5",
    "Cowen & Tabarrok, Ch. 5",
    "Cowen & Tabarrok, Ch. 5",
    "Cowen & Tabarrok, Ch. 5"
  )
)

equations %>%
  kable(align = "rlll",
        caption = "Master Equation Reference — Class #11") %>%
  kable_styling(bootstrap_options = c("striped", "hover"), full_width = FALSE) %>%
  column_spec(3, monospace = TRUE)
Master Equation Reference — Class #11
# Concept Equation Source
1 Price Elasticity of Demand Ed = &#124;(%ΔQd) / (%ΔP)&#124; Cowen & Tabarrok, Ch. 5
2 Midpoint Formula Ed = &#124;((Q₂−Q₁)/((Q₂+Q₁)/2)) / ((P₂−P₁)/((P₂+P₁)/2))&#124; Cowen & Tabarrok, Ch. 5
3 Elastic threshold Ed > 1 → elastic Cowen & Tabarrok, Ch. 5
4 Inelastic threshold Ed < 1 → inelastic Cowen & Tabarrok, Ch. 5
5 Complements / Substitutes (cross-price) ∂QB/∂PA < 0 (complements) or > 0 (substitutes) Cowen & Tabarrok, Ch. 5

FRED Data Series Reference

Show R code
fred_ref <- tibble(
  Concept = c("Consumer Price Index", "Ground Beef (avg price)", "Whole Chicken (avg price)"),
  `FRED Series ID` = c("CPIAUCSL", "APU0000FC1101", "APU0000FF1101"),
  `Full Name` = c(
    "Consumer Price Index for All Urban Consumers: All Items",
    "Average Price: Ground Beef, 100% Beef, Per Lb.",
    "Average Price: Chicken, Fresh, Whole, Per Lb."
  ),
  `Source Agency` = c("Bureau of Labor Statistics (BLS)",
                       "Bureau of Labor Statistics (BLS)",
                       "Bureau of Labor Statistics (BLS)"),
  Units = c("Index 1982–84 = 100, SA", "$/lb, NSA", "$/lb, NSA")
)

fred_ref %>%
  kable(align = "lllll",
        caption = paste0(
          "FRED Series Reference — All data sourced from fred.stlouisfed.org\n",
          "R package: fredr (CRAN) | API key: fred.stlouisfed.org/docs/api/api_key.html"
        )) %>%
  kable_styling(bootstrap_options = c("striped", "hover"), full_width = FALSE) %>%
  column_spec(2, monospace = TRUE, bold = TRUE)
FRED Series Reference — All data sourced from fred.stlouisfed.org R package: fredr (CRAN) | API key: fred.stlouisfed.org/docs/api/api_key.html
Concept FRED Series ID Full Name Source Agency Units
Consumer Price Index CPIAUCSL Consumer Price Index for All Urban Consumers: All Items Bureau of Labor Statistics (BLS) Index 1982–84 = 100, SA
Ground Beef (avg price) APU0000FC1101 Average Price: Ground Beef, 100% Beef, Per Lb. Bureau of Labor Statistics (BLS) $/lb, NSA
Whole Chicken (avg price) APU0000FF1101 Average Price: Chicken, Fresh, Whole, Per Lb. Bureau of Labor Statistics (BLS) $/lb, NSA

🐐 Check Yourself: Practice Problems

Test your understanding before looking at the answers.

Show R code
quiz <- tibble(
  `#` = 1:6,
  Question = c(
    "The price of gasoline rises 20%, and the quantity demanded falls 5%. Calculate Ed. Is gasoline elastic or inelastic?",
    "Tortilla chips come in two brands that taste identical. One raises its price by $1. What happens to the other brand's sales? Why?",
    "Using the midpoint formula: Q falls from 100 to 80 when P rises from $5 to $7. Calculate Ed.",
    "Toilets and toilet paper are _____ (complements / substitutes). Beef and chicken are _____ (complements / substitutes).",
    "Draw the S&D graph for the PUBLIC toilet paper market during COVID. Which curve shifts? Which direction? What happens to P and Q?",
    "Insulin has an elasticity close to zero. Draw the demand curve. What shape is it? Why can pharmaceutical companies charge nearly any price?"
  ),
  `Your Answer` = rep("___", 6)
)

quiz %>%
  kable(align = "rll",
        caption = "Practice Problems: Elasticity, Complements, and Substitutes") %>%
  kable_styling(bootstrap_options = c("striped", "hover"), full_width = FALSE)
Practice Problems: Elasticity, Complements, and Substitutes
# Question Your Answer
1 The price of gasoline rises 20%, and the quantity demanded falls 5%. Calculate Ed. Is gasoline elastic or inelastic? ___
2 Tortilla chips come in two brands that taste identical. One raises its price by $1. What happens to the other brand's sales? Why? ___
3 Using the midpoint formula: Q falls from 100 to 80 when P rises from $5 to $7. Calculate Ed. ___
4 Toilets and toilet paper are _____ (complements / substitutes). Beef and chicken are _____ (complements / substitutes). ___
5 Draw the S&D graph for the PUBLIC toilet paper market during COVID. Which curve shifts? Which direction? What happens to P and Q? ___
6 Insulin has an elasticity close to zero. Draw the demand curve. What shape is it? Why can pharmaceutical companies charge nearly any price? ___
# Answer Explanation
1 Ed = 0.25 → Inelastic Ed = |−5% / 20%| = 0.25. Less than 1 → inelastic. People need gas even when it’s expensive.
2 Other brand’s sales increase They’re substitutes. When one gets more expensive, consumers switch to the cheaper option.
3 Ed ≈ 0.56 → Inelastic %ΔQ = (80−100)/90 = −22.2%. %ΔP = (7−5)/6 = 33.3%. Ed = 22.2/33.3 = 0.67.
4 Complements; Substitutes Toilets & TP are always used together (complements). Beef & chicken replace each other (substitutes).
5 Demand shifts LEFT → P↓, Q↓ People leave public spaces → demand falls → both price and quantity decrease. Supply doesn’t shift.
6 Vertical (perfectly inelastic) With zero substitutes and a life-or-death need, quantity demanded doesn’t change regardless of price. Companies can charge nearly anything because patients have no choice.

Discussion Questions

  1. The Two Toilets Test: Explain in your own words why public and private toilet paper are not perfect substitutes, even though they perform the same basic function. What supply chain characteristics prevent them from being interchangeable? (Cowen and Tabarrok (2023), Ch. 5)

  2. The Meat Market: Ground beef is now $8–10/lb. Chicken is under $2/lb. Pork is $1.10/lb. Draw a supply and demand diagram for the chicken market showing what happens when the price of beef (a substitute) rises dramatically. Which curve shifts? Which direction?

  3. The Caffeine Addiction Test: Dr. Downes admits she’d “give up most everything in my life to be able to have coffee.” Using elasticity language, what does this tell you about her personal price elasticity of demand for coffee? Would her demand curve be steep or flat?

  4. The Tiger Paradox: Explain the “scarcity spiral” for tigers. As poaching reduces the tiger population, what happens to the price of tiger parts on the black market? How does inelastic demand make the conservation problem worse over time?

  5. The Insulin Policy Question: If insulin demand is perfectly inelastic (Ed = 0), what limits — if any — exist on how much a pharmaceutical company can charge? Should the government intervene? Use the concepts of consumer surplus and deadweight loss to frame your argument.

  6. CPI Detective: Look at the CPI data for 2020 vs. 2021. Inflation was only 1.2% in 2020 but jumped to 4.7% in 2021. Using S&D analysis, explain what was happening to aggregate demand and supply during each year. Why did price levels behave so differently? (U.S. Bureau of Labor Statistics (2026))

The Dead Cat Problem: A Preview

Important🐱 Within Every Dead Cat Lies a Profitable Opportunity

That’s the setup for what comes next. We’ve spent the last several lectures building the consumer side of economics — demand, willingness to pay, consumer surplus, elasticity. Now it’s time to flip to the producer side.

The Dead Cat Problem asks: what problems do you see in the world around you? Every problem is a market failure or imbalance. Every market failure is an opportunity. And every opportunity — if you can analyze it with the S&D tools we’ve built — becomes your Profit Fish project.

Start looking at the world with fresh eyes. What problems do you see? What’s connected? What inspires a “hmm, that’s interesting” moment? The best solutions come from being inquisitive.

Coming next: producer surplus, cost structures, and the economics of supply. 🐟

Looking Ahead

We’ve now covered the core toolkit of demand-side economics: the law of demand, supply and demand equilibrium, shifts in demand, consumer surplus, and — today — complements, substitutes, and elasticity. You can read a graph, shift a curve, identify what changed and why, and calculate how responsive consumers are to price changes.

Next, we cross to the other side of the market: producers. How do firms decide what to produce? What are their costs? When do they enter or exit a market? That’s where the Dead Cat Problem and the Profit Fish project begin.

And remember: the shape of the demand curve tells you everything. Flat → elastic → substitutes available → shop on price. Steep → inelastic → few substitutes → pay whatever it takes. Vertical → perfectly inelastic → life or death.

Everywhere a human goes, a toilet is sure to follow. And now you know why that matters. 🧻🏴‍☠️


ECON 2120G-M02 — Principles of Microeconomics | Dr. Meghan Downes | Spring 2026 | NMSU

Cowen & Tabarrok, Modern Principles: Microeconomics, 5th Edition — Ch. 5 (Elasticity and Its Applications)

Data: U.S. Bureau of Labor Statistics (BLS) — CPI-U and Average Prices — accessed via Federal Reserve Bank of St. Louis (FRED) using R package fredr

FRED Series: CPIAUCSL, APU0000FC1101, APU0000FF1101 | Retrieved February 2026


Important

ECON2120G-M05 — Principles of Microeconomics Class #11 | Tuesday, February 25, 2026 | A Tale of Two Toilets Chapter: Cowen & Tabarrok 5e, Ch. 5 SLO-03: The student will use supply and demand curves to analyze how external events affect market equilibrium. SLO-05: The student will calculate and interpret price elasticity of demand.

Pirate Pete’s Tales of Misadventure — Episode 5: Elasticity, Complements, and Substitutes “Everywhere a human goes, a toilet is sure to follow.” 🧻🐐

Back to top

References

Blake, William. 1794. The Tyger.
Cowen, Tyler, and Alex Tabarrok. 2023. Modern Principles: Microeconomics. 5th ed. Worth Publishers.
U.S. Bureau of Labor Statistics. 2026. Consumer Price Index for All Urban Consumers: All Items in u.s. City Average. FRED, Federal Reserve Bank of St. Louis. https://fred.stlouisfed.org/series/CPIAUCSL.