[object Object]

← back to CelebritySignatures

add 27 themed signature murals (Wikidata P109 sigs + Met PD art) → 30 murals on /murals; catalog-roster drawer

5af58db1fdbe583e8b1895c3931c663318c32869 · 2026-06-19 10:45:22 -0700 · SteveStudio2

Files touched

Diff

commit 5af58db1fdbe583e8b1895c3931c663318c32869
Author: SteveStudio2 <stevestudio2@SteveStacStudio.lan>
Date:   Fri Jun 19 10:45:22 2026 -0700

    add 27 themed signature murals (Wikidata P109 sigs + Met PD art) → 30 murals on /murals; catalog-roster drawer
---
 .gitignore                                    |   1 +
 data/mural-themes.json                        |  29 ++
 data/murals-catalog.json                      | 519 ++++++++++++++++++++++++++
 output/collage-abolitionists-named.png        | Bin 0 -> 144974 bytes
 output/collage-abolitionists.png              | Bin 0 -> 134138 bytes
 output/collage-american-authors-named.png     | Bin 0 -> 208004 bytes
 output/collage-american-authors.png           | Bin 0 -> 189068 bytes
 output/collage-american-presidents-named.png  | Bin 0 -> 140171 bytes
 output/collage-american-presidents.png        | Bin 0 -> 126628 bytes
 output/collage-astronomers-named.png          | Bin 0 -> 134716 bytes
 output/collage-astronomers.png                | Bin 0 -> 125376 bytes
 output/collage-aviation-pioneers-named.png    | Bin 0 -> 153200 bytes
 output/collage-aviation-pioneers.png          | Bin 0 -> 141931 bytes
 output/collage-composers-named.png            | Bin 0 -> 215661 bytes
 output/collage-composers.png                  | Bin 0 -> 195107 bytes
 output/collage-economists-named.png           | Bin 0 -> 123587 bytes
 output/collage-economists.png                 | Bin 0 -> 114283 bytes
 output/collage-european-statesmen-named.png   | Bin 0 -> 199801 bytes
 output/collage-european-statesmen.png         | Bin 0 -> 183159 bytes
 output/collage-explorers-navigators-named.png | Bin 0 -> 212707 bytes
 output/collage-explorers-navigators.png       | Bin 0 -> 199160 bytes
 output/collage-founding-fathers-named.png     | Bin 0 -> 170855 bytes
 output/collage-founding-fathers.png           | Bin 0 -> 159261 bytes
 output/collage-generals-commanders-named.png  | Bin 0 -> 183078 bytes
 output/collage-generals-commanders.png        | Bin 0 -> 166478 bytes
 output/collage-industrialists-named.png       | Bin 0 -> 130926 bytes
 output/collage-industrialists.png             | Bin 0 -> 120740 bytes
 output/collage-mathematicians-named.png       | Bin 0 -> 143455 bytes
 output/collage-mathematicians.png             | Bin 0 -> 132466 bytes
 output/collage-medicine-named.png             | Bin 0 -> 168137 bytes
 output/collage-medicine.png                   | Bin 0 -> 155530 bytes
 output/collage-monarchs-named.png             | Bin 0 -> 200847 bytes
 output/collage-monarchs.png                   | Bin 0 -> 186803 bytes
 output/collage-naturalists-named.png          | Bin 0 -> 153188 bytes
 output/collage-naturalists.png                | Bin 0 -> 143412 bytes
 output/collage-nobel-laureates-named.png      | Bin 0 -> 159137 bytes
 output/collage-nobel-laureates.png            | Bin 0 -> 144155 bytes
 output/collage-painters-artists-named.png     | Bin 0 -> 153044 bytes
 output/collage-painters-artists.png           | Bin 0 -> 138809 bytes
 output/collage-philosophers-named.png         | Bin 0 -> 185271 bytes
 output/collage-philosophers.png               | Bin 0 -> 170013 bytes
 output/collage-physicists-named.png           | Bin 0 -> 169321 bytes
 output/collage-physicists.png                 | Bin 0 -> 154068 bytes
 output/collage-playwrights-named.png          | Bin 0 -> 158163 bytes
 output/collage-playwrights.png                | Bin 0 -> 142907 bytes
 output/collage-poets-named.png                | Bin 0 -> 202655 bytes
 output/collage-poets.png                      | Bin 0 -> 187706 bytes
 output/collage-renaissance-masters-named.png  | Bin 0 -> 133108 bytes
 output/collage-renaissance-masters.png        | Bin 0 -> 123698 bytes
 output/collage-revolutionaries-named.png      | Bin 0 -> 160302 bytes
 output/collage-revolutionaries.png            | Bin 0 -> 147820 bytes
 output/collage-scientists-inventors-named.png | Bin 0 -> 199216 bytes
 output/collage-scientists-inventors.png       | Bin 0 -> 182198 bytes
 output/collage-suffragists-named.png          | Bin 0 -> 126243 bytes
 output/collage-suffragists.png                | Bin 0 -> 117447 bytes
 output/collage-writers-novelists-named.png    | Bin 0 -> 213034 bytes
 output/collage-writers-novelists.png          | Bin 0 -> 194794 bytes
 public/murals.html                            |   9 +-
 scripts/build-theme-murals.py                 | 205 ++++++++++
 59 files changed, 762 insertions(+), 1 deletion(-)

diff --git a/.gitignore b/.gitignore
index 9681475..e966f20 100644
--- a/.gitignore
+++ b/.gitignore
@@ -19,3 +19,4 @@ data/sessions.json
 
 .deploy.conf
 tmp_oldest_cache/
+tmp_theme_cache/
diff --git a/data/mural-themes.json b/data/mural-themes.json
new file mode 100644
index 0000000..82338d2
--- /dev/null
+++ b/data/mural-themes.json
@@ -0,0 +1,29 @@
+[
+  {"code":"si","slug":"scientists-inventors","title":"Scientists & Inventors","blurb":"The minds who remade the modern world, in their own hand.","met_query":"scientific instrument astrolabe","roster":["Thomas Edison","Nikola Tesla","Albert Einstein","Isaac Newton","Charles Darwin","Louis Pasteur","Marie Curie","Michael Faraday","Benjamin Franklin","Alessandro Volta","Alexander Graham Bell","Samuel Morse","Galileo Galilei","George Washington Carver"]},
+  {"code":"co","slug":"composers","title":"Great Composers","blurb":"The signatures behind the symphonies — Bach to Mahler.","met_query":"musical instrument violin","roster":["Wolfgang Amadeus Mozart","Ludwig van Beethoven","Johann Sebastian Bach","Frederic Chopin","Richard Wagner","Giuseppe Verdi","Johannes Brahms","Franz Liszt","Pyotr Ilyich Tchaikovsky","George Frideric Handel","Joseph Haydn","Franz Schubert","Gustav Mahler","Claude Debussy"]},
+  {"code":"wr","slug":"writers-novelists","title":"Writers & Novelists","blurb":"The hands that wrote the canon — Austen to Tolstoy.","met_query":"illuminated manuscript book","roster":["Charles Dickens","Mark Twain","Leo Tolstoy","Fyodor Dostoevsky","Victor Hugo","Alexandre Dumas","Jane Austen","Edgar Allan Poe","Nathaniel Hawthorne","Herman Melville","Oscar Wilde","Arthur Conan Doyle","Robert Louis Stevenson","Jules Verne"]},
+  {"code":"po","slug":"poets","title":"The Poets","blurb":"Romantics and visionaries — every verse signed.","met_query":"Romantic landscape painting","roster":["William Wordsworth","Lord Byron","John Keats","Percy Bysshe Shelley","Walt Whitman","Alfred Tennyson","Henry Wadsworth Longfellow","Samuel Taylor Coleridge","Johann Wolfgang von Goethe","Robert Burns","William Blake","Elizabeth Barrett Browning","William Butler Yeats"]},
+  {"code":"ph","slug":"philosophers","title":"Philosophers","blurb":"The architects of thought — Descartes to Nietzsche.","met_query":"ancient Greek bust marble","roster":["Immanuel Kant","Voltaire","Jean-Jacques Rousseau","Friedrich Nietzsche","David Hume","John Locke","Rene Descartes","Arthur Schopenhauer","John Stuart Mill","Francis Bacon","Baruch Spinoza","Georg Wilhelm Friedrich Hegel"]},
+  {"code":"pa","slug":"painters-artists","title":"Painters & Masters","blurb":"The signatures of the easel — Rembrandt to Van Gogh.","met_query":"old master oil painting","roster":["Rembrandt van Rijn","Peter Paul Rubens","Vincent van Gogh","Claude Monet","Pierre-Auguste Renoir","Paul Cezanne","Edgar Degas","Francisco Goya","Eugene Delacroix","Edouard Manet","Paul Gauguin","J. M. W. Turner","John Singer Sargent"]},
+  {"code":"ex","slug":"explorers-navigators","title":"Explorers & Navigators","blurb":"The hands that charted the world.","met_query":"antique navigation globe map","roster":["Christopher Columbus","Ferdinand Magellan","James Cook","Francis Drake","Amerigo Vespucci","Samuel de Champlain","Hernan Cortes","Meriwether Lewis","William Clark","Henry Hudson","Vasco da Gama"]},
+  {"code":"ge","slug":"generals-commanders","title":"Generals & Commanders","blurb":"The signatures that moved armies.","met_query":"arms and armor sword","roster":["Napoleon Bonaparte","Duke of Wellington","George Washington","Ulysses S. Grant","Robert E. Lee","Horatio Nelson","Giuseppe Garibaldi","Simon Bolivar","Marquis de Lafayette","William Tecumseh Sherman","John J. Pershing"]},
+  {"code":"mo","slug":"monarchs","title":"Monarchs & Royalty","blurb":"Kings and queens — sovereign hands across the centuries.","met_query":"royal crown jewelry portrait","roster":["Henry VIII of England","Elizabeth I of England","Louis XIV of France","Queen Victoria","Catherine the Great","Frederick the Great","Charles I of England","Marie Antoinette","Philip II of Spain","Napoleon Bonaparte"]},
+  {"code":"py","slug":"physicists","title":"Physicists","blurb":"From classical mechanics to the quantum age.","met_query":"scientific instrument brass","roster":["Albert Einstein","Isaac Newton","Niels Bohr","Max Planck","James Clerk Maxwell","Michael Faraday","Marie Curie","Ernest Rutherford","Erwin Schrodinger","Enrico Fermi","Nikola Tesla"]},
+  {"code":"ma","slug":"mathematicians","title":"Mathematicians","blurb":"The signatures behind the theorems.","met_query":"astrolabe geometry instrument","roster":["Carl Friedrich Gauss","Leonhard Euler","Isaac Newton","Gottfried Wilhelm Leibniz","Blaise Pascal","Pierre-Simon Laplace","Bernhard Riemann","Henri Poincare","David Hilbert","Georg Cantor"]},
+  {"code":"na","slug":"naturalists","title":"Naturalists","blurb":"The observers of the living world.","met_query":"Audubon bird natural history print","roster":["Charles Darwin","John James Audubon","Alexander von Humboldt","Alfred Russel Wallace","Carl Linnaeus","Gregor Mendel","John Muir","Louis Agassiz","Luther Burbank"]},
+  {"code":"as","slug":"astronomers","title":"Astronomers","blurb":"The hands that mapped the heavens.","met_query":"celestial globe astronomy","roster":["Galileo Galilei","Johannes Kepler","Nicolaus Copernicus","Isaac Newton","William Herschel","Edmond Halley","Tycho Brahe","Edwin Hubble","Camille Flammarion"]},
+  {"code":"av","slug":"aviation-pioneers","title":"Aviation Pioneers","blurb":"The first to sign the sky.","met_query":"early aviation flight","roster":["Wilbur Wright","Orville Wright","Charles Lindbergh","Amelia Earhart","Alberto Santos-Dumont","Louis Bleriot","Ferdinand von Zeppelin","Eddie Rickenbacker","Jimmy Doolittle"]},
+  {"code":"ab","slug":"abolitionists","title":"Abolition & Liberty","blurb":"The signatures that helped end slavery.","met_query":"19th century American history","roster":["Frederick Douglass","Abraham Lincoln","Harriet Beecher Stowe","William Lloyd Garrison","John Brown","Sojourner Truth","William Wilberforce","Charles Sumner","Wendell Phillips"]},
+  {"code":"su","slug":"suffragists","title":"Suffragists & Pioneers","blurb":"Women who signed history forward.","met_query":"19th century portrait woman","roster":["Susan B. Anthony","Elizabeth Cady Stanton","Emmeline Pankhurst","Florence Nightingale","Clara Barton","Marie Curie","Helen Keller","Lucretia Mott","Ida B. Wells"]},
+  {"code":"re","slug":"renaissance-masters","title":"Renaissance Masters","blurb":"The hands of the rebirth — Leonardo to Machiavelli.","met_query":"Italian Renaissance drawing","roster":["Leonardo da Vinci","Michelangelo Buonarroti","Galileo Galilei","Albrecht Durer","Niccolo Machiavelli","Desiderius Erasmus","Nicolaus Copernicus","Titian","Raphael"]},
+  {"code":"pl","slug":"playwrights","title":"Playwrights","blurb":"The dramatists, signed and staged.","met_query":"theater mask drama","roster":["William Shakespeare","Moliere","Johann Wolfgang von Goethe","Friedrich Schiller","Henrik Ibsen","Anton Chekhov","Oscar Wilde","George Bernard Shaw","Christopher Marlowe"]},
+  {"code":"ec","slug":"economists","title":"Economists & Thinkers","blurb":"The signatures behind the wealth of nations.","met_query":"18th century engraving portrait","roster":["Adam Smith","Karl Marx","David Ricardo","John Maynard Keynes","Thomas Robert Malthus","John Stuart Mill","Jeremy Bentham","Friedrich Engels"]},
+  {"code":"in","slug":"industrialists","title":"Industrialists & Tycoons","blurb":"The hands that built the Gilded Age.","met_query":"Gilded Age decorative arts","roster":["Andrew Carnegie","John D. Rockefeller","Cornelius Vanderbilt","J. P. Morgan","Henry Ford","Thomas Edison","George Westinghouse","John Jacob Astor"]},
+  {"code":"aa","slug":"american-authors","title":"American Authors","blurb":"The signatures that built American letters — Irving to London.","met_query":"American 19th century painting","roster":["Mark Twain","Edgar Allan Poe","Nathaniel Hawthorne","Herman Melville","Walt Whitman","Ralph Waldo Emerson","Henry David Thoreau","Washington Irving","James Fenimore Cooper","Harriet Beecher Stowe","Jack London","Louisa May Alcott","Henry Wadsworth Longfellow"]},
+  {"code":"ff","slug":"founding-fathers","title":"Founding Fathers","blurb":"The architects of the American republic.","met_query":"American Federal period portrait","roster":["George Washington","Thomas Jefferson","Benjamin Franklin","John Adams","James Madison","Alexander Hamilton","John Jay","James Monroe","Patrick Henry","Thomas Paine"]},
+  {"code":"pr","slug":"american-presidents","title":"American Presidents","blurb":"Executive signatures across two centuries.","met_query":"American presidential portrait","roster":["George Washington","Thomas Jefferson","Abraham Lincoln","Theodore Roosevelt","Andrew Jackson","Ulysses S. Grant","Woodrow Wilson","Franklin D. Roosevelt","Grover Cleveland","William McKinley"]},
+  {"code":"rv","slug":"revolutionaries","title":"Revolutionaries","blurb":"The hands that overturned the old order.","met_query":"French Revolution print","roster":["Maximilien Robespierre","Georges Danton","Jean-Paul Marat","Simon Bolivar","Jose de San Martin","Giuseppe Garibaldi","Vladimir Lenin","Sun Yat-sen","Lajos Kossuth"]},
+  {"code":"nl","slug":"nobel-laureates","title":"Nobel Laureates","blurb":"Early signatures of the prize.","met_query":"medal portrait engraving","roster":["Marie Curie","Albert Einstein","Theodore Roosevelt","Rudyard Kipling","Ernest Rutherford","Max Planck","Rabindranath Tagore","George Bernard Shaw","Guglielmo Marconi","Ivan Pavlov"]},
+  {"code":"md","slug":"medicine","title":"Medicine & Healers","blurb":"The signatures that conquered disease.","met_query":"medical instrument apothecary","roster":["Louis Pasteur","Joseph Lister","Robert Koch","Edward Jenner","Alexander Fleming","Florence Nightingale","William Harvey","William Osler","Sigmund Freud"]},
+  {"code":"es","slug":"european-statesmen","title":"European Statesmen","blurb":"The hands that steered nations — Talleyrand to Churchill.","met_query":"19th century European portrait","roster":["Otto von Bismarck","William Ewart Gladstone","Benjamin Disraeli","Klemens von Metternich","Winston Churchill","Georges Clemenceau","Camillo Benso di Cavour","Charles Maurice de Talleyrand","Robert Peel","Giuseppe Mazzini","Lord Palmerston","Charles de Gaulle"]}
+]
diff --git a/data/murals-catalog.json b/data/murals-catalog.json
index bad0cfb..9e57fb2 100644
--- a/data/murals-catalog.json
+++ b/data/murals-catalog.json
@@ -30,6 +30,525 @@
       "title": "Politics & Statecraft",
       "blurb": "Presidents, premiers and world leaders across two centuries.",
       "sigs": 53
+    },
+    {
+      "code": "si",
+      "slug": "scientists-inventors",
+      "title": "Scientists & Inventors",
+      "blurb": "The minds who remade the modern world, in their own hand.",
+      "sigs": 13,
+      "roster": [
+        "Thomas Edison",
+        "Nikola Tesla",
+        "Albert Einstein",
+        "Isaac Newton",
+        "Charles Darwin",
+        "Louis Pasteur",
+        "Marie Curie",
+        "Michael Faraday",
+        "Benjamin Franklin",
+        "Alexander Graham Bell",
+        "Samuel Morse",
+        "Galileo Galilei",
+        "George Washington Carver"
+      ],
+      "met_accent": "Barometer"
+    },
+    {
+      "code": "co",
+      "slug": "composers",
+      "title": "Great Composers",
+      "blurb": "The signatures behind the symphonies — Bach to Mahler.",
+      "sigs": 14,
+      "roster": [
+        "Wolfgang Amadeus Mozart",
+        "Ludwig van Beethoven",
+        "Johann Sebastian Bach",
+        "Frederic Chopin",
+        "Richard Wagner",
+        "Giuseppe Verdi",
+        "Johannes Brahms",
+        "Franz Liszt",
+        "Pyotr Ilyich Tchaikovsky",
+        "George Frideric Handel",
+        "Joseph Haydn",
+        "Franz Schubert",
+        "Gustav Mahler",
+        "Claude Debussy"
+      ],
+      "met_accent": "Drum"
+    },
+    {
+      "code": "wr",
+      "slug": "writers-novelists",
+      "title": "Writers & Novelists",
+      "blurb": "The hands that wrote the canon — Austen to Tolstoy.",
+      "sigs": 14,
+      "roster": [
+        "Charles Dickens",
+        "Mark Twain",
+        "Leo Tolstoy",
+        "Fyodor Dostoevsky",
+        "Victor Hugo",
+        "Alexandre Dumas",
+        "Jane Austen",
+        "Edgar Allan Poe",
+        "Nathaniel Hawthorne",
+        "Herman Melville",
+        "Oscar Wilde",
+        "Arthur Conan Doyle",
+        "Robert Louis Stevenson",
+        "Jules Verne"
+      ],
+      "met_accent": "Jaharis Byzantine Lectionary"
+    },
+    {
+      "code": "po",
+      "slug": "poets",
+      "title": "The Poets",
+      "blurb": "Romantics and visionaries — every verse signed.",
+      "sigs": 10,
+      "roster": [
+        "Lord Byron",
+        "John Keats",
+        "Percy Bysshe Shelley",
+        "Walt Whitman",
+        "Henry Wadsworth Longfellow",
+        "Samuel Taylor Coleridge",
+        "Johann Wolfgang von Goethe",
+        "Robert Burns",
+        "William Blake",
+        "William Butler Yeats"
+      ],
+      "met_accent": "Evening: Landscape with an Aqueduct"
+    },
+    {
+      "code": "ph",
+      "slug": "philosophers",
+      "title": "Philosophers",
+      "blurb": "The architects of thought — Descartes to Nietzsche.",
+      "sigs": 10,
+      "roster": [
+        "Immanuel Kant",
+        "Voltaire",
+        "Jean-Jacques Rousseau",
+        "Friedrich Nietzsche",
+        "John Locke",
+        "Rene Descartes",
+        "Arthur Schopenhauer",
+        "John Stuart Mill",
+        "Baruch Spinoza",
+        "Georg Wilhelm Friedrich Hegel"
+      ],
+      "met_accent": "Aristotle with a Bust of Homer"
+    },
+    {
+      "code": "pa",
+      "slug": "painters-artists",
+      "title": "Painters & Masters",
+      "blurb": "The signatures of the easel — Rembrandt to Van Gogh.",
+      "sigs": 10,
+      "roster": [
+        "Claude Monet",
+        "Pierre-Auguste Renoir",
+        "Paul Cezanne",
+        "Edgar Degas",
+        "Francisco Goya",
+        "Eugene Delacroix",
+        "Edouard Manet",
+        "Paul Gauguin",
+        "J. M. W. Turner",
+        "John Singer Sargent"
+      ],
+      "met_accent": "Rothschild lamp"
+    },
+    {
+      "code": "ex",
+      "slug": "explorers-navigators",
+      "title": "Explorers & Navigators",
+      "blurb": "The hands that charted the world.",
+      "sigs": 9,
+      "roster": [
+        "Christopher Columbus",
+        "Ferdinand Magellan",
+        "James Cook",
+        "Francis Drake",
+        "Samuel de Champlain",
+        "Hernan Cortes",
+        "Meriwether Lewis",
+        "William Clark",
+        "Vasco da Gama"
+      ],
+      "met_accent": "Lucas van Uffel (died 1637)"
+    },
+    {
+      "code": "ge",
+      "slug": "generals-commanders",
+      "title": "Generals & Commanders",
+      "blurb": "The signatures that moved armies.",
+      "sigs": 10,
+      "roster": [
+        "Napoleon Bonaparte",
+        "Duke of Wellington",
+        "Ulysses S. Grant",
+        "Robert E. Lee",
+        "Horatio Nelson",
+        "Giuseppe Garibaldi",
+        "Simon Bolivar",
+        "Marquis de Lafayette",
+        "William Tecumseh Sherman",
+        "John J. Pershing"
+      ],
+      "met_accent": "Pair of Four-Barreled Turnover Percussion Pistols of Henry Pelham Fiennes Pelham-Clinton, 4th Duke of Newcastle-under-Lyne (1785–1851), with Pair of Box-Lock Turn-Off Pocket Pistols, Case, and Accessories"
+    },
+    {
+      "code": "mo",
+      "slug": "monarchs",
+      "title": "Monarchs & Royalty",
+      "blurb": "Kings and queens — sovereign hands across the centuries.",
+      "sigs": 9,
+      "roster": [
+        "Henry VIII of England",
+        "Elizabeth I of England",
+        "Louis XIV of France",
+        "Queen Victoria",
+        "Catherine the Great",
+        "Frederick the Great",
+        "Charles I of England",
+        "Philip II of Spain",
+        "Napoleon Bonaparte"
+      ],
+      "met_accent": "Standing Figure with Feathered Headdress"
+    },
+    {
+      "code": "py",
+      "slug": "physicists",
+      "title": "Physicists",
+      "blurb": "From classical mechanics to the quantum age.",
+      "sigs": 11,
+      "roster": [
+        "Albert Einstein",
+        "Isaac Newton",
+        "Niels Bohr",
+        "Max Planck",
+        "James Clerk Maxwell",
+        "Michael Faraday",
+        "Marie Curie",
+        "Ernest Rutherford",
+        "Erwin Schrodinger",
+        "Enrico Fermi",
+        "Nikola Tesla"
+      ],
+      "met_accent": "Altar bell with Portuguese heads"
+    },
+    {
+      "code": "ma",
+      "slug": "mathematicians",
+      "title": "Mathematicians",
+      "blurb": "The signatures behind the theorems.",
+      "sigs": 8,
+      "roster": [
+        "Carl Friedrich Gauss",
+        "Leonhard Euler",
+        "Isaac Newton",
+        "Gottfried Wilhelm Leibniz",
+        "Blaise Pascal",
+        "Pierre-Simon Laplace",
+        "Bernhard Riemann",
+        "Henri Poincare"
+      ],
+      "met_accent": "Celestial globe with clockwork"
+    },
+    {
+      "code": "na",
+      "slug": "naturalists",
+      "title": "Naturalists",
+      "blurb": "The observers of the living world.",
+      "sigs": 6,
+      "roster": [
+        "Charles Darwin",
+        "John James Audubon",
+        "Alexander von Humboldt",
+        "Alfred Russel Wallace",
+        "Carl Linnaeus",
+        "Luther Burbank"
+      ],
+      "met_accent": "Flowers in a Grotesque Vase"
+    },
+    {
+      "code": "as",
+      "slug": "astronomers",
+      "title": "Astronomers",
+      "blurb": "The hands that mapped the heavens.",
+      "sigs": 7,
+      "roster": [
+        "Galileo Galilei",
+        "Johannes Kepler",
+        "Nicolaus Copernicus",
+        "Isaac Newton",
+        "William Herschel",
+        "Tycho Brahe",
+        "Edwin Hubble"
+      ],
+      "met_accent": "Celestial globe with clockwork"
+    },
+    {
+      "code": "av",
+      "slug": "aviation-pioneers",
+      "title": "Aviation Pioneers",
+      "blurb": "The first to sign the sky.",
+      "sigs": 8,
+      "roster": [
+        "Wilbur Wright",
+        "Orville Wright",
+        "Charles Lindbergh",
+        "Amelia Earhart",
+        "Alberto Santos-Dumont",
+        "Louis Bleriot",
+        "Ferdinand von Zeppelin",
+        "Eddie Rickenbacker"
+      ],
+      "met_accent": "Portrait of a Noblewoman, Probably Isabella of Portugal (1397–1472)"
+    },
+    {
+      "code": "ab",
+      "slug": "abolitionists",
+      "title": "Abolition & Liberty",
+      "blurb": "The signatures that helped end slavery.",
+      "sigs": 7,
+      "roster": [
+        "Frederick Douglass",
+        "Abraham Lincoln",
+        "Harriet Beecher Stowe",
+        "John Brown",
+        "William Wilberforce",
+        "Charles Sumner",
+        "Wendell Phillips"
+      ],
+      "met_accent": "Madame Georges Charpentier (Marguerite-Louise Lemonnier, 1848–1904) and Her Children, Georgette-Berthe (1872–1945) and Paul-Emile-Charles (1875–1895)"
+    },
+    {
+      "code": "su",
+      "slug": "suffragists",
+      "title": "Suffragists & Pioneers",
+      "blurb": "Women who signed history forward.",
+      "sigs": 6,
+      "roster": [
+        "Susan B. Anthony",
+        "Elizabeth Cady Stanton",
+        "Florence Nightingale",
+        "Clara Barton",
+        "Marie Curie",
+        "Helen Keller"
+      ],
+      "met_accent": "Portrait of Sor Juana de Nuestra Señora de Guadalupe"
+    },
+    {
+      "code": "re",
+      "slug": "renaissance-masters",
+      "title": "Renaissance Masters",
+      "blurb": "The hands of the rebirth — Leonardo to Machiavelli.",
+      "sigs": 7,
+      "roster": [
+        "Leonardo da Vinci",
+        "Michelangelo Buonarroti",
+        "Galileo Galilei",
+        "Albrecht Durer",
+        "Niccolo Machiavelli",
+        "Nicolaus Copernicus",
+        "Titian"
+      ],
+      "met_accent": "Pilgrim flask"
+    },
+    {
+      "code": "pl",
+      "slug": "playwrights",
+      "title": "Playwrights",
+      "blurb": "The dramatists, signed and staged.",
+      "sigs": 9,
+      "roster": [
+        "William Shakespeare",
+        "Moliere",
+        "Johann Wolfgang von Goethe",
+        "Friedrich Schiller",
+        "Henrik Ibsen",
+        "Anton Chekhov",
+        "Oscar Wilde",
+        "George Bernard Shaw",
+        "Christopher Marlowe"
+      ],
+      "met_accent": "Mask"
+    },
+    {
+      "code": "ec",
+      "slug": "economists",
+      "title": "Economists & Thinkers",
+      "blurb": "The signatures behind the wealth of nations.",
+      "sigs": 6,
+      "roster": [
+        "Adam Smith",
+        "Karl Marx",
+        "John Maynard Keynes",
+        "John Stuart Mill",
+        "Jeremy Bentham",
+        "Friedrich Engels"
+      ],
+      "met_accent": "Commode"
+    },
+    {
+      "code": "in",
+      "slug": "industrialists",
+      "title": "Industrialists & Tycoons",
+      "blurb": "The hands that built the Gilded Age.",
+      "sigs": 7,
+      "roster": [
+        "Andrew Carnegie",
+        "Cornelius Vanderbilt",
+        "J. P. Morgan",
+        "Henry Ford",
+        "Thomas Edison",
+        "George Westinghouse",
+        "John Jacob Astor"
+      ],
+      "met_accent": "Enameled and Gilded Bottle"
+    },
+    {
+      "code": "aa",
+      "slug": "american-authors",
+      "title": "American Authors",
+      "blurb": "The signatures that built American letters — Irving to London.",
+      "sigs": 13,
+      "roster": [
+        "Mark Twain",
+        "Edgar Allan Poe",
+        "Nathaniel Hawthorne",
+        "Herman Melville",
+        "Walt Whitman",
+        "Ralph Waldo Emerson",
+        "Henry David Thoreau",
+        "Washington Irving",
+        "James Fenimore Cooper",
+        "Harriet Beecher Stowe",
+        "Jack London",
+        "Louisa May Alcott",
+        "Henry Wadsworth Longfellow"
+      ],
+      "met_accent": "Portrait of Sor Juana de Nuestra Señora de Guadalupe"
+    },
+    {
+      "code": "ff",
+      "slug": "founding-fathers",
+      "title": "Founding Fathers",
+      "blurb": "The architects of the American republic.",
+      "sigs": 9,
+      "roster": [
+        "Thomas Jefferson",
+        "Benjamin Franklin",
+        "John Adams",
+        "James Madison",
+        "Alexander Hamilton",
+        "John Jay",
+        "James Monroe",
+        "Patrick Henry",
+        "Thomas Paine"
+      ],
+      "met_accent": "Desk and Bookcase"
+    },
+    {
+      "code": "pr",
+      "slug": "american-presidents",
+      "title": "American Presidents",
+      "blurb": "Executive signatures across two centuries.",
+      "sigs": 9,
+      "roster": [
+        "Thomas Jefferson",
+        "Abraham Lincoln",
+        "Theodore Roosevelt",
+        "Andrew Jackson",
+        "Ulysses S. Grant",
+        "Woodrow Wilson",
+        "Franklin D. Roosevelt",
+        "Grover Cleveland",
+        "William McKinley"
+      ],
+      "met_accent": "Life Mask of Abraham Lincoln"
+    },
+    {
+      "code": "rv",
+      "slug": "revolutionaries",
+      "title": "Revolutionaries",
+      "blurb": "The hands that overturned the old order.",
+      "sigs": 9,
+      "roster": [
+        "Maximilien Robespierre",
+        "Georges Danton",
+        "Jean-Paul Marat",
+        "Simon Bolivar",
+        "Jose de San Martin",
+        "Giuseppe Garibaldi",
+        "Vladimir Lenin",
+        "Sun Yat-sen",
+        "Lajos Kossuth"
+      ],
+      "met_accent": "Sabine Houdon (1787–1836)"
+    },
+    {
+      "code": "nl",
+      "slug": "nobel-laureates",
+      "title": "Nobel Laureates",
+      "blurb": "Early signatures of the prize.",
+      "sigs": 9,
+      "roster": [
+        "Marie Curie",
+        "Albert Einstein",
+        "Theodore Roosevelt",
+        "Rudyard Kipling",
+        "Ernest Rutherford",
+        "Max Planck",
+        "Rabindranath Tagore",
+        "George Bernard Shaw",
+        "Guglielmo Marconi"
+      ],
+      "met_accent": "Henry IV, King of France (b. 1553, r. 1589–1610)"
+    },
+    {
+      "code": "md",
+      "slug": "medicine",
+      "title": "Medicine & Healers",
+      "blurb": "The signatures that conquered disease.",
+      "sigs": 9,
+      "roster": [
+        "Louis Pasteur",
+        "Joseph Lister",
+        "Robert Koch",
+        "Edward Jenner",
+        "Alexander Fleming",
+        "Florence Nightingale",
+        "William Harvey",
+        "William Osler",
+        "Sigmund Freud"
+      ],
+      "met_accent": "Bronze instrument"
+    },
+    {
+      "code": "es",
+      "slug": "european-statesmen",
+      "title": "European Statesmen",
+      "blurb": "The hands that steered nations — Talleyrand to Churchill.",
+      "sigs": 10,
+      "roster": [
+        "Otto von Bismarck",
+        "William Ewart Gladstone",
+        "Benjamin Disraeli",
+        "Klemens von Metternich",
+        "Georges Clemenceau",
+        "Camillo Benso di Cavour",
+        "Charles Maurice de Talleyrand",
+        "Robert Peel",
+        "Giuseppe Mazzini",
+        "Charles de Gaulle"
+      ],
+      "met_accent": "Madame Georges Charpentier (Marguerite-Louise Lemonnier, 1848–1904) and Her Children, Georgette-Berthe (1872–1945) and Paul-Emile-Charles (1875–1895)"
     }
   ]
 }
\ No newline at end of file
diff --git a/output/collage-abolitionists-named.png b/output/collage-abolitionists-named.png
new file mode 100644
index 0000000..0cf87dc
Binary files /dev/null and b/output/collage-abolitionists-named.png differ
diff --git a/output/collage-abolitionists.png b/output/collage-abolitionists.png
new file mode 100644
index 0000000..b3365f0
Binary files /dev/null and b/output/collage-abolitionists.png differ
diff --git a/output/collage-american-authors-named.png b/output/collage-american-authors-named.png
new file mode 100644
index 0000000..68211a1
Binary files /dev/null and b/output/collage-american-authors-named.png differ
diff --git a/output/collage-american-authors.png b/output/collage-american-authors.png
new file mode 100644
index 0000000..bbc48ab
Binary files /dev/null and b/output/collage-american-authors.png differ
diff --git a/output/collage-american-presidents-named.png b/output/collage-american-presidents-named.png
new file mode 100644
index 0000000..4111dc1
Binary files /dev/null and b/output/collage-american-presidents-named.png differ
diff --git a/output/collage-american-presidents.png b/output/collage-american-presidents.png
new file mode 100644
index 0000000..467e04b
Binary files /dev/null and b/output/collage-american-presidents.png differ
diff --git a/output/collage-astronomers-named.png b/output/collage-astronomers-named.png
new file mode 100644
index 0000000..a6e7846
Binary files /dev/null and b/output/collage-astronomers-named.png differ
diff --git a/output/collage-astronomers.png b/output/collage-astronomers.png
new file mode 100644
index 0000000..3e5a203
Binary files /dev/null and b/output/collage-astronomers.png differ
diff --git a/output/collage-aviation-pioneers-named.png b/output/collage-aviation-pioneers-named.png
new file mode 100644
index 0000000..680cd54
Binary files /dev/null and b/output/collage-aviation-pioneers-named.png differ
diff --git a/output/collage-aviation-pioneers.png b/output/collage-aviation-pioneers.png
new file mode 100644
index 0000000..a24ea2e
Binary files /dev/null and b/output/collage-aviation-pioneers.png differ
diff --git a/output/collage-composers-named.png b/output/collage-composers-named.png
new file mode 100644
index 0000000..04f08ed
Binary files /dev/null and b/output/collage-composers-named.png differ
diff --git a/output/collage-composers.png b/output/collage-composers.png
new file mode 100644
index 0000000..b83b91e
Binary files /dev/null and b/output/collage-composers.png differ
diff --git a/output/collage-economists-named.png b/output/collage-economists-named.png
new file mode 100644
index 0000000..3ec156d
Binary files /dev/null and b/output/collage-economists-named.png differ
diff --git a/output/collage-economists.png b/output/collage-economists.png
new file mode 100644
index 0000000..56d5c1a
Binary files /dev/null and b/output/collage-economists.png differ
diff --git a/output/collage-european-statesmen-named.png b/output/collage-european-statesmen-named.png
new file mode 100644
index 0000000..7715992
Binary files /dev/null and b/output/collage-european-statesmen-named.png differ
diff --git a/output/collage-european-statesmen.png b/output/collage-european-statesmen.png
new file mode 100644
index 0000000..abc2ee6
Binary files /dev/null and b/output/collage-european-statesmen.png differ
diff --git a/output/collage-explorers-navigators-named.png b/output/collage-explorers-navigators-named.png
new file mode 100644
index 0000000..730403a
Binary files /dev/null and b/output/collage-explorers-navigators-named.png differ
diff --git a/output/collage-explorers-navigators.png b/output/collage-explorers-navigators.png
new file mode 100644
index 0000000..8a890d2
Binary files /dev/null and b/output/collage-explorers-navigators.png differ
diff --git a/output/collage-founding-fathers-named.png b/output/collage-founding-fathers-named.png
new file mode 100644
index 0000000..7d9c08d
Binary files /dev/null and b/output/collage-founding-fathers-named.png differ
diff --git a/output/collage-founding-fathers.png b/output/collage-founding-fathers.png
new file mode 100644
index 0000000..8870cd0
Binary files /dev/null and b/output/collage-founding-fathers.png differ
diff --git a/output/collage-generals-commanders-named.png b/output/collage-generals-commanders-named.png
new file mode 100644
index 0000000..e643a71
Binary files /dev/null and b/output/collage-generals-commanders-named.png differ
diff --git a/output/collage-generals-commanders.png b/output/collage-generals-commanders.png
new file mode 100644
index 0000000..87932c5
Binary files /dev/null and b/output/collage-generals-commanders.png differ
diff --git a/output/collage-industrialists-named.png b/output/collage-industrialists-named.png
new file mode 100644
index 0000000..eae12f8
Binary files /dev/null and b/output/collage-industrialists-named.png differ
diff --git a/output/collage-industrialists.png b/output/collage-industrialists.png
new file mode 100644
index 0000000..899b4d9
Binary files /dev/null and b/output/collage-industrialists.png differ
diff --git a/output/collage-mathematicians-named.png b/output/collage-mathematicians-named.png
new file mode 100644
index 0000000..e8d9bd8
Binary files /dev/null and b/output/collage-mathematicians-named.png differ
diff --git a/output/collage-mathematicians.png b/output/collage-mathematicians.png
new file mode 100644
index 0000000..fd9cea3
Binary files /dev/null and b/output/collage-mathematicians.png differ
diff --git a/output/collage-medicine-named.png b/output/collage-medicine-named.png
new file mode 100644
index 0000000..1e9280b
Binary files /dev/null and b/output/collage-medicine-named.png differ
diff --git a/output/collage-medicine.png b/output/collage-medicine.png
new file mode 100644
index 0000000..79824b6
Binary files /dev/null and b/output/collage-medicine.png differ
diff --git a/output/collage-monarchs-named.png b/output/collage-monarchs-named.png
new file mode 100644
index 0000000..04f8a03
Binary files /dev/null and b/output/collage-monarchs-named.png differ
diff --git a/output/collage-monarchs.png b/output/collage-monarchs.png
new file mode 100644
index 0000000..2be5dea
Binary files /dev/null and b/output/collage-monarchs.png differ
diff --git a/output/collage-naturalists-named.png b/output/collage-naturalists-named.png
new file mode 100644
index 0000000..759d0ba
Binary files /dev/null and b/output/collage-naturalists-named.png differ
diff --git a/output/collage-naturalists.png b/output/collage-naturalists.png
new file mode 100644
index 0000000..3302d24
Binary files /dev/null and b/output/collage-naturalists.png differ
diff --git a/output/collage-nobel-laureates-named.png b/output/collage-nobel-laureates-named.png
new file mode 100644
index 0000000..1e90301
Binary files /dev/null and b/output/collage-nobel-laureates-named.png differ
diff --git a/output/collage-nobel-laureates.png b/output/collage-nobel-laureates.png
new file mode 100644
index 0000000..0771cea
Binary files /dev/null and b/output/collage-nobel-laureates.png differ
diff --git a/output/collage-painters-artists-named.png b/output/collage-painters-artists-named.png
new file mode 100644
index 0000000..8ae1188
Binary files /dev/null and b/output/collage-painters-artists-named.png differ
diff --git a/output/collage-painters-artists.png b/output/collage-painters-artists.png
new file mode 100644
index 0000000..d51c9a4
Binary files /dev/null and b/output/collage-painters-artists.png differ
diff --git a/output/collage-philosophers-named.png b/output/collage-philosophers-named.png
new file mode 100644
index 0000000..1477998
Binary files /dev/null and b/output/collage-philosophers-named.png differ
diff --git a/output/collage-philosophers.png b/output/collage-philosophers.png
new file mode 100644
index 0000000..9355405
Binary files /dev/null and b/output/collage-philosophers.png differ
diff --git a/output/collage-physicists-named.png b/output/collage-physicists-named.png
new file mode 100644
index 0000000..e1c35fb
Binary files /dev/null and b/output/collage-physicists-named.png differ
diff --git a/output/collage-physicists.png b/output/collage-physicists.png
new file mode 100644
index 0000000..2140d00
Binary files /dev/null and b/output/collage-physicists.png differ
diff --git a/output/collage-playwrights-named.png b/output/collage-playwrights-named.png
new file mode 100644
index 0000000..0be4f46
Binary files /dev/null and b/output/collage-playwrights-named.png differ
diff --git a/output/collage-playwrights.png b/output/collage-playwrights.png
new file mode 100644
index 0000000..19c3ae0
Binary files /dev/null and b/output/collage-playwrights.png differ
diff --git a/output/collage-poets-named.png b/output/collage-poets-named.png
new file mode 100644
index 0000000..8218539
Binary files /dev/null and b/output/collage-poets-named.png differ
diff --git a/output/collage-poets.png b/output/collage-poets.png
new file mode 100644
index 0000000..a8a9e0a
Binary files /dev/null and b/output/collage-poets.png differ
diff --git a/output/collage-renaissance-masters-named.png b/output/collage-renaissance-masters-named.png
new file mode 100644
index 0000000..06005fa
Binary files /dev/null and b/output/collage-renaissance-masters-named.png differ
diff --git a/output/collage-renaissance-masters.png b/output/collage-renaissance-masters.png
new file mode 100644
index 0000000..f080caf
Binary files /dev/null and b/output/collage-renaissance-masters.png differ
diff --git a/output/collage-revolutionaries-named.png b/output/collage-revolutionaries-named.png
new file mode 100644
index 0000000..2fbd6f5
Binary files /dev/null and b/output/collage-revolutionaries-named.png differ
diff --git a/output/collage-revolutionaries.png b/output/collage-revolutionaries.png
new file mode 100644
index 0000000..3a219ad
Binary files /dev/null and b/output/collage-revolutionaries.png differ
diff --git a/output/collage-scientists-inventors-named.png b/output/collage-scientists-inventors-named.png
new file mode 100644
index 0000000..7603967
Binary files /dev/null and b/output/collage-scientists-inventors-named.png differ
diff --git a/output/collage-scientists-inventors.png b/output/collage-scientists-inventors.png
new file mode 100644
index 0000000..fa0ce8d
Binary files /dev/null and b/output/collage-scientists-inventors.png differ
diff --git a/output/collage-suffragists-named.png b/output/collage-suffragists-named.png
new file mode 100644
index 0000000..1959f08
Binary files /dev/null and b/output/collage-suffragists-named.png differ
diff --git a/output/collage-suffragists.png b/output/collage-suffragists.png
new file mode 100644
index 0000000..15a38ce
Binary files /dev/null and b/output/collage-suffragists.png differ
diff --git a/output/collage-writers-novelists-named.png b/output/collage-writers-novelists-named.png
new file mode 100644
index 0000000..c9b2698
Binary files /dev/null and b/output/collage-writers-novelists-named.png differ
diff --git a/output/collage-writers-novelists.png b/output/collage-writers-novelists.png
new file mode 100644
index 0000000..0a0af83
Binary files /dev/null and b/output/collage-writers-novelists.png differ
diff --git a/public/murals.html b/public/murals.html
index 0c9e0c4..b5cf69c 100644
--- a/public/murals.html
+++ b/public/murals.html
@@ -292,7 +292,14 @@ async function boot(){
     SIGS = all.filter(r => r.usable_in_commercial_collage === 'yes');
     for(const r of SIGS) sigByKey[r.category + '|' + r.rank] = r;
     for(const m of CAT.murals){
-      byMural[m.code] = SIGS.filter(r => r.category === CATEGORY_OF[m.slug]).sort((a,b)=>a.rank-b.rank);
+      if(CATEGORY_OF[m.slug]){
+        byMural[m.code] = SIGS.filter(r => r.category === CATEGORY_OF[m.slug]).sort((a,b)=>a.rank-b.rank);
+      } else if(Array.isArray(m.roster)){
+        // themed murals carry their resolved roster in the catalog entry
+        byMural[m.code] = m.roster.map((nm,i)=>({full_name:nm, category:m.slug, rank:i+1, signature_image_url:''}));
+      } else {
+        byMural[m.code] = [];
+      }
     }
   }catch(e){ SIGS = []; }
 
diff --git a/scripts/build-theme-murals.py b/scripts/build-theme-murals.py
new file mode 100644
index 0000000..e8d4833
--- /dev/null
+++ b/scripts/build-theme-murals.py
@@ -0,0 +1,205 @@
+#!/usr/bin/env python3
+"""
+Themed signature-mural factory.
+
+Reads data/mural-themes.json (list of {slug,title,blurb,met_query,roster:[names]}).
+For each theme:
+  - resolves every roster name's REAL signature via Wikidata P109 (signature image)
+    -> Commons thumbnail. Names without a P109 signature are SKIPPED (never invented).
+  - pulls one PUBLIC-DOMAIN Met artifact (Met Open Access API) as a header accent.
+  - composes output/collage-<slug>.png  and  collage-<slug>-named.png  (the murals
+    page toggles base<->named), matching the existing storefront image convention.
+Then appends/updates each theme in data/murals-catalog.json with its resolved roster.
+
+Free / local + public no-key HTTP (Wikidata + Commons + Met). $0.
+"""
+import json, os, re, time, urllib.parse, urllib.request, sys
+from io import BytesIO
+from concurrent.futures import ThreadPoolExecutor
+from PIL import Image, ImageDraw, ImageFont
+
+ROOT = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
+THEMES = os.path.join(ROOT, "data", "mural-themes.json")
+CATALOG = os.path.join(ROOT, "data", "murals-catalog.json")
+CACHE = os.path.join(ROOT, "tmp_theme_cache")
+OUT = os.path.join(ROOT, "output")
+UA = "CelebritySignatures-research/1.0 (steve@designerwallcoverings.com)"
+WD = "https://www.wikidata.org/w/api.php"
+COMMONS = "https://commons.wikimedia.org/w/api.php"
+MET = "https://collectionapi.metmuseum.org/public/collection/v1"
+os.makedirs(CACHE, exist_ok=True); os.makedirs(OUT, exist_ok=True)
+
+
+def get(url, tries=5):
+    for i in range(tries):
+        try:
+            req = urllib.request.Request(url, headers={"User-Agent": UA})
+            with urllib.request.urlopen(req, timeout=25) as r:
+                return r.read()
+        except Exception:
+            if i == tries - 1:
+                return None
+            time.sleep(1.0 * (i + 1))   # gentle backoff to avoid Wikidata 429s
+
+
+def getjson(url):
+    b = get(url)
+    try:
+        return json.loads(b) if b else None
+    except Exception:
+        return None
+
+
+def wd_signature(name):
+    """name -> (commons thumb url) via Wikidata P109, or None."""
+    s = getjson(f"{WD}?action=wbsearchentities&search={urllib.parse.quote(name)}"
+                f"&language=en&format=json&type=item&limit=1&maxlag=5")
+    try:
+        qid = s["search"][0]["id"]
+    except Exception:
+        return None
+    c = getjson(f"{WD}?action=wbgetclaims&entity={qid}&property=P109&format=json&maxlag=5")
+    try:
+        fname = c["claims"]["P109"][0]["mainsnak"]["datavalue"]["value"]
+    except Exception:
+        return None
+    title = "File:" + fname
+    q = (f"{COMMONS}?action=query&titles={urllib.parse.quote(title)}"
+         f"&prop=imageinfo&iiprop=url&iiurlwidth=600&format=json")
+    d = getjson(q)
+    try:
+        info = next(iter(d["query"]["pages"].values()))["imageinfo"][0]
+        return info.get("thumburl") or info.get("url")
+    except Exception:
+        return None
+
+
+def met_public_domain(query):
+    s = getjson(f"{MET}/search?q={urllib.parse.quote(query)}&hasImages=true")
+    for oid in (s or {}).get("objectIDs", [])[:10]:
+        o = getjson(f"{MET}/objects/{oid}")
+        if o and o.get("isPublicDomain") and (o.get("primaryImageSmall") or o.get("primaryImage")):
+            return o.get("primaryImageSmall") or o.get("primaryImage"), o.get("title", "")
+    return None, ""
+
+
+def cache_img(url, key):
+    if not url:
+        return None
+    p = os.path.join(CACHE, re.sub(r"[^a-z0-9]+", "-", key.lower()).strip("-") + ".png")
+    if os.path.exists(p) and os.path.getsize(p) > 200:
+        return p
+    b = get(url)
+    if not b:
+        return None
+    try:
+        Image.open(BytesIO(b)).convert("RGBA").save(p)
+        return p
+    except Exception:
+        return None
+
+
+def font(sz, bold=False):
+    for p in (["/System/Library/Fonts/Supplemental/Georgia Bold.ttf" if bold else "/System/Library/Fonts/Supplemental/Georgia.ttf",
+               "/System/Library/Fonts/Supplemental/Arial Bold.ttf" if bold else "/System/Library/Fonts/Supplemental/Arial.ttf"]):
+        try:
+            return ImageFont.truetype(p, sz)
+        except Exception:
+            continue
+    return ImageFont.load_default()
+
+
+def resolve_theme(theme):
+    roster = theme["roster"]
+    workers = int(os.environ.get("WORKERS", "3"))
+    with ThreadPoolExecutor(max_workers=workers) as ex:
+        thumbs = list(ex.map(wd_signature, roster))
+    sigs = []
+    for name, url in zip(roster, thumbs):
+        if url:
+            local = cache_img(url, theme["slug"] + "-" + name)
+            if local:
+                sigs.append({"name": name, "local": local})
+    met_url, met_title = met_public_domain(theme.get("met_query", theme["title"]))
+    met_local = cache_img(met_url, theme["slug"] + "-met") if met_url else None
+    return sigs, met_local, met_title
+
+
+def build(theme, sigs, met_local, named):
+    n = len(sigs)
+    COLS = 5 if n > 12 else 4 if n > 6 else 3
+    rows = max(1, (n + COLS - 1) // COLS)
+    BOX_W, BOX_H = 300, 110
+    LABEL_H = 30 if named else 0
+    PAD = 22
+    CELL_W = BOX_W + PAD
+    CELL_H = BOX_H + LABEL_H + PAD
+    HEADER_H = 180
+    FOOT = 54
+    W = COLS * CELL_W + PAD
+    H = HEADER_H + rows * CELL_H + FOOT
+    INK = (26, 24, 22); MUTED = (122, 118, 112); LINE = (226, 222, 216)
+    img = Image.new("RGB", (W, H), (250, 249, 246))
+    d = ImageDraw.Draw(img)
+    # Met accent top-right
+    if met_local and os.path.exists(met_local):
+        try:
+            im = Image.open(met_local).convert("RGB"); im.thumbnail((150, 150), Image.LANCZOS)
+            img.paste(im, (W - im.width - PAD - 6, 22))
+        except Exception:
+            pass
+    d.text((PAD + 6, 46), theme["title"], font=font(46, True), fill=INK)
+    d.text((PAD + 8, 110), theme.get("blurb", "")[:120], font=font(19), fill=MUTED)
+    d.line([(PAD + 6, HEADER_H - 16), (W - PAD - 6, HEADER_H - 16)], fill=LINE, width=2)
+    for i, s in enumerate(sigs):
+        cx = PAD + (i % COLS) * CELL_W
+        cy = HEADER_H + (i // COLS) * CELL_H
+        try:
+            im = Image.open(s["local"]).convert("RGBA")
+            bg = Image.new("RGBA", im.size, (255, 255, 255, 255))
+            im = Image.alpha_composite(bg, im).convert("RGB")
+            im.thumbnail((BOX_W, BOX_H), Image.LANCZOS)
+            img.paste(im, (cx + (BOX_W - im.width) // 2, cy + (BOX_H - im.height) // 2))
+        except Exception:
+            pass
+        if named:
+            d.text((cx + 4, cy + BOX_H + 6), s["name"][:30], font=font(17, True), fill=INK)
+    d.text((PAD + 6, H - 38),
+           f"{n} signatures · every name traced to a public-domain Wikimedia source · accent artifact: The Met (public domain).",
+           font=font(13), fill=MUTED)
+    suffix = "-named" if named else ""
+    img.save(os.path.join(OUT, f"collage-{theme['slug']}{suffix}.png"))
+
+
+def main():
+    only = sys.argv[1] if len(sys.argv) > 1 else None
+    themes = json.load(open(THEMES))
+    if only:
+        themes = [t for t in themes if t["slug"] == only]
+    catalog = json.load(open(CATALOG))
+    by_slug = {m["slug"]: m for m in catalog["murals"]}
+    built = []
+    for t in themes:
+        sigs, met_local, met_title = resolve_theme(t)
+        if len(sigs) < 4:
+            print(f"[{t['slug']}] only {len(sigs)} signatures resolved — SKIP (need >=4)")
+            continue
+        build(t, sigs, met_local, named=True)
+        build(t, sigs, met_local, named=False)
+        entry = {"code": t.get("code", t["slug"][:2]), "slug": t["slug"], "title": t["title"],
+                 "blurb": t["blurb"], "sigs": len(sigs),
+                 "roster": [s["name"] for s in sigs], "met_accent": met_title}
+        by_slug[t["slug"]] = entry
+        built.append((t["slug"], len(sigs)))
+        print(f"[{t['slug']}] -> {len(sigs)} sigs · met:{met_title[:30]}")
+    # preserve original first three, then themed by order
+    order = ["oldest-signatures", "declaration-of-independence", "politics"]
+    murals = [by_slug[s] for s in order if s in by_slug]
+    murals += [by_slug[t["slug"]] for t in json.load(open(THEMES)) if t["slug"] in by_slug and t["slug"] not in order]
+    catalog["murals"] = murals
+    json.dump(catalog, open(CATALOG, "w"), indent=2, ensure_ascii=False)
+    print(f"\nBuilt {len(built)} themed murals. Catalog now lists {len(murals)} murals total.")
+
+
+if __name__ == "__main__":
+    main()

← 3a77cd8 wire Oldest Signatures into storefront: grid category + mura  ·  back to CelebritySignatures  ·  chore: macstudio3 migration — reconcile from mac2 + repoint b3b1355 →