diff --git a/.gitignore b/.gitignore index 67702e7..2ad7ddd 100644 --- a/.gitignore +++ b/.gitignore @@ -13,3 +13,4 @@ evidence/ coverage/ *.tsbuildinfo .DS_Store +.gstack/ diff --git a/CONTENT_LICENSE.md b/CONTENT_LICENSE.md index 03bd786..18707ca 100644 --- a/CONTENT_LICENSE.md +++ b/CONTENT_LICENSE.md @@ -6,6 +6,7 @@ To the extent copyright exists, original literary commentary and the original st - `editions/odyssey/research/**/*.md` - `editions/odyssey/{foundations,comparison,motif-index,reading-method}.md` +- Original commentary in `editions/odyssey/culture.json`; images and museum records retain their separate terms below. - `editions/starter/*.md` and `editions/starter/*.txt` [License summary](https://creativecommons.org/licenses/by/4.0/) · [Full legal terms](https://creativecommons.org/licenses/by/4.0/legalcode) diff --git a/DESIGN.md b/DESIGN.md index a2358d4..159f541 100644 --- a/DESIGN.md +++ b/DESIGN.md @@ -218,6 +218,22 @@ A passage is a ruled figure with a larger blockquote in the selected reading fac The leaf holds the chapter heading, source disclosure and prose. Evidence labels remain inline sans-serif distinctions. The original-text view has an edition note and source-preserving paragraphs. Its opening paragraph uses the same text size as the rest of the prose. Source disclosures use native details/summary behavior and a rotated chevron in the open state. +### Art gallery + +Whole artworks sit on the existing wash surface with quiet artist/date captions, Literata titles and short descriptions. The featured work leads into a gallery filtered by artwork type and a native book selector. Type buttons expose their pressed state; a status line reports the result count. An empty result offers Show all works. Hover changes the title to the reading accent and moves its arrow slightly; the images retain their own colors and proportions. + +### Artwork detail + +A large uncropped image precedes the reading essay. Its caption retains medium, collection, accession when supplied, and linked image credits and rights. The facts area connects the work to chapters and collection sources, explicitly distinguishing an episode depiction from a cultural comparison. The essay separates historical context, close looking, a labeled interpretation and a concluding question. Related works link onward without interrupting the essay. + +### Cultural themes and book connections + +Ruled native details/summary disclosures present a Literata theme title, muted lead and rotating chevron. Opening one reveals prose, chapter links and sources. The book-by-book entries use the same rules and typographic hierarchy, linking directly to the complete text and any associated artworks. Collection titles and introductions come from culture data; chapter labels follow the active edition. + +### Artwork enlargement + +Enlarge artwork opens a native modal on the reading leaf with a dimmed blue backdrop, caption and rights links. It starts with the whole image fitted to the available space and focuses the close button. Zoom in switches to an image at 200% of the stage width inside a scrollable, keyboard-focusable region; Fit to screen restores the fitted view. The toggle exposes its pressed state. The close button, Escape and a click on the dialog background dismiss it. Entry motion is brief and removed for reduced motion; zoom is an immediate state change. + ## Do's and Don'ts ### Do: diff --git a/README.md b/README.md index a6ef33f..935bc18 100644 --- a/README.md +++ b/README.md @@ -6,6 +6,8 @@ A quiet, open-source reading edition for novels and epics. Read the original tex [Read the Odyssey edition](https://ithaca-odyssey.vercel.app) · [Make an edition](docs/authoring.md) · [Deploy](docs/deployment.md) +[The Odyssey in art](https://ithaca-odyssey.vercel.app/culture) brings twelve paintings, prints and ancient objects into conversation with the poem. Each work has a credited image, collection sources, close-looking notes and chapter connections. Eight cultural essays and a guide to all 24 books connect the gallery to the whole story. + The included edition has all 24 books of Homer's *Odyssey* in Samuel Butler's translation, all 18 episodes of James Joyce's *Ulysses*, 42 chapter commentaries, five reading companions and 156 source-checked quotations. A small original story demonstrates how to replace this collection without changing React code. ## Start locally @@ -24,7 +26,7 @@ npm run build npm run preview ``` -The generated `dist/` directory is the deployable site. Keep a fallback to `index.html` for `/read/*` and `/about` routes; [Vercel configuration](vercel.json) is included. +The generated `dist/` directory is the deployable site. Keep a fallback to `index.html` for `/read/*`, `/culture/*`, `/culture` and `/about` routes; [Vercel configuration](vercel.json) is included. ## Make it yours @@ -48,6 +50,7 @@ To publish your edition, set `READER_EDITION` to its manifest path in your hosti - Chapter bookmarks, saved quotations and a remembered reading position. - Literata, Georgia and Manrope reading options, four sizes, day and night themes. - Commentary search and a global Texts menu. +- An optional art and culture companion with whole-image views, filters, sources and chapter links. - Keyboard navigation, visible focus, reduced-motion support and responsive layouts. - Local-only preferences: no reader account, analytics script or cross-device sync. diff --git a/THIRD_PARTY_NOTICES.md b/THIRD_PARTY_NOTICES.md index 0cb7216..addc728 100644 --- a/THIRD_PARTY_NOTICES.md +++ b/THIRD_PARTY_NOTICES.md @@ -17,3 +17,24 @@ The source works above are offered by Project Gutenberg as public domain in the The source records in `editions/odyssey/sources/manifest.md` identify editions and segmentation. The quotation ledger records selected passages; it does not grant rights beyond those of the source. The linked scholarship remains with its respective authors and publishers. Georgia is a system-font option, not a bundled font. Other npm dependencies retain their respective package licenses. No Pinterest image is distributed in this repository. + +## Odyssey art and culture collection + +Images in `public/images/culture/` are resized without cropping and converted to WebP; `-small` files are thumbnails. Sources, rights evidence and the distributed files’ SHA-256 checksums are recorded in `editions/odyssey/culture-sources.json`. The original image providers retain their own site and catalogue-text terms. All essays and close-looking notes in the reader are newly written. + +| Work | Collection and image terms | +| --- | --- | +| Joseph Mallord William Turner, *Ulysses deriding Polyphemus – Homer’s Odyssey*, 1829 (NG508) | [National Gallery, London](https://www.nationalgallery.org.uk/paintings/joseph-mallord-william-turner-ulysses-deriding-polyphemus-homer-s-odyssey). National Gallery, London, via Wikimedia Commons. [Public domain](https://commons.wikimedia.org/wiki/File:Turner_-_Ulysses_deriding_Polyphemus_1829.jpg). | +| Attributed to the Persephone Painter · Greek, Attic, *Terracotta calyx-krater (bowl for mixing wine and water)*, ca. 440 BCE (41.83) | [The Metropolitan Museum of Art, New York](https://www.metmuseum.org/art/collection/search/253627). The Metropolitan Museum of Art, New York. Gift of Amelia E. White, 1941. [The Met Open Access · CC0](https://creativecommons.org/publicdomain/zero/1.0/). | +| Gerard de Lairesse, *Odysseus and Calypso*, c. 1680 (SK-A-211) | [Rijksmuseum, Amsterdam](https://www.rijksmuseum.nl/en/collection/object/Odysseus-and-Calypso--6bfcc507d5625d026e8be4acd9c52b75). Rijksmuseum, Amsterdam, via Wikimedia Commons. [CC0 / public domain](https://commons.wikimedia.org/wiki/File:Lairesse_-_Odysseus_bij_Calypso_Rijksmuseum_SK-A-211.jpg). | +| Attributed to the Amasis Painter · Greek, Attic, *Terracotta lekythos (oil flask)*, ca. 550–530 BCE (31.11.10) | [The Metropolitan Museum of Art, New York](https://www.metmuseum.org/art/collection/search/253348). The Metropolitan Museum of Art, New York. Fletcher Fund, 1931. [The Met Open Access · CC0](https://creativecommons.org/publicdomain/zero/1.0/). | +| Joachim von Sandrart the Elder, *Odysseus and Nausicaa*, c. 1630–1688 (SK-A-4278) | [Rijksmuseum, Amsterdam](https://www.rijksmuseum.nl/en/collection/object/Odysseus-and-Nausicaa--36d44d38106f0cc843b11a1b12dc5c09). Rijksmuseum, Amsterdam, via Wikimedia Commons. [Public domain](https://commons.wikimedia.org/wiki/File:Joachim_von_Sandrart_-_Odysseus_und_Nausikaa_(Rijksmuseum_Amsterdam).jpg). | +| Unknown maker · Greek, Melian, *Terracotta plaque*, ca. 460–450 BCE (30.11.9) | [The Metropolitan Museum of Art, New York](https://www.metmuseum.org/art/collection/search/253053). The Metropolitan Museum of Art, New York. Fletcher Fund, 1930. [The Met Open Access · CC0](https://creativecommons.org/publicdomain/zero/1.0/). | +| Giovanni Benedetto Castiglione, *Circe with the Companions of Ulysses Changed into Animals*, 1650–1651 (RP-P-OB-12.200) | [Rijksmuseum, Amsterdam](https://www.rijksmuseum.nl/en/collection/object/Circe-met-de-in-dieren-veranderde-reisgenoten-van-Odysseus--2fc8beaf14360d90f5350a14da9dc730). Rijksmuseum, Amsterdam, via Wikimedia Commons. [CC0 / public domain](https://commons.wikimedia.org/wiki/File:Circe_met_de_in_dieren_veranderde_reisgenoten_van_Odysseus,_RP-P-OB-12.200.jpg). | +| Unknown maker · Greek, Eastern Mediterranean, *Glass phiale (libation bowl)*, late 5th–early 4th century BCE (69.11.6) | [The Metropolitan Museum of Art, New York](https://www.metmuseum.org/art/collection/search/255281). The Metropolitan Museum of Art, New York. Purchase, Arthur Darby Nock Bequest, in honor of Gisela Richter, 1969. [The Met Open Access · CC0](https://creativecommons.org/publicdomain/zero/1.0/). | +| Unknown printmaker after Annibale Carracci, *Odysseus and the Sirens*, 17th century (RP-P-1890-A-15254) | [Rijksmuseum, Amsterdam](https://www.rijksmuseum.nl/en/collection/object/Odysseus-en-de-sirenen--ffcba44976c5d7e039f8500945a1cbcb). Rijksmuseum, Amsterdam, via Wikimedia Commons. [CC0 / public domain](https://commons.wikimedia.org/wiki/File:Odysseus_en_de_sirenen,_RP-P-1890-A-15254.jpg). | +| Attributed to the Workshop of New York MMA 34.11.2 · Greek, Attic, *Terracotta krater*, ca. 775 BCE (34.11.2) | [The Metropolitan Museum of Art, New York](https://www.metmuseum.org/art/collection/search/253422). The Metropolitan Museum of Art, New York. Fletcher Fund, 1934. [The Met Open Access · CC0](https://creativecommons.org/publicdomain/zero/1.0/). | +| Theodoor van Thulden, after Francesco Primaticcio and Nicolò dell’Abate, *Odysseus and Penelope on Their Way to Bed*, 1633 (BI-1883-1235-50) | [Rijksmuseum, Amsterdam](https://www.rijksmuseum.nl/en/collection/object/Odysseus-en-Penelope-op-weg-naar-bed--600886f3a19288d769027143cd7dbe99). Rijksmuseum, Amsterdam, via Wikimedia Commons. [CC0 / public domain](https://commons.wikimedia.org/wiki/File:Odysseus_en_Penelope_op_weg_naar_bed_Les_Travaux_d%27Ulysse_(serietitel)_De_werken_van_Odysseus_(serietitel),_BI-1883-1235-50.jpg). | +| Unknown scribe · Greek, Ptolemaic, *Papyrus fragment with lines from Homer's Odyssey*, ca. 285–250 BCE (09.182.50) | [The Metropolitan Museum of Art, New York](https://www.metmuseum.org/art/collection/search/248134). The Metropolitan Museum of Art, New York. Gift of Egypt Exploration Fund, 1909. [The Met Open Access · CC0](https://creativecommons.org/publicdomain/zero/1.0/). | + +The six Met photographs are provided through [The Met Open Access](https://www.metmuseum.org/hubs/open-access) under [CC0](https://creativecommons.org/publicdomain/zero/1.0/). The linked collection records identify the photographs as Public Domain. This does not apply CC0 to museum interpretive essays. Rijksmuseum and Turner reproductions use the specific Commons rights records linked above. diff --git a/docs/authoring.md b/docs/authoring.md index 79eb54b..f081950 100644 --- a/docs/authoring.md +++ b/docs/authoring.md @@ -53,10 +53,21 @@ Keep IDs and `site.storageKey` stable once readers have saved their place. A new | `works[].endLink` | Final chapter destination with `href`, `label` | | `sectionLabels` | Map full note headings to shorter menu labels | | `frontispiece` | Optional image, caption, title, description, href and linkLabel | +| `culture` | Optional path to an art and culture JSON file; see below | | `downloads` | Array of `{file, filename, label}`; generated under `/texts/` | Place artwork in `public/images/`, reference it as `/images/name.jpg`, and add provenance and terms to both the edition's About page and `THIRD_PARTY_NOTICES.md`. Remove unused default artwork if you do not want to distribute it. The supplied Content Security Policy expects images and fonts on the same origin. +### Optional art and culture companion + +Set `"culture": "culture.json"` in the manifest to enable `/culture`, individual artwork pages and chapter links. Omit the field to keep an edition entirely text-based. The starter does not require this feature. + +Use `editions/odyssey/culture.json` as an example and `src/culture-types.ts` as the field reference. The file contains `title`, `description`, introductory paragraphs, a `featured` artwork ID, an `artworks` array, `themes` and a `chapters` guide. Artwork IDs are stable URL slugs. Every artwork needs an artist/maker, date, medium, collection, local image with dimensions and alt text, a summary, historical context, close-looking notes, literary interpretation, a question, chapter IDs, collection sources and image rights. `kind` is `painting` (including prints) or `object`; `relationship` distinguishes an episode depiction from a contextual comparison. + +Themes have original paragraphs, source links and chapter connections. Each guide entry connects an existing chapter ID to a cultural prompt and theme IDs. The compiler rejects missing rights, duplicate IDs, invalid images and unknown chapter/theme references. It does not validate historical accuracy or establish permission to publish an image. Review the actual image, collection record and rights notice together. Keep uncertainty explicit and distinguish the object's date from the era of the story. + +Store optimized images in `public/images/` and optional thumbnails alongside them; preserve the whole work when resizing. The Odyssey's `culture-sources.json` records image provenance, transformations and checksums. Add equivalent records and update credits when adapting the collection. Remove the default assets if your edition does not use them. + ## 3. Add the original text Use UTF-8 `.txt` files with blank lines between paragraphs. The compiler joins line wraps within prose paragraphs, preserves blocks whose every line is indented by at least two spaces as verse, and renders `_underscores_` as emphasis. Source HTML is escaped. diff --git a/editions/odyssey/about.md b/editions/odyssey/about.md index 23a465b..c1e1bc1 100644 --- a/editions/odyssey/about.md +++ b/editions/odyssey/about.md @@ -45,6 +45,12 @@ These notes are a reading companion, not a Greek-language critical edition. A ch Literata by TypeTogether is designed for sustained reading on screens. Manrope supplies navigation and the Clear reading option. Georgia is a system-serif option. The bundled fonts are self-hosted under the SIL Open Font License: [Literata license](/licenses/literata-license.txt) and [Manrope license](/licenses/manrope-license.txt). +## Art & cultural context {#culture} + +[The Odyssey in art](/culture) brings together twelve paintings, prints, and ancient objects, eight cultural essays, and a guide through all 24 books. Each artwork page links to its museum record and image rights. The six Met images are [Open Access photographs](https://www.metmuseum.org/hubs/open-access), released under CC0. The other reproductions come from Wikimedia Commons with their public-domain or CC0 notices. Images are resized without cropping. + +Historical facts follow the cited museum and scholarly sources. Close-looking notes describe the selected image; connections to Homer are AI-assisted interpretations. A later painting is a response to the poem, while an ancient object may illuminate a practice without illustrating any episode. The collection contains full-story spoilers. [Complete image notices](https://github.com/siddath/ithaca-reader/blob/main/THIRD_PARTY_NOTICES.md) and [image provenance](https://github.com/siddath/ithaca-reader/blob/main/editions/odyssey/culture-sources.json) are available in the public repository. + ## Your place, kept locally {#privacy} Reading position, appearance, chapter bookmarks and saved quotations live in your browser. There is no account, tracking script, or cross-device synchronisation. Clearing this site’s browser data removes the saved material. The hosting provider receives normal page requests. diff --git a/editions/odyssey/culture-sources.json b/editions/odyssey/culture-sources.json new file mode 100644 index 0000000..ae2032b --- /dev/null +++ b/editions/odyssey/culture-sources.json @@ -0,0 +1,263 @@ +{ + "version": 1, + "records": [ + { + "id": "turner-ulysses-polyphemus", + "collectionUrl": "https://www.nationalgallery.org.uk/paintings/joseph-mallord-william-turner-ulysses-deriding-polyphemus-homer-s-odyssey", + "originalImageUrl": "https://commons.wikimedia.org/wiki/Special:FilePath/Turner%20-%20Ulysses%20deriding%20Polyphemus%201829.jpg", + "rightsEvidenceUrl": "https://commons.wikimedia.org/wiki/File:Turner_-_Ulysses_deriding_Polyphemus_1829.jpg", + "checkedAt": "2026-09-18", + "evidenceNote": "Metadata follows the museum record. Visual and literary commentary is original interpretation.", + "transform": "Resized to fit without cropping; converted to WebP.", + "files": [ + { + "path": "public/images/culture/turner-ulysses-polyphemus.webp", + "bytes": 419966, + "sha256": "91151467aefa5f854bb4b450390eff4d44219b16e952d2ad354b4485caeba4a7" + }, + { + "path": "public/images/culture/turner-ulysses-polyphemus-small.webp", + "bytes": 51026, + "sha256": "0eb07438d3abaf71b3bedbf037bcff192657f147d50378288ffce2f317e35455" + } + ] + }, + { + "id": "lairesse-odysseus-calypso", + "collectionUrl": "https://www.rijksmuseum.nl/en/collection/object/Odysseus-and-Calypso--6bfcc507d5625d026e8be4acd9c52b75", + "originalImageUrl": "https://commons.wikimedia.org/wiki/Special:FilePath/Lairesse%20-%20Odysseus%20bij%20Calypso%20Rijksmuseum%20SK-A-211.jpg", + "rightsEvidenceUrl": "https://commons.wikimedia.org/wiki/File:Lairesse_-_Odysseus_bij_Calypso_Rijksmuseum_SK-A-211.jpg", + "checkedAt": "2026-09-18", + "evidenceNote": "Metadata follows the museum record. Visual and literary commentary is original interpretation.", + "transform": "Resized to fit without cropping; converted to WebP.", + "files": [ + { + "path": "public/images/culture/lairesse-odysseus-calypso.webp", + "bytes": 350810, + "sha256": "22b411c02b1ba8172929af8823f1eaabe7d0866a376ad54b70ef27355a8fc44a" + }, + { + "path": "public/images/culture/lairesse-odysseus-calypso-small.webp", + "bytes": 56208, + "sha256": "47d8a8b5a8693ef1931cfebf0d4e106332cb3f5cc904398273a5a0dec4c071f4" + } + ] + }, + { + "id": "sandrart-odysseus-nausicaa", + "collectionUrl": "https://www.rijksmuseum.nl/en/collection/object/Odysseus-and-Nausicaa--36d44d38106f0cc843b11a1b12dc5c09", + "originalImageUrl": "https://commons.wikimedia.org/wiki/Special:FilePath/Joachim%20von%20Sandrart%20-%20Odysseus%20und%20Nausikaa%20%28Rijksmuseum%20Amsterdam%29.jpg", + "rightsEvidenceUrl": "https://commons.wikimedia.org/wiki/File:Joachim_von_Sandrart_-_Odysseus_und_Nausikaa_(Rijksmuseum_Amsterdam).jpg", + "checkedAt": "2026-09-18", + "evidenceNote": "Metadata follows the museum record. Visual and literary commentary is original interpretation.", + "transform": "Resized to fit without cropping; converted to WebP.", + "files": [ + { + "path": "public/images/culture/sandrart-odysseus-nausicaa.webp", + "bytes": 245862, + "sha256": "7085562686d9c0a5c2e305fc1c742850e0ab2949c61ffa898d43b1637656f6fc" + }, + { + "path": "public/images/culture/sandrart-odysseus-nausicaa-small.webp", + "bytes": 30026, + "sha256": "d7a9088b37676de4c88bbdb56e51384eb11ffabdfa7a4302ed5a244ea34a4121" + } + ] + }, + { + "id": "castiglione-circe-companions", + "collectionUrl": "https://www.rijksmuseum.nl/en/collection/object/Circe-met-de-in-dieren-veranderde-reisgenoten-van-Odysseus--2fc8beaf14360d90f5350a14da9dc730", + "originalImageUrl": "https://commons.wikimedia.org/wiki/Special:FilePath/Circe%20met%20de%20in%20dieren%20veranderde%20reisgenoten%20van%20Odysseus%2C%20RP-P-OB-12.200.jpg", + "rightsEvidenceUrl": "https://commons.wikimedia.org/wiki/File:Circe_met_de_in_dieren_veranderde_reisgenoten_van_Odysseus,_RP-P-OB-12.200.jpg", + "checkedAt": "2026-09-18", + "evidenceNote": "Metadata follows the museum record. Visual and literary commentary is original interpretation.", + "transform": "Resized to fit without cropping; converted to WebP.", + "files": [ + { + "path": "public/images/culture/castiglione-circe-companions.webp", + "bytes": 1301230, + "sha256": "f37b904fec1187b1eab6dcca83a28f2a52437e49e10bc4133d93439ecdef3456" + }, + { + "path": "public/images/culture/castiglione-circe-companions-small.webp", + "bytes": 163264, + "sha256": "62b2f7f194969601c9a3f8bb13e5ed0958e85d4226f3254b0bab92bcbd81e4cf" + } + ] + }, + { + "id": "carracci-sirens-rijks", + "collectionUrl": "https://www.rijksmuseum.nl/en/collection/object/Odysseus-en-de-sirenen--ffcba44976c5d7e039f8500945a1cbcb", + "originalImageUrl": "https://commons.wikimedia.org/wiki/Special:FilePath/Odysseus%20en%20de%20sirenen%2C%20RP-P-1890-A-15254.jpg", + "rightsEvidenceUrl": "https://commons.wikimedia.org/wiki/File:Odysseus_en_de_sirenen,_RP-P-1890-A-15254.jpg", + "checkedAt": "2026-09-18", + "evidenceNote": "Metadata follows the museum record. Visual and literary commentary is original interpretation.", + "transform": "Resized to fit without cropping; converted to WebP.", + "files": [ + { + "path": "public/images/culture/carracci-sirens-rijks.webp", + "bytes": 614622, + "sha256": "458a5aed0a9d6ffd1c0c1147f56f255794a7a3e964095133551bedbd82d2d28d" + }, + { + "path": "public/images/culture/carracci-sirens-rijks-small.webp", + "bytes": 94150, + "sha256": "d9419f8d417c6791247421496f5edda8793795ba2ef79ed571cb97de0253f552" + } + ] + }, + { + "id": "van-thulden-penelope-reunion", + "collectionUrl": "https://www.rijksmuseum.nl/en/collection/object/Odysseus-en-Penelope-op-weg-naar-bed--600886f3a19288d769027143cd7dbe99", + "originalImageUrl": "https://commons.wikimedia.org/wiki/Special:FilePath/Odysseus%20en%20Penelope%20op%20weg%20naar%20bed%20Les%20Travaux%20d%27Ulysse%20%28serietitel%29%20De%20werken%20van%20Odysseus%20%28serietitel%29%2C%20BI-1883-1235-50.jpg", + "rightsEvidenceUrl": "https://commons.wikimedia.org/wiki/File:Odysseus_en_Penelope_op_weg_naar_bed_Les_Travaux_d%27Ulysse_(serietitel)_De_werken_van_Odysseus_(serietitel),_BI-1883-1235-50.jpg", + "checkedAt": "2026-09-18", + "evidenceNote": "Metadata follows the museum record. Visual and literary commentary is original interpretation.", + "transform": "Resized to fit without cropping; converted to WebP.", + "files": [ + { + "path": "public/images/culture/van-thulden-penelope-reunion.webp", + "bytes": 927166, + "sha256": "2a7b55cd761d7031f3716a7a6ed6279fd1a3b2ee6fa4e9f324fc957896420897" + }, + { + "path": "public/images/culture/van-thulden-penelope-reunion-small.webp", + "bytes": 106828, + "sha256": "cf7f5413cf44761662ed868222b744a062a222f9a9266194cfc333b188bd3098" + } + ] + }, + { + "id": "circe-calyx-krater", + "collectionUrl": "https://www.metmuseum.org/art/collection/search/253627", + "originalImageUrl": "https://collectionapi.metmuseum.org/api/collection/v1/iiif/253627/540361/main-image", + "rightsEvidenceUrl": "https://www.metmuseum.org/art/collection/search/253627", + "rightsPolicyUrl": "https://www.metmuseum.org/hubs/open-access", + "checkedAt": "2026-09-18", + "evidenceNote": "The museum identifies the companion heads as a boar and a horse or mule. The specific original use of this individual krater at a symposium is not established by the cited object record; symposium use describes the vessel type and its wider cultural context.", + "transform": "Resized to fit without cropping; converted to WebP.", + "files": [ + { + "path": "public/images/culture/circe-calyx-krater.webp", + "bytes": 108062, + "sha256": "df31c0e4df019afa6b8186ed1cb06b149884bf220ce8812f981136c5dbb6d714" + }, + { + "path": "public/images/culture/circe-calyx-krater-small.webp", + "bytes": 42704, + "sha256": "2ef4f1abdcbdd34eb02cdf460cc5acba5bcc384f315cd24dbd6ca812063b8d21" + } + ] + }, + { + "id": "penelope-return-plaque", + "collectionUrl": "https://www.metmuseum.org/art/collection/search/253053", + "originalImageUrl": "https://collectionapi.metmuseum.org/api/collection/v1/iiif/253053/537182/main-image", + "rightsEvidenceUrl": "https://www.metmuseum.org/art/collection/search/253053", + "rightsPolicyUrl": "https://www.metmuseum.org/hubs/open-access", + "checkedAt": "2026-09-18", + "evidenceNote": "The catalogue securely identifies the homecoming subject. The grouping is not a literal illustration of one complete scene in the surviving poem. No claim is made about the plaque's original display or ritual function, or the purpose of its two holes.", + "transform": "Resized to fit without cropping; converted to WebP.", + "files": [ + { + "path": "public/images/culture/penelope-return-plaque.webp", + "bytes": 250712, + "sha256": "3b649ef369468e4a27b4f3d8839ea59721d194057716b9a8fcfeb2c27bb8738e" + }, + { + "path": "public/images/culture/penelope-return-plaque-small.webp", + "bytes": 102524, + "sha256": "f289cbbd8c984dee243bae69d3e49dc4de1d117c257fd76ae4fb9d6221ada5a9" + } + ] + }, + { + "id": "amasis-textile-lekythos", + "collectionUrl": "https://www.metmuseum.org/art/collection/search/253348", + "originalImageUrl": "https://collectionapi.metmuseum.org/api/collection/v1/iiif/253348/2236286/main-image", + "rightsEvidenceUrl": "https://www.metmuseum.org/art/collection/search/253348", + "rightsPolicyUrl": "https://www.metmuseum.org/hubs/open-access", + "checkedAt": "2026-09-18", + "evidenceNote": "The primary photograph shows cloth handling and spinning, not the upright loom on another side. The figures' legal status is not established here. The museum's suggestion that this and another flask may have been wedding gifts remains a suggestion and is not asserted in the entry.", + "transform": "Resized to fit without cropping; converted to WebP.", + "files": [ + { + "path": "public/images/culture/amasis-textile-lekythos.webp", + "bytes": 81226, + "sha256": "c210d595867719c462c43dcd01f9f9a628035d6b2398a4023710746cfb348ec2" + }, + { + "path": "public/images/culture/amasis-textile-lekythos-small.webp", + "bytes": 29948, + "sha256": "e8d1691e83c7f18c6c96ade33afa851cf5a339181305bd027eb7d434aac2c08b" + } + ] + }, + { + "id": "glass-libation-phiale", + "collectionUrl": "https://www.metmuseum.org/art/collection/search/255281", + "originalImageUrl": "https://collectionapi.metmuseum.org/api/collection/v1/iiif/255281/530537/main-image", + "rightsEvidenceUrl": "https://www.metmuseum.org/art/collection/search/255281", + "rightsPolicyUrl": "https://www.metmuseum.org/hubs/open-access", + "checkedAt": "2026-09-18", + "evidenceNote": "The page identifies the vessel type as a libation bowl. Its exact ancient contents and use history are unknown. The photograph is monochrome, so the entry separates visible tonal effects from the catalogue's report of color.", + "transform": "Resized to fit without cropping; converted to WebP.", + "files": [ + { + "path": "public/images/culture/glass-libation-phiale.webp", + "bytes": 85552, + "sha256": "fd7a0c2962df9247e7b13e96a24c47d3a5a2e9e0fd636fcf4582587760f1cd75" + }, + { + "path": "public/images/culture/glass-libation-phiale-small.webp", + "bytes": 52078, + "sha256": "96201c70d304112b08a8747adb5ccd096a64c365426a40ab4953a07c27fab8d1" + } + ] + }, + { + "id": "geometric-seafaring-krater", + "collectionUrl": "https://www.metmuseum.org/art/collection/search/253422", + "originalImageUrl": "https://collectionapi.metmuseum.org/api/collection/v1/iiif/253422/541180/main-image", + "rightsEvidenceUrl": "https://www.metmuseum.org/art/collection/search/253422", + "rightsPolicyUrl": "https://www.metmuseum.org/hubs/open-access", + "checkedAt": "2026-09-18", + "evidenceNote": "The funeral function and two possible readings of the narrative follow the museum record. The museum publication on this object discusses its ships in detail. Its historical proximity to early epic does not establish that the painted scenes illustrate the surviving Odyssey.", + "transform": "Resized to fit without cropping; converted to WebP.", + "files": [ + { + "path": "public/images/culture/geometric-seafaring-krater.webp", + "bytes": 153654, + "sha256": "c5ade78a0ced55b9794146cc0ad00dffbea2fe27adf75ab8a82eeb09f6452cdb" + }, + { + "path": "public/images/culture/geometric-seafaring-krater-small.webp", + "bytes": 52398, + "sha256": "8706121d767ab238b973d4541a8de9d9fe12809708cc8973957be4bdcfd1a181" + } + ] + }, + { + "id": "odyssey-papyrus", + "collectionUrl": "https://www.metmuseum.org/art/collection/search/248134", + "originalImageUrl": "https://collectionapi.metmuseum.org/api/collection/v1/iiif/248134/534364/main-image", + "rightsEvidenceUrl": "https://www.metmuseum.org/art/collection/search/248134", + "rightsPolicyUrl": "https://www.metmuseum.org/hubs/open-access", + "checkedAt": "2026-09-18", + "evidenceNote": "The claim about three additional lines is the museum's catalogued finding; the entry does not independently transcribe or collate the Greek. The broader object record's claim that Alexandria was the first comprehensive public library is omitted because it is unnecessary to this entry and requires separate historical qualification.", + "transform": "Resized to fit without cropping; converted to WebP.", + "files": [ + { + "path": "public/images/culture/odyssey-papyrus.webp", + "bytes": 88844, + "sha256": "f7463dbc05e9a14f713df333f0bf6a316c980656e29754990f5cc357069c274b" + }, + { + "path": "public/images/culture/odyssey-papyrus-small.webp", + "bytes": 31398, + "sha256": "e57d912a3446fc9586133dce498d2ad9cbe327172819aa0f8dc8e24574baa3c9" + } + ] + } + ] +} diff --git a/editions/odyssey/culture.json b/editions/odyssey/culture.json new file mode 100644 index 0000000..0673ef6 --- /dev/null +++ b/editions/odyssey/culture.json @@ -0,0 +1,896 @@ +{ + "title": "The Odyssey in art", + "description": "Ancient objects, painted journeys, and the culture behind the poem.", + "featured": "turner-ulysses-polyphemus", + "introduction": [ + "The Odyssey has lived in more than words. Potters, painters and printmakers have returned to its strangers, enchanted islands and difficult homecomings. Looking at their work can make a familiar passage unfamiliar again.", + "Read these images in two directions: toward the poem, and toward the people who made them. A Classical vase, a seventeenth-century print and a painting by Turner belong to different worlds. Some depict an episode; others reveal a practice or material that helps us ask better questions. None is a photograph of Homer’s heroic past." + ], + "artworks": [ + { + "id": "turner-ulysses-polyphemus", + "title": "Ulysses deriding Polyphemus – Homer’s Odyssey", + "artist": "Joseph Mallord William Turner", + "date": "1829", + "medium": "Oil on canvas", + "collection": "National Gallery, London", + "accession": "NG508", + "kind": "painting", + "relationship": "episode", + "chapterIds": [ + "odyssey-9" + ], + "image": { + "src": "/images/culture/turner-ulysses-polyphemus.webp", + "thumbnail": "/images/culture/turner-ulysses-polyphemus-small.webp", + "alt": "A glowing sunrise behind Ulysses’s ship as he raises a flaming stake toward the giant Polyphemus on a cliff.", + "width": 1920, + "height": 1250 + }, + "summary": "A radiant escape, shadowed by the boast that will prolong the voyage.", + "context": "The National Gallery identifies the subject as Book IX: after blinding the Cyclops, Ulysses sails away while Polyphemus lies above the cliff. The catalogue records Turner’s title, 1829 date, oil medium, 132.5 × 203 cm dimensions, Turner Bequest (1856), and NG508. The episode belongs to Odysseus’s first-person account to the Phaeacians: escape succeeds, but his taunting disclosure of his name prepares the later curse. Turner adds luminous sea nymphs and flying fish, so this is an interpretation rather than a neutral transcription.", + "looking": "A ship cuts across a glowing horizon. Ulysses stands beneath a red banner, arms raised, holding the flaming olive stake toward the dark bulk of Polyphemus on the cliff. The sun burns through mist behind them; rigging, tiny sailors, sea creatures, and the red cloak create a crowded vertical flare against blue, pink, and gold atmosphere.", + "reading": "Victory and self-display occupy the same gesture. The raised arms look triumphant, yet the giant remains large enough to dominate the scene. Turner’s radiant dawn makes escape feel like revelation, while the red signals keep dangerous boasting visible inside celebration. The painting holds success and cost together without requiring one fixed moral lesson.", + "question": "Where does the painting make Ulysses look most powerful, and where does Polyphemus still control the composition?", + "sources": [ + { + "label": "National Gallery, London · NG508", + "url": "https://www.nationalgallery.org.uk/paintings/joseph-mallord-william-turner-ulysses-deriding-polyphemus-homer-s-odyssey" + } + ], + "rights": { + "label": "Public domain", + "url": "https://commons.wikimedia.org/wiki/File:Turner_-_Ulysses_deriding_Polyphemus_1829.jpg", + "credit": "National Gallery, London, via Wikimedia Commons." + } + }, + { + "id": "circe-calyx-krater", + "title": "Terracotta calyx-krater (bowl for mixing wine and water)", + "artist": "Attributed to the Persephone Painter · Greek, Attic", + "date": "ca. 440 BCE", + "medium": "Terracotta; red-figure", + "collection": "The Metropolitan Museum of Art, New York", + "accession": "41.83", + "kind": "object", + "relationship": "episode", + "chapterIds": [ + "odyssey-7", + "odyssey-8", + "odyssey-10" + ], + "image": { + "src": "/images/culture/circe-calyx-krater.webp", + "thumbnail": "/images/culture/circe-calyx-krater-small.webp", + "alt": "Black two-handled mixing bowl with orange figures in two bands; in the upper scene, animal-headed men follow Odysseus toward a retreating Circe.", + "width": 946, + "height": 1200 + }, + "summary": "Circe’s dangerous welcome, painted on a vessel for mixing wine.", + "context": "Made in Athens around 440 BCE, this red-figure mixing bowl is attributed to the Persephone Painter. A krater held wine diluted with water, an important part of the symposium, where privileged men drank, talked, and heard music or poetry. The museum identifies the upper scene as Odysseus pursuing Circe, with transformed companions behind him. Its imagery belongs to the Classical reception of epic.", + "looking": "The broad rim spreads above two stacked scenes. In the upper band, long-snouted heads disturb the otherwise human procession. Outstretched arms direct attention toward the running woman at the right. A chair breaks the line of bodies. Below, a second pursuit repeats the sense of movement on a smaller scale.", + "reading": "In Book 10, food and drink become instruments of captivity before Circe becomes a host and guide. That reversal gives a wine-mixing vessel a pointed connection to hospitality: what does a guest accept when accepting a cup? Compare the welcome in Books 7–8. The animal-headed men also expose artistic invention: the poem turns the companions into swine, while this painter includes another animal type.", + "question": "How do the repeated gestures make danger legible before you know the characters' names?", + "sources": [ + { + "label": "The Met object record, 41.83", + "url": "https://www.metmuseum.org/art/collection/search/253627" + }, + { + "label": "The Met: The Symposium in Ancient Greece", + "url": "https://www.metmuseum.org/essays/the-symposium-in-ancient-greece" + } + ], + "rights": { + "label": "The Met Open Access · CC0", + "url": "https://creativecommons.org/publicdomain/zero/1.0/", + "credit": "The Metropolitan Museum of Art, New York. Gift of Amelia E. White, 1941." + } + }, + { + "id": "lairesse-odysseus-calypso", + "title": "Odysseus and Calypso", + "artist": "Gerard de Lairesse", + "date": "c. 1680", + "medium": "Oil on canvas", + "collection": "Rijksmuseum, Amsterdam", + "accession": "SK-A-211", + "kind": "painting", + "relationship": "episode", + "chapterIds": [ + "odyssey-5" + ], + "image": { + "src": "/images/culture/lairesse-odysseus-calypso.webp", + "thumbnail": "/images/culture/lairesse-odysseus-calypso-small.webp", + "alt": "Seated Calypso holds a staff beside armored Odysseus, while a small winged child touches his helmet.", + "width": 1226, + "height": 1600 + }, + "summary": "An intimate island scene offers a different view of Odysseus’s desire to leave.", + "context": "The Rijksmuseum dates this canvas to about 1680, identifies oil on canvas, and records SK-A-211. Its subject heading is the moment when Mercury orders Calypso to release Ulysses (Odyssey V); the museum marks the object public domain. In Homer, Calypso’s island shelters and detains Odysseus for years; Zeus’s message makes departure unavoidable. The title names both figures, but the collection record’s subject is the divine command that changes a private enclosure into the beginning of return.", + "looking": "Calypso sits at the lower left, holding a long staff, while an armored Odysseus reclines beside her. A small winged child reaches toward the crest of his helmet; another winged figure or putto appears behind the group. Dark trees, drapery, a peacock, and a pale blue cloth frame the bodies. The picture is a close, theatrical encounter rather than a wide landscape of departure.", + "reading": "The image makes captivity look intimate and sensuous before the catalogue’s Book V subject—Mercury’s order to release Odysseus—enters the reading. Odysseus’s armor keeps his martial identity present even as his body leans into Calypso’s space. The winged child complicates the scene with allegorical or erotic associations, but the reproduction alone cannot securely identify every figure. The interpretation therefore distinguishes visible motifs from the museum’s indexed episode.", + "question": "Which details belong to the enclosed life with Calypso, and which point toward Odysseus’s departure?", + "sources": [ + { + "label": "Rijksmuseum, Amsterdam · SK-A-211", + "url": "https://www.rijksmuseum.nl/en/collection/object/Odysseus-and-Calypso--6bfcc507d5625d026e8be4acd9c52b75" + } + ], + "rights": { + "label": "CC0 / public domain", + "url": "https://commons.wikimedia.org/wiki/File:Lairesse_-_Odysseus_bij_Calypso_Rijksmuseum_SK-A-211.jpg", + "credit": "Rijksmuseum, Amsterdam, via Wikimedia Commons." + } + }, + { + "id": "amasis-textile-lekythos", + "title": "Terracotta lekythos (oil flask)", + "artist": "Attributed to the Amasis Painter · Greek, Attic", + "date": "ca. 550–530 BCE", + "medium": "Terracotta; black-figure", + "collection": "The Metropolitan Museum of Art, New York", + "accession": "31.11.10", + "kind": "object", + "relationship": "context", + "chapterIds": [ + "odyssey-2", + "odyssey-5", + "odyssey-19", + "odyssey-24" + ], + "image": { + "src": "/images/culture/amasis-textile-lekythos.webp", + "thumbnail": "/images/culture/amasis-textile-lekythos-small.webp", + "alt": "Small black-figure oil flask showing women handling cloth and spinning equipment, with folded fabric on a low stand and a basket near their feet.", + "width": 800, + "height": 1200 + }, + "summary": "The tools and gestures of textile work behind the poem’s weaving metaphors.", + "context": "This small Athenian oil flask, attributed to the Amasis Painter, dates to about 550–530 BCE. Its painted circuit brings several stages of wool-working together: weighing, spinning, weaving, and folding finished cloth. The museum identifies a loom elsewhere around the body and explains that textile production occupied a central place in women's household work. The scene does not identify its workers by name.", + "looking": "In this view, dark figures stand against the warm clay. At the left, women hold a broad piece of fabric above a low support carrying folded material. Thin lines connect raised hands and spinning equipment on the right, while a basket anchors the scene near their feet. The curve lets part of the activity disappear from view.", + "reading": "Penelope's unfinished shroud in Books 2 and 19 depends on expertise, time, and repeated bodily work. This image gives substance to that labor before we treat weaving as a metaphor for cunning. It also invites attention to the household workers surrounding the poem's named rulers. The comparison remains analogical: these are Archaic Athenian images of textile work, with no identification as Penelope's workshop.", + "question": "Which small objects reveal stages of work that a finished garment would conceal?", + "sources": [ + { + "label": "The Met object record and audio transcript, 31.11.10", + "url": "https://www.metmuseum.org/art/collection/search/253348" + } + ], + "rights": { + "label": "The Met Open Access · CC0", + "url": "https://creativecommons.org/publicdomain/zero/1.0/", + "credit": "The Metropolitan Museum of Art, New York. Fletcher Fund, 1931." + } + }, + { + "id": "sandrart-odysseus-nausicaa", + "title": "Odysseus and Nausicaa", + "artist": "Joachim von Sandrart the Elder", + "date": "c. 1630–1688", + "medium": "Oil on canvas", + "collection": "Rijksmuseum, Amsterdam", + "accession": "SK-A-4278", + "kind": "painting", + "relationship": "episode", + "chapterIds": [ + "odyssey-6" + ], + "image": { + "src": "/images/culture/sandrart-odysseus-nausicaa.webp", + "thumbnail": "/images/culture/sandrart-odysseus-nausicaa-small.webp", + "alt": "Nausicaa and attendants encounter the nearly naked Odysseus at the edge of a wooded beach.", + "width": 2000, + "height": 1221 + }, + "summary": "A kneeling stranger meets a gathered group: the first gestures of welcome.", + "context": "The Rijksmuseum identifies this painting as the meeting of Nausicaa and Ulysses after his shipwreck, dates it broadly to c. 1630–1688, records oil on canvas, 103.5 × 168.5 cm, and gives SK-A-4278. Its subject is Book VI: the nearly naked stranger emerges from bushes and startles Nausicaa and her companions. Nausicaa’s food, clothing, and guidance turn exposure into the next stage of return. The museum marks the object public domain.", + "looking": "A dark landscape opens around a group of women and a dog on the left, a standing woman in blue at center, and a kneeling, nearly naked Odysseus at right. One attendant carries a bowl or tray of fruit. A distant small figure appears high in the sky or haze. Trees and open water separate the social group from the exposed stranger.", + "reading": "Hospitality appears as negotiated distance. Odysseus is visible and vulnerable but not yet absorbed into community; Nausicaa’s group controls the social center. Bright textiles and carried objects make care concrete. The image invites attention to how a stranger becomes legible through a community’s response, while leaving open whether the scene feels compassionate, comic, or tense.", + "question": "What visual boundary separates Odysseus from Nausicaa’s group, and how might the objects between them change the encounter?", + "sources": [ + { + "label": "Rijksmuseum, Amsterdam · SK-A-4278", + "url": "https://www.rijksmuseum.nl/en/collection/object/Odysseus-and-Nausicaa--36d44d38106f0cc843b11a1b12dc5c09" + } + ], + "rights": { + "label": "Public domain", + "url": "https://commons.wikimedia.org/wiki/File:Joachim_von_Sandrart_-_Odysseus_und_Nausikaa_(Rijksmuseum_Amsterdam).jpg", + "credit": "Rijksmuseum, Amsterdam, via Wikimedia Commons." + } + }, + { + "id": "penelope-return-plaque", + "title": "Terracotta plaque", + "artist": "Unknown maker · Greek, Melian", + "date": "ca. 460–450 BCE", + "medium": "Terracotta", + "collection": "The Metropolitan Museum of Art, New York", + "accession": "30.11.9", + "kind": "object", + "relationship": "episode", + "chapterIds": [ + "odyssey-17", + "odyssey-19", + "odyssey-23", + "odyssey-24" + ], + "image": { + "src": "/images/culture/penelope-return-plaque.webp", + "thumbnail": "/images/culture/penelope-return-plaque-small.webp", + "alt": "Clay relief of Penelope seated with her head resting on one hand; Odysseus leans toward her from the right while three men watch from the left.", + "width": 1200, + "height": 919 + }, + "summary": "Penelope’s stillness holds the center of an ancient image of homecoming.", + "context": "The museum identifies this fifth-century BCE Melian relief as Odysseus returning to Penelope. It belongs to a distinctive group of terracotta plaques associated with Melos. The accompanying figures are identified as Telemachus, Laertes, and Eumaeus. No individual maker is named. This surviving image records a way of picturing the homecoming within Classical Greek visual culture.", + "looking": "Penelope occupies the center but folds inward, resting her tilted head on one hand. Odysseus bends toward her from the right; his extended arm bridges the space between them. Three figures cluster behind her, one seated low and two standing. Cracks cross the clay, and two small holes punctuate the background above her head.", + "reading": "Read this beside the guarded conversation in Book 19 and the recognition in Book 23. Arrival alone cannot restore a marriage: identity must become credible to the person who has waited. The relief compresses the wider homecoming into one gathering. In the poem, Odysseus meets Laertes separately in Book 24, so the father's presence here should be read as an artistic choice.", + "question": "Whose body seems most active, and whose stillness controls the emotional center?", + "sources": [ + { + "label": "The Met object record and audio transcript, 30.11.9", + "url": "https://www.metmuseum.org/art/collection/search/253053" + } + ], + "rights": { + "label": "The Met Open Access · CC0", + "url": "https://creativecommons.org/publicdomain/zero/1.0/", + "credit": "The Metropolitan Museum of Art, New York. Fletcher Fund, 1930." + } + }, + { + "id": "castiglione-circe-companions", + "title": "Circe with the Companions of Ulysses Changed into Animals", + "artist": "Giovanni Benedetto Castiglione", + "date": "1650–1651", + "medium": "Etching on paper", + "collection": "Rijksmuseum, Amsterdam", + "accession": "RP-P-OB-12.200", + "kind": "painting", + "relationship": "episode", + "chapterIds": [ + "odyssey-10" + ], + "image": { + "src": "/images/culture/castiglione-circe-companions.webp", + "thumbnail": "/images/culture/castiglione-circe-companions-small.webp", + "alt": "Circe sits by ruins with books and a wand while Odysseus’s transformed companions crowd the right side as animals.", + "width": 2000, + "height": 1454 + }, + "summary": "Books, a wand, and animal bodies give visible form to Circe’s power.", + "context": "The Rijksmuseum records this etching as Castiglione’s own design, made in Italy in 1650–1651, on paper, 218 × 309 mm, accession RP-P-OB-12.200. Its description identifies Circe seated by a ruin with a wand and magic books while Odysseus’s companions, newly transformed, appear as animals. The museum links the subject to Homer and marks the print public domain. In Book X, transformation exposes the danger of accepting Circe’s hospitality without protection; Hermes’s herb lets Odysseus resist and reverse it.", + "looking": "Circe sits near a ruined fragment, wand lifted, with books scattered at her feet. To the right, animal bodies and human movement gather around the aftermath. Ruin, foliage, and low horizon create a rough stage for the contrast between learned magical authority and bodies that have lost human form.", + "reading": "Knowledge is made material. Books and wand anchor Circe’s power, while transformed companions become a crowd rather than individualized heroes. The compression can make the episode feel like a crisis of recognition: who counts as a person when appearance and appetite change? This is a reading from image and poem, not a claim about Castiglione’s intention.", + "question": "How do the books, wand, ruin, and animal bodies distribute power across the print?", + "sources": [ + { + "label": "Rijksmuseum, Amsterdam · RP-P-OB-12.200", + "url": "https://www.rijksmuseum.nl/en/collection/object/Circe-met-de-in-dieren-veranderde-reisgenoten-van-Odysseus--2fc8beaf14360d90f5350a14da9dc730" + } + ], + "rights": { + "label": "CC0 / public domain", + "url": "https://commons.wikimedia.org/wiki/File:Circe_met_de_in_dieren_veranderde_reisgenoten_van_Odysseus,_RP-P-OB-12.200.jpg", + "credit": "Rijksmuseum, Amsterdam, via Wikimedia Commons." + } + }, + { + "id": "glass-libation-phiale", + "title": "Glass phiale (libation bowl)", + "artist": "Unknown maker · Greek, Eastern Mediterranean", + "date": "late 5th–early 4th century BCE", + "medium": "Glass; cast and cut", + "collection": "The Metropolitan Museum of Art, New York", + "accession": "69.11.6", + "kind": "object", + "relationship": "context", + "chapterIds": [ + "odyssey-3", + "odyssey-7", + "odyssey-11" + ], + "image": { + "src": "/images/culture/glass-libation-phiale.webp", + "thumbnail": "/images/culture/glass-libation-phiale-small.webp", + "alt": "Monochrome side view of a shallow glass bowl with an outward-flaring rim, two horizontal grooves, and pointed petal reliefs around its lower wall.", + "width": 973, + "height": 487 + }, + "summary": "A ritual vessel invites attention to the gestures of receiving, praying, and pouring.", + "context": "The museum classifies this vessel as a Greek libation bowl from the eastern Mediterranean, made around the end of the fifth or beginning of the fourth century BCE. Its glass was cast and cut, and the catalogue describes twenty pointed petals and a rosette underneath. Repairs and replacement material are recorded. These details document the surviving object's manufacture and condition; they do not identify an ancient owner.", + "looking": "The photograph shows a low bowl with a broad, open mouth and no handles. Two grooves circle its wall above the pointed relief decoration. Light catches the rim while patches below interrupt the surface. This is a monochrome photograph; the catalogue's description of a faint green tint cannot be checked from this image alone.", + "reading": "At Pylos in Book 3, prayer and the passing of a cup belong to Telemachus's reception. Reading beside this bowl makes ritual a sequence of hand movements: receive, pray, pour, pass. Book 11 gives pouring a different setting, beside the dead. The connection concerns ritual action across different contexts; this later glass vessel is not the particular cup described in either episode.", + "question": "How might the wide rim and shallow body shape the gesture of pouring?", + "sources": [ + { + "label": "The Met object record, 69.11.6", + "url": "https://www.metmuseum.org/art/collection/search/255281" + } + ], + "rights": { + "label": "The Met Open Access · CC0", + "url": "https://creativecommons.org/publicdomain/zero/1.0/", + "credit": "The Metropolitan Museum of Art, New York. Purchase, Arthur Darby Nock Bequest, in honor of Gisela Richter, 1969." + } + }, + { + "id": "carracci-sirens-rijks", + "title": "Odysseus and the Sirens", + "artist": "Unknown printmaker after Annibale Carracci", + "date": "17th century", + "medium": "Etching on paper", + "collection": "Rijksmuseum, Amsterdam", + "accession": "RP-P-1890-A-15254", + "kind": "painting", + "relationship": "episode", + "chapterIds": [ + "odyssey-12" + ], + "image": { + "src": "/images/culture/carracci-sirens-rijks.webp", + "thumbnail": "/images/culture/carracci-sirens-rijks-small.webp", + "alt": "Odysseus is tied to the mast while his crew row the ship past the Sirens.", + "width": 1106, + "height": 1600 + }, + "summary": "The desire to listen becomes survivable through ropes and the crew’s labor.", + "context": "The Rijksmuseum identifies this print as an etching on paper, accession RP-P-1890-A-15254, showing Ulysses tied to the mast while passing the Sirens. The record notes that it follows a fresco by Annibale Carracci and marks the object public domain. In Book XII, Circe’s warning leads Odysseus to choose controlled exposure: crew stop their ears with wax and bind him so he can hear without steering toward the song. The scene is about collective safety as much as temptation.", + "looking": "The mast rises through the center of the vertical sheet, with a helmeted figure at its base and rowers, shields, oars, and a large sail filling the vessel below. Three lightly draped figures stand on the shore at left; birds cross the sky, and a rocky coastline closes the distance. The print’s catalogue identifies the subject as the Sirens, although their song is represented through the encounter’s setting rather than by a clearly labelled attribute.", + "reading": "Heroism shifts from resistance alone to designing constraints. Odysseus’s desire to hear is acknowledged, but the crew’s labor makes it survivable. The bound body can look humiliating or intelligent depending on attention. Either way, the picture gives the episode an ethical shape in which foresight depends on trusting others with the means to stop you.", + "question": "Who is doing the practical work that keeps the ship safe, and how does the mast organize your eye?", + "sources": [ + { + "label": "Rijksmuseum, Amsterdam · RP-P-1890-A-15254", + "url": "https://www.rijksmuseum.nl/en/collection/object/Odysseus-en-de-sirenen--ffcba44976c5d7e039f8500945a1cbcb" + } + ], + "rights": { + "label": "CC0 / public domain", + "url": "https://commons.wikimedia.org/wiki/File:Odysseus_en_de_sirenen,_RP-P-1890-A-15254.jpg", + "credit": "Rijksmuseum, Amsterdam, via Wikimedia Commons." + } + }, + { + "id": "geometric-seafaring-krater", + "title": "Terracotta krater", + "artist": "Attributed to the Workshop of New York MMA 34.11.2 · Greek, Attic", + "date": "ca. 775 BCE", + "medium": "Terracotta", + "collection": "The Metropolitan Museum of Art, New York", + "accession": "34.11.2", + "kind": "object", + "relationship": "context", + "chapterIds": [ + "odyssey-9", + "odyssey-11", + "odyssey-12", + "odyssey-24" + ], + "image": { + "src": "/images/culture/geometric-seafaring-krater.webp", + "thumbnail": "/images/culture/geometric-seafaring-krater-small.webp", + "alt": "Large footed clay krater covered in geometric bands; a small funeral panel sits above a frieze with a ship and marching shield-bearing figures.", + "width": 960, + "height": 1200 + }, + "summary": "Ships and mourning share the surface of a monumental grave marker.", + "context": "This monumental Athenian krater was made around 775 BCE. The museum interprets its scenes of a body laid out for mourning as evidence that it served as a grave marker. A lower frieze combines ships, combat, and warriors. Whether these scenes recall the deceased's experiences or an ancestral heroic expedition remains uncertain; the catalogue explicitly allows both possibilities. The image cannot securely be named as an Odyssey episode.", + "looking": "Dense geometric patterns surround a narrow funeral panel near the rim. Below it, a long ship lies beside a procession of figures carrying pinched, rounded shields. The contrast between tiny bodies and the vessel's broad surface asks the eye to alternate between pattern and story. Its tall pedestal gives the bowl an imposing silhouette.", + "reading": "In Books 11–12, Elpenor asks for burial and a marker so that his death will be remembered; Book 24 recalls the funeral and enduring fame of Achilles. This vessel offers a material comparison for making loss publicly visible. Its ships also complicate any pleasant picture of seafaring: mobility accompanies organized violence. Neither the grave's occupant nor the painted voyage has been identified as one of Homer's characters.", + "question": "What changes when you first read the decoration as pattern, then as remembered human actions?", + "sources": [ + { + "label": "The Met object record, 34.11.2", + "url": "https://www.metmuseum.org/art/collection/search/253422" + }, + { + "label": "The Metropolitan Museum Journal: Ships on a Wine-Dark Sea in the Age of Homer", + "url": "https://resources.metmuseum.org/resources/metpublications/pdf/Ships_on_a_Wine_Dark_Sea_in_the_Age_of_Homer_The_Metropolitan_Museum_Journal_v_35_2000.pdf" + } + ], + "rights": { + "label": "The Met Open Access · CC0", + "url": "https://creativecommons.org/publicdomain/zero/1.0/", + "credit": "The Metropolitan Museum of Art, New York. Fletcher Fund, 1934." + } + }, + { + "id": "van-thulden-penelope-reunion", + "title": "Odysseus and Penelope on Their Way to Bed", + "artist": "Theodoor van Thulden, after Francesco Primaticcio and Nicolò dell’Abate", + "date": "1633", + "medium": "Etching and engraving on paper", + "collection": "Rijksmuseum, Amsterdam", + "accession": "BI-1883-1235-50", + "kind": "painting", + "relationship": "episode", + "chapterIds": [ + "odyssey-23" + ], + "image": { + "src": "/images/culture/van-thulden-penelope-reunion.webp", + "thumbnail": "/images/culture/van-thulden-penelope-reunion-small.webp", + "alt": "Odysseus and Penelope move through an interior toward a curtained bed as a central figure carries a torch.", + "width": 1944, + "height": 1600 + }, + "summary": "A torchlit procession turns recognition into the prospect of rest.", + "context": "The Rijksmuseum records this 1633 album leaf as an etching and engraving on paper by Theodoor van Thulden, after designs associated with Primaticcio and Nicolò dell’Abate, accession BI-1883-1235-50. Its subject is Book XXIII: after recognition and reconciliation, Odysseus and Penelope go toward bed while a servant leads with a candle and the bed is prepared. The museum marks the print public domain. It belongs to Les Travaux d’Ulysse, presenting reunion as one staged chapter in an extended cycle.", + "looking": "A torch-bearing figure stands near the center of the interior while Odysseus and Penelope move together at right, surrounded by attendants. At left, a bed or curtained sleeping space recedes behind another figure. Columns, tiled floor, and the bright flame organize the crowded room. The image is an album leaf with printed text below, so its page, frame, and caption are part of what a viewer sees.", + "reading": "The print turns reunion into a public, ceremonial passage toward privacy. The Rijksmuseum describes the torch-bearer as a servant and the background action as bed preparation; the figure’s exact allegorical identity is not asserted here. The crowded attendants make recognition social, while the dark sleeping space points beyond spectacle. The close reading remains grounded in the visible torch, bed, and procession rather than inventing an intention for van Thulden.", + "question": "How does the torch-bearing figure and the curtained bed turn the couple’s reunion into a procession?", + "sources": [ + { + "label": "Rijksmuseum, Amsterdam · BI-1883-1235-50", + "url": "https://www.rijksmuseum.nl/en/collection/object/Odysseus-en-Penelope-op-weg-naar-bed--600886f3a19288d769027143cd7dbe99" + } + ], + "rights": { + "label": "CC0 / public domain", + "url": "https://commons.wikimedia.org/wiki/File:Odysseus_en_Penelope_op_weg_naar_bed_Les_Travaux_d%27Ulysse_(serietitel)_De_werken_van_Odysseus_(serietitel),_BI-1883-1235-50.jpg", + "credit": "Rijksmuseum, Amsterdam, via Wikimedia Commons." + } + }, + { + "id": "odyssey-papyrus", + "title": "Papyrus fragment with lines from Homer's Odyssey", + "artist": "Unknown scribe · Greek, Ptolemaic", + "date": "ca. 285–250 BCE", + "medium": "Papyrus", + "collection": "The Metropolitan Museum of Art, New York", + "accession": "09.182.50", + "kind": "object", + "relationship": "context", + "chapterIds": [ + "odyssey-8", + "odyssey-20" + ], + "image": { + "src": "/images/culture/odyssey-papyrus.webp", + "thumbnail": "/images/culture/odyssey-papyrus-small.webp", + "alt": "Narrow, ragged strip of tan papyrus bearing rows of dark Greek writing, interrupted by missing sections and exposed fibers.", + "width": 1200, + "height": 1200 + }, + "summary": "A fragment of Book XX preserves evidence of the poem’s changing written text.", + "context": "Copied in the early Ptolemaic period, this papyrus preserves part of Book 20. The museum reports that it includes three lines absent from the standard text transmitted today. That difference provides direct evidence of textual variation in the third century BCE. The record also places Homeric scholarship within the collecting and editorial work associated with Alexandria. The individual who wrote this fragment is unknown.", + "looking": "The surviving strip is tall and irregular, with projecting scraps at the top and broad losses along one side. Dark letters line up in rows across pale fibers. Near the bottom, writing gives way to torn strands and larger gaps. The material makes absence visible: a reader must distinguish a surviving mark from a missing piece.", + "reading": "Book 8 stages storytelling before an audience; this object preserves the poem through writing. Together they prompt a question about what changes when a performed story becomes a copied, edited text. Open Book 20 with that history in mind. The extra lines establish a variant witness, not an automatically more authentic version, and this fragment cannot by itself reconstruct the poem's earliest form.", + "question": "How does seeing damaged writing alter your sense of the complete text on the screen?", + "sources": [ + { + "label": "The Met object record, 09.182.50", + "url": "https://www.metmuseum.org/art/collection/search/248134" + } + ], + "rights": { + "label": "The Met Open Access · CC0", + "url": "https://creativecommons.org/publicdomain/zero/1.0/", + "credit": "The Metropolitan Museum of Art, New York. Gift of Egypt Exploration Fund, 1909." + } + } + ], + "themes": [ + { + "id": "song", + "title": "A poem made to be heard", + "lead": "Memory, performance, and the authority to tell a story.", + "paragraphs": [ + "The Odyssey belongs to a tradition of oral epic. Repeated phrases, epithets and familiar scenes support a poetry made in and for performance. Scholars disagree about how that tradition became the written text we inherit; “Homer” is not a securely documented modern author biography. Gregory Nagy’s account offers one influential way to understand the relationship between oral tradition, performance and writing.", + "The poem stages this problem inside its own story. Phemius sings in the occupied household; Demodocus performs for the Phaeacians; Odysseus becomes the narrator of his own wanderings. In Book 8, a victor at Troy weeps while hearing the war made into song. As a reading, this unsettles the idea that heroic fame is simply a reward: the beautiful story can wound the person who lived it.", + "When looking at an image of a bard, a manuscript or an ancient text fragment, ask what kind of authority it represents. A surviving papyrus witnesses the poem’s later circulation. It cannot show us a single original performance." + ], + "chapterIds": [ + "odyssey-1", + "odyssey-8", + "odyssey-9", + "odyssey-24" + ], + "sources": [ + { + "label": "Gregory Nagy: oral traditions and authorship", + "url": "https://www-current.chs.harvard.edu/curated-article/gregory-nagy-oral-traditions-written-texts-and-questions-of-authorship/" + } + ] + }, + { + "id": "hospitality", + "title": "The stranger at the door", + "lead": "Food, gifts, safety, and the obligations of a host.", + "paragraphs": [ + "Watch the order of a welcome. In Book 1, Telemachus offers the stranger food and a seat before asking who they are. In Book 6, Nausicaa makes safety practical through clothing, food and directions. Eumaeus later shares what little he has. These scenes give xenia, the relationship between guest and host, a material form; it is more than a pleasant attitude.", + "Polyphemus eats his guests, while the suitors consume a house that is not theirs. The comparison is an interpretation of the poem’s repeated meals: both turn nourishment into predation. Odysseus also enters the Cyclops’s cave expecting gifts, so the story does not excuse every demand made by a guest.", + "Wine vessels help us imagine the work and hierarchy behind a feast. But a Classical symposium, an organized elite male drinking party, is not identical to a Homeric banquet. The Met’s account describes later conventions of mixing, serving and drinking. Use that evidence to sharpen questions about service and inclusion, not to reconstruct every detail of Alcinous’s hall." + ], + "chapterIds": [ + "odyssey-1", + "odyssey-3", + "odyssey-6", + "odyssey-7", + "odyssey-9", + "odyssey-14", + "odyssey-17" + ], + "sources": [ + { + "label": "The Met: the symposium in ancient Greece", + "url": "https://www.metmuseum.org/essays/the-symposium-in-ancient-greece" + } + ] + }, + { + "id": "household", + "title": "The house and the loom", + "lead": "A home is a structure of work, memory, property and power.", + "paragraphs": [ + "Penelope’s weaving delays a decision over the household’s future. She promises to finish a shroud for Laertes, then secretly undoes her work at night. Making and unmaking become ways of controlling time. This is a literary reading of the action, not a claim that weaving always symbolizes deception.", + "Textile production was skilled and necessary labor. A painted scene of spinning or weaving can return attention to hands, tools and workers that a summary of “the faithful wife” leaves out. Museum accounts of Classical Greek households place textile work among women’s responsibilities, but later Athenian social rules should not be projected wholesale onto Homer’s Ithaca.", + "The rooted bed in Book 23 turns craftsmanship into a test of shared knowledge. Odysseus describes a room built around a living olive tree. Penelope recognizes the maker through an intimate fact, not merely through his appearance. Read the bed with the shroud: one object anchors a relationship; the other keeps a forced decision unfinished." + ], + "chapterIds": [ + "odyssey-2", + "odyssey-6", + "odyssey-19", + "odyssey-21", + "odyssey-23" + ], + "sources": [ + { + "label": "The Met: women in Classical Greece", + "url": "https://www.metmuseum.org/essays/women-in-classical-greece" + } + ] + }, + { + "id": "labor", + "title": "Who makes the household possible?", + "lead": "Enslavement, service and the unequal terms of belonging.", + "paragraphs": [ + "Eumaeus tells how he was abducted as a child and sold; Eurycleia’s purchase is recalled near the poem’s beginning. They have histories and affections, but not the freedom of their owners. “Servant” can conceal the coercion that the story itself records. Keep status, loyalty and consent as different questions.", + "The return depends on people who tend animals, carry water, prepare food and preserve household knowledge. A useful reading of domestic art asks who appears as a named figure and who becomes background labor. In the reunion print, the candle-bearing attendant makes the couple’s movement possible even while attention settles on the married pair.", + "Book 22 makes this scrutiny urgent. The killing of the enslaved women cannot be made ethically harmless by calling it housekeeping or restoration. Read the poem’s allocation of speech and blame closely, and distinguish its social hierarchy from an obligation for the modern reader to endorse that hierarchy." + ], + "chapterIds": [ + "odyssey-1", + "odyssey-14", + "odyssey-15", + "odyssey-19", + "odyssey-20", + "odyssey-22", + "odyssey-23" + ], + "sources": [ + { + "label": "Homer, Odyssey Book 15, Butler translation", + "url": "https://classics.mit.edu/Homer/odyssey.15.xv.html" + }, + { + "label": "Homer, Odyssey Book 22, Butler translation", + "url": "https://classics.mit.edu/Homer/odyssey.22.xxii.html" + } + ] + }, + { + "id": "gods", + "title": "Gods in a world of practices", + "lead": "A prayer, an offering, an omen, a disputed command.", + "paragraphs": [ + "Ancient Greek religion included many gods, local sanctuaries, festivals and repeated acts of offering. It did not rest on one exclusive sacred book. A vessel for pouring a libation belongs to a world of actions between people and gods, not only to an illustrated catalogue of myths.", + "In the Odyssey, divine help does not remove effort or conflict. Athena protects and disguises; Poseidon obstructs; Zeus weighs competing claims. The same gods participate in relationships and rivalries. Reducing Athena to “intelligence” may offer an allegorical reading, but it leaves out the goddess acting as a person within the narrative.", + "Book 12 tests the distance between knowing a prohibition and keeping it under pressure. The cattle belong to Helios, not simply to an interchangeable “sun god” from another retelling. In Book 24, Athena halts further violence. Whether that ending demonstrates justice, the limits of human settlement, or an uneasy settlement imposed by power remains a question for interpretation." + ], + "chapterIds": [ + "odyssey-1", + "odyssey-3", + "odyssey-5", + "odyssey-12", + "odyssey-13", + "odyssey-20", + "odyssey-24" + ], + "sources": [ + { + "label": "The Met: Greek gods and religious practices", + "url": "https://www.metmuseum.org/essays/greek-gods-and-religious-practices" + } + ] + }, + { + "id": "sea", + "title": "The sea connects and divides", + "lead": "Travel, exchange, piracy and the limits of a map.", + "paragraphs": [ + "The Mediterranean linked communities through sailing, trade and settlement. Greek art itself preserves evidence of exchange: materials, motifs and objects crossed political and linguistic boundaries. The Met’s account places expanding Greek maritime networks within the Geometric and Archaic periods; this is historical context, not a date-stamped itinerary for Odysseus.", + "The poem distinguishes different experiences of movement. A merchant seeks profit; a raider takes captives; a shipwrecked stranger asks for shelter; a returning warrior longs for a particular household. Eumaeus’s account in Book 15 makes the sea a route of abduction, while the Phaeacian voyage makes it a route home.", + "Treat proposed modern locations for fabulous islands as hypotheses. The poem is not a reliable navigation chart. Its story also doubles back: Books 9–12 recount earlier travels after the reader has already reached Phaeacia. An artwork may compress several moments into one scene, just as the poem makes remembered travel part of a present encounter." + ], + "chapterIds": [ + "odyssey-5", + "odyssey-9", + "odyssey-10", + "odyssey-12", + "odyssey-13", + "odyssey-15" + ], + "sources": [ + { + "label": "The Met: colonization, trade and Greek art", + "url": "https://www.metmuseum.org/essays/ancient-greek-colonization-and-trade-and-their-influence-on-greek-art" + } + ] + }, + { + "id": "memory", + "title": "The living owe the dead", + "lead": "Burial, grief and the doubtful comfort of lasting fame.", + "paragraphs": [ + "Elpenor first asks for burial, not an abstract explanation of death. Anticleia tells her son what his absence has cost. Achilles’s shade prefers a lowly life on earth to sovereignty among the dead. Book 11 repeatedly gives the grand idea of an underworld a particular voice and an unfinished human claim.", + "Greek funerary art records acts of laying out, mourning, procession and remembrance. A Geometric grave marker is therefore evidence for a ritual and a commemorative object, not a portrait of Elpenor’s funeral. Its figures can help us notice the living community that gives a death public form.", + "As an interpretation, the Odyssey asks whether the name preserved in a song can compensate for the body lost. Achilles makes that exchange uncomfortable. Book 24 returns to the dead and to their families, so the cost of the restored household does not disappear when the marriage bed has been recognized." + ], + "chapterIds": [ + "odyssey-4", + "odyssey-11", + "odyssey-12", + "odyssey-24" + ], + "sources": [ + { + "label": "The Met: death, burial and the afterlife", + "url": "https://www.metmuseum.org/essays/death-burial-and-the-afterlife-in-ancient-greece" + } + ] + }, + { + "id": "recognition", + "title": "Cunning, recognition and return", + "lead": "Being home is not the same as being known.", + "paragraphs": [ + "Odysseus survives partly through metis: resourceful intelligence that can include disguise, fabrication and patient timing. “Nobody” allows escape from Polyphemus; announcing the famous name puts the voyage in danger again. The difference between a useful story and a destructive performance of identity runs through the whole poem.", + "In Ithaca, knowledge is distributed unevenly. Telemachus learns, Eurycleia feels the scar, the servants are tested, and Penelope asks about the bed. These scenes use different kinds of evidence: divine transformation, bodily memory, conduct and shared craft. A recognition picture selects one of those moments and may make a difficult process look instantaneous.", + "Return, or nostos, is consequently more than arrival on an island. It involves recovering a place among people whose lives continued during the absence. The poem’s final violence and commanded peace prevent the comforting claim that everything has simply become as it was. Read reunion with its costs still in view." + ], + "chapterIds": [ + "odyssey-9", + "odyssey-13", + "odyssey-16", + "odyssey-19", + "odyssey-21", + "odyssey-22", + "odyssey-23", + "odyssey-24" + ], + "sources": [ + { + "label": "Sheila Murnaghan: disguise and recognition", + "url": "https://www-current.chs.harvard.edu/sheila-murnaghan-disguise-and-recognition-in-the-odyssey-chapter-1/" + } + ] + } + ], + "chapters": [ + { + "chapterId": "odyssey-1", + "title": "A welcome in an occupied house", + "text": "Telemachus seats and feeds the disguised Athena while the suitors consume the estate. Watch who controls the food, the song and the right to speak.", + "themeIds": [ + "hospitality", + "song", + "labor" + ] + }, + { + "chapterId": "odyssey-2", + "title": "Time held on a loom", + "text": "Penelope’s shroud turns textile work into a delay. The assembly exposes how household disorder becomes a public problem.", + "themeIds": [ + "household", + "recognition" + ] + }, + { + "chapterId": "odyssey-3", + "title": "A meal offered to the gods", + "text": "Nestor’s sacrifice places Telemachus inside a public ritual and a memory of the war. Hospitality introduces him to a different model of authority.", + "themeIds": [ + "gods", + "hospitality" + ] + }, + { + "chapterId": "odyssey-4", + "title": "Luxury, stories and grief", + "text": "Helen and Menelaus remember Troy differently. Beautiful possessions and generous feasting coexist with loss; Proteus’s changes make knowledge something that must be held fast.", + "themeIds": [ + "memory", + "hospitality" + ] + }, + { + "chapterId": "odyssey-5", + "title": "Leaving an immortal enclosure", + "text": "Calypso’s island offers abundance while preventing return. Hermes’s message opens a route out, but Odysseus must build, sail and survive with his own body.", + "themeIds": [ + "gods", + "sea" + ] + }, + { + "chapterId": "odyssey-6", + "title": "Clothing a stranger", + "text": "Nausicaa’s laundry outing puts textile labor beside the possibility of welcome. Clothing, food and careful speech begin to turn a terrifying stranger into a guest.", + "themeIds": [ + "household", + "hospitality" + ] + }, + { + "chapterId": "odyssey-7", + "title": "Entering the hall", + "text": "Odysseus approaches Arete and Alcinous as a suppliant. The question is not only whether a palace is splendid, but whether its people will use their power to shelter him.", + "themeIds": [ + "hospitality", + "gods" + ] + }, + { + "chapterId": "odyssey-8", + "title": "The hero hears his own story", + "text": "Games and Demodocus’s songs test how fame works before an audience. Tears interrupt public celebration and force attention back to a war’s lived cost.", + "themeIds": [ + "song", + "memory" + ] + }, + { + "chapterId": "odyssey-9", + "title": "The danger of a name", + "text": "The Cyclops episode breaks the rules of a shared meal. Odysseus’s clever anonymity secures escape; his demand to be known gives the injured giant a target for his curse.", + "themeIds": [ + "hospitality", + "recognition" + ] + }, + { + "chapterId": "odyssey-10", + "title": "Bodies changed, trust broken", + "text": "The wind-bag, the Laestrygonian harbor and Circe’s cup turn seeming safety into danger. Compare how a later print and an ancient vase give visible form to transformation.", + "themeIds": [ + "sea", + "recognition" + ] + }, + { + "chapterId": "odyssey-11", + "title": "The claims of the dead", + "text": "Elpenor needs burial; Anticleia brings news of home; Achilles troubles the promise of glorious fame. Listen for individual concerns inside the great gathering of shades.", + "themeIds": [ + "memory", + "song" + ] + }, + { + "chapterId": "odyssey-12", + "title": "Knowledge under pressure", + "text": "The Sirens offer a fatal kind of knowing. Restraints, warnings and the taboo on Helios’s cattle ask whether foresight can survive desire, fear and hunger.", + "themeIds": [ + "gods", + "sea" + ] + }, + { + "chapterId": "odyssey-13", + "title": "An island made unfamiliar", + "text": "The sleeping arrival does not finish the return. Mist and disguise separate possession of the land from recognition within the household; even Phaeacian hospitality has a cost.", + "themeIds": [ + "recognition", + "hospitality" + ] + }, + { + "chapterId": "odyssey-14", + "title": "A poor host’s generosity", + "text": "Eumaeus shelters a beggar while worrying about his absent master. Measure his meal against the wealth being consumed in the palace.", + "themeIds": [ + "hospitality", + "labor" + ] + }, + { + "chapterId": "odyssey-15", + "title": "The sea as a route of abduction", + "text": "Eumaeus recounts how he was taken and sold. His history interrupts the adventure story with the experience of a child treated as a movable possession.", + "themeIds": [ + "labor", + "sea" + ] + }, + { + "chapterId": "odyssey-16", + "title": "A father made visible", + "text": "The reunion with Telemachus changes who knows the plan. Recognition does not yet make the household safe; it creates a small alliance within an unequal field of information.", + "themeIds": [ + "recognition", + "household" + ] + }, + { + "chapterId": "odyssey-17", + "title": "The beggar at his own door", + "text": "Argos, Eumaeus and the suitors respond differently to the disguised man. The threshold becomes a place to judge memory, care and violence.", + "themeIds": [ + "hospitality", + "recognition" + ] + }, + { + "chapterId": "odyssey-18", + "title": "Spectacle inside the house", + "text": "The fight with Irus turns poverty into entertainment. Penelope’s appearance and her negotiation over gifts expose a second struggle over public attention.", + "themeIds": [ + "household", + "labor" + ] + }, + { + "chapterId": "odyssey-19", + "title": "A scar interrupts a story", + "text": "Penelope questions the stranger while Eurycleia recognizes the body she once tended. The scar and the fabricated life place different kinds of evidence beside each other.", + "themeIds": [ + "recognition", + "labor" + ] + }, + { + "chapterId": "odyssey-20", + "title": "The household before the rupture", + "text": "Work, omens and suppressed anger fill a waiting day. The surviving papyrus reminds us that even these domestic details reached us through fragile material copies.", + "themeIds": [ + "song", + "gods", + "labor" + ] + }, + { + "chapterId": "odyssey-21", + "title": "An object that remembers its maker", + "text": "The bow makes expertise visible. Like the bed later, a familiar object can hold knowledge that a changed face cannot supply.", + "themeIds": [ + "recognition", + "household" + ] + }, + { + "chapterId": "odyssey-22", + "title": "The cost of restoration", + "text": "The killing of the suitors is followed by the punishment of enslaved people. Keep violence and unequal power visible when the narrative calls the house reclaimed.", + "themeIds": [ + "labor", + "recognition" + ] + }, + { + "chapterId": "odyssey-23", + "title": "A room built around a tree", + "text": "Penelope’s test makes shared knowledge decisive. Art can show the couple’s intimacy, but the poem first requires proof of a life they built together.", + "themeIds": [ + "household", + "recognition" + ] + }, + { + "chapterId": "odyssey-24", + "title": "Peace after the homecoming", + "text": "The shades, Laertes and the dead suitors’ families extend the story beyond the marriage bed. Athena’s command stops the feud; it does not erase the losses behind it.", + "themeIds": [ + "memory", + "gods", + "recognition" + ] + } + ] +} diff --git a/editions/odyssey/edition.json b/editions/odyssey/edition.json index a77fa1f..3ea9668 100644 --- a/editions/odyssey/edition.json +++ b/editions/odyssey/edition.json @@ -570,5 +570,6 @@ "label": "Ulysses" } ], - "checksums": "sources/checksums.json" + "checksums": "sources/checksums.json", + "culture": "culture.json" } diff --git a/playwright.config.ts b/playwright.config.ts index 522afb7..e05d8fb 100644 --- a/playwright.config.ts +++ b/playwright.config.ts @@ -2,7 +2,7 @@ import { defineConfig } from '@playwright/test'; const external=process.env.BASE_URL; export default defineConfig({ testDir:'./tests', - testIgnore:process.env.READER_TEST_EDITION==='starter'?['**/reader.spec.ts','**/refinement.spec.ts']:[], + testIgnore:process.env.READER_TEST_EDITION==='starter'?['**/reader.spec.ts','**/refinement.spec.ts','**/culture.spec.ts']:[], fullyParallel:false, workers:1, retries:process.env.CI?1:0, diff --git a/public/images/culture/amasis-textile-lekythos-small.webp b/public/images/culture/amasis-textile-lekythos-small.webp new file mode 100644 index 0000000..2831873 Binary files /dev/null and b/public/images/culture/amasis-textile-lekythos-small.webp differ diff --git a/public/images/culture/amasis-textile-lekythos.webp b/public/images/culture/amasis-textile-lekythos.webp new file mode 100644 index 0000000..bab2b9c Binary files /dev/null and b/public/images/culture/amasis-textile-lekythos.webp differ diff --git a/public/images/culture/carracci-sirens-rijks-small.webp b/public/images/culture/carracci-sirens-rijks-small.webp new file mode 100644 index 0000000..3b8956c Binary files /dev/null and b/public/images/culture/carracci-sirens-rijks-small.webp differ diff --git a/public/images/culture/carracci-sirens-rijks.webp b/public/images/culture/carracci-sirens-rijks.webp new file mode 100644 index 0000000..ad44999 Binary files /dev/null and b/public/images/culture/carracci-sirens-rijks.webp differ diff --git a/public/images/culture/castiglione-circe-companions-small.webp b/public/images/culture/castiglione-circe-companions-small.webp new file mode 100644 index 0000000..7da9d07 Binary files /dev/null and b/public/images/culture/castiglione-circe-companions-small.webp differ diff --git a/public/images/culture/castiglione-circe-companions.webp b/public/images/culture/castiglione-circe-companions.webp new file mode 100644 index 0000000..b0e91a5 Binary files /dev/null and b/public/images/culture/castiglione-circe-companions.webp differ diff --git a/public/images/culture/circe-calyx-krater-small.webp b/public/images/culture/circe-calyx-krater-small.webp new file mode 100644 index 0000000..20df449 Binary files /dev/null and b/public/images/culture/circe-calyx-krater-small.webp differ diff --git a/public/images/culture/circe-calyx-krater.webp b/public/images/culture/circe-calyx-krater.webp new file mode 100644 index 0000000..fb24f1c Binary files /dev/null and b/public/images/culture/circe-calyx-krater.webp differ diff --git a/public/images/culture/geometric-seafaring-krater-small.webp b/public/images/culture/geometric-seafaring-krater-small.webp new file mode 100644 index 0000000..01898b6 Binary files /dev/null and b/public/images/culture/geometric-seafaring-krater-small.webp differ diff --git a/public/images/culture/geometric-seafaring-krater.webp b/public/images/culture/geometric-seafaring-krater.webp new file mode 100644 index 0000000..dadaa8b Binary files /dev/null and b/public/images/culture/geometric-seafaring-krater.webp differ diff --git a/public/images/culture/glass-libation-phiale-small.webp b/public/images/culture/glass-libation-phiale-small.webp new file mode 100644 index 0000000..578d325 Binary files /dev/null and b/public/images/culture/glass-libation-phiale-small.webp differ diff --git a/public/images/culture/glass-libation-phiale.webp b/public/images/culture/glass-libation-phiale.webp new file mode 100644 index 0000000..082066e Binary files /dev/null and b/public/images/culture/glass-libation-phiale.webp differ diff --git a/public/images/culture/lairesse-odysseus-calypso-small.webp b/public/images/culture/lairesse-odysseus-calypso-small.webp new file mode 100644 index 0000000..55829ca Binary files /dev/null and b/public/images/culture/lairesse-odysseus-calypso-small.webp differ diff --git a/public/images/culture/lairesse-odysseus-calypso.webp b/public/images/culture/lairesse-odysseus-calypso.webp new file mode 100644 index 0000000..b8c7ebf Binary files /dev/null and b/public/images/culture/lairesse-odysseus-calypso.webp differ diff --git a/public/images/culture/odyssey-papyrus-small.webp b/public/images/culture/odyssey-papyrus-small.webp new file mode 100644 index 0000000..dda9350 Binary files /dev/null and b/public/images/culture/odyssey-papyrus-small.webp differ diff --git a/public/images/culture/odyssey-papyrus.webp b/public/images/culture/odyssey-papyrus.webp new file mode 100644 index 0000000..c313713 Binary files /dev/null and b/public/images/culture/odyssey-papyrus.webp differ diff --git a/public/images/culture/penelope-return-plaque-small.webp b/public/images/culture/penelope-return-plaque-small.webp new file mode 100644 index 0000000..5ef70b9 Binary files /dev/null and b/public/images/culture/penelope-return-plaque-small.webp differ diff --git a/public/images/culture/penelope-return-plaque.webp b/public/images/culture/penelope-return-plaque.webp new file mode 100644 index 0000000..0d88848 Binary files /dev/null and b/public/images/culture/penelope-return-plaque.webp differ diff --git a/public/images/culture/sandrart-odysseus-nausicaa-small.webp b/public/images/culture/sandrart-odysseus-nausicaa-small.webp new file mode 100644 index 0000000..0864f9c Binary files /dev/null and b/public/images/culture/sandrart-odysseus-nausicaa-small.webp differ diff --git a/public/images/culture/sandrart-odysseus-nausicaa.webp b/public/images/culture/sandrart-odysseus-nausicaa.webp new file mode 100644 index 0000000..f51b3da Binary files /dev/null and b/public/images/culture/sandrart-odysseus-nausicaa.webp differ diff --git a/public/images/culture/turner-ulysses-polyphemus-small.webp b/public/images/culture/turner-ulysses-polyphemus-small.webp new file mode 100644 index 0000000..9d9d1b7 Binary files /dev/null and b/public/images/culture/turner-ulysses-polyphemus-small.webp differ diff --git a/public/images/culture/turner-ulysses-polyphemus.webp b/public/images/culture/turner-ulysses-polyphemus.webp new file mode 100644 index 0000000..b01e038 Binary files /dev/null and b/public/images/culture/turner-ulysses-polyphemus.webp differ diff --git a/public/images/culture/van-thulden-penelope-reunion-small.webp b/public/images/culture/van-thulden-penelope-reunion-small.webp new file mode 100644 index 0000000..fa01e11 Binary files /dev/null and b/public/images/culture/van-thulden-penelope-reunion-small.webp differ diff --git a/public/images/culture/van-thulden-penelope-reunion.webp b/public/images/culture/van-thulden-penelope-reunion.webp new file mode 100644 index 0000000..69d2880 Binary files /dev/null and b/public/images/culture/van-thulden-penelope-reunion.webp differ diff --git a/scripts/content.mjs b/scripts/content.mjs index 294d541..b300fd3 100644 --- a/scripts/content.mjs +++ b/scripts/content.mjs @@ -2,6 +2,7 @@ import fs from 'node:fs'; import path from 'node:path'; import { createHash } from 'node:crypto'; import { decodeHTML } from 'entities'; +import { validateCulture } from './culture.mjs'; import { marked } from 'marked'; import sanitize from 'sanitize-html'; @@ -113,7 +114,8 @@ export function compileEdition(manifest){ if(work.groups?.length&&numbers.some(n=>work.groups.filter(g=>n>=g.start&&n<=g.end).length!==1))throw Error(`Groups must cover every chapter exactly once: ${work.id}`); } const aboutFile=fileInside(root,config.about);const about=splitNotes(rewrite(fs.readFileSync(aboutFile,'utf8'),aboutFile)); - const metadata={...config,chapters:undefined,downloads:(config.downloads??[]).map(d=>({label:d.label,href:`/texts/${d.filename}`})),about}; + const culture=config.culture?validateCulture(JSON.parse(fs.readFileSync(fileInside(root,config.culture),'utf8')),library):undefined; + const metadata={...config,culture,chapters:undefined,downloads:(config.downloads??[]).map(d=>({label:d.label,href:`/texts/${d.filename}`})),about}; return {root,config,metadata,library,chapters,quotes,downloads}; } export function writeEdition(result,out,generated){ diff --git a/scripts/culture.mjs b/scripts/culture.mjs new file mode 100644 index 0000000..86555ea --- /dev/null +++ b/scripts/culture.mjs @@ -0,0 +1,27 @@ +const validId=/^[a-z0-9][a-z0-9-]*$/; +const isUrl=value=>typeof value==='string'&&/^https:\/\//.test(value)&&!/[\\\x00-\x20]/.test(value); +function requiredStrings(value,fields,context){for(const field of fields)if(typeof value?.[field]!=='string'||!value[field].trim())throw Error(`${context}: missing ${field}`);} +export function validateCulture(culture,library){ + requiredStrings(culture,['title','description','featured'],'culture'); + if(!Array.isArray(culture.introduction)||!culture.introduction.length||culture.introduction.some(p=>typeof p!=='string'))throw Error('culture requires introduction paragraphs'); + const chapterIds=new Set(library.map(c=>c.id)); + const verifyChapters=(ids,label)=>{if(!Array.isArray(ids)||!ids.length||ids.some(id=>!chapterIds.has(id)))throw Error(`${label}: unknown or missing chapter references`);}; + const unique=(list,label)=>{if(!Array.isArray(list)||!list.length)throw Error(`culture requires ${label}`);const ids=new Set();for(const item of list){if(typeof item?.id!=='string'||!validId.test(item.id)||ids.has(item.id))throw Error(`${label}: duplicate or invalid ID`);ids.add(item.id);}return ids;}; + const sourceLinks=(sources,label)=>{if(!Array.isArray(sources)||!sources.length||sources.some(s=>!s.label||!isUrl(s.url)))throw Error(`${label}: source links require a label and HTTPS URL`);}; + const artIds=unique(culture.artworks,'artworks');const themeIds=unique(culture.themes,'themes'); + if(!artIds.has(culture.featured))throw Error('culture featured artwork is missing'); + for(const art of culture.artworks){ + requiredStrings(art,['title','artist','date','medium','collection','summary','context','looking','reading','question'],art.id); + if(!['painting','object'].includes(art.kind)||!['episode','context'].includes(art.relationship))throw Error(`${art.id}: invalid artwork classification`); + verifyChapters(art.chapterIds,art.id);sourceLinks(art.sources,art.id); + requiredStrings(art.rights,['label','url','credit'],art.id);if(!isUrl(art.rights.url))throw Error(`${art.id}: missing rights URL`); + requiredStrings(art.image,['src','alt'],art.id); + for(const src of [art.image.src,art.image.thumbnail].filter(Boolean))if(!/^\/images\/[a-zA-Z0-9/_-]+\.(jpg|jpeg|png|webp)$/.test(src)||src.includes('..'))throw Error(`${art.id}: image must be a local /images/ file`); + if(!Number.isInteger(art.image.width)||!Number.isInteger(art.image.height)||art.image.width<1||art.image.height<1)throw Error(`${art.id}: invalid image dimensions`); + } + for(const theme of culture.themes){requiredStrings(theme,['title','lead'],theme.id);verifyChapters(theme.chapterIds,theme.id);sourceLinks(theme.sources,theme.id);if(!Array.isArray(theme.paragraphs)||!theme.paragraphs.length||theme.paragraphs.some(p=>typeof p!=='string'||!p.trim()))throw Error(`${theme.id}: missing paragraphs`);} + if(!Array.isArray(culture.chapters)||!culture.chapters.length)throw Error('culture requires a chapter guide'); + const guideIds=new Set(); + for(const chapter of culture.chapters){requiredStrings(chapter,['chapterId','title','text'],'culture chapter');verifyChapters([chapter.chapterId],chapter.chapterId);if(guideIds.has(chapter.chapterId))throw Error('Duplicate culture chapter guide');guideIds.add(chapter.chapterId);if(!Array.isArray(chapter.themeIds)||chapter.themeIds.some(id=>!themeIds.has(id)))throw Error('Unknown culture theme');} + return culture; +} diff --git a/src/Culture.tsx b/src/Culture.tsx new file mode 100644 index 0000000..01f80a4 --- /dev/null +++ b/src/Culture.tsx @@ -0,0 +1,53 @@ +import { useEffect, useRef, useState } from 'react'; +import { ArrowLeft, ArrowRight, ArrowUpRight, CaretDown, MagnifyingGlassPlus, X } from '@phosphor-icons/react'; +import type { Artwork, Culture as CultureData } from './culture-types'; +import { numberFor, workFor } from './edition'; +import './culture.css'; + +type ChapterEntry={id:string;work:string;number:number;title:string}; +function chapterLabel(entry:ChapterEntry){return `${workFor(entry.work).unit} ${numberFor(entry.work,entry.number)}`;} +function Chapters({ids,library}:{ids:string[];library:ChapterEntry[]}){ + return
{ids.map(id=>{const entry=library.find(c=>c.id===id);return entry&&{chapterLabel(entry)} ;})}
; +} +function Credits({art}:{art:Artwork}){return

{art.rights.credit} {art.rights.label}.

;} + +export function CulturePreview({data}:{data:CultureData}){ + const art=data.artworks.find(a=>a.id===data.featured)!; + return
{art.image.alt}
{art.artist}, {art.title}, {art.date}.

Art & culture

Another way
into the story.

{data.description}

Explore the collection
; +} + +export default function Culture({data,path,library}:{data:CultureData;path:string;library:ChapterEntry[]}){ + const [kind,setKind]=useState('all'); + const [book,setBook]=useState('all'); + const [expanded,setExpanded]=useState(false); + const [zoomed,setZoomed]=useState(false); + const dialog=useRef(null); + const closeButton=useRef(null); + const id=path.startsWith('/culture/')?path.slice('/culture/'.length):null; + const art=data.artworks.find(a=>a.id===id); + const featured=data.artworks.find(a=>a.id===data.featured)!; + const filtered=data.artworks.filter(a=>(kind==='all'||a.kind===kind)&&(book==='all'||a.chapterIds.includes(book))); + useEffect(()=>{setExpanded(false);},[path]); + useEffect(()=>{const d=dialog.current;if(!d)return;if(expanded&&!d.open){setZoomed(false);d.showModal();closeButton.current?.focus();}else if(!expanded&&d.open)d.close();},[expanded]); + if(id&&!art)return

This work isn’t in the collection.

Return to art & culture
; + if(art)return
+ Art & culture +

{art.relationship==='episode'?'The story in images':'Material culture'}

{art.title}

{art.artist} · {art.date}

+
{art.medium} · {art.collection}{art.accession?` · ${art.accession}`:''}
+
+

The work in its own time

{art.context}

Look closely

{art.looking}

Reading it with the text

Interpretation

{art.reading}

A question to sit with

{art.question}

+ + setExpanded(false)} onClose={()=>setExpanded(false)} onClick={e=>{if(e.target===e.currentTarget)setExpanded(false);}}>
{art.image.alt}/
{art.title} · {art.artist}
+
; + return
+
The reading edition

{data.title}

{data.description}

+
{featured.image.alt}
{featured.artist}, {featured.title}, {featured.date}.Look closer
+
{data.introduction.map(p=>

{p}

)}
+

The artworks

{[['all','All works'],['painting','Paintings & prints'],['object','Objects & manuscripts']].map(([value,label])=>)}

{filtered.length} {filtered.length===1?'work':'works'}{book!=='all'?' connected to this book':''}

+ {filtered.length?
{filtered.map(a=>
{a.image.alt}

{a.artist} · {a.date}

{a.title}

{a.summary}
)}
:

No works match this combination. The book-by-book guide still offers cultural context for every chapter.

} +
+

The world of the book

Cultural themes that travel through the story, connecting everyday practices, images, and ideas to the text.

{data.themes.map(theme=>
{theme.title}{theme.lead}
{theme.paragraphs.map(p=>

{p}

)}

Sources: {theme.sources.map((s,i)=>{i>0?' · ':''}{s.label})}

)}
+

Culture, book by book

A cultural thread through the whole work. Links lead back to the complete text; the connections below are reading prompts, not claims that every object illustrates the text.

{data.chapters.map(c=>{const entry=library.find(e=>e.id===c.chapterId)!;const connected=data.artworks.filter(a=>a.chapterIds.includes(c.chapterId));return ;})}
+

Artwork facts follow the linked collection records. Close-looking and literary connections are this edition’s interpretations. The objects and images belong to different historical worlds. The collection includes full-story spoilers.

+
; +} diff --git a/src/culture-types.ts b/src/culture-types.ts new file mode 100644 index 0000000..0cec574 --- /dev/null +++ b/src/culture-types.ts @@ -0,0 +1,14 @@ +export type CultureSource = {label:string;url:string}; +export type Artwork = { + id:string;title:string;artist:string;date:string;medium:string;collection:string;accession?:string; + kind:'painting'|'object';relationship:'episode'|'context';chapterIds:string[]; + image:{src:string;alt:string;width:number;height:number;thumbnail?:string}; + summary:string;context:string;looking:string;reading:string;question:string; + sources:CultureSource[];rights:{label:string;url:string;credit:string}; +}; +export type CulturalTheme = {id:string;title:string;lead:string;paragraphs:string[];chapterIds:string[];sources:CultureSource[]}; +export type Culture = { + title:string;description:string;introduction:string[];featured:string; + artworks:Artwork[];themes:CulturalTheme[]; + chapters:{chapterId:string;title:string;text:string;themeIds:string[]}[]; +}; diff --git a/src/culture.css b/src/culture.css new file mode 100644 index 0000000..4c24576 --- /dev/null +++ b/src/culture.css @@ -0,0 +1,11 @@ +/* Art and culture inherits the reading edition: whole images, quiet captions, room to look. */ +.culture-page,.art-detail{max-width:1324px;margin:0 auto;padding:30px 0 80px}.culture-heading{max-width:880px;margin:0 0 42px}.culture-heading h1{font:450 clamp(40px,4.6vw,65px)/1.16 'Literata Variable',Georgia,serif;letter-spacing:-.03em;margin:24px 0 20px;text-wrap:balance}.culture-heading>p{font:20px/1.7 var(--reading-font);max-width:620px;color:var(--muted)}.culture-navigation{display:flex;flex-wrap:wrap;gap:18px 35px;margin-top:30px}.culture-navigation a{font-size:13px;text-decoration:none;min-height:44px;display:flex;align-items:center;border-bottom:1px solid var(--rule)}.culture-feature{margin:0}.culture-feature>a{display:block}.culture-feature img{width:100%;max-height:690px;object-fit:contain;background:var(--wash)}.culture-feature figcaption{display:flex;justify-content:space-between;align-items:baseline;gap:20px;font-size:12px;line-height:1.7;color:var(--muted);padding-top:14px}.culture-feature figcaption>a{display:inline-flex;gap:8px;align-items:center;white-space:nowrap;min-height:36px;text-decoration:none}.culture-introduction{max-width:760px!important;margin:65px 0 80px!important}.culture-page h2{font:450 clamp(30px,3vw,42px)/1.25 'Literata Variable',Georgia,serif;letter-spacing:-.025em}.culture-gallery,.culture-world,.culture-books{scroll-margin-top:28px}.gallery-controls{display:flex;justify-content:space-between;align-items:end;gap:24px;border-bottom:1px solid var(--rule);padding:20px 0 0}.gallery-controls .work-tabs button{font-size:13px;padding:15px 0}.art-book-filter{display:flex;align-items:center;gap:12px;font-size:12px;color:var(--muted);padding:0 0 12px}.art-book-filter select{font:inherit;color:var(--ink);background:var(--leaf);border:1px solid var(--rule);padding:9px 34px 9px 12px;border-radius:3px;min-height:42px;max-width:210px}.gallery-result{font-size:12px;color:var(--muted);margin:18px 0 30px}.art-grid{display:grid;grid-template-columns:minmax(0,1.2fr) minmax(0,1fr);gap:65px 64px;align-items:start}.gallery-work:nth-child(even){padding-top:80px}.gallery-work>a{display:block;text-decoration:none}.art-image{display:flex;justify-content:center;align-items:center;background:var(--wash);min-height:240px;overflow:hidden}.art-image img{width:100%;max-height:490px;object-fit:contain}.gallery-work.object .art-image{padding:20px}.gallery-work.object .art-image img{max-height:440px}.gallery-work figcaption{padding-top:19px}.gallery-work figcaption>p{font-size:12px;color:var(--muted);line-height:1.7;margin-bottom:7px}.gallery-work h3{display:flex;align-items:baseline;justify-content:space-between;gap:18px;font:450 25px/1.4 'Literata Variable',Georgia,serif;letter-spacing:-.015em}.gallery-work h3 svg{flex-shrink:0;transition:transform 180ms var(--ease-out)}.gallery-work figcaption>span{display:block;max-width:520px;font-size:14px;line-height:1.75;color:var(--muted);margin-top:10px}.gallery-empty{padding:30px 0;max-width:560px;line-height:1.8}.culture-world,.culture-books{margin-top:100px}.culture-world>header,.culture-books>header{max-width:740px;margin-bottom:35px}.culture-world>header>p,.culture-books>header>p{font:18px/1.8 var(--reading-font);color:var(--muted);margin-top:18px}.cultural-themes{border-top:1px solid var(--rule)}.cultural-themes details{border-bottom:1px solid var(--rule);scroll-margin-top:30px}.cultural-themes summary{list-style:none;cursor:pointer;padding:25px 0;display:flex;justify-content:space-between;align-items:center;gap:35px}.cultural-themes summary::-webkit-details-marker{display:none}.cultural-themes summary strong{display:block;font:450 24px/1.4 'Literata Variable',Georgia,serif}.cultural-themes summary span>span{display:block;font-size:13px;line-height:1.7;color:var(--muted);margin-top:7px}.cultural-themes summary svg{flex-shrink:0;transition:transform 180ms var(--ease-out)}.cultural-themes details[open] summary svg{transform:rotate(180deg)}.theme-body{margin:8px 0 30px!important;max-width:760px!important}.theme-sources{font:12px/1.8 'Manrope Variable',sans-serif!important;color:var(--muted);margin-top:20px!important}.culture-chapter-links{display:flex;flex-wrap:wrap;gap:8px 22px;margin:20px 0}.culture-chapter-links a{display:inline-flex;align-items:center;gap:6px;min-height:36px;font:12px/1.5 'Manrope Variable',sans-serif;text-decoration:none;color:var(--accent)}.culture-book-grid{display:grid;grid-template-columns:1fr 1fr;gap:42px 70px}.culture-book-grid article{border-top:1px solid var(--rule);padding-top:22px;scroll-margin-top:35px}.culture-book-title{text-decoration:none}.culture-book-title>span{font-size:12px;color:var(--accent)}.culture-book-title h3{display:flex;align-items:baseline;justify-content:space-between;gap:18px;font:450 23px/1.5 'Literata Variable',Georgia,serif;margin:8px 0 16px}.culture-book-grid article>p{font:16px/1.8 var(--reading-font);color:var(--muted)}.book-art-links{display:flex;flex-wrap:wrap;gap:10px 20px;margin-top:18px}.book-art-links a{font-size:12px;line-height:1.6;color:var(--accent);min-height:28px}.culture-colophon{font-size:12px;line-height:1.9;color:var(--muted);max-width:760px;margin:65px 0 0;padding-top:30px;border-top:1px solid var(--rule)} +.culture-preview{max-width:1208px;margin:0 auto 84px;display:grid;grid-template-columns:1.35fr 1fr;align-items:center;gap:65px}.culture-preview figure img{width:100%;height:auto}.culture-preview figcaption{font-size:11px;color:var(--muted);line-height:1.7;margin-top:12px}.culture-eyebrow{font-size:12px;line-height:1.8;color:var(--accent);margin-bottom:16px}.culture-preview h2{font:450 38px/1.25 'Literata Variable',Georgia,serif;letter-spacing:-.025em}.culture-preview>div>p:not(.culture-eyebrow){font:18px/1.8 var(--reading-font);color:var(--muted);margin:22px 0;max-width:360px}.culture-foundations{display:block;font-size:12px;line-height:1.7;color:var(--muted);margin-top:13px}.culture-nav{white-space:nowrap}.chapter-culture-link{display:inline-flex;gap:9px;align-items:center;font-size:12px;color:var(--accent);text-decoration:none;min-height:40px;margin-top:14px} +.art-heading{max-width:980px;margin:33px 0}.art-heading h1{font:450 clamp(32px,4vw,54px)/1.24 'Literata Variable',Georgia,serif;letter-spacing:-.025em;text-wrap:balance;margin:12px 0 18px}.art-heading>p:last-child{font-size:15px;line-height:1.8;color:var(--muted)}.art-plate{margin:0}.art-enlarge{display:block;position:relative;width:100%;padding:0;background:var(--wash);text-align:left}.art-enlarge>img{width:100%;max-height:760px;object-fit:contain}.art-enlarge>span{display:flex;justify-content:flex-end;align-items:center;gap:8px;padding:10px 14px;min-height:42px;font-size:12px;background:var(--leaf);color:var(--accent)}.art-plate>figcaption{font-size:12px;line-height:1.8;color:var(--muted);padding:17px 0}.art-credit{font-size:11px;line-height:1.8;margin:5px 0 0}.art-essay-layout{display:grid;grid-template-columns:270px minmax(0,760px);gap:80px;margin:60px 0 0;justify-content:space-between}.art-facts{font-size:14px;line-height:1.9;color:var(--muted)}.art-facts h2{font-size:12px;font-weight:700;line-height:1.7;margin:30px 0 12px;color:var(--ink)}.art-facts>a{display:block;font-size:12px;line-height:1.8;margin:10px 0;color:var(--accent)}.art-relationship{font-size:12px}.art-essay{width:100%}.art-essay section:first-child{margin-top:0}.art-essay section{margin-top:40px}.art-essay .art-reading-label{font:11px/1.6 'Manrope Variable',sans-serif;color:var(--muted);margin:-8px 0 12px}.looking-question{border-top:1px solid var(--rule);padding-top:30px}.art-next{display:grid;grid-template-columns:1fr 1fr;gap:50px;margin-top:70px;border-top:1px solid var(--rule);padding-top:35px}.art-next>a{display:grid;grid-template-columns:130px 1fr;align-items:center;gap:24px;text-decoration:none}.art-next img{width:130px;height:115px;object-fit:contain;background:var(--wash)}.art-next>a>span{font:18px/1.5 'Literata Variable',Georgia,serif}.art-next small{display:block;font:11px/1.6 'Manrope Variable',sans-serif;color:var(--muted);margin-bottom:8px}.art-next svg{margin-left:12px}.art-lightbox{border:0;background:var(--leaf);color:var(--ink);padding:60px 24px 25px;width:calc(100% - 40px);max-width:1500px;max-height:94dvh;border-radius:4px;overscroll-behavior:contain}.art-lightbox::backdrop{background:#061e2de0}.art-lightbox>.icon-button{position:absolute;right:12px;top:10px}.art-zoom-stage>img{width:100%;height:auto;max-height:calc(94dvh - 150px);object-fit:contain}.art-lightbox figcaption{font-size:13px;line-height:1.7;margin-top:16px;text-align:center}.art-lightbox[open]{animation:art-open 200ms var(--ease-out)}@keyframes art-open{from{opacity:.5;transform:translateY(8px)}to{opacity:1;transform:translateY(0)}} +@media(hover:hover) and (pointer:fine){.gallery-work>a:hover h3,.culture-navigation a:hover{color:var(--accent)}.gallery-work>a:hover h3 svg{transform:translate(2px,-2px)}} +@media(max-width:1440px){.culture-page,.art-detail{margin:0 58px}.culture-preview{margin-left:58px;margin-right:58px}} +@media(max-width:1000px){.art-essay-layout{grid-template-columns:220px minmax(0,1fr);gap:35px}.art-grid{gap:40px}.culture-preview{gap:35px}.gallery-controls{align-items:start;flex-direction:column;gap:15px}.art-book-filter{padding-bottom:15px}.art-next>a{grid-template-columns:100px 1fr;gap:16px}.art-next img{width:100px;height:100px}} +@media(max-width:700px){.culture-page,.art-detail{margin:0;padding:24px 25px 55px}.culture-heading{margin-bottom:30px}.culture-heading h1{font-size:38px;margin-top:22px}.culture-heading>p{font-size:18px}.culture-navigation{gap:10px 22px;margin-top:24px}.culture-navigation a{font-size:12px}.culture-feature figcaption{align-items:start;font-size:11px;flex-direction:column;gap:5px}.culture-introduction{margin:40px 0 55px!important}.art-grid{grid-template-columns:1fr;gap:38px}.gallery-work:nth-child(even){padding-top:0}.gallery-controls .work-tabs{gap:22px}.gallery-controls .work-tabs button{font-size:12px}.art-image{min-height:0}.art-image img{max-height:420px}.gallery-work h3{font-size:23px}.culture-world,.culture-books{margin-top:65px}.culture-world>header>p,.culture-books>header>p{font-size:17px}.cultural-themes summary{gap:20px;padding:23px 0}.cultural-themes summary strong{font-size:22px}.culture-book-grid{grid-template-columns:1fr;gap:32px}.culture-preview{margin:0 25px 60px;grid-template-columns:1fr;gap:26px}.culture-preview h2{font-size:33px}.culture-preview h2 br{display:none}.culture-preview>div>p:not(.culture-eyebrow){max-width:none;font-size:17px;margin:15px 0}.culture-nav .culture-nav-extra{display:none}.art-essay-layout{grid-template-columns:1fr;gap:25px;margin-top:30px}.art-facts{border-bottom:1px solid var(--rule);padding-bottom:22px}.art-facts>h2{margin-top:20px}.art-facts>a{display:inline-block;margin-right:20px}.art-facts .culture-chapter-links{margin:8px 0 12px}.art-heading{margin:24px 0}.art-heading h1{font-size:33px}.art-plate>figcaption{font-size:11px}.art-next{grid-template-columns:1fr;gap:25px}.art-lightbox{width:calc(100% - 20px);padding:60px 12px 20px}.art-zoom-stage>img{max-height:calc(94dvh - 195px)}.art-lightbox figcaption{font-size:12px}.culture-colophon{margin-top:40px}} +@media(prefers-reduced-motion:reduce){.art-lightbox[open]{animation:none}.gallery-work h3 svg,.cultural-themes summary svg{transition:none}} + +.art-zoom-button{position:absolute;top:10px;left:20px;min-height:44px;padding:0 10px;font-size:13px;color:var(--accent)}.art-zoom-stage{max-height:calc(94dvh - 175px)}.is-zoomed .art-zoom-stage{overflow:auto;overscroll-behavior:contain}.is-zoomed .art-zoom-stage>img{width:200%;max-width:none;max-height:none;height:auto}.art-zoom-stage:focus-visible{outline:2px solid var(--accent);outline-offset:4px} diff --git a/src/edition.ts b/src/edition.ts index 9347bad..176101f 100644 --- a/src/edition.ts +++ b/src/edition.ts @@ -1,4 +1,5 @@ import data from './generated/edition.json'; +import type { Culture } from './culture-types'; type Image = {src:string;alt:string;width:number;height:number}; type Link = {href:string;label:string}; @@ -6,7 +7,7 @@ export type Work = {id:string;title:string;author?:string;unit:string;plural?:st type Edition = { site:{name:string;wordmark?:string;title:string;description:string;storageKey:string;lang?:string;direction?:'ltr'|'rtl';footer?:string;searchPlaceholder?:string;typePreview?:string}; cover:{author?:string;prefix?:string;title:string;description?:string;caption?:string;editionLine?:string;start:string;image?:Image}; - works:Work[];navLink?:Link;sectionLabels?:Record; + works:Work[];culture?:Culture;navLink?:Link;sectionLabels?:Record; frontispiece?:{image:Image;caption:string;title:string;description:string;href:string;linkLabel:string}; downloads:{href:string;label:string}[]; about:{title:string;intro:string;sections:{id:string;title:string;html:string}[]}; diff --git a/src/main.tsx b/src/main.tsx index 2c4fa5d..a0f10dd 100644 --- a/src/main.tsx +++ b/src/main.tsx @@ -6,6 +6,7 @@ import '@fontsource-variable/literata/standard-italic.css'; import '@fontsource-variable/manrope'; import './style.css'; import { edition, workFor, numberFor } from './edition'; +import Culture, { CulturePreview } from './Culture'; type Section = {id:string;title:string;html:string;text:string}; type Entry = {id:string;work:string;number:number;title:string;words:number;minutes:number;quoteCount:number;search:string;sections:{id:string;title:string}[]}; @@ -91,7 +92,7 @@ function App(){ if(!writeStore('preferences',preferences))setStorageIssue(true); },[preferences]); useEffect(()=>{ - if(!chapterId){setChapter(null);document.title=edition.site.title;return;} + if(!chapterId){setChapter(null);return;} setChapter(null);setChapterError(false); const c=new AbortController(); fetch(`/content/${encodeURIComponent(chapterId)}.json`,{signal:c.signal}).then(r=>{if(!r.ok)throw Error();return r.json();}) @@ -99,6 +100,12 @@ function App(){ .catch(e=>{if(e.name!=='AbortError')setChapterError(true);}); return()=>c.abort(); },[chapterId]); + useEffect(()=>{ + if(chapterId)return; + const culture=edition.culture; + const art=culture?.artworks.find(a=>path===`/culture/${a.id}`); + document.title=culture&&path.startsWith('/culture')?`${art?.title??culture.title} · Art & culture`:edition.site.title; + },[path,chapterId]); useEffect(()=>{ if(chapterId||!library.length)return; const frame=requestAnimationFrame(()=>{ @@ -205,6 +212,7 @@ function App(){ {edition.site.wordmark??edition.site.name}. {chapterError?

We couldn’t open this chapter.

The link may be wrong, or your connection may have dropped.

Return to contents
:!chapter?

Opening the book…

:<> -

{mode==='text'?workName:label(chapter)}

{mode==='text'?label(chapter):chapter.title}

{mode==='notes'?`${chapter.minutes} minute commentary · ${chapter.quoteCount?`${chapter.quoteCount} selected passages`:'A reading companion'}`:activeWork.sourceLabel??activeWork.author}

+

{mode==='text'?workName:label(chapter)}

{mode==='text'?label(chapter):chapter.title}

{mode==='notes'?`${chapter.minutes} minute commentary · ${chapter.quoteCount?`${chapter.quoteCount} selected passages`:'A reading companion'}`:activeWork.sourceLabel??activeWork.author}

{edition.culture?.chapters.some(c=>c.chapterId===chapter.id)&&Art & cultural context }
{mode==='text'&&chapter.source?<>
{activeWork.sourceNote}Edition notes
{chapter.source.formatted.map((p,i)=>

)}:<>

Text, sources & reading key

Text marks what happens; Interpretation marks an argued reading. Other labels identify context, comparisons and open questions. Full reading key

{selectedSection&&

{selectedSection.title}

}}
diff --git a/test/unit/culture.test.mjs b/test/unit/culture.test.mjs new file mode 100644 index 0000000..e6e717f --- /dev/null +++ b/test/unit/culture.test.mjs @@ -0,0 +1,23 @@ +import test from 'node:test';import assert from 'node:assert/strict';import fs from 'node:fs';import path from 'node:path';import { createHash } from 'node:crypto'; +import { compileEdition } from '../../scripts/content.mjs';import { validateCulture } from '../../scripts/culture.mjs'; +const root=path.resolve(import.meta.dirname,'../..'); +const edition=compileEdition(path.join(root,'editions/odyssey/edition.json')); +test('culture has twelve credited images, eight themes and all 24 Odyssey books',()=>{ + const c=edition.metadata.culture;assert.equal(c.artworks.length,12);assert.equal(c.themes.length,8); + assert.deepEqual(c.chapters.map(e=>e.chapterId),Array.from({length:24},(_,i)=>`odyssey-${i+1}`)); + const records=JSON.parse(fs.readFileSync(path.join(root,'editions/odyssey/culture-sources.json'))).records; + for(const art of c.artworks){const record=records.find(r=>r.id===art.id);assert.ok(record);for(const src of [art.image.src,art.image.thumbnail]){const f=record.files.find(f=>f.path===`public${src}`);assert.ok(f);const bytes=fs.readFileSync(path.join(root,f.path));assert.equal(createHash('sha256').update(bytes).digest('hex'),f.sha256);}} +}); +test('culture rejects broken references and missing provenance before publishing',()=>{ + for(const [mutate,error] of [ + [c=>c.artworks[0].chapterIds=['missing'],/chapter references/], + [c=>delete c.artworks[0].rights,/missing label/], + [c=>delete c.artworks[0].id,/invalid ID/], + [c=>c.artworks[0].image.src='https://external.test/art.jpg',/local/], + [c=>c.artworks[0].sources[0].url='javascript:alert(1)',/HTTPS/], + [c=>c.featured='missing',/featured/], + [c=>c.chapters[0].themeIds=['missing'],/Unknown culture theme/], + [c=>c.chapters.push(c.chapters[0]),/Duplicate culture chapter/], + ]){const c=structuredClone(edition.metadata.culture);mutate(c);assert.throws(()=>validateCulture(c,edition.library),error);} +}); +test('the standalone starter does not require or inherit Odyssey culture',()=>{assert.equal(compileEdition(path.join(root,'editions/starter/edition.json')).metadata.culture,undefined);}); diff --git a/tests/culture.spec.ts b/tests/culture.spec.ts new file mode 100644 index 0000000..8cdf13e --- /dev/null +++ b/tests/culture.spec.ts @@ -0,0 +1,52 @@ +import { test, expect } from '@playwright/test'; +import AxeBuilder from '@axe-core/playwright'; +import fs from 'node:fs'; +import type { Culture } from '../src/culture-types'; +const culture=JSON.parse(fs.readFileSync(new URL('../editions/odyssey/culture.json',import.meta.url),'utf8')) as Culture; + +test('gallery filters, empty state, cultural essays and whole-book guide',async({page})=>{ + await page.goto('/');await page.getByRole('link',{name:'Art & culture',exact:true}).click(); + await expect(page.locator('.gallery-work')).toHaveCount(12); + await page.getByRole('button',{name:'Paintings & prints',exact:true}).click();await expect(page.locator('.gallery-work')).toHaveCount(6); + await page.getByRole('button',{name:'Objects & manuscripts',exact:true}).click();await expect(page.locator('.gallery-work')).toHaveCount(6); + await page.getByRole('combobox',{name:'Filter artworks by book'}).selectOption('odyssey-20');await expect(page.locator('.gallery-work')).toHaveCount(1); + await page.getByRole('button',{name:'Paintings & prints',exact:true}).click();await expect(page.locator('.gallery-empty')).toBeVisible(); + await page.getByRole('button',{name:'Show all works'}).click();await expect(page.locator('.gallery-work')).toHaveCount(12); + await expect(page.locator('.culture-book-grid>article')).toHaveCount(24); + for(const theme of culture.themes){const details=page.locator(`#theme-${theme.id}`);await details.locator('summary').focus();await page.keyboard.press('Enter');await expect(details).toHaveAttribute('open','');await expect(details.locator('.theme-body')).toBeVisible();await page.keyboard.press('Enter');await expect(details).not.toHaveAttribute('open','');} + await page.locator('.culture-navigation a[href$="#books"]').click();await expect(page.locator('#books')).toBeFocused(); + await page.locator('#culture-odyssey-24 .culture-book-title').click();await expect(page.locator('#original-0')).toBeVisible(); + await page.getByRole('link',{name:'Art & cultural context'}).click();await expect(page.locator('#culture-odyssey-24')).toBeFocused(); +}); + +test('every artwork has a working image, credits, chapter links and enlargement',async({page})=>{ + const errors:string[]=[];page.on('pageerror',e=>errors.push(e.message)); + for(const art of culture.artworks){ + await page.goto(`/culture/${art.id}`);await expect(page.locator('main h1')).toHaveText(art.title);await expect(page).toHaveTitle(`${art.title} · Art & culture`); + const image=page.locator('.art-plate img');await expect.poll(()=>image.evaluate((e:HTMLImageElement)=>e.complete&&e.naturalWidth>0)).toBeTruthy(); + await expect(page.locator('.art-plate .art-credit a')).toHaveAttribute('href',art.rights.url); + await expect(page.locator('.art-facts .culture-chapter-links a')).toHaveCount(art.chapterIds.length); + const trigger=page.locator('.art-enlarge');await trigger.click();await expect(page.locator('.art-lightbox')).toBeVisible(); + await expect(page.getByRole('button',{name:'Close enlarged artwork',exact:true})).toBeFocused(); + await page.getByRole('button',{name:'Zoom in',exact:true}).click();await expect(page.getByRole('region',{name:'Scrollable artwork detail'})).toBeVisible(); + expect(await page.locator('.art-zoom-stage').evaluate(e=>e.scrollWidth>e.clientWidth)).toBeTruthy(); + await page.getByRole('button',{name:'Fit to screen',exact:true}).click(); + await page.keyboard.press('Escape');await expect(page.locator('.art-lightbox')).not.toBeVisible();await expect(trigger).toBeFocused(); + } + expect(errors).toEqual([]); +}); + +test('art remains accessible on a phone, in night mode and with large text',async({page})=>{ + await page.goto('/culture/circe-calyx-krater'); + await page.getByRole('button',{name:'Reading appearance',exact:true}).click();await page.getByRole('button',{name:'Largest text',exact:true}).click();await page.locator('.font-clear').click();await page.locator('.night-option').click();await page.keyboard.press('Escape'); + for(const width of [320,390,768,1440]){await page.setViewportSize({width,height:900});expect(await page.evaluate(()=>document.documentElement.scrollWidth<=innerWidth)).toBeTruthy();} + expect((await new AxeBuilder({page}).withTags(['wcag2a','wcag2aa','wcag21aa']).analyze()).violations).toEqual([]); + await page.emulateMedia({reducedMotion:'reduce'});await page.locator('.art-enlarge').click();await expect(page.locator('.art-lightbox')).toHaveCSS('animation-name','none'); + expect((await new AxeBuilder({page}).withTags(['wcag2a','wcag2aa','wcag21aa']).analyze()).violations).toEqual([]); + await page.getByRole('button',{name:'Close enlarged artwork'}).click();await page.locator('.back-link').click(); + await page.setViewportSize({width:320,height:900});expect(await page.evaluate(()=>document.documentElement.scrollWidth<=innerWidth)).toBeTruthy(); + await page.locator('#theme-hospitality summary').click(); + expect((await new AxeBuilder({page}).withTags(['wcag2a','wcag2aa','wcag21aa']).analyze()).violations).toEqual([]); +}); + +test('unknown artwork route gives a working way back',async({page})=>{await page.goto('/culture/absent-work');await page.getByRole('link',{name:'Return to art & culture'}).click();await expect(page.locator('.gallery-work')).toHaveCount(12);});