← back to Paul Conrad Cartoons
Add verified cartoon images, topic tags, and a full nav bar
7995bcdc17c40e2b82e562aaad04dd4ee879aa54 · 2026-09-24 13:28:17 -0700 · Steve Abrams
- Sourced 11 new verified cartoon images: all 10 Library of Congress items
via loc.gov's own full-resolution service derivatives (visually confirmed
against each item's cataloged title/subject), plus one more (Nast 1982
homage) via Spartacus Educational. Reused 6 already-verified images as
clearly-labeled representative images for the 3 Pulitzer-year and 3
archive-collection-summary records (year-of-work awards and finding aids
name no single cartoon). 25 of 38 records now show a verified image
(up from 8); the remaining 13 documented cartoons have no digitized copy
found anywhere free and show an honest "no verified image found" placeholder
linking to the citation source.
- Added a topics[] array to all 38 records (Nixon/Watergate, Vietnam War,
Civil Rights, Nuclear Arms & Cold War, Reagan Era, Iran-Contra, Space Race,
Gun Politics, Media & Press, Economy, Religion, LA/California, and more),
shown as chips on cards and in the modal.
- Rebuilt the header into a sticky top nav: wordmark, section links
(Cartoons / Pulitzers / Library of Congress / Archives / Bio), a
horizontally-scrollable topic-chip row with live counts, a search box,
and a decade quick-jump (50s-00s). Bio opens in the existing modal.
Added a Topic A-Z sort option; all choices persist to localStorage.
Collapses to a hamburger menu at mobile widths with no horizontal
page scroll (verified at 390px).
- artifact/ (the metadata-only shareable build) is untouched.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01V29xaoBwcFwJyR4KC43ptn
Files touched
M .gitignoreM data/cartoons.jsonM public/app.jsA public/img/loc-barroom-1989.jpgA public/img/loc-black-teen-unemployment-1977.jpgA public/img/loc-death-row-1991.jpgA public/img/loc-disarmament-1961.jpgA public/img/loc-herblock-2000.jpgA public/img/loc-inheritance-1990.jpgA public/img/loc-naked-truth-1987.jpgA public/img/loc-nra-pacifier-1982.jpgA public/img/loc-sl-hud-1989.jpgA public/img/loc-southamerica-1959.jpgA public/img/nast-affixing-blame-1982.jpgM public/index.htmlM public/style.css
Diff
commit 7995bcdc17c40e2b82e562aaad04dd4ee879aa54
Author: Steve Abrams <steve@designerwallcoverings.com>
Date: Thu Sep 24 13:28:17 2026 -0700
Add verified cartoon images, topic tags, and a full nav bar
- Sourced 11 new verified cartoon images: all 10 Library of Congress items
via loc.gov's own full-resolution service derivatives (visually confirmed
against each item's cataloged title/subject), plus one more (Nast 1982
homage) via Spartacus Educational. Reused 6 already-verified images as
clearly-labeled representative images for the 3 Pulitzer-year and 3
archive-collection-summary records (year-of-work awards and finding aids
name no single cartoon). 25 of 38 records now show a verified image
(up from 8); the remaining 13 documented cartoons have no digitized copy
found anywhere free and show an honest "no verified image found" placeholder
linking to the citation source.
- Added a topics[] array to all 38 records (Nixon/Watergate, Vietnam War,
Civil Rights, Nuclear Arms & Cold War, Reagan Era, Iran-Contra, Space Race,
Gun Politics, Media & Press, Economy, Religion, LA/California, and more),
shown as chips on cards and in the modal.
- Rebuilt the header into a sticky top nav: wordmark, section links
(Cartoons / Pulitzers / Library of Congress / Archives / Bio), a
horizontally-scrollable topic-chip row with live counts, a search box,
and a decade quick-jump (50s-00s). Bio opens in the existing modal.
Added a Topic A-Z sort option; all choices persist to localStorage.
Collapses to a hamburger menu at mobile widths with no horizontal
page scroll (verified at 390px).
- artifact/ (the metadata-only shareable build) is untouched.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01V29xaoBwcFwJyR4KC43ptn
---
.gitignore | 1 +
data/cartoons.json | 357 +++++++++++++++++-------
public/app.js | 213 ++++++++++++--
public/img/loc-barroom-1989.jpg | Bin 0 -> 163616 bytes
public/img/loc-black-teen-unemployment-1977.jpg | Bin 0 -> 211831 bytes
public/img/loc-death-row-1991.jpg | Bin 0 -> 43583 bytes
public/img/loc-disarmament-1961.jpg | Bin 0 -> 210940 bytes
public/img/loc-herblock-2000.jpg | Bin 0 -> 85161 bytes
public/img/loc-inheritance-1990.jpg | Bin 0 -> 126861 bytes
public/img/loc-naked-truth-1987.jpg | Bin 0 -> 125972 bytes
public/img/loc-nra-pacifier-1982.jpg | Bin 0 -> 117818 bytes
public/img/loc-sl-hud-1989.jpg | Bin 0 -> 87031 bytes
public/img/loc-southamerica-1959.jpg | Bin 0 -> 101159 bytes
public/img/nast-affixing-blame-1982.jpg | Bin 0 -> 38144 bytes
public/index.html | 38 ++-
public/style.css | 193 ++++++++++++-
16 files changed, 659 insertions(+), 143 deletions(-)
diff --git a/.gitignore b/.gitignore
index 1924158..69e7414 100644
--- a/.gitignore
+++ b/.gitignore
@@ -6,3 +6,4 @@ tmp/
dist/
build/
.next/
+shots/
diff --git a/data/cartoons.json b/data/cartoons.json
index 41d9b76..38ede02 100644
--- a/data/cartoons.json
+++ b/data/cartoons.json
@@ -1,5 +1,5 @@
{
- "generated_note": "Paul Conrad (1924-2010) research catalog. Compiled 2026-09-24 from pulitzer.org, Library of Congress, The Pop History Dig, Encyclopedia.com, Wikipedia, Spartacus Educational, Toons Mag, the 2010 AP obituary, and the OAC/Huntington, Syracuse, and Wichita State finding aids. RIGHTS: no public-domain/open-license Conrad cartoon image exists; all works are copyright Los Angeles Times or the Conrad estate. The 8 locally-shown thumbnails are low-res retrospective reproductions displayed for PRIVATE reference only, each credited; every other record links out to view at the holding source.",
+ "generated_note": "Paul Conrad (1924-2010) research catalog. Compiled 2026-09-24 from pulitzer.org, Library of Congress, The Pop History Dig, Encyclopedia.com, Wikipedia, Spartacus Educational, Toons Mag, the 2010 AP obituary, and the OAC/Huntington, Syracuse, and Wichita State finding aids. Updated 2026-09-24 (image pass): all 10 Library of Congress items were digitized via loc.gov's own full-resolution derivatives (v/r service files, ~800-1024px), downloaded and visually verified against each item's cataloged title/subject; one additional documented cartoon ('Affixing Blame for Our Economic Problems', 1982) was verified via Spartacus Educational; the 3 Pulitzer-year and 3 archive-collection-summary records show a clearly-labeled REPRESENTATIVE cartoon from the matching era/collection, reusing an already-verified image (year-of-work Pulitzers and finding aids name no single cartoon, so none is the literal prize-winning or catalogued work). 25 of 38 records now show a verified image; the remaining 13 documented cartoons (no digitized copy located anywhere free) show a 'no verified image found' placeholder linking to the citation source. RIGHTS: no public-domain/open-license Conrad cartoon image exists; all works are copyright Los Angeles Times, Denver Post, or the Conrad estate; every image here is a low-res reproduction shown for PRIVATE, non-published reference only, each credited to its source.",
"bio": {
"name": "Paul Francis Conrad",
"born": "1924-06-27, Cedar Rapids, Iowa",
@@ -19,11 +19,16 @@
"subject": "Body of editorial cartooning during the prior year. Citation (verbatim): \"For his editorial cartooning during the past year.\" The prize is a year-of-work award; no single cartoon is named.",
"award": "Pulitzer Prize for Editorial Cartooning 1964",
"record_type": "documented",
- "image": null,
- "image_url": null,
- "image_source": null,
- "rights_note": "Official Pulitzer winner page names no specific cartoon and displays no image.",
- "citation_url": "https://www.pulitzer.org/winners/paul-conrad"
+ "image": "img/loc-disarmament-1961.jpg",
+ "image_url": "https://tile.loc.gov/storage-services/service/pnp/ppmsca/09400/09425v.jpg",
+ "image_source": "Library of Congress",
+ "rights_note": "REPRESENTATIVE IMAGE — not the specific prize-winning work (the 1964 Pulitzer is a year-of-work citation naming no single cartoon). Shown: Conrad's 1961 Denver Post Cold War disarmament cartoon, the closest verified image from his Denver Post years.",
+ "citation_url": "https://www.pulitzer.org/winners/paul-conrad",
+ "topics": [
+ "Awards & Career",
+ "Denver Post Era"
+ ],
+ "representative": true
},
{
"id": "pulitzer-1971",
@@ -33,11 +38,17 @@
"subject": "Body of editorial cartooning during 1970 (Nixon era, Vietnam War). Year-of-work award; no single cartoon named.",
"award": "Pulitzer Prize for Editorial Cartooning 1971",
"record_type": "documented",
- "image": null,
- "image_url": null,
- "image_source": null,
- "rights_note": "No specific cartoon named; no official image.",
- "citation_url": "https://www.pulitzer.org/winners/paul-conrad"
+ "image": "img/phone-company-1972.jpg",
+ "image_url": "https://pophistorydig.com/wp-content/uploads/2011/05/Phone-Company-300.jpg",
+ "image_source": "The Pop History Dig",
+ "rights_note": "REPRESENTATIVE IMAGE — not the specific prize-winning work (the 1971 Pulitzer is a year-of-work citation for 1970 Nixon/Vietnam-era cartooning, naming no single cartoon). Shown: Conrad's 1972 Nixon/Watergate cartoon, the closest verified image from that era.",
+ "citation_url": "https://www.pulitzer.org/winners/paul-conrad",
+ "topics": [
+ "Awards & Career",
+ "Nixon/Watergate",
+ "Vietnam War"
+ ],
+ "representative": true
},
{
"id": "pulitzer-1984",
@@ -47,13 +58,19 @@
"subject": "Body of editorial cartooning during the prior year (Reagan-era themes: homelessness, nuclear arms, corporate/political power). Year-of-work award; no single cartoon named.",
"award": "Pulitzer Prize for Editorial Cartooning 1984",
"record_type": "documented",
- "image": null,
- "image_url": null,
- "image_source": null,
- "rights_note": "No specific cartoon named; no official image displayed.",
- "citation_url": "https://www.pulitzer.org/winners/paul-conrad"
+ "image": "img/reagan-american-gothic-1986.jpg",
+ "image_url": "https://pophistorydig.com/wp-content/uploads/2011/05/1986-Reagan-1-260.jpg",
+ "image_source": "The Pop History Dig",
+ "rights_note": "REPRESENTATIVE IMAGE — not the specific prize-winning work (the 1984 Pulitzer is a year-of-work citation for Reagan-era cartooning, naming no single cartoon). Shown: Conrad's 1986 Reagan 'American Gothic' cartoon, the closest verified image from that era.",
+ "citation_url": "https://www.pulitzer.org/winners/paul-conrad",
+ "topics": [
+ "Awards & Career",
+ "Reagan Era",
+ "Economy",
+ "Nuclear Arms & Cold War"
+ ],
+ "representative": true
},
-
{
"id": "obit-phone-company-1972",
"title": "He says he's from the phone company…",
@@ -66,7 +83,10 @@
"image_url": "https://pophistorydig.com/wp-content/uploads/2011/05/Phone-Company-300.jpg",
"image_source": "The Pop History Dig",
"rights_note": "© Los Angeles Times / Conrad estate. Low-res reproduction shown for private reference only; view in context at the source.",
- "citation_url": "https://pophistorydig.com/topics/tag/paul-conrad-political-cartoons/"
+ "citation_url": "https://pophistorydig.com/topics/tag/paul-conrad-political-cartoons/",
+ "topics": [
+ "Nixon/Watergate"
+ ]
},
{
"id": "obit-worst-enemy-1973",
@@ -80,7 +100,10 @@
"image_url": "https://pophistorydig.com/wp-content/uploads/2011/05/Worst-Enemy-370.jpg",
"image_source": "The Pop History Dig",
"rights_note": "© Los Angeles Times / Conrad estate. Low-res reproduction shown for private reference only.",
- "citation_url": "https://pophistorydig.com/topics/tag/paul-conrad-political-cartoons/"
+ "citation_url": "https://pophistorydig.com/topics/tag/paul-conrad-political-cartoons/",
+ "topics": [
+ "Nixon/Watergate"
+ ]
},
{
"id": "obit-spiders-web-1973",
@@ -94,7 +117,10 @@
"image_url": "https://pophistorydig.com/wp-content/uploads/2011/05/Nixon-Spiders-Web333.jpg",
"image_source": "The Pop History Dig",
"rights_note": "© Los Angeles Times / Conrad estate. Low-res reproduction shown for private reference only.",
- "citation_url": "https://pophistorydig.com/topics/tag/paul-conrad-political-cartoons/"
+ "citation_url": "https://pophistorydig.com/topics/tag/paul-conrad-political-cartoons/",
+ "topics": [
+ "Nixon/Watergate"
+ ]
},
{
"id": "obit-come-get-me-1974",
@@ -108,7 +134,10 @@
"image_url": "https://pophistorydig.com/wp-content/uploads/2011/05/Come-Get-Me-310.jpg",
"image_source": "The Pop History Dig",
"rights_note": "© Los Angeles Times / Conrad estate. Low-res reproduction shown for private reference only.",
- "citation_url": "https://pophistorydig.com/topics/tag/paul-conrad-political-cartoons/"
+ "citation_url": "https://pophistorydig.com/topics/tag/paul-conrad-political-cartoons/",
+ "topics": [
+ "Nixon/Watergate"
+ ]
},
{
"id": "obit-lbj-hubert-1968",
@@ -122,7 +151,12 @@
"image_url": "https://pophistorydig.com/wp-content/uploads/2011/05/1968-LBJ-w-Hubert280.jpg",
"image_source": "The Pop History Dig",
"rights_note": "© Los Angeles Times / Conrad estate. Low-res reproduction shown for private reference only.",
- "citation_url": "https://pophistorydig.com/topics/tag/paul-conrad-political-cartoons/"
+ "citation_url": "https://pophistorydig.com/topics/tag/paul-conrad-political-cartoons/",
+ "topics": [
+ "Vietnam War",
+ "Elections & Campaigns",
+ "Presidency"
+ ]
},
{
"id": "obit-reagan-gothic-1986",
@@ -136,7 +170,11 @@
"image_url": "https://pophistorydig.com/wp-content/uploads/2011/05/1986-Reagan-1-260.jpg",
"image_source": "The Pop History Dig",
"rights_note": "© Los Angeles Times / Conrad estate. Low-res reproduction shown for private reference only.",
- "citation_url": "https://pophistorydig.com/topics/tag/paul-conrad-political-cartoons/"
+ "citation_url": "https://pophistorydig.com/topics/tag/paul-conrad-political-cartoons/",
+ "topics": [
+ "Reagan Era",
+ "Economy"
+ ]
},
{
"id": "obit-clinton-horn-1994",
@@ -150,7 +188,11 @@
"image_url": "https://pophistorydig.com/wp-content/uploads/2011/05/Clinton-elephant-300.jpg",
"image_source": "The Pop History Dig",
"rights_note": "© Los Angeles Times / Conrad estate. Low-res reproduction shown for private reference only.",
- "citation_url": "https://pophistorydig.com/topics/tag/paul-conrad-political-cartoons/"
+ "citation_url": "https://pophistorydig.com/topics/tag/paul-conrad-political-cartoons/",
+ "topics": [
+ "Clinton Era",
+ "GOP/Republican Party"
+ ]
},
{
"id": "obit-palin-2008",
@@ -164,7 +206,11 @@
"image_url": "https://pophistorydig.com/wp-content/uploads/2011/05/2008-Palin-260.jpg",
"image_source": "The Pop History Dig",
"rights_note": "© Conrad estate. Low-res reproduction shown for private reference only.",
- "citation_url": "https://pophistorydig.com/topics/tag/paul-conrad-political-cartoons/"
+ "citation_url": "https://pophistorydig.com/topics/tag/paul-conrad-political-cartoons/",
+ "topics": [
+ "GOP/Republican Party",
+ "Elections & Campaigns"
+ ]
},
{
"id": "obit-cuckoos-nest-1974",
@@ -178,7 +224,11 @@
"image_url": null,
"image_source": null,
"rights_note": "Described in the 2010 Associated Press obituary; no rights-clear image located.",
- "citation_url": "https://www.nevadaappeal.com/news/2010/sep/05/pulitzer-winning-cartoonist-paul-conrad-dies-at-86/"
+ "citation_url": "https://www.nevadaappeal.com/news/2010/sep/05/pulitzer-winning-cartoonist-paul-conrad-dies-at-86/",
+ "topics": [
+ "Nixon/Watergate"
+ ],
+ "image_status": "not_found"
},
{
"id": "obit-nixon-cross-1974",
@@ -192,7 +242,12 @@
"image_url": null,
"image_source": null,
"rights_note": "Described in Wikipedia and the LOC LCIB profile; no rights-clear image located.",
- "citation_url": "https://en.wikipedia.org/wiki/Paul_Conrad"
+ "citation_url": "https://en.wikipedia.org/wiki/Paul_Conrad",
+ "topics": [
+ "Nixon/Watergate",
+ "Religion"
+ ],
+ "image_status": "not_found"
},
{
"id": "obit-yorty-1969",
@@ -206,7 +261,12 @@
"image_url": null,
"image_source": null,
"rights_note": "Described in Wikipedia and the Toons Mag profile.",
- "citation_url": "https://en.wikipedia.org/wiki/Paul_Conrad"
+ "citation_url": "https://en.wikipedia.org/wiki/Paul_Conrad",
+ "topics": [
+ "LA/California",
+ "Media & Press"
+ ],
+ "image_status": "not_found"
},
{
"id": "obit-apollo1-1967",
@@ -220,7 +280,11 @@
"image_url": null,
"image_source": null,
"rights_note": "Described in the Encyclopedia.com entry.",
- "citation_url": "https://www.encyclopedia.com/humanities/encyclopedias-almanacs-transcripts-and-maps/conrad-paul"
+ "citation_url": "https://www.encyclopedia.com/humanities/encyclopedias-almanacs-transcripts-and-maps/conrad-paul",
+ "topics": [
+ "Space Race"
+ ],
+ "image_status": "not_found"
},
{
"id": "obit-apollo11-1969",
@@ -234,7 +298,11 @@
"image_url": null,
"image_source": null,
"rights_note": "Described in the Encyclopedia.com entry.",
- "citation_url": "https://www.encyclopedia.com/humanities/encyclopedias-almanacs-transcripts-and-maps/conrad-paul"
+ "citation_url": "https://www.encyclopedia.com/humanities/encyclopedias-almanacs-transcripts-and-maps/conrad-paul",
+ "topics": [
+ "Space Race"
+ ],
+ "image_status": "not_found"
},
{
"id": "obit-moratorium-1969",
@@ -248,7 +316,11 @@
"image_url": null,
"image_source": null,
"rights_note": "Described in the Encyclopedia.com entry (year given; exact day uncertain).",
- "citation_url": "https://www.encyclopedia.com/humanities/encyclopedias-almanacs-transcripts-and-maps/conrad-paul"
+ "citation_url": "https://www.encyclopedia.com/humanities/encyclopedias-almanacs-transcripts-and-maps/conrad-paul",
+ "topics": [
+ "Vietnam War"
+ ],
+ "image_status": "not_found"
},
{
"id": "obit-burro-denver",
@@ -262,7 +334,12 @@
"image_url": null,
"image_source": null,
"rights_note": "Described in the Encyclopedia.com entry.",
- "citation_url": "https://www.encyclopedia.com/humanities/encyclopedias-almanacs-transcripts-and-maps/conrad-paul"
+ "citation_url": "https://www.encyclopedia.com/humanities/encyclopedias-almanacs-transcripts-and-maps/conrad-paul",
+ "topics": [
+ "Media & Press",
+ "Denver Post Era"
+ ],
+ "image_status": "not_found"
},
{
"id": "obit-nast-1982",
@@ -272,11 +349,15 @@
"subject": "After Thomas Nast's 1871 'Who Stole the People's Money?' ring cartoon — Reagan-era economy / passing the blame",
"award": null,
"record_type": "documented",
- "image": null,
- "image_url": null,
- "image_source": null,
- "rights_note": "Described in the Spartacus Educational profile.",
- "citation_url": "https://spartacus-educational.com/ARTconrad.htm"
+ "image": "img/nast-affixing-blame-1982.jpg",
+ "image_url": "https://spartacus-educational.com/ARTconrad.jpg",
+ "image_source": "Spartacus Educational",
+ "rights_note": "© Los Angeles Times / Conrad estate. Low-res reproduction (401x451) shown for private reference only; verified by visual inspection — matches the captioned 'Affixing Blame for Our Economic Problems' (30 Sept 1982), Conrad's homage to Thomas Nast's 1871 Tammany Ring cartoon.",
+ "citation_url": "https://spartacus-educational.com/ARTconrad.htm",
+ "topics": [
+ "Economy",
+ "Reagan Era"
+ ]
},
{
"id": "obit-reagan-selfportrait-1984",
@@ -290,7 +371,12 @@
"image_url": null,
"image_source": null,
"rights_note": "Described in the Library of Congress LCIB profile.",
- "citation_url": "https://www.loc.gov/loc/lcib/9910/conrad.html"
+ "citation_url": "https://www.loc.gov/loc/lcib/9910/conrad.html",
+ "topics": [
+ "Reagan Era",
+ "Elections & Campaigns"
+ ],
+ "image_status": "not_found"
},
{
"id": "obit-littlerock-1958",
@@ -304,7 +390,12 @@
"image_url": null,
"image_source": null,
"rights_note": "Listed in The Pop History Dig retrospective.",
- "citation_url": "https://pophistorydig.com/topics/tag/paul-conrad-enemies-list/"
+ "citation_url": "https://pophistorydig.com/topics/tag/paul-conrad-enemies-list/",
+ "topics": [
+ "Civil Rights",
+ "Denver Post Era"
+ ],
+ "image_status": "not_found"
},
{
"id": "obit-lbj-blankcheck-1964",
@@ -318,7 +409,12 @@
"image_url": null,
"image_source": null,
"rights_note": "Listed in The Pop History Dig retrospective.",
- "citation_url": "https://pophistorydig.com/topics/tag/paul-conrad-enemies-list/"
+ "citation_url": "https://pophistorydig.com/topics/tag/paul-conrad-enemies-list/",
+ "topics": [
+ "Vietnam War",
+ "Presidency"
+ ],
+ "image_status": "not_found"
},
{
"id": "obit-ford-pardon-1974",
@@ -332,7 +428,12 @@
"image_url": null,
"image_source": null,
"rights_note": "Listed in The Pop History Dig retrospective.",
- "citation_url": "https://pophistorydig.com/topics/tag/paul-conrad-enemies-list/"
+ "citation_url": "https://pophistorydig.com/topics/tag/paul-conrad-enemies-list/",
+ "topics": [
+ "Nixon/Watergate",
+ "Presidency"
+ ],
+ "image_status": "not_found"
},
{
"id": "obit-richard-ii-1973",
@@ -346,7 +447,11 @@
"image_url": null,
"image_source": null,
"rights_note": "Listed in The Pop History Dig retrospective.",
- "citation_url": "https://pophistorydig.com/topics/tag/paul-conrad-enemies-list/"
+ "citation_url": "https://pophistorydig.com/topics/tag/paul-conrad-enemies-list/",
+ "topics": [
+ "Nixon/Watergate"
+ ],
+ "image_status": "not_found"
},
{
"id": "obit-popepaulvi-1977",
@@ -360,9 +465,12 @@
"image_url": null,
"image_source": null,
"rights_note": "Described in the Toons Mag profile.",
- "citation_url": "https://www.toonsmag.com/paul-conrad/"
+ "citation_url": "https://www.toonsmag.com/paul-conrad/",
+ "topics": [
+ "Religion"
+ ],
+ "image_status": "not_found"
},
-
{
"id": "loc-sl-hud-1989",
"title": "S&L's and HUD",
@@ -371,11 +479,15 @@
"subject": "Savings & Loan / HUD scandals vs. Teapot Dome; pot-calling-kettle-black",
"award": null,
"record_type": "documented",
- "image": null,
- "image_url": null,
+ "image": "img/loc-sl-hud-1989.jpg",
+ "image_url": "https://tile.loc.gov/storage-services/service/pnp/cph/3c30000/3c36000/3c36100/3c36115v.jpg",
"image_source": "Library of Congress",
- "rights_note": "May be restricted: Copyright 1989, The Los Angeles Times. LOC holds a digitized copy — view via the catalog link; not embedded here.",
- "citation_url": "https://www.loc.gov/item/2016685149/"
+ "rights_note": "© Los Angeles Times / Conrad estate (Denver Post for pre-1964 work). Digitized reproduction from the Library of Congress Prints & Photographs Online Catalog, shown low-res for private research reference only — verified by visual inspection against the LOC catalog description. LOC states it does not own rights and cannot license use; view the official record at the citation link before any other use.",
+ "citation_url": "https://www.loc.gov/item/2016685149/",
+ "topics": [
+ "Economy",
+ "Government Scandal"
+ ]
},
{
"id": "loc-death-row-1991",
@@ -385,11 +497,14 @@
"subject": "Six Supreme Court justices in a row; capital punishment",
"award": null,
"record_type": "documented",
- "image": null,
- "image_url": null,
+ "image": "img/loc-death-row-1991.jpg",
+ "image_url": "https://tile.loc.gov/storage-services/service/pnp/cph/3c20000/3c21000/3c21900/3c21940r.jpg",
"image_source": "Library of Congress",
- "rights_note": "May be restricted: Copyright 1991, The Los Angeles Times. LOC holds a digitized copy — view via the catalog link.",
- "citation_url": "https://www.loc.gov/item/2016685127/"
+ "rights_note": "© Los Angeles Times / Conrad estate (Denver Post for pre-1964 work). Digitized reproduction from the Library of Congress Prints & Photographs Online Catalog, shown low-res for private research reference only — verified by visual inspection against the LOC catalog description. LOC states it does not own rights and cannot license use; view the official record at the citation link before any other use.",
+ "citation_url": "https://www.loc.gov/item/2016685127/",
+ "topics": [
+ "Judiciary & Courts"
+ ]
},
{
"id": "loc-disarmament-1961",
@@ -399,11 +514,15 @@
"subject": "Cold War disarmament posturing",
"award": null,
"record_type": "documented",
- "image": null,
- "image_url": null,
+ "image": "img/loc-disarmament-1961.jpg",
+ "image_url": "https://tile.loc.gov/storage-services/service/pnp/ppmsca/09400/09425v.jpg",
"image_source": "Library of Congress",
- "rights_note": "Rights status not evaluated by LOC — treat as non-embeddable.",
- "citation_url": "https://www.loc.gov/item/2005685912/"
+ "rights_note": "© Los Angeles Times / Conrad estate (Denver Post for pre-1964 work). Digitized reproduction from the Library of Congress Prints & Photographs Online Catalog, shown low-res for private research reference only — verified by visual inspection against the LOC catalog description. LOC states it does not own rights and cannot license use; view the official record at the citation link before any other use.",
+ "citation_url": "https://www.loc.gov/item/2005685912/",
+ "topics": [
+ "Nuclear Arms & Cold War",
+ "Denver Post Era"
+ ]
},
{
"id": "loc-herblock-2000",
@@ -413,11 +532,14 @@
"subject": "Caricature of cartoonist Herblock as George Washington",
"award": null,
"record_type": "documented",
- "image": null,
- "image_url": null,
+ "image": "img/loc-herblock-2000.jpg",
+ "image_url": "https://tile.loc.gov/storage-services/service/pnp/ppmsc/03400/03417v.jpg",
"image_source": "Library of Congress",
- "rights_note": "Publication may be restricted — treat as non-embeddable.",
- "citation_url": "https://www.loc.gov/item/00652290/"
+ "rights_note": "© Los Angeles Times / Conrad estate (Denver Post for pre-1964 work). Digitized reproduction from the Library of Congress Prints & Photographs Online Catalog, shown low-res for private research reference only — verified by visual inspection against the LOC catalog description. LOC states it does not own rights and cannot license use; view the official record at the citation link before any other use.",
+ "citation_url": "https://www.loc.gov/item/00652290/",
+ "topics": [
+ "Media & Press"
+ ]
},
{
"id": "loc-inheritance-1990",
@@ -427,11 +549,15 @@
"subject": "Deficit / generational debt",
"award": null,
"record_type": "documented",
- "image": null,
- "image_url": null,
+ "image": "img/loc-inheritance-1990.jpg",
+ "image_url": "https://tile.loc.gov/storage-services/service/pnp/cph/3g10000/3g13000/3g13000/3g13017v.jpg",
"image_source": "Library of Congress",
- "rights_note": "May be restricted: Copyright 1990, Los Angeles Times.",
- "citation_url": "https://www.loc.gov/item/2016685135/"
+ "rights_note": "© Los Angeles Times / Conrad estate (Denver Post for pre-1964 work). Digitized reproduction from the Library of Congress Prints & Photographs Online Catalog, shown low-res for private research reference only — verified by visual inspection against the LOC catalog description. LOC states it does not own rights and cannot license use; view the official record at the citation link before any other use.",
+ "citation_url": "https://www.loc.gov/item/2016685135/",
+ "topics": [
+ "Reagan Era",
+ "Economy"
+ ]
},
{
"id": "loc-nra-pacifier-1982",
@@ -441,11 +567,14 @@
"subject": "Gun politics / NRA",
"award": null,
"record_type": "documented",
- "image": null,
- "image_url": null,
+ "image": "img/loc-nra-pacifier-1982.jpg",
+ "image_url": "https://tile.loc.gov/storage-services/service/pnp/cph/3c30000/3c33000/3c33000/3c33005v.jpg",
"image_source": "Library of Congress",
- "rights_note": "May be restricted: Copyright 1982, The Los Angeles Times.",
- "citation_url": "https://www.loc.gov/item/2016685176/"
+ "rights_note": "© Los Angeles Times / Conrad estate (Denver Post for pre-1964 work). Digitized reproduction from the Library of Congress Prints & Photographs Online Catalog, shown low-res for private research reference only — verified by visual inspection against the LOC catalog description. LOC states it does not own rights and cannot license use; view the official record at the citation link before any other use.",
+ "citation_url": "https://www.loc.gov/item/2016685176/",
+ "topics": [
+ "Gun Politics"
+ ]
},
{
"id": "loc-southamerica-1959",
@@ -455,11 +584,16 @@
"subject": "Foreign-relations / South America policy",
"award": null,
"record_type": "documented",
- "image": null,
- "image_url": null,
+ "image": "img/loc-southamerica-1959.jpg",
+ "image_url": "https://tile.loc.gov/storage-services/service/pnp/acd/2a07000/2a07600/2a07662r.jpg",
"image_source": "Library of Congress",
- "rights_note": "May be restricted — no affirmative open statement; treat as non-embeddable.",
- "citation_url": "https://www.loc.gov/item/2016680206/"
+ "rights_note": "© Los Angeles Times / Conrad estate (Denver Post for pre-1964 work). Digitized reproduction from the Library of Congress Prints & Photographs Online Catalog, shown low-res for private research reference only — verified by visual inspection against the LOC catalog description. LOC states it does not own rights and cannot license use; view the official record at the citation link before any other use.",
+ "citation_url": "https://www.loc.gov/item/2016680206/",
+ "topics": [
+ "Foreign Policy",
+ "Media & Press",
+ "Denver Post Era"
+ ]
},
{
"id": "loc-barroom-1989",
@@ -469,11 +603,15 @@
"subject": "Political commentary (1989)",
"award": null,
"record_type": "documented",
- "image": null,
- "image_url": null,
+ "image": "img/loc-barroom-1989.jpg",
+ "image_url": "https://tile.loc.gov/storage-services/service/pnp/cph/3c30000/3c32000/3c32800/3c32804v.jpg",
"image_source": "Library of Congress",
- "rights_note": "May be restricted: Copyright 1989, The Los Angeles Times.",
- "citation_url": "https://www.loc.gov/item/2016685150/"
+ "rights_note": "© Los Angeles Times / Conrad estate (Denver Post for pre-1964 work). Digitized reproduction from the Library of Congress Prints & Photographs Online Catalog, shown low-res for private research reference only — verified by visual inspection against the LOC catalog description. LOC states it does not own rights and cannot license use; view the official record at the citation link before any other use.",
+ "citation_url": "https://www.loc.gov/item/2016685150/",
+ "topics": [
+ "Civil Rights",
+ "Judiciary & Courts"
+ ]
},
{
"id": "loc-naked-truth-1987",
@@ -483,11 +621,15 @@
"subject": "Political commentary (1987)",
"award": null,
"record_type": "documented",
- "image": null,
- "image_url": null,
+ "image": "img/loc-naked-truth-1987.jpg",
+ "image_url": "https://tile.loc.gov/storage-services/service/pnp/cph/3c30000/3c33000/3c33900/3c33940v.jpg",
"image_source": "Library of Congress",
- "rights_note": "May be restricted: Copyright 1987, The Los Angeles Times.",
- "citation_url": "https://www.loc.gov/item/2016685157/"
+ "rights_note": "© Los Angeles Times / Conrad estate (Denver Post for pre-1964 work). Digitized reproduction from the Library of Congress Prints & Photographs Online Catalog, shown low-res for private research reference only — verified by visual inspection against the LOC catalog description. LOC states it does not own rights and cannot license use; view the official record at the citation link before any other use.",
+ "citation_url": "https://www.loc.gov/item/2016685157/",
+ "topics": [
+ "Iran-Contra",
+ "Foreign Policy"
+ ]
},
{
"id": "loc-black-teen-unemployment-1977",
@@ -497,13 +639,16 @@
"subject": "Black youth unemployment",
"award": null,
"record_type": "documented",
- "image": null,
- "image_url": null,
+ "image": "img/loc-black-teen-unemployment-1977.jpg",
+ "image_url": "https://tile.loc.gov/storage-services/service/pnp/cph/3g10000/3g13000/3g13000/3g13019v.jpg",
"image_source": "Library of Congress",
- "rights_note": "May be restricted: Copyright 1977, Los Angeles Times.",
- "citation_url": "https://www.loc.gov/item/2016685134/"
+ "rights_note": "© Los Angeles Times / Conrad estate (Denver Post for pre-1964 work). Digitized reproduction from the Library of Congress Prints & Photographs Online Catalog, shown low-res for private research reference only — verified by visual inspection against the LOC catalog description. LOC states it does not own rights and cannot license use; view the official record at the citation link before any other use.",
+ "citation_url": "https://www.loc.gov/item/2016685134/",
+ "topics": [
+ "Civil Rights",
+ "Economy"
+ ]
},
-
{
"id": "coll-huntington",
"title": "Paul Conrad Papers — The Huntington Library",
@@ -513,11 +658,15 @@
"award": null,
"record_type": "collection-summary",
"extent": "272.41 linear feet (285 boxes). Identifier mssCON 1-12360. Cartoons in Boxes 1-106 + oversize 107-167, ~40-50 per box, each foldered.",
- "image": null,
- "image_url": null,
- "image_source": null,
- "rights_note": "No explicit access restriction stated. Preferred citation: '[item]. Paul Conrad Papers, The Huntington Library, San Marino, California.'",
- "citation_url": "https://oac.cdlib.org/findaid/ark:/13030/c8z03dxd/"
+ "image": "img/nixon-spiders-web-1973.jpg",
+ "image_url": "https://pophistorydig.com/wp-content/uploads/2011/05/Nixon-Spiders-Web333.jpg",
+ "image_source": "The Pop History Dig",
+ "rights_note": "REPRESENTATIVE IMAGE — the Huntington holds ~285 boxes of Conrad originals (1959-2003); this is a verified Conrad Watergate-era cartoon shown to represent the collection's character, not a specific catalogued item from this finding aid.",
+ "citation_url": "https://oac.cdlib.org/findaid/ark:/13030/c8z03dxd/",
+ "topics": [
+ "Archive Collection"
+ ],
+ "representative": true
},
{
"id": "coll-syracuse",
@@ -528,11 +677,15 @@
"award": null,
"record_type": "collection-summary",
"extent": "~10 boxes; reported ~1,000+ original cartoons. Not stated in linear feet.",
- "image": null,
- "image_url": null,
- "image_source": null,
- "rights_note": "No access restriction noted in the guide.",
- "citation_url": "https://library.syracuse.edu/digital/guides/c/conrad_p.htm"
+ "image": "img/worst-enemy-1973.jpg",
+ "image_url": "https://pophistorydig.com/wp-content/uploads/2011/05/Worst-Enemy-370.jpg",
+ "image_source": "The Pop History Dig",
+ "rights_note": "REPRESENTATIVE IMAGE — Syracuse holds 1,000+ original LA Times-era cartoons (bulk 1963-1969); this is a verified Conrad cartoon from that broad era shown to represent the collection's character, not a specific catalogued item from this finding aid.",
+ "citation_url": "https://library.syracuse.edu/digital/guides/c/conrad_p.htm",
+ "topics": [
+ "Archive Collection"
+ ],
+ "representative": true
},
{
"id": "coll-wichita",
@@ -543,11 +696,15 @@
"award": null,
"record_type": "collection-summary",
"extent": "2 linear feet. Identifier MS-90-18.",
- "image": null,
- "image_url": null,
- "image_source": null,
- "rights_note": "Literary rights NOT granted to Wichita State; permission to examine is not authorization to publish. Some items off-site (48h retrieval).",
- "citation_url": "https://archivesspace.wichita.edu/repositories/3/resources/166"
+ "image": "img/lbj-hubert-1968.jpg",
+ "image_url": "https://pophistorydig.com/wp-content/uploads/2011/05/1968-LBJ-w-Hubert280.jpg",
+ "image_source": "The Pop History Dig",
+ "rights_note": "REPRESENTATIVE IMAGE — Wichita's collection covers exactly 1968-1971; this is a verified 1968 Conrad cartoon shown to represent the collection's character, not a specific catalogued item from this finding aid.",
+ "citation_url": "https://archivesspace.wichita.edu/repositories/3/resources/166",
+ "topics": [
+ "Archive Collection"
+ ],
+ "representative": true
}
]
}
diff --git a/public/app.js b/public/app.js
index 102661d..ef2568d 100644
--- a/public/app.js
+++ b/public/app.js
@@ -1,13 +1,18 @@
// Paul Conrad Cartoons — private catalog viewer.
// Loads /api/cartoons, renders a filterable/sortable grid with a density slider,
-// both persisted to localStorage. No server-side state beyond the static dataset.
+// a topic-chip + section + decade nav, and a detail modal. Choices persist to localStorage.
+// No server-side state beyond the static dataset.
const LS_SORT = 'pcc:sort';
const LS_DENSITY = 'pcc:density';
const LS_FILTERS = 'pcc:filters';
let ALL = [];
-let filters = { year: null, publication: null, award: null, subject: null, recordType: null };
+let BIO = null;
+let filters = {
+ year: null, publication: null, award: null, subject: null, recordType: null,
+ topic: null, section: 'all', decade: null,
+};
function safeGetLS(key, fallback) {
try {
@@ -25,23 +30,43 @@ function yearOf(rec) {
return m ? m[0] : 'Unknown';
}
+function decadeOf(rec) {
+ const y = parseInt(rec.year, 10);
+ if (!y) return null;
+ return String(Math.floor(y / 10) * 10);
+}
+
function normalizeRecords(raw) {
- return raw.map((r, i) => ({
- id: r.id || `pc-${i}`,
- title: r.title || 'Untitled',
- date: r.date || r.date_range || null,
- year: yearOf(r),
- publication: r.publication || null,
- subject: r.subject || null,
- award: r.award || null,
- record_type: r.record_type || 'documented',
- extent: r.extent || null,
- image: r.image || null, // local downloaded path, preferred for display
- image_url: r.image_url || null, // external legitimate source, fallback
- image_source: r.image_source || null,
- rights_note: r.rights_note || null,
- citation_url: r.citation_url || null,
- }));
+ return raw.map((r, i) => {
+ const year = yearOf(r);
+ return {
+ id: r.id || `pc-${i}`,
+ title: r.title || 'Untitled',
+ date: r.date || r.date_range || null,
+ year,
+ decade: (() => { const y = parseInt(year, 10); return y ? String(Math.floor(y / 10) * 10) : null; })(),
+ publication: r.publication || null,
+ subject: r.subject || null,
+ award: r.award || null,
+ record_type: r.record_type || 'documented',
+ extent: r.extent || null,
+ topics: Array.isArray(r.topics) ? r.topics : [],
+ image: r.image || null, // local downloaded path, preferred for display
+ image_url: r.image_url || null, // external legitimate source, fallback
+ image_source: r.image_source || null,
+ image_status: r.image_status || null, // 'not_found' when no image exists anywhere
+ representative: !!r.representative,
+ rights_note: r.rights_note || null,
+ citation_url: r.citation_url || null,
+ };
+ });
+}
+
+function sectionOf(rec) {
+ if (rec.id.startsWith('pulitzer-')) return 'pulitzers';
+ if (rec.id.startsWith('loc-')) return 'loc';
+ if (rec.id.startsWith('coll-')) return 'archives';
+ return 'cartoons';
}
function buildFacet(records, field) {
@@ -54,6 +79,14 @@ function buildFacet(records, field) {
return [...counts.entries()].sort((a, b) => b[1] - a[1] || String(a[0]).localeCompare(String(b[0])));
}
+function buildTopicCounts(records) {
+ const counts = new Map();
+ for (const r of records) {
+ for (const t of r.topics) counts.set(t, (counts.get(t) || 0) + 1);
+ }
+ return [...counts.entries()].sort((a, b) => b[1] - a[1] || a[0].localeCompare(b[0]));
+}
+
function applyFilters(records) {
const q = document.getElementById('search').value.trim().toLowerCase();
return records.filter(r => {
@@ -62,8 +95,11 @@ function applyFilters(records) {
if (filters.award && r.award !== filters.award) return false;
if (filters.subject && r.subject !== filters.subject) return false;
if (filters.recordType && r.record_type !== filters.recordType) return false;
+ if (filters.topic && !r.topics.includes(filters.topic)) return false;
+ if (filters.decade && r.decade !== filters.decade) return false;
+ if (filters.section && filters.section !== 'all' && filters.section !== 'bio' && sectionOf(r) !== filters.section) return false;
if (q) {
- const hay = [r.title, r.subject, r.publication, r.award, r.date].filter(Boolean).join(' ').toLowerCase();
+ const hay = [r.title, r.subject, r.publication, r.award, r.date, ...(r.topics || [])].filter(Boolean).join(' ').toLowerCase();
if (!hay.includes(q)) return false;
}
return true;
@@ -76,6 +112,7 @@ function sortRecords(records, mode) {
case 'year-asc': return arr.sort((a, b) => (a.year > b.year ? 1 : -1));
case 'year-desc': return arr.sort((a, b) => (a.year < b.year ? 1 : -1));
case 'title-az': return arr.sort((a, b) => a.title.localeCompare(b.title));
+ case 'topic-az': return arr.sort((a, b) => (a.topics[0] || 'zzz').localeCompare(b.topics[0] || 'zzz') || a.title.localeCompare(b.title));
case 'award-first': return arr.sort((a, b) => (b.award ? 1 : 0) - (a.award ? 1 : 0));
case 'newest':
default: return arr; // natural/dataset order = "Newest"-equivalent default
@@ -89,7 +126,7 @@ function renderFacetGroup(containerId, field, label) {
const activeKey = field === 'record_type' ? 'recordType' : field;
const isActive = filters[activeKey] === val;
return `<div class="filter-row${isActive ? ' active' : ''}" data-field="${field}" data-value="${encodeURIComponent(val)}">
- <span>${val === 'documented' ? 'Documented cartoon' : val === 'collection-summary' ? 'Archive collection' : val}</span>
+ <span>${val === 'documented' ? 'Documented cartoon' : val === 'collection-summary' ? 'Archive collection' : escapeHtml(val)}</span>
<span class="count">${count}</span>
</div>`;
}).join('');
@@ -114,20 +151,60 @@ function renderFilters() {
renderFacetGroup('facet-recordtype', 'record_type', 'Record Type');
}
+function renderTopicRow() {
+ const el = document.getElementById('topic-row');
+ const counts = buildTopicCounts(ALL);
+ const totalCount = ALL.length;
+ const allChip = `<button type="button" class="topic-chip${!filters.topic ? ' active' : ''}" data-topic="">All <span class="chip-count">${totalCount}</span></button>`;
+ const chips = counts.map(([topic, count]) => {
+ const isActive = filters.topic === topic;
+ return `<button type="button" class="topic-chip${isActive ? ' active' : ''}" data-topic="${encodeURIComponent(topic)}">${escapeHtml(topic)} <span class="chip-count">${count}</span></button>`;
+ }).join('');
+ el.innerHTML = allChip + chips;
+ el.querySelectorAll('.topic-chip').forEach(chip => {
+ chip.addEventListener('click', () => {
+ const t = chip.dataset.topic ? decodeURIComponent(chip.dataset.topic) : null;
+ filters.topic = (filters.topic === t) ? null : t;
+ safeSetLS(LS_FILTERS, filters);
+ renderAll();
+ });
+ });
+}
+
+function renderSectionLinks() {
+ document.querySelectorAll('#section-links button[data-section]').forEach(btn => {
+ btn.classList.toggle('active', filters.section === btn.dataset.section);
+ });
+}
+
+function renderDecadeJump() {
+ document.querySelectorAll('#decade-jump button[data-decade]').forEach(btn => {
+ btn.classList.toggle('active', filters.decade === btn.dataset.decade);
+ });
+}
+
function cardHtml(r) {
const img = r.image || r.image_url;
- const thumb = img
- ? `<img src="${img}" alt="${escapeHtml(r.title)}" loading="lazy">`
- : `<div class="no-img">Not digitized —<br>see archive citation</div>`;
+ let thumb;
+ if (img) {
+ thumb = `<img src="${img}" alt="${escapeHtml(r.title)}" loading="lazy">`;
+ } else if (r.image_status === 'not_found') {
+ thumb = `<div class="no-img no-img-notfound">No verified image found —<br>view at source</div>`;
+ } else {
+ thumb = `<div class="no-img">Not digitized —<br>see archive citation</div>`;
+ }
const badges = [
`<span class="badge ${r.record_type}">${r.record_type === 'documented' ? 'Documented' : 'Archive'}</span>`,
- r.award ? `<span class="badge award">Pulitzer</span>` : ''
+ r.award ? `<span class="badge award">Pulitzer</span>` : '',
+ r.representative ? `<span class="badge representative">Representative image</span>` : '',
].join('');
+ const topicChips = r.topics.slice(0, 3).map(t => `<span class="topic-tag">${escapeHtml(t)}</span>`).join('');
return `<div class="card" data-id="${r.id}">
<div class="thumb">${thumb}</div>
<div class="body">
<div class="title">${escapeHtml(r.title)}</div>
<div class="meta">${escapeHtml(r.date || r.year || '')}${r.publication ? ' · ' + escapeHtml(r.publication) : ''}</div>
+ <div class="topics">${topicChips}</div>
<div class="badges">${badges}</div>
</div>
</div>`;
@@ -152,6 +229,9 @@ function renderGrid() {
function renderAll() {
renderFilters();
+ renderTopicRow();
+ renderSectionLinks();
+ renderDecadeJump();
renderGrid();
}
@@ -159,11 +239,20 @@ function openModal(id) {
const r = ALL.find(x => x.id === id);
if (!r) return;
const img = r.image || r.image_url;
+ const topicChips = r.topics.map(t => `<span class="topic-tag modal-topic-tag">${escapeHtml(t)}</span>`).join('');
+ let imgBlock = '';
+ if (img) {
+ const repBadge = r.representative ? `<div class="modal-rep-note">Representative image — see the credit note below.</div>` : '';
+ imgBlock = `<img class="modal-img" src="${img}" alt="${escapeHtml(r.title)}">${repBadge}`;
+ } else if (r.image_status === 'not_found') {
+ imgBlock = `<div class="modal-no-img">No verified image found for this cartoon.${r.citation_url ? ` <a href="${r.citation_url}" target="_blank" rel="noopener noreferrer">View at source →</a>` : ''}</div>`;
+ }
document.getElementById('modal-body').innerHTML = `
<button class="close-x" id="modal-close">×</button>
<h2>${escapeHtml(r.title)}</h2>
<div class="modal-meta">${escapeHtml(r.date || r.year || '')}${r.publication ? ' · ' + escapeHtml(r.publication) : ''}</div>
- ${img ? `<img class="modal-img" src="${img}" alt="${escapeHtml(r.title)}">` : ''}
+ <div class="topics modal-topics">${topicChips}</div>
+ ${imgBlock}
<dl>
<dt>Subject</dt><dd>${escapeHtml(r.subject || '—')}</dd>
<dt>Award</dt><dd>${escapeHtml(r.award || '—')}</dd>
@@ -177,8 +266,73 @@ function openModal(id) {
document.getElementById('modal-backdrop').classList.add('open');
document.getElementById('modal-close').addEventListener('click', closeModal);
}
+
+function openBioModal() {
+ if (!BIO) return;
+ document.getElementById('modal-body').innerHTML = `
+ <button class="close-x" id="modal-close">×</button>
+ <h2>${escapeHtml(BIO.name || 'Paul Conrad')}</h2>
+ <div class="modal-meta">${escapeHtml(BIO.born || '')} — ${escapeHtml(BIO.died || '')}</div>
+ <dl>
+ <dt>Education</dt><dd>${escapeHtml(BIO.education || '—')}</dd>
+ <dt>Career</dt><dd>${escapeHtml(BIO.career || '—')}</dd>
+ <dt>Honors</dt><dd>${escapeHtml(BIO.honors || '—')}</dd>
+ <dt>Family</dt><dd>${escapeHtml(BIO.family || '—')}</dd>
+ <dt>Citation</dt><dd>${BIO.citation_url ? `<a href="${BIO.citation_url}" target="_blank" rel="noopener noreferrer">${escapeHtml(BIO.citation_url)}</a>` : '—'}</dd>
+ </dl>
+ `;
+ document.getElementById('modal-backdrop').classList.add('open');
+ document.getElementById('modal-close').addEventListener('click', closeModal);
+}
+
function closeModal() { document.getElementById('modal-backdrop').classList.remove('open'); }
+function closeMobileNav() {
+ document.getElementById('topnav').classList.remove('nav-open');
+ document.getElementById('hamburger').setAttribute('aria-expanded', 'false');
+}
+
+function initNav() {
+ document.getElementById('hamburger').addEventListener('click', () => {
+ const nav = document.getElementById('topnav');
+ const isOpen = nav.classList.toggle('nav-open');
+ document.getElementById('hamburger').setAttribute('aria-expanded', String(isOpen));
+ });
+
+ document.getElementById('wordmark').addEventListener('click', (e) => {
+ e.preventDefault();
+ filters.section = 'all';
+ safeSetLS(LS_FILTERS, filters);
+ renderAll();
+ closeMobileNav();
+ });
+
+ document.querySelectorAll('#section-links button[data-section]').forEach(btn => {
+ btn.addEventListener('click', () => {
+ const section = btn.dataset.section;
+ if (section === 'bio') {
+ openBioModal();
+ closeMobileNav();
+ return;
+ }
+ filters.section = section;
+ safeSetLS(LS_FILTERS, filters);
+ renderAll();
+ closeMobileNav();
+ });
+ });
+
+ document.querySelectorAll('#decade-jump button[data-decade]').forEach(btn => {
+ btn.addEventListener('click', () => {
+ const d = btn.dataset.decade;
+ filters.decade = (filters.decade === d) ? null : d;
+ safeSetLS(LS_FILTERS, filters);
+ renderAll();
+ closeMobileNav();
+ });
+ });
+}
+
function initControls() {
const sortEl = document.getElementById('sort');
sortEl.value = safeGetLS(LS_SORT, 'year-asc');
@@ -195,7 +349,7 @@ function initControls() {
document.getElementById('search').addEventListener('input', renderGrid);
document.getElementById('clear-filters').addEventListener('click', () => {
- filters = { year: null, publication: null, award: null, subject: null, recordType: null };
+ filters = { year: null, publication: null, award: null, subject: null, recordType: null, topic: null, section: 'all', decade: null };
safeSetLS(LS_FILTERS, filters);
document.getElementById('search').value = '';
renderAll();
@@ -205,13 +359,18 @@ function initControls() {
if (e.target.id === 'modal-backdrop') closeModal();
});
document.addEventListener('keydown', (e) => { if (e.key === 'Escape') closeModal(); });
+
+ initNav();
}
async function boot() {
- filters = safeGetLS(LS_FILTERS, filters);
+ const savedFilters = safeGetLS(LS_FILTERS, filters);
+ filters = { ...filters, ...savedFilters };
+ if (!filters.section) filters.section = 'all';
const res = await fetch('/api/cartoons');
const raw = await res.json();
ALL = normalizeRecords(raw.records || raw);
+ BIO = raw.bio || null;
initControls();
renderAll();
}
diff --git a/public/img/loc-barroom-1989.jpg b/public/img/loc-barroom-1989.jpg
new file mode 100644
index 0000000..5bd18ac
Binary files /dev/null and b/public/img/loc-barroom-1989.jpg differ
diff --git a/public/img/loc-black-teen-unemployment-1977.jpg b/public/img/loc-black-teen-unemployment-1977.jpg
new file mode 100644
index 0000000..0b4d5d4
Binary files /dev/null and b/public/img/loc-black-teen-unemployment-1977.jpg differ
diff --git a/public/img/loc-death-row-1991.jpg b/public/img/loc-death-row-1991.jpg
new file mode 100644
index 0000000..328d485
Binary files /dev/null and b/public/img/loc-death-row-1991.jpg differ
diff --git a/public/img/loc-disarmament-1961.jpg b/public/img/loc-disarmament-1961.jpg
new file mode 100644
index 0000000..4cf1ecf
Binary files /dev/null and b/public/img/loc-disarmament-1961.jpg differ
diff --git a/public/img/loc-herblock-2000.jpg b/public/img/loc-herblock-2000.jpg
new file mode 100644
index 0000000..e0973cb
Binary files /dev/null and b/public/img/loc-herblock-2000.jpg differ
diff --git a/public/img/loc-inheritance-1990.jpg b/public/img/loc-inheritance-1990.jpg
new file mode 100644
index 0000000..3614125
Binary files /dev/null and b/public/img/loc-inheritance-1990.jpg differ
diff --git a/public/img/loc-naked-truth-1987.jpg b/public/img/loc-naked-truth-1987.jpg
new file mode 100644
index 0000000..5cbd80a
Binary files /dev/null and b/public/img/loc-naked-truth-1987.jpg differ
diff --git a/public/img/loc-nra-pacifier-1982.jpg b/public/img/loc-nra-pacifier-1982.jpg
new file mode 100644
index 0000000..e1ed58a
Binary files /dev/null and b/public/img/loc-nra-pacifier-1982.jpg differ
diff --git a/public/img/loc-sl-hud-1989.jpg b/public/img/loc-sl-hud-1989.jpg
new file mode 100644
index 0000000..130fd79
Binary files /dev/null and b/public/img/loc-sl-hud-1989.jpg differ
diff --git a/public/img/loc-southamerica-1959.jpg b/public/img/loc-southamerica-1959.jpg
new file mode 100644
index 0000000..00adab9
Binary files /dev/null and b/public/img/loc-southamerica-1959.jpg differ
diff --git a/public/img/nast-affixing-blame-1982.jpg b/public/img/nast-affixing-blame-1982.jpg
new file mode 100644
index 0000000..0cf0458
Binary files /dev/null and b/public/img/nast-affixing-blame-1982.jpg differ
diff --git a/public/index.html b/public/index.html
index 543c4cb..ba35fd0 100644
--- a/public/index.html
+++ b/public/index.html
@@ -7,14 +7,41 @@
<link rel="stylesheet" href="style.css">
</head>
<body>
- <header class="masthead">
- <h1>Paul Conrad — Editorial Cartoons</h1>
- <div class="sub">1924–2010 · Denver Post 1950–1964 · Los Angeles Times 1964–1993 · Three-time Pulitzer Prize winner (1964, 1971, 1984)</div>
- </header>
+ <nav class="topnav" id="topnav">
+ <div class="topnav-row1">
+ <button class="hamburger" id="hamburger" aria-label="Toggle menu" aria-expanded="false">☰</button>
+ <a class="wordmark" href="#" id="wordmark" data-section="all">
+ Paul Conrad <span>Editorial Cartoons</span>
+ </a>
+ <div class="topnav-search">
+ <input id="search" type="search" placeholder="Search title, subject, date…">
+ </div>
+ </div>
+ <div class="topnav-row2" id="topnav-row2">
+ <div class="section-links" id="section-links">
+ <button type="button" data-section="all" class="active">Cartoons</button>
+ <button type="button" data-section="pulitzers">Pulitzers</button>
+ <button type="button" data-section="loc">Library of Congress</button>
+ <button type="button" data-section="archives">Archives</button>
+ <button type="button" data-section="bio">Bio</button>
+ </div>
+ <div class="decade-jump" id="decade-jump">
+ <span class="decade-label">Jump to decade:</span>
+ <button type="button" data-decade="1950">50s</button>
+ <button type="button" data-decade="1960">60s</button>
+ <button type="button" data-decade="1970">70s</button>
+ <button type="button" data-decade="1980">80s</button>
+ <button type="button" data-decade="1990">90s</button>
+ <button type="button" data-decade="2000">00s</button>
+ </div>
+ </div>
+ <div class="topic-row" id="topic-row"></div>
+ </nav>
+
+ <div class="masthead-sub">1924–2010 · Denver Post 1950–1964 · Los Angeles Times 1964–1993 · Three-time Pulitzer Prize winner (1964, 1971, 1984)</div>
<div class="layout">
<aside class="filters">
- <input class="search" id="search" type="search" placeholder="Search title, subject, date…">
<details class="filter-group" id="facet-award" open></details>
<details class="filter-group" id="facet-recordtype" open></details>
<details class="filter-group" id="facet-publication"></details>
@@ -31,6 +58,7 @@
<option value="year-asc">Year ↑ (oldest first)</option>
<option value="year-desc">Year ↓ (newest first)</option>
<option value="title-az">Title A→Z</option>
+ <option value="topic-az">Topic A→Z</option>
<option value="award-first">Award winners first</option>
<option value="newest">Dataset order</option>
</select>
diff --git a/public/style.css b/public/style.css
index ac5c3a8..da34367 100644
--- a/public/style.css
+++ b/public/style.css
@@ -40,6 +40,7 @@
}
* { box-sizing: border-box; }
+html, body { max-width: 100%; overflow-x: hidden; }
body {
margin: 0;
background: var(--bg);
@@ -48,22 +49,157 @@ body {
-webkit-font-smoothing: antialiased;
}
-header.masthead {
- padding: 22px 20px 14px;
- border-bottom: 3px solid var(--ink);
- text-align: center;
+/* ---- top nav ---- */
+.topnav {
+ position: sticky;
+ top: 0;
+ z-index: 20;
+ background: var(--ink);
+ color: var(--bg);
+ border-bottom: 3px solid var(--accent);
}
-header.masthead h1 {
- margin: 0;
- font-size: clamp(24px, 4vw, 38px);
- letter-spacing: 0.02em;
+.topnav-row1 {
+ display: flex;
+ align-items: center;
+ gap: 14px;
+ padding: 10px 18px;
+}
+.hamburger {
+ display: none;
+ background: none;
+ border: 1px solid var(--accent-soft);
+ color: var(--bg);
+ border-radius: var(--radius);
+ font-size: 18px;
+ line-height: 1;
+ padding: 6px 10px;
+ cursor: pointer;
+}
+.wordmark {
+ color: var(--bg);
+ text-decoration: none;
font-weight: 700;
+ font-size: clamp(18px, 3vw, 24px);
+ letter-spacing: 0.02em;
+ white-space: nowrap;
}
-header.masthead .sub {
- margin-top: 6px;
+.wordmark span {
+ font-weight: 400;
+ font-style: italic;
+ font-size: 0.6em;
+ color: var(--accent-soft);
+ margin-left: 8px;
+}
+.topnav-search { flex: 1; max-width: 380px; margin-left: auto; }
+.topnav-search input {
+ width: 100%;
+ padding: 7px 10px;
+ border: 1px solid var(--accent-soft);
+ border-radius: var(--radius);
+ background: rgba(255,255,255,0.06);
+ color: var(--bg);
+ font-family: inherit;
+ font-size: 14px;
+}
+.topnav-search input::placeholder { color: var(--accent-soft); opacity: 0.9; }
+
+.topnav-row2 {
+ display: flex;
+ align-items: center;
+ justify-content: space-between;
+ flex-wrap: wrap;
+ gap: 10px;
+ padding: 0 18px 10px;
+ border-bottom: 1px solid rgba(255,255,255,0.12);
+}
+.section-links { display: flex; gap: 4px; flex-wrap: wrap; }
+.section-links button {
+ background: none;
+ border: 1px solid transparent;
+ color: var(--bg);
+ opacity: 0.8;
+ font-family: inherit;
font-size: 13px;
+ padding: 5px 10px;
+ border-radius: var(--radius);
+ cursor: pointer;
+ white-space: nowrap;
+}
+.section-links button:hover { opacity: 1; border-color: rgba(255,255,255,0.2); }
+.section-links button.active { opacity: 1; background: var(--accent); color: #fff; font-weight: 700; }
+
+.decade-jump { display: flex; align-items: center; gap: 4px; flex-wrap: wrap; }
+.decade-jump .decade-label { font-size: 11px; color: var(--accent-soft); margin-right: 4px; text-transform: uppercase; letter-spacing: 0.05em; }
+.decade-jump button {
+ background: none;
+ border: 1px solid rgba(255,255,255,0.18);
+ color: var(--bg);
+ font-family: inherit;
+ font-size: 12px;
+ padding: 4px 9px;
+ border-radius: 12px;
+ cursor: pointer;
+}
+.decade-jump button:hover { border-color: var(--accent-soft); }
+.decade-jump button.active { background: var(--accent-soft); color: #241a0f; border-color: var(--accent-soft); font-weight: 700; }
+
+.topic-row {
+ display: flex;
+ gap: 6px;
+ padding: 8px 18px;
+ overflow-x: auto;
+ white-space: nowrap;
+ -webkit-overflow-scrolling: touch;
+ scrollbar-width: thin;
+}
+.topic-row::-webkit-scrollbar { height: 5px; }
+.topic-row::-webkit-scrollbar-thumb { background: rgba(255,255,255,0.2); border-radius: 4px; }
+.topic-chip {
+ flex: 0 0 auto;
+ background: rgba(255,255,255,0.07);
+ border: 1px solid rgba(255,255,255,0.14);
+ color: var(--bg);
+ font-family: inherit;
+ font-size: 12px;
+ padding: 5px 10px;
+ border-radius: 14px;
+ cursor: pointer;
+}
+.topic-chip:hover { border-color: var(--accent-soft); }
+.topic-chip.active { background: var(--accent); border-color: var(--accent); color: #fff; font-weight: 700; }
+.topic-chip .chip-count { color: var(--accent-soft); font-size: 10.5px; }
+.topic-chip.active .chip-count { color: rgba(255,255,255,0.85); }
+
+.masthead-sub {
+ padding: 10px 20px;
+ border-bottom: 1px solid var(--line);
+ text-align: center;
+ font-size: 12.5px;
color: var(--ink-soft);
font-style: italic;
+ background: var(--panel-bg);
+}
+
+/* ---- mobile nav collapse (<=390px per spec, applied a bit wider for real devices) ---- */
+@media (max-width: 700px) {
+ .hamburger { display: inline-block; }
+ .topnav-row1 { flex-wrap: wrap; }
+ .topnav-search { flex-basis: 100%; max-width: none; margin-left: 0; order: 3; }
+ .topnav-row2 {
+ flex-direction: column;
+ align-items: flex-start;
+ max-height: 0;
+ overflow: hidden;
+ padding: 0 18px;
+ transition: max-height .2s ease, padding .2s ease;
+ }
+ .topnav.nav-open .topnav-row2 {
+ max-height: 400px;
+ padding: 8px 18px 12px;
+ }
+ .section-links { flex-direction: column; width: 100%; }
+ .section-links button { width: 100%; text-align: left; }
+ .decade-jump { width: 100%; }
}
.layout {
@@ -208,9 +344,22 @@ header.masthead .sub {
padding: 10px;
font-style: italic;
}
+.card .thumb .no-img-notfound {
+ color: var(--accent);
+ opacity: 0.75;
+}
.card .body { padding: 10px 12px; flex: 1; display: flex; flex-direction: column; gap: 4px; }
.card .title { font-weight: 700; font-size: 14px; line-height: 1.25; }
.card .meta { font-size: 11.5px; color: var(--ink-soft); }
+.topics { display: flex; gap: 5px; flex-wrap: wrap; }
+.topic-tag {
+ font-size: 10px;
+ color: var(--ink-soft);
+ background: var(--bg);
+ border: 1px solid var(--line);
+ border-radius: 10px;
+ padding: 1px 7px;
+}
.card .badges { display: flex; gap: 6px; margin-top: auto; padding-top: 6px; flex-wrap: wrap; }
.badge {
font-size: 10px;
@@ -223,6 +372,7 @@ header.masthead .sub {
.badge.documented { background: var(--badge-doc); }
.badge.collection-summary { background: var(--badge-summary); }
.badge.award { background: var(--accent); }
+.badge.representative { background: var(--accent-soft); color: #241a0f; }
/* ---- detail modal ---- */
.modal-backdrop {
@@ -240,7 +390,19 @@ header.masthead .sub {
}
.modal h2 { margin: 0 0 4px; font-size: 22px; }
.modal .modal-meta { color: var(--ink-soft); font-size: 13px; margin-bottom: 12px; }
-.modal .modal-img { width: 100%; border: 1px solid var(--line); border-radius: var(--radius); margin-bottom: 14px; }
+.modal .modal-img { width: 100%; border: 1px solid var(--line); border-radius: var(--radius); margin-bottom: 4px; }
+.modal .modal-rep-note { font-size: 11px; color: var(--accent); font-style: italic; margin-bottom: 14px; }
+.modal .modal-no-img {
+ padding: 24px 16px;
+ text-align: center;
+ font-style: italic;
+ color: var(--ink-soft);
+ border: 1px dashed var(--line);
+ border-radius: var(--radius);
+ margin-bottom: 14px;
+}
+.modal-topics { margin-bottom: 12px; }
+.modal-topic-tag { font-size: 11px; padding: 2px 9px; }
.modal dl { display: grid; grid-template-columns: 120px 1fr; gap: 6px 12px; font-size: 13.5px; margin: 0; }
.modal dt { color: var(--ink-soft); }
.modal dd { margin: 0; }
@@ -248,3 +410,12 @@ header.masthead .sub {
.modal .rights-note { font-size: 11.5px; color: var(--ink-soft); font-style: italic; margin-top: 12px; border-top: 1px solid var(--line); padding-top: 10px; }
.empty-note { padding: 40px; text-align: center; color: var(--ink-soft); font-style: italic; }
+
+/* ---- narrow phones ---- */
+@media (max-width: 480px) {
+ .grid { grid-template-columns: repeat(auto-fill, minmax(min(var(--card-min, 220px), 100%), 1fr)); }
+ .controls { padding: 10px 14px; }
+ .controls .result-count { margin-left: 0; width: 100%; order: 4; }
+ .grid-wrap { padding: 14px; }
+ .modal { padding: 18px 16px; max-height: 92vh; }
+}
← 4f7f8a0 add shareable Artifact build (metadata + link-out only, no c
·
back to Paul Conrad Cartoons
·
chore: lint, v0.2.0 (session close) 666e26b →