Code, design and documents 51 files
.gitignore +1 −0
@@ -11,2 +11,3 @@ tmp/
/books/
renders/
+/*.mkv
astro.config.mjs +2 −0
@@ -56,4 +56,6 @@ export default defineConfig({
site: process.env.SITE || "https://cannabisfreedom.fyi",
base,
+ // exhibitions moved from culture/exhibitions/ to exhibitions/, and each one to the shortest address, <slug>/
+ redirects: { "/culture/exhibitions": "/exhibitions", "/culture/exhibitions/reefer-madness": "/reefer-madness" },
// Icons are Iconoir (https://iconoir.com/), through astro-icon and Iconify: `<Icon name="iconoir:…" />`
// inlines the SVG at build time, so only the icons a page uses end up in it.
docs/CULTURE.md +4 −0
@@ -230,4 +230,8 @@ The site turns that into a figure with the caption and credit underneath. Rules:
appear in the atlas entry's history list. If the text names a law that has no history entry,
add the entry to `changes.yaml` first.
+- Every id in `history` must be mentioned and linked in the text (`/history/<id>/`), and every
+ history entry the text links to must be in `history`. The build fails otherwise. When a new
+ history entry concerns a person, word or tradition that has an atlas entry, add it to that
+ entry's `history` (and `history_cards` if it is about them) and write it into the text with a link.
- `history_cards` selects which of those events should show this entry under **In the cultural
atlas** on the history page. Use it only when the event is specifically about the word,
docs/research/history-belarus-gap-2026-10-22.md +130 −0
@@ -0,0 +1,130 @@
+# Belarus history expansion, 22 October 2026
+
+Belarus had eleven events and no articles at the start of this batch. It now has fourteen
+events and six articles. Project totals are 2,833 events and 722 articles. The ongoing history
+coverage goal remains open.
+
+Read `docs/WRITING.md` and the current category rules in `docs/DATA-MAINTENANCE.md` before
+editing. All nine added or revised events have vocabulary-checked tags and country-first,
+plain-language titles below eighty characters. Articles have an unheaded opening and four
+sections, with 390–407 words each.
+
+## Changes made
+
+| Event | Treatment |
+| --- | --- |
+| `belarus-narcotics-law-2012` | Retained signature date, 13 July 2012; specified that this was signing, retained `other`, added home-growing tag and accessible statutory clause. |
+| `belarus-decree-6-2014` | Stable ID retained; moved to operative date, 1 January 2015. Narrowed the maximum-sentence claim to supply causing death and the age change to supply. Added article. |
+| `belarus-cultivation-ban-2016` | Retained 31 December commencement; changed category to `hemp`. Replaced generic first-growing-ban claim with the environmental list’s actual scope. Added article. |
+| `belarus-mothers-328-hunger-strike-2018` | Kept month-level date; replaced conflicting participant totals and unsupported prisoner totals. Added article and links to later reform. |
+| `belarus-article-328-amended-2019` | Moved from July to 27 October commencement. Distinguished basic supply, aggravated supply and organised-group offences. Added article. |
+| `belarus-victory-amnesty-2025` | Retained 30 April lower-house stage; changed category to `debate`, effects conditional, and linked final act. |
+| `belarus-drug-sentencing-options-2025` | New scored `easing` event, 3 March, with article. |
+| `belarus-first-drug-offence-amnesty-2025` | New conditional-clemency `easing` event, 8 May, with article and an explicit unscored reason. |
+| `belarus-drug-sentence-report-2026` | New `enforcement` event, 23 June; official sentencing report, with all-drug scope explicit. |
+
+## Legal sources read and their limits
+
+Research downloads, extracted text, the visually inspected environmental gazette page,
+baseline snapshots, source metadata, apply script and verifier are retained in
+`tmp/history-belarus-gap-2026-10-22/`. The build output has been removed.
+
+- **2019 Criminal Code:** WIPO Lex record 20145 and its full statutory text. Read articles
+ 54–55, 328, 328² and 329. This is a historical consolidation, not a complete 2026 code.
+ It distinguishes short custody, restriction of liberty, personal possession, supply,
+ repeat public-use offences and cultivation. The cultivation clause was also read in the
+ public AVCD reproduction and agrees on the basic fine/short-custody/up-to-three-year
+ alternatives. AVCD’s separate drug-possession page is stale and was rejected as a source
+ for the 2025 changes despite its current-year page title.
+- **2012 cultivation clause:** Read article 11 in Zakony-by’s statutory reproduction.
+ The original Etalon page was region-blocked. Alta’s full reproduction was partly readable
+ earlier but later timed out; no article relying on unread sections was added. The entry
+ uses the signed date, not an assumed commencement date.
+- **2014 decree:** The President’s site returned its caption without operative body.
+ SPInform’s free preview supplied the preamble, early paragraphs and commencement metadata.
+ The detailed policy discussion comes from printed page 8 onwards of the 2021 Institute
+ of Sociology assessment hosted by UN Belarus. The title page identifies ministries, the
+ academy institute and OHCHR; its disclaimer does not make every research conclusion a UN
+ institutional position. Later code wording supplies the precisely qualified sentence
+ ranges. The free preview was not treated as the full decree.
+- **2016 environment rule:** Downloaded the original two-page gazette PDF from FAOLEX,
+ extracted it and visually inspected page 1. Resolution 35 amends the 2008 rule, removes
+ its wild-growing qualifier, replaces the plant list and specifies commencement. The
+ heading concerns introduction/acclimatisation. The text does not itself establish all
+ the crop-growing, sale, destruction and scientific-exception claims repeated in news
+ reports. Those broader claims were omitted. It does not prove that every listed species
+ was newly prohibited in 2016.
+- **2019 amendment:** Read the actual drug amendment in SPInform’s public preview of law
+ 220-Z. Article 1(2) lowers the two minima; metadata gives 27 October commencement.
+ Read the Current Time vote report for campaign reaction. Its June vote date is not the
+ final operative date, and its description of the group tier is less precise than the law.
+- **2025 reform:** Read the public amendment metadata for law 61-Z, the National Legal
+ Portal explanation reproduced by Seldon, the updated statutory reproduction at Legalize
+ Belarus, and the official prosecutor’s public post. The original national legal-register
+ sites were region-blocked; no access restriction or paid preview was bypassed. The
+ legal-portal explanation supplies the operative date and rationale. The three independent
+ accessible texts agree on the drug-sentencing changes.
+- **2025 amnesty:** Read law 70-Z’s public opening articles and commencement metadata,
+ LawPike’s reproduced exclusions, and Minsk lawyer Kristina Asryan’s explanation of the
+ relief routes and implementation. Eligibility is conditional; the first-conviction
+ exception for the principal drug offence’s first four parts does not also appear in
+ part 5. Other eligibility exceptions exist, so no blanket claim that every death-related
+ case was excluded was made. Later clauses outside the legal database’s preview were
+ not represented as personally read in the original act.
+- **2025 controlled-drug list:** Downloaded the Health Ministry consolidation hosted by
+ the Eurasian Economic Commission. Read its title/amendment history and list 5 entries
+ 5.1, 5.4 and 5.5. The explicit whole-seed exclusion replaces the profile’s unsupported
+ claim that the narcotics legislation says nothing about seeds. Holding seed status and
+ trade status were preserved; no permission to sow was inferred.
+
+## Reporting and attribution
+
+The 2018 Krytyka Polityczna interview was read for the campaign and participant’s account.
+Its synthetic-drug family case is identified as such. Its numerical mapping of proposed
+penalties conflicts with the enacted law and was not used to describe the offences.
+Advocacy prisoner totals and claims about typical marijuana sentences were removed from
+the current profile rather than repeated as official statistics.
+
+The 2026 sentencing figures come from the Prosecutor General’s own public Telegram post
+13713, timestamped 23 June. They cover drug offences generally. The article and profile do
+not relabel restriction of liberty as probation or assume it is always served at home.
+Secondary first-half-2026 court statistics were left out because the underlying court data
+were not recovered. A separate Supreme Court joint-purchase interpretation remains a lead;
+it was not turned into a new sharing exception or score change without the ruling.
+
+## Profile and score treatment
+
+Replaced the profile’s stale possession and supply ranges with the 2025 alternatives.
+Explained the difference between short custody and restriction of liberty, corrected the
+separate cultivation offence, and included the repeated-public-use criminal rule. Removed
+unsupported claims about typical prison sentences, uniforms and club nonexistence.
+Preserved recreational and medical classifications, planting and club/visitor statuses,
+and the independent UNODC seizure block verbatim.
+
+Current cultivation is corrected from 0 to 1 because the separate basic offence includes a
+fine and shorter alternatives. This is a calibration correction, not a claim that growing
+was newly liberalised in 2025: the 2015 step no longer invents a cultivation decline.
+The March 2025 event raises enforcement from 0 to 1 for the new shorter sentencing options,
+supported by later official practice. Possession, sharing, consumption and access stay 0.
+
+The resulting rounded chain is 1934: 100→19; 1974: 19→8; January 2015: 8→3;
+March 2025: 3→5. The chain checker flags no discontinuities. The inherited 1934 and 1974
+entries still need stronger primary sourcing; this batch did not independently authenticate
+those first-restriction claims. The inherited personal-possession component of the 2015
+scoring also needs a separate historical review: the WIPO 2010 consolidation already
+contains the two-to-five-year personal-possession prison range. This batch does not claim
+that the decree first created that range.
+
+## Verification and cleanup
+
+- `verify.mjs`: all existing events outside the six selected IDs are deeply unchanged
+ against the start-of-batch snapshot. Existing selected `before` records are preserved.
+ Checked tags, categories, dates, sources, related IDs, article length/structure and
+ independent profile data.
+- One production build: **3,587 pages, 57.04 seconds**.
+- Generated checks passed for the nine events, six article bodies, citations, section
+ headings, internal links, tag pages, JSON export, country page and both affected score
+ steps. `node scripts/data-status.cjs chain by` passed.
+- Deleted `tmp/history-belarus-gap-2026-10-22/site` after verification. Research material
+ and the build log remain. No staging, commits, package installation or changes to the
+ concurrent exhibition/front-end work were performed.
docs/research/history-guatemala-gap-2026-10-22.md +2 −0
@@ -37,2 +37,4 @@ Profile now uses primary legal clauses for possession, cultivation, supply, cons
The batch verifier checks allowed categories/tags, dates, related IDs, citations, five article lengths and four-section structure, unchanged unrelated history, all existing before vectors/current scores, current status values and the separate UNODC seizure block. Generated-page verification and temporary-build cleanup are recorded below after completion.
+
+Source verification passed, and `node scripts/data-status.cjs chain gt` reported **zero flagged steps**. `npm run build -- --outDir tmp/history-guatemala-gap-2026-10-22/site` succeeded: **3,564 pages in 26.92 seconds**. The generated verifier passed for Guatemala's timeline and history export, tag pages, all five articles, citations, section headings, internal links, statutory penalties and conditional proposal wording. Scoped `git diff HEAD --check` passed. The temporary `site` build was deleted after verification; audits found no remaining temporary site/build directories or generated `history/index.html` files under `tmp`. Research files and build log are retained.
docs/research/history-jordan-gap-2026-10-22.md +63 −0
@@ -0,0 +1,63 @@
+# Jordan history gap — 22 October 2026
+
+Added six articles, four history events and revisions to three existing events. The selected country had eleven events and no articles. Project totals after this batch: 2,825 events and 709 articles. Each added or revised event has tags; the current writing and category rules were reread before editing.
+
+## Material corrections
+
+- The existing `jordan-first-time-users-2015` event now dates the first-use treatment guarantee to **16 May 2013**, keeping its existing ID and URL. The original amendment requires referral within twenty-four hours without prosecution or a judicial precedent. It also requires a special administrative register: this is not erasure of every record.
+- The 2016 replacement permits prosecution of first-time users and judicial substitution of inpatient or outpatient treatment. Its original first-offence record protection already includes personal growing and manufacture. The 2021 amendment therefore did not first protect those acts or remove criminal intent.
+- The 2016 death provision retains an international-gang trafficking clause present in the earlier amended drug law. It is a tagged, explicitly unscored retention event, with a new article explaining ordinary trafficking, aggravated trafficking and personal use separately.
+- The September 2021 Lower House vote is now **debate**, dated 1 September, distinct from the enacted amendment operative **23 October 2021**. The enacted change is **other**, reflecting its mixture of penalty tiers, record protection, wider distribution control and licensing authority.
+- The regulator’s September 2026 schedules put **cannabis plant material in table 3**, with personal-use punishment of **one to six months OR JOD200–500 OR both** under article 9(a)(4). **Resin remains in table 1**, with **one to three years AND JOD1,000–3,000** under article 9(a)(1). The old profile’s blanket one-to-three-year statement was wrong for current plant material.
+- The 2023 instructions supply a product approval/prescription route for CBD natural medicines/preparations at no more than 0.3% THC, alongside external-use cosmetics/devices and hemp seed foods. Two events and articles distinguish those product routes. Medical CBD is filed under medical by the category priority; its narrow product rules are explicitly unscored.
+
+## Sources actually read
+
+Source URLs and locators are preserved in the selected entries, article front matter and `tmp/history-jordan-gap-2026-10-22/sources.json`. Downloads, extracted text and PDF renders remain in that folder as research material.
+
+1. **Original 2013 amendment, law 7/2013**, Official Gazette 5222, pp. 2153–2154. Both law pages were visually read in the official DocuWare public viewer, document **33212**. Article 1 takes effect on publication. The issue cover in the official index document **33211** was visually checked: **Thursday 16 May 2013**. The 4 April signature date is not used as commencement. Viewer metadata/storage dates were not used as gazette dates.
+2. **Petra, Senate approval, 24 March 2013**. Full relevant report read: Taher Masri presided; the Senate adopted the Lower House version despite its legal committee’s objection. Interior Minister Awad Khleifat described a social reform and treatment/education rationale. Names and political context in the 2013 article come from this report.
+3. **Original 2016 law**, Official Gazette 5402, public viewer document **44887**. Original printed pages 3197–3199 were visually read: plant prohibition, personal-use acts and punishment, first-offence record protection, third-offence mitigation floor, court treatment substitution, confidentiality and voluntary treatment protection. The earlier accidentally opened document **44910** contains appointments, is not the drug law, and is not cited.
+4. **JFDA consolidated law**: both the previously retained URL and the current law-index download were fetched and extracted. Rendered pages 3–4 and 7 were visually read for penalty tiers, first-offence protection and capital clause numbering. Text was read for related clauses. Official final notes explicitly give **15 August 2016** commencement and **23 October 2021** amendment commencement; these are preferred over a secondary 14 August date.
+5. **Earlier law 11/1988, amended compilation on Haqqi**: relevant PDF text was read through the web tool, including articles 8–10 and 14. The copy includes 2003/2006 amendments and predates the 2013 diversion clause. It proves the international-gang death clause predates the 2016 replacement, and that own-use cultivation was already treated separately from trafficking. It is not an unamended original 1988 facsimile. Local fetches were blocked; no local PDF is claimed.
+6. **2021 amendment, law 24/2021**, Adaleh transcription reproduced by ChemReg: downloaded and relevant text read. Articles 2, 5, 8, 13 and 15 cover unpaid distribution, licensing, personal-use bands and the wider record-protection list. This is a transcription, not a gazette facsimile. JFDA’s primary consolidation agrees with the pertinent amended clauses.
+7. **Petra, Lower House vote 1 September and publication 23 September 2021**: both full relevant reports read. The first is a legislative stage. Publication and commencement are separately dated.
+8. **JFDA schedules updated 30 September 2026**: retrieved using the actual regulator-index href. Table 1, PDF page 2, was read as extracted text and visually for resin/extracts/tinctures. Table 3, page 8, was visually read for the whole cannabis plant, CBD-dominant preparations above 0.3% delta-9 THC, and exclusion of CBD-dominant preparations at or below 0.3% from all tables. These current schedules establish the present classifications, not the date of the plant’s reclassification.
+9. **JFDA instructions 1/2023**, Official Gazette 5844, pp. 912–916: retrieved through the regulator’s actual instructions-index href. All five original facsimile pages were rendered and visually read. A Hasana transcription supplied clearer searchable text, but claims are anchored to the regulator’s facsimile. Health Minister Firas Ibrahim Hawari signed. Article 1 operates on publication; only the year is used because the issue’s precise publication day was not checked.
+10. **7iber, Ezz al-Din al-Natour, 21 July 2016**, contemporary reporting with interviews: read in full. The government’s legislation bureau head Nofan al-Ajarmeh described abandoning the three-year experiment; rights lawyer Hala Ahed criticised the reversal. The account explicitly describes mandatory first-use treatment as the law still in force before the 2016 replacement. The article’s occasional two-year sentence references are not used over the original statute’s one-to-three-year text.
+
+## CBD and food details
+
+- CBD natural medicines/preparations at no more than **0.3% delta-9 THC** require classification/approval and a valid medical prescription. Medical import/export requires a controlled-substances-section stamped invoice. Manufacturer approval, quality evidence and stability testing are specified.
+- Finished external-use cosmetics/devices in a non-abusable form have a particular exemption from separate controlled-substances import/export approval; this is not general permission for all CBD retail. Other product rules still apply. Cosmetic/device manufacture under these instructions is external-use only, with raw-material controls and annual quantity/buyer reports.
+- Food ingredients are hulled, **nonviable** hemp seeds and specified derivatives, with a source-plant maximum of **0.1% THC in leaves and flowering tops**. That crop condition is not a general permission for personal growing.
+- Annex food limits: hulled seeds/hemp hearts **5 mg THC/kg**; seed oil **10 mg/kg**; hemp seed milk/beverages **0.2 mg/kg**; flour/protein/seed cake **5 mg/kg**. Each listed category has a **75 mg CBD/kg** ceiling. Seed oil requires an unsuitable-for-cooking label. Plant imagery is prohibited except seed pictures, with restrictions on hemp promotion and psychoactive claims in packaging and advertising.
+- No named registered medicine, actual pharmacy availability or visitor/foreign-prescription import route was established. No broad medical-flower programme is claimed.
+
+## Scores and status treatment
+
+Current cultivation changes **0 → 1** as a correction to the legal baseline: own-use growing has a lesser offence than cultivation for trafficking, as shown by the earlier amended law, original 2016 text and current law. This distinction existed before the 2013 treatment amendment. The 2013 event’s `before.cultivation` is consequently rebased to 1, and the new 2016 step retains it. This is a calibration correction, not an invented 2013 cultivation reform. The estimated first restriction and its wide historical uncertainty remain; this batch establishes no precise early date for the separate-own-use penalty.
+
+The scored timeline is now estimated first restriction **100 → 4**, the 2013 first-use diversion **4 → 8**, and the 2016 return to prosecution **8 → 6**. Enforcement rises from 0 to 2 for the first-use diversion guarantee, then falls to the retained current 1 when prosecution returns. The middle value represents partial first-offence protection while repeat offences remain punishable, not a claim that fines were empirically typical in 2013.
+
+Other current segment values remain unchanged, including possession 0 because imprisonment remains possible and resin still attracts the longer range. No claim about typical cannabis sentencing frequency is added. Current recreational death remains tied to aggravated trafficking. Medical **none** and access **0** remain: the schema expressly excludes CBD/hemp alone from establishing cannabis medical status. The new CBD article explains the narrow product route. The 2021 mixed event is unscored context: exact historical table placement for cannabis in 2021 was not established, and the wider record list is too narrow to move an additional whole segment point beyond the existing first-offence protection.
+
+## Boundaries and outstanding leads
+
+- Do not date cannabis plant reclassification to the 2026 schedule file update. Recover an earlier gazetted table change before adding a dated easing event.
+- News reports in May 2014 described a repeal of first-use diversion, reproducing a Legislation and Opinion Bureau text without an enacted law number. No enactment was recovered. Contemporary 2016 interviews explicitly describe the three-year diversion policy being reversed then. The 2014 reports are not used as proof of an operative amendment or as a new event.
+- The 1988 entry remains unrevised: the available consolidated copy cannot establish every original penalty. Earlier 1937 and estimated first-restriction entries also remain untouched.
+- New food and CBD events use **2023**, not an invented exact date. Recover the cover of gazette 5844 for greater precision.
+- Research-production claims reported in October 2026 were not added without an actual licence or primary official confirmation.
+- Arrest, UNODC seizure and driving sections were preserved exactly as parsed from the baseline, including source references. They were not revalidated as part of this legal-history batch.
+- Concurrent frontend, package, source-archive and staged changes were preserved. No staging, reset, commit or package installation was performed.
+
+## Verification
+
+Source verifier passed: all six articles are 350–500 words with four sections, their citations and country links are present, seven touched events have valid tags/categories/related IDs, and all unrelated history entries match the saved baseline. Only cultivation changes in current scores; the one previously scored event is rebased explicitly. Independent profile sections and status flags are preserved.
+
+Build, generated-page checks and cleanup are recorded below after completion.
+
+The production build passed: **3,579 pages in 29.20 seconds** (including concurrent frontend work). The generated verifier passed for history JSON, both scored steps, the country timeline, all seven events’ tag links and tag pages, and all six articles’ citations, headings and internal links. Rendered country text and the food table contain the checked penalty ranges and product limits. The six article word counts are **406, 437, 397, 406, 408 and 454**.
+
+The temporary output directory `tmp/history-jordan-gap-2026-10-22/site` was deleted after verification. A subsequent search found no temporary site/build/dist/prerender directories and no generated `history/index.html` files under `tmp`. Source downloads, extracts, research renders, scripts and the build log were retained. Scoped `git diff HEAD --check` passed. The broad history-coverage goal remains active; this batch is verified progress.
docs/research/history-serbia-gap-2026-10-22.md +54 −0
@@ -0,0 +1,54 @@
+# Serbia history gap — 22 October 2026
+
+Serbia had eleven history entries and no articles. This batch adds **seven articles**, **five events** and revisions to **five existing events**, all with tags and plain titles. Current project totals: **2,830 events and 716 articles**. The previous Jordan turn was verified progress; this batch continues the broader active coverage goal.
+
+## Changes and corrections
+
+- The 2009 small-personal-possession event now explains the fine/up-to-three-year offence, existing possibility of waiver, separate cultivation offence and later 2019 changes. It remains **other**, because separating offences was not itself decriminalisation and personal-use waiver already existed.
+- The controlled-substances law entry uses **22 December 2010**, the Assembly’s adoption date, and explicitly describes adoption. Its original text, not just the 2018 consolidation, confirms the cannabis threshold, licensed hemp purposes and destruction duties. Commencement is not equated with adoption.
+- Added the **1 August 2013** hemp rulebook, requiring registered varieties **below 0.3% THC**, an application before sowing/by **30 April**, seed and land records, eligibility documents and a permit for one production year. This is hemp regulation, not adult home-growing legalisation.
+- The patients’ request is corrected to **18 February 2015**: the report published on 19 February says it was delivered the previous day. The event is **debate**, replacing the previous medical category, and identifies Milan Savić and the association’s request without endorsing the claimed treatment outcomes.
+- The ministry committee’s 2016 recommendation is **debate**, dated **January**, as the exact committee decision day was not established. Contemporary reporting says registration and prescribing arrangements were still being pursued. The article does not claim pharmacies actually stocked the medicines.
+- The 2017 Green Party bill now has a parliamentary source. Added the **26 March 2018** agenda refusal, with **ten supporting votes and 149 MPs not voting**. This was refusal to schedule consideration, not a merits vote rejecting the bill. The article connects the earlier patient campaign, medicine proposals, bill and agenda outcome.
+- Added the **23 January 2019** court judgment upholding Marko Luković’s three-year sentence for production/possession for sale after collecting flowers near the Morava, drying and packaging them. The judgment distinguishes cultivation from further processing. It does not establish that he grew those plants, so no such claim is made.
+- Added the **1 December 2019** amendment: cultivation raised from **six months–five years to two–eight years**; large possession newly carries **three–ten years**; smaller personal possession retains a fine/up to three years/possible waiver. It also changes aggravated supply and equipment offences. There is no fixed gram or plant allowance.
+- Added the **25 January 2024** Supreme Court judgment upholding Senad Makaji’s three-year sentence for about **372 g** for personal use. The original trial sentence was thirteen months; the appeal applied the large-quantity offence. Judges assess large quantity case by case. Neither 372 g nor any smaller quantity is presented as a universal legal cutoff.
+- The profile now separates cultivation from production and distinguishes giving drugs for consumption (**six months–five years**, article 247) from production/supply (**three–twelve years**, article 246). It removes unsupported blanket claims that most cases receive deferred prosecution and that no clubs are known.
+
+## Sources actually read
+
+URLs and precise locators are in entry citations, article front matter and `tmp/history-serbia-gap-2026-10-22/sources.json`. Downloads, extracted text and key-page renders are retained in that directory.
+
+1. **Constitutional Court Bulletin 2/2009**, printed pp. 62–64, especially 63. Downloaded with ordinary certificate-validating curl after Python’s certificate chain failed. The relevant text was extracted and p. 63 visually read. It confirms the former personal-use waiver and **11 September 2009** commencement of the separation into two offences.
+2. **Criminal Code through Gazette 94/2016**, published by the Serbian public prosecution service. Relevant articles 246–247 were read, including editorial annotations identifying provisions from 72/2009. It supplies the pre-2019 cultivation and possession wording.
+3. **Original 2010 controlled-substances law**, official Assembly archive. The enacted-laws index identifies adoption **22 December 2010**, Gazette 99/2010. Its actual PDF link downloads a ZIP containing `3210_10LAT.pdf`; that PDF was extracted as `act2010.pdf`. Relevant articles 5, 58–60 and 116–117 were read; p. 22 was visually inspected for hemp purposes and licensing. This is original parliamentary text, not a gazette facsimile. The current Paragraf consolidation was also read for the amended law.
+4. **2013 agriculture rulebook**, three-page ministerial text reproduced by Agroklub and also found on FAOLEX. FAOLEX’s PDF was readable through the web tool but the local fetch was blocked; the discovered Agroklub PDF downloaded successfully. All relevant articles 1–9 were read as extracted text and p. 1 was visually inspected for the threshold, publication date and application deadline. The document identifies publication on **24 July 2013** and operation on the eighth day, giving **1 August 2013**. It was signed on **15 July** by **Goran Knežević**. No 0.2% limit or human-food permission is imported from inconsistent secondary summaries.
+5. **Radio 021, 19 February 2015**, full relevant report read. It reproduces the association’s statement and identifies delivery the previous day by Milan Savić. Several-hundred-patient documentation is described as submitted material, not independently verified efficacy.
+6. **Novosti, B. Radivojević, 16 January 2016**, original report read after locating it through the Paragraf reproduction. Health Ministry statements describe committee support for dronabinol, nabilone and Sativex and pending registration/prescriber arrangements. The headline says approved, but the article’s actual conditions do not prove completed medicine registration or sales.
+7. **Assembly verbatim record, 26 March 2018**, PDF p. 41. Downloaded, extracted and visually read in full for this item. It confirms the bill submission **23 June 2017**, Čabradi’s speech and the **10/149** vote. The previous item on p. 40 had a different **15/144** vote; those numbers are not assigned to the cannabis motion. Čabradi’s patient-count and international comparisons are political claims and are not reproduced as verified statistics.
+8. **Government 2018 controlled-substances amendment proposal**, official Assembly PDF, especially printed pp. 26–27. The consultation report accepts objections to reducing the hemp THC ceiling and rejects suggestions to legalise cannabis oil, citing clinical-evidence concerns and existing cannabis-based medicines. This does not establish local product availability. The original 2017 Čabradi bill text was not recovered; no detailed licence, diagnosis or plant allowance is invented for it.
+9. **Original 2019 Criminal Code amendment**, official Assembly PDF, articles 21–23 and 42. Downloaded and read; p. 4 visually confirms the exact cultivation increase and large-possession wording. Article 42 explicitly gives **1 December 2019** commencement. The enacted-laws index records Assembly adoption **21 May 2019**, kept distinct from commencement.
+10. **Kzz 1521/2018, 23 January 2019**, original Supreme Court of Cassation judgment, three-page official PDF. Full relevant text read for Luković’s sentence, flowers, drying, packaging and sale purpose. This is application of the existing offence, so the event is **enforcement** with a court tag.
+11. **Kzz 27/2024, 25 January 2024**, full relevant official Supreme Court judgment read and saved as HTML/text. It gives **372.42 g** in one confiscation passage and **372.40 g** in the facts passage; the site uses **about 372 g**. It expressly says large quantity is assessed in each case and that personal purpose does not prevent application of the large-possession offence. This is **enforcement**, with no new scored step.
+12. **Belgrade First Basic Public Prosecutor’s Office FAQ**, full deferred-prosecution section read after ordinary curl succeeded. It explains Criminal Procedure Code article 283: eligible fine/up-to-five-year offences, accepted obligations, at most one year for compliance and dismissal after completion. This supports an available route, not a claim about how frequently cannabis cases use it.
+13. **2026 controlled-drug schedules**, Gazette **12/2026**, reproduced by Paragraf: relevant text downloaded and read. Cannabis and resin appear in **both lists 1 and 2**. A third-party claim based only on list 1 omits the overlapping list-2 control. Article 12 of the law permits list-2 substances for research and exceptionally for therapy under its conditions. Article 69 permits minister-authorised imports of unregistered controlled medicines through a licensed wholesaler for a patient or group. The profile now states these general legal authorities, without claiming routine cannabis access or a registered product.
+
+## Scores, scope and open questions
+
+Current **cultivation changes 1 → 0**, tied to the new scored **1 December 2019** tightening. The statutory minimum rose from six months to two years, with a two-to-eight-year range and no personal plant allowance. The higher prison exposure is severe enough to remove the earlier one-point credit for the lesser cultivation penalty. The profile still explains that cultivation and production are legally distinct; the score is not a claim that they are identical offences.
+
+All other current score values remain, as do every earlier `before` vector and recreational/medical/status values. The timeline is **100 → 14 in 1930**, **14 → 11 in 1950**, **11 → 9 in December 2019**, with no direction mismatch. New context, bills and individual sentences do not themselves change a score.
+
+Medical **none** and access **0** are retained pending evidence of an operational or specifically authorised cannabis-medicine route. General controlled-medicine authorisation powers are recorded in the prose; they are not turned into an assumed cannabis programme. The 2016 support report alone cannot establish registration or patient access. Recover a current ALIMS registration or an actual ministerial cannabis-medicine authorisation before making a stronger availability/status claim. The 2026 schedule is not evidence of a newly introduced cannabis reform, and no such dated event is added.
+
+The 1930 first-restriction entry, 1932 and 1950 Yugoslav entries, first march and treaties remain untouched. An original 1930 text still needs checking before revising that early score drop or writing its article. The attempted 2021 Supreme Court bulletin download had a broken page tree and was not read or cited; individual original judgments supplied the needed evidence.
+
+Driving and black-market sections, their source references and parsed values are preserved exactly. This batch does not revalidate their independent observations. Concurrent Jordan, frontend, package, source-archive, staged and media-download changes were preserved; no staging, reset, commit or package installation was performed.
+
+## Verification
+
+The source verifier passed for all seven 350–500-word articles with four sections, ten tagged entries, dates, citations, related IDs, unchanged unrelated history and preserved independent profile blocks. Current cultivation is the only profile-score change, tied to the new 2019 event. Build, generated-page verification and temporary-build cleanup follow below.
+
+The production build passed: **3,584 pages in 1 minute 35 seconds**, including concurrent frontend work. Generated-page checks passed for all ten events, tags and tag pages, the history JSON export, the **11 → 9** score step, the country profile and all seven articles’ citations, headings and internal links. Article lengths are **402, 406, 407, 405, 404, 401 and 401 words**.
+
+The temporary directory `tmp/history-serbia-gap-2026-10-22/site` was deleted after verification. A subsequent audit found no temporary site/build/dist/prerender directories or generated `history/index.html` files under `tmp`. Research downloads, extracts, key-page renders, scripts and the build log remain. Scoped whitespace checks and the final Serbia chain check passed. The broader coverage goal remains active.
package-lock.json +30 −0
Not shown: a picture, a generated file, or past the length shown per change.
package.json +3 −0
@@ -20,4 +20,7 @@
"@fontsource-variable/inter": "^5.3.0",
"@fontsource-variable/jetbrains-mono": "^5.3.0",
+ "@fontsource/limelight": "^5.3.0",
+ "@fontsource/old-standard-tt": "^5.3.0",
+ "@fontsource/special-elite": "^5.3.0",
"@iconify-json/iconoir": "^1.2.12",
"astro": "^7.3.5",
public/exhibitions/reefer-madness/trailer-poster.jpg +0 −0
Not shown: a picture, a generated file, or past the length shown per change.
public/exhibitions/reefer-madness/trailer.en.vtt +89 −0
Not shown: a picture, a generated file, or past the length shown per change.
public/exhibitions/reefer-madness/trailer.mp4 +0 −0
Not shown: a picture, a generated file, or past the length shown per change.
source-archive.json +161 −5
Not shown: a picture, a generated file, or past the length shown per change.
src/assets/exhibitions/reefer-madness/machinery/fbn-badge-1930.png +0 −0
Not shown: a picture, a generated file, or past the length shown per change.
src/assets/exhibitions/reefer-madness/machinery/killer-drug-poster.jpg +0 −0
Not shown: a picture, a generated file, or past the length shown per change.
src/assets/exhibitions/reefer-madness/machinery/locoed-weed-1915.jpg +0 −0
Not shown: a picture, a generated file, or past the length shown per change.
src/assets/exhibitions/reefer-madness/machinery/oliphant-1937.jpg +0 −0
Not shown: a picture, a generated file, or past the length shown per change.
src/assets/exhibitions/reefer-madness/posters/srita-1.jpg +0 −0
Not shown: a picture, a generated file, or past the length shown per change.
src/assets/exhibitions/reefer-madness/posters/srita-10.jpg +0 −0
Not shown: a picture, a generated file, or past the length shown per change.
src/assets/exhibitions/reefer-madness/posters/srita-11.jpg +0 −0
Not shown: a picture, a generated file, or past the length shown per change.
src/assets/exhibitions/reefer-madness/posters/srita-12.jpg +0 −0
Not shown: a picture, a generated file, or past the length shown per change.
src/assets/exhibitions/reefer-madness/posters/srita-13.jpg +0 −0
Not shown: a picture, a generated file, or past the length shown per change.
src/assets/exhibitions/reefer-madness/posters/srita-18.jpg +0 −0
Not shown: a picture, a generated file, or past the length shown per change.
src/assets/exhibitions/reefer-madness/posters/srita-19.jpg +0 −0
Not shown: a picture, a generated file, or past the length shown per change.
src/assets/exhibitions/reefer-madness/posters/srita-23.jpg +0 −0
Not shown: a picture, a generated file, or past the length shown per change.
src/assets/exhibitions/reefer-madness/posters/srita-25.jpg +0 −0
Not shown: a picture, a generated file, or past the length shown per change.
src/assets/exhibitions/reefer-madness/posters/srita-27.jpg +0 −0
Not shown: a picture, a generated file, or past the length shown per change.
src/assets/exhibitions/reefer-madness/posters/srita-28.jpg +0 −0
Not shown: a picture, a generated file, or past the length shown per change.
src/assets/exhibitions/reefer-madness/posters/srita-3.jpg +0 −0
Not shown: a picture, a generated file, or past the length shown per change.
src/assets/exhibitions/reefer-madness/posters/srita-36.jpg +0 −0
Not shown: a picture, a generated file, or past the length shown per change.
src/assets/exhibitions/reefer-madness/posters/srita-38.jpg +0 −0
Not shown: a picture, a generated file, or past the length shown per change.
src/assets/exhibitions/reefer-madness/posters/srita-4.jpg +0 −0
Not shown: a picture, a generated file, or past the length shown per change.
src/assets/exhibitions/reefer-madness/posters/srita-41.jpg +0 −0
Not shown: a picture, a generated file, or past the length shown per change.
src/assets/exhibitions/reefer-madness/posters/srita-45.jpg +0 −0
Not shown: a picture, a generated file, or past the length shown per change.
src/assets/exhibitions/reefer-madness/posters/srita-46.jpg +0 −0
Not shown: a picture, a generated file, or past the length shown per change.
src/assets/exhibitions/reefer-madness/posters/srita-48.jpg +0 −0
Not shown: a picture, a generated file, or past the length shown per change.
src/assets/exhibitions/reefer-madness/posters/srita-5.jpg +0 −0
Not shown: a picture, a generated file, or past the length shown per change.
src/assets/exhibitions/reefer-madness/posters/srita-53.jpg +0 −0
Not shown: a picture, a generated file, or past the length shown per change.
src/assets/exhibitions/reefer-madness/posters/srita-55.jpg +0 −0
Not shown: a picture, a generated file, or past the length shown per change.
src/assets/exhibitions/reefer-madness/posters/srita-58.jpg +0 −0
Not shown: a picture, a generated file, or past the length shown per change.
src/assets/exhibitions/reefer-madness/posters/srita-7.jpg +0 −0
Not shown: a picture, a generated file, or past the length shown per change.
src/lib/culture.js +6 −0
@@ -39,4 +39,10 @@ export const cultureEntries = (await getCollection("culture"))
return ch;
});
+ // every history entry the text relies on is linked in the text, and every history entry the
+ // text links to is listed in `history`, so the entry, its history list and the history pages
+ // stay interlinked (docs/CULTURE.md, Countries and links)
+ const linked = new Set([...(entry.body ?? "").matchAll(/\]\(\/history\/([^/)#]+)/g)].map((m) => m[1]));
+ for (const id of d.history) if (!linked.has(id)) throw new Error(`${file}: "${id}" is in history but the text never links to /history/${id}/; mention and link it in the text`);
+ for (const id of linked) if (!d.history.includes(id)) throw new Error(`${file}: the text links to /history/${id}/ but "${id}" is not in history`);
// links in the text to pages of this site must lead somewhere
for (const [, kind, id] of (entry.body ?? "").matchAll(/\]\(\/(history|country)\/([^/)#]+)\/?[^)]*\)/g)) {
src/lib/exhibitions.js +45 −0
@@ -0,0 +1,45 @@
+// Exhibitions: self-contained experiences, each about one episode in humanity's relationship with
+// cannabis, each with a look of its own (src/pages/<slug>.astro, so its address is as short as it can be),
+// listed under Culture. Charts measure,
+// Culture documents, Exhibitions ask why and how something happened. An exhibition is built on
+// the same sourced history as the rest of the site and keeps three things apart: primary
+// evidence, documented connections, and interpretation.
+// history: the history entries an exhibition is about; their pages link back to it with a banner
+export const EXHIBITIONS = [
+ {
+ slug: "reefer-madness", no: "001", theme: "Propaganda", title: "Reefer Madness", line: "The invention of a monster",
+ about: "The original trailer, a newspaper front page, an investigation board, a court docket and an evidence room: how a plant was sold to America as a killer, who sold it, and what later research found.",
+ open: true,
+ history: ["us-reefer-madness-1938", "us-assassin-of-youth-1937", "us-marihuana-tax-act-1937", "us-tax-bill-introduced-1937", "us-el-paso-ban-1915",
+ "us-anslinger-crime-claim-1931", "us-federal-bureau-of-narcotics-1930", "us-la-guardia-report-1944", "us-fbn-plays-down-marijuana-1938",
+ "us-isbell-testimony-1951", "us-shafer-commission-1972", "league-russell-anslinger-exchange-1938", "us-anslinger-petty-cases-memo-1937", "us-first-tax-act-convictions-1937",
+ "us-house-passes-tax-bill-1937", "us-uniform-state-narcotic-act-1932", "us-pharmacopeia-1942", "us-siler-report-1933", "panama-canal-zone-committee-1926",
+ "india-hemp-drugs-commission-1894", "us-boggs-act-1951", "us-fifth-circuit-judges-1954", "us-leary-1969", "geneva-egypt-proposal-1924",
+ "egypt-russell-hashish-report-1930", "mexico-salazar-marijuana-myth-1938", "mexico-regulation-suspended-1940",
+ "us-mezzrow-arrest-1940", "us-gene-krupa-arrest-1943", "us-anita-oday-arrest-1947", "us-mitchum-arrest-1948", "us-lee-otis-johnson-1968", "us-little-boggs-acts-1953", "us-reefer-madness-campus-revival-1972",
+ "us-locoed-weed-headline-1915", "us-licata-murders-1933", "us-production-code-drugs-1934", "us-road-to-ruin-1934", "us-marihuana-film-1936", "us-assassin-of-youth-film-1937",
+ "us-ama-woodward-testimony-1937", "us-devils-harvest-film-1942", "us-she-shoulda-said-no-1949", "us-reefer-madness-musical-1998", "us-schlosser-reefer-madness-2003"],
+ // the rooms, in order: id is the anchor on the page; also listed in search
+ rooms: [
+ { id: "claims", no: "I", name: "The Claims", learn: "Ten claims of the era, each checked against what later research found." },
+ { id: "machinery", no: "II", name: "The Machinery", learn: "Who spread the story, and how the press, the bureau, Congress and the film fed one another." },
+ { id: "cycle", no: "III", name: "The Cycle", learn: "The other drug films of the time, and why they were all made outside Hollywood." },
+ { id: "gallery", no: "IV", name: "The Gallery", learn: "Posters and paperback covers that sold the panic, and the tricks they used." },
+ { id: "defendants", no: "V", name: "The Defendants", learn: "Real people sentenced under the new laws, and how long the sentences grew." },
+ { id: "export", no: "VI", name: "The Export", learn: "How the same picture of the drug reached international law, and who argued against it." },
+ { id: "afterlife", no: "VII", name: "The Afterlife", learn: "How the film became a joke, and its title the name for any drug scare." },
+ { id: "evidence", no: "VIII", name: "The Evidence Room", learn: "Every document behind the exhibition, labelled for what kind of evidence it is." },
+ ],
+ // a still for banners elsewhere on the site, from public/
+ cover: "exhibitions/reefer-madness/trailer-poster.jpg",
+ },
+ { slug: "genealogy-of-prohibition", no: "002", theme: "Institutions", title: "The Genealogy of Prohibition", line: "How laws learned to reproduce", about: "A forensic tracing of how national, imperial and treaty law copied one another.", open: false, history: [] },
+ { slug: "a-thousand-names", no: "003", theme: "Language", title: "A Thousand Names for One Plant", line: "The linguistic archaeology of cannabis", about: "The words for the plant as a living specimen: where each came from and where it travelled.", open: false, history: [] },
+ { slug: "the-crime-of-gardening", no: "004", theme: "Criminalisation", title: "The Crime of Gardening", line: "When growing a plant became an offence", about: "One person, one plant, many countries and decades, and what the same act cost in each.", open: false, history: [] },
+ { slug: "medicine-contraband", no: "005", theme: "Medicine", title: "The Medicine That Became Contraband", line: "How institutions changed what counted as knowledge", about: "Pharmacopoeias, physicians and regulators: how cannabis left medicine and how it came back.", open: false, history: [] },
+];
+// the page's path below the site's base, e.g. "reefer-madness/"
+export const exhibitionPath = (e) => `${e.slug}/`;
+export const openExhibitions = EXHIBITIONS.filter((e) => e.open);
+// history entry id → the open exhibitions about it
+export const exhibitionsOf = (id) => openExhibitions.filter((e) => e.history.includes(id));
src/lib/search-icons.js +3 −3
@@ -14,9 +14,9 @@ export const PAGE_ICONS = {
"culture/": "iconoir:palette", "culture/people/": "iconoir:community", "culture/stories/": "iconoir:book", "sources/": "iconoir:bookmark-book",
};
-// pages that come in families: a part of the cultural atlas, a map view, a history period, a history tag
-export const CULTURE_SECTION_ICON = "iconoir:palette", MAP_VIEW_ICON = "iconoir:map", PERIOD_ICON = "iconoir:calendar", TAG_ICON = "iconoir:hashtag";
+// pages that come in families: a part of the cultural atlas, a map view, a history period, a history tag, an exhibition
+export const CULTURE_SECTION_ICON = "iconoir:palette", MAP_VIEW_ICON = "iconoir:map", PERIOD_ICON = "iconoir:calendar", TAG_ICON = "iconoir:hashtag", EXHIBITION_ICON = "iconoir:frame-alt";
export const SEARCH_ICONS = [...new Set([
- ...Object.values(RESULT_KIND_ICONS), ...Object.values(PAGE_ICONS), CULTURE_SECTION_ICON, MAP_VIEW_ICON, PERIOD_ICON, TAG_ICON,
+ ...Object.values(RESULT_KIND_ICONS), ...Object.values(PAGE_ICONS), CULTURE_SECTION_ICON, MAP_VIEW_ICON, PERIOD_ICON, TAG_ICON, EXHIBITION_ICON,
...Object.values(CHANGE_CATEGORIES).map((k) => k.icon),
])];
src/pages/culture/index.astro +17 −1
@@ -7,4 +7,6 @@ import CultureCards from "../../components/CultureCards.astro";
import DidYouKnow from "../../components/DidYouKnow.astro";
import { CULTURE_SECTIONS, cultureEntries, cultureIn } from "../../lib/culture.js";
+import { openExhibitions, exhibitionPath } from "../../lib/exhibitions.js";
+import "@fontsource/limelight";
const base = import.meta.env.BASE_URL;
@@ -13,5 +15,6 @@ const countryCount = new Set(cultureEntries.flatMap((e) => e.countries)).size;
// as a large lead card; the rest are a click away.
const preview = (id) => cultureIn(id).slice(0, 5);
-const count = (s) => (s.entries ? `${cultureIn(s.id).length} ${cultureIn(s.id).length === 1 ? "entry" : "entries"}` : s.soon ? "In preparation" : "Collecting soon");
+const count = (s) => (s.entries ? `${cultureIn(s.id).length} ${cultureIn(s.id).length === 1 ? "entry" : "entries"}`
+ : s.soon ? "In preparation" : "Collecting soon");
---
<Base title="Culture" description="A cultural atlas of cannabis around the world: the words people have for it, the rituals and traditions it belongs to, and the people behind the laws.">
@@ -35,4 +38,17 @@ const count = (s) => (s.entries ? `${cultureIn(s.id).length} ${cultureIn(s.id).l
</section>
+ <section class="section cu-exhibits">
+ {openExhibitions.map((e) => (
+ <a class="cu-exhibit" href={`${base}${exhibitionPath(e)}`}>
+ <small>Exhibition {e.no} · {e.theme}</small>
+ <b>{e.title}</b>
+ <span class="cu-exhibit-line">{e.line}</span>
+ <span class="cu-exhibit-about">{e.about}</span>
+ <span class="cu-exhibit-go">Enter the exhibition →</span>
+ </a>
+ ))}
+ <p class="cu-all front-go"><a class="go" href={`${base}exhibitions/`}>All exhibitions <span aria-hidden="true">→</span></a></p>
+ </section>
+
<section class="section cu-dyk"><DidYouKnow group="culture" /></section>
src/pages/exhibitions/index.astro +61 −0
@@ -0,0 +1,61 @@
+---
+// Exhibitions: the lobby. Each exhibition is a page of its own with its own look; this
+// page only shows the doors, the open ones first.
+import Base from "../../layouts/Base.astro";
+import { EXHIBITIONS, exhibitionPath } from "../../lib/exhibitions.js";
+import "@fontsource/limelight";
+const base = import.meta.env.BASE_URL;
+---
+<Base title="Exhibitions" description="Exhibitions: self-contained experiences, each about one strange episode in humanity's relationship with cannabis. The first is Reefer Madness, the invention of a monster.">
+ <main class="exh-lobby">
+ <header class="exh-lobby-head">
+ <p class="exh-kicker">Exhibitions <span>· also in <a href={`${base}culture/`}>Culture</a></span></p>
+ <h1>Why and how it happened</h1>
+ <p class="exh-lede">The map shows what is happening, the charts what patterns exist, the rest of the culture atlas what people made and said. Each exhibition takes one episode and asks why and how, in a form of its own. Every room keeps primary evidence, documented connections and interpretation apart.</p>
+ </header>
+ <ol class="exh-doors">
+ {EXHIBITIONS.map((e) => (
+ <li class:list={["exh-door", { open: e.open }]}>
+ {e.open ? (
+ <a href={`${base}${exhibitionPath(e)}`}>
+ <span class="exh-no">Exhibition {e.no} · {e.theme}</span>
+ <span class="exh-title">{e.title}</span>
+ <span class="exh-line">{e.line}</span>
+ <span class="exh-about">{e.about}</span>
+ <span class="exh-enter">Enter the exhibition →</span>
+ </a>
+ ) : (
+ <div>
+ <span class="exh-no">Exhibition {e.no} · {e.theme}</span>
+ <span class="exh-title">{e.title}</span>
+ <span class="exh-line">{e.line}</span>
+ <span class="exh-about">{e.about}</span>
+ <span class="exh-soon">In preparation</span>
+ </div>
+ )}
+ </li>
+ ))}
+ </ol>
+ </main>
+</Base>
+
+<style>
+ .exh-lobby { max-width: 1180px; margin: 0 auto; padding: 56px 24px 96px; }
+ .exh-kicker a { color: inherit; }
+ .exh-kicker { margin: 0; font: 400 12px var(--mono); text-transform: uppercase; letter-spacing: .12em; color: var(--muted); }
+ .exh-lobby-head h1 { margin: 10px 0 0; font: 400 clamp(44px, 7vw, 96px)/.95 var(--display); letter-spacing: -.03em; }
+ .exh-lede { max-width: 62ch; margin: 18px 0 0; font-size: 17px; line-height: 1.55; color: var(--text-2); }
+ .exh-doors { display: grid; grid-template-columns: repeat(auto-fill, minmax(320px, 1fr)); gap: 18px; margin: 56px 0 0; padding: 0; list-style: none; }
+ .exh-door > a, .exh-door > div { display: flex; flex-direction: column; gap: 10px; height: 100%; min-height: 300px; padding: 26px 24px; border: 1px solid var(--border); border-radius: 4px; color: var(--text); box-sizing: border-box; }
+ .exh-door.open > a { background: #0b0a08; color: #efe6d2; border-color: #0b0a08; position: relative; overflow: hidden; transition: transform .2s; }
+ .exh-door.open > a::after { content: ""; position: absolute; inset: 0; background: radial-gradient(ellipse at 50% 120%, rgba(179, 38, 30, .45), transparent 60%); pointer-events: none; }
+ .exh-door.open > a:hover { transform: translateY(-3px); }
+ .exh-door.open .exh-title { font-family: "Limelight", var(--display); letter-spacing: .02em; color: #e8d9b0; }
+ .exh-no { font: 400 11px var(--mono); text-transform: uppercase; letter-spacing: .1em; opacity: .7; }
+ .exh-title { font: 400 34px/1.05 var(--display); }
+ .exh-line { font: italic 400 19px var(--display); opacity: .85; }
+ .exh-about { margin-top: auto; font-size: 14px; line-height: 1.5; opacity: .8; }
+ .exh-enter { font: 500 13px var(--mono); color: #e05a4f; position: relative; z-index: 1; }
+ .exh-door:not(.open) > div { color: var(--text-2); border-style: dashed; }
+ .exh-soon { font: 400 11px var(--mono); text-transform: uppercase; letter-spacing: .1em; color: var(--muted); }
+</style>
src/pages/history/[id].astro +26 −12
@@ -11,4 +11,6 @@ import { Image } from "astro:assets";
import { cultureOfChange, cultureSection } from "../../lib/culture.js";
import { relatedTo } from "../../lib/related.js";
+import { exhibitionsOf, exhibitionPath } from "../../lib/exhibitions.js";
+import "@fontsource/limelight";
// One history entry: what happened, large; the score before and after when it moved one; how
@@ -38,4 +40,5 @@ const span = c ? 3 : 5;
const nearby = at < 0 ? [] : own.slice(Math.max(0, Math.min(at - span, own.length - 2 * span - 1)), Math.max(at + span + 1, 2 * span + 1));
const related = relatedTo(ch, nearby);
+const exhibits = exhibitionsOf(ch.id);
// an entry about one state or province: that state's card beside it, its country's under it
const unitGroup = ch.unit ? subunitsOf(c.id) : null;
@@ -51,4 +54,15 @@ const ledeInBody = !article;
</div>
+ {exhibits.map((e) => (
+ <a class="ev-exhibit" href={`${base}${exhibitionPath(e)}`} style={e.cover ? `--cover:url(${base}${e.cover})` : undefined}>
+ <span class="ev-exhibit-text">
+ <small>Exhibition {e.no} · {e.theme} · this event is part of it</small>
+ <b>{e.title}</b>
+ <span>{e.line}</span>
+ </span>
+ <span class="ev-exhibit-go">Enter the exhibition <span aria-hidden="true">→</span></span>
+ </a>
+ ))}
+
<header class={`ev-top${ch.score ? "" : " ev-plain"}`}>
<div>
@@ -87,4 +101,16 @@ const ledeInBody = !article;
</section>
)}
+ {article && (
+ <section class="ev-article">
+ <p class="ev-label">The story{article.data.model && <span class="ev-by"> · written by {article.data.model}{article.data.written && <>, <time datetime={article.data.written.toISOString().slice(0, 10)}>{formatDate(article.data.written.toISOString().slice(0, 10))}</time></>}</span>}</p>
+ {article.data.image && (
+ <figure>
+ <Picture src={article.data.image.src} alt={article.data.image.alt} style={article.data.image.fit === "contain" ? "object-fit: contain" : undefined} formats={["webp"]} widths={[480, 800, 1280]} sizes="(max-width: 900px) 100vw, 760px" />
+ <figcaption>{article.data.image.caption} <span>{article.data.image.credit_url ? <a href={article.data.image.credit_url} target="_blank" rel="noopener noreferrer">{article.data.image.credit}</a> : article.data.image.credit}{article.data.image.license_url && <> · <a href={article.data.image.license_url} target="_blank" rel="noopener noreferrer">Licence</a></>}</span></figcaption>
+ </figure>
+ )}
+ <div class="prose"><Article /></div>
+ </section>
+ )}
{culture.length > 0 && (
<section class="ev-culture" aria-label="In the cultural atlas">
@@ -102,16 +128,4 @@ const ledeInBody = !article;
</section>
)}
- {article && (
- <section class="ev-article">
- <p class="ev-label">The story{article.data.model && <span class="ev-by"> · written by {article.data.model}{article.data.written && <>, <time datetime={article.data.written.toISOString().slice(0, 10)}>{formatDate(article.data.written.toISOString().slice(0, 10))}</time></>}</span>}</p>
- {article.data.image && (
- <figure>
- <Picture src={article.data.image.src} alt={article.data.image.alt} style={article.data.image.fit === "contain" ? "object-fit: contain" : undefined} formats={["webp"]} widths={[480, 800, 1280]} sizes="(max-width: 900px) 100vw, 760px" />
- <figcaption>{article.data.image.caption} <span>{article.data.image.credit_url ? <a href={article.data.image.credit_url} target="_blank" rel="noopener noreferrer">{article.data.image.credit}</a> : article.data.image.credit}{article.data.image.license_url && <> · <a href={article.data.image.license_url} target="_blank" rel="noopener noreferrer">Licence</a></>}</span></figcaption>
- </figure>
- )}
- <div class="prose"><Article /></div>
- </section>
- )}
{ch.score && (
<section>
src/pages/reefer-madness.astro +1367 −0
@@ -0,0 +1,1367 @@
+---
+// Exhibition 001 · Reefer Madness. An entrance with the original trailer and a programme that
+// introduces the subject, then seven rooms, each in its own form: a newspaper front page whose headlines open into case files with a verdict (the
+// claims), an investigation board (the machinery), a wall of lobby cards (the exploitation cycle),
+// a court docket (the defendants), a stack of telegrams (the export), a film strip (the afterlife)
+// and an archive of index cards (the evidence room). Everything rests on the site's sourced history entries and the sources
+// listed with each item; every item says whether it is primary evidence, a documented connection
+// or an interpretation, and disputed points are marked as disputed.
+import Base from "../layouts/Base.astro";
+import { Image } from "astro:assets";
+import { EXHIBITIONS } from "../lib/exhibitions.js";
+import "@fontsource/limelight";
+import "@fontsource/special-elite";
+import "@fontsource/old-standard-tt/400.css";
+import "@fontsource/old-standard-tt/700.css";
+import "@fontsource/old-standard-tt/400-italic.css";
+import poster from "../assets/culture/reefer-madness-1936.jpg";
+import magazine from "../assets/culture/assassin-of-youth-1937.jpg";
+import anslingerPhoto from "../assets/culture/harry-anslinger-1938.jpg";
+import taxStamp from "../assets/culture/marihuana-tax-stamp-1937.jpg";
+import locoedWeed from "../assets/exhibitions/reefer-madness/machinery/locoed-weed-1915.jpg";
+import killerDrug from "../assets/exhibitions/reefer-madness/machinery/killer-drug-poster.jpg";
+import fbnBadge from "../assets/exhibitions/reefer-madness/machinery/fbn-badge-1930.png";
+import oliphantPhoto from "../assets/exhibitions/reefer-madness/machinery/oliphant-1937.jpg";
+
+const base = import.meta.env.BASE_URL;
+const h = (id) => `${base}history/${id}/`;
+// a page of the cultural atlas, e.g. c("people/harry-anslinger")
+const c = (path) => `${base}culture/${path}/`;
+
+// where the claims and connections come from
+const SRC = {
+ afi: ["American Film Institute Catalog — Tell Your Children", "https://catalog.afi.com/Catalog/moviedetails/7885"],
+ wiki: ["Wikipedia — Reefer Madness", "https://en.wikipedia.org/wiki/Reefer_Madness"],
+ archive: ["Internet Archive — Reefer Madness (1938), the film", "https://archive.org/details/reefer_madness1938"],
+ loc: ["Library of Congress — Marihuana, Topics in Chronicling America", "https://guides.loc.gov/chronicling-america-marihuana"],
+ licata: ["Wikipedia — Victor Licata", "https://en.wikipedia.org/wiki/Victor_Licata"],
+ fee: ["FEE — How an axe murderer helped make weed illegal", "https://fee.org/articles/how-an-axe-murderer-helped-make-weed-illegal/"],
+ assassin: ["Marijuana, Assassin of Youth — transcription of the July 1937 article", "https://www.cannabis-marijuana.com/assassin-of-youth.html"],
+ hashmuseum: ["Hash Marihuana & Hemp Museum — Reefer Madness collection", "https://hashmuseum.com/en/collection/reefer-madness/"],
+ stanford: ["Stanford Research into the Impact of Tobacco Advertising — Reefer Madness", "https://tobacco.stanford.edu/marijuanas/reefer-madness/"],
+ taxact: ["Wikipedia — Marihuana Tax Act of 1937", "https://en.wikipedia.org/wiki/Marihuana_Tax_Act_of_1937"],
+ laguardia: ["Wikipedia — LaGuardia Committee report", "https://en.wikipedia.org/wiki/LaGuardia_Committee_report"],
+ nyam: ["New York Academy of Medicine — Mayor LaGuardia commissions the Academy's report", "https://nyam.org/175th/175th-timeline/mayor-laguardia-commissions-academy-report-marijuana-usage"],
+ shafer: ["Wikipedia — Shafer Commission", "https://en.wikipedia.org/wiki/Shafer_Commission"],
+ avclub: ["The A.V. Club — An oral history of Reefer Madness: The Movie Musical", "https://www.avclub.com/reefer-madness-the-movie-musical-oral-history"],
+ roadToRuin: ["Wikipedia — The Road to Ruin (1934 film)", "https://en.wikipedia.org/wiki/The_Road_to_Ruin_(1934_film)"],
+ devilsHarvest: ["American Film Institute Catalog — Devil's Harvest (1942)", "https://catalog.afi.com/Film/27604-DEVIL'S-HARVEST"],
+ stroupFind: ["Hazlitt — Is Reefer Madness for real?", "https://hazlitt.net/node/848471"],
+ schlosser: ["Salon — review of Eric Schlosser's Reefer Madness (2003)", "https://www.salon.com/2003/05/15/reefer/"],
+ colorized: ["Film Threat — Reefer Madness: Special Addiction (DVD)", "https://filmthreat.com/uncategorized/reefer-madness-special-addiction-dvd/"],
+ ngram: ["Google Books Ngram Viewer — “reefer madness”, English, 1960–2022", "https://books.google.com/ngrams/graph?content=reefer+madness&year_start=1960&year_end=2022&corpus=en&smoothing=0&case_insensitive=true"],
+ schaefer: ["Eric Schaefer — “Bold! Daring! Shocking! True!”: A History of Exploitation Films, 1919–1959 (1999)", "https://books.google.com/books?id=CSBZqe0zPaMC"],
+ conviction: ["Richard Bonnie and Charles Whitebread — The Marijuana Conviction (1974)", ""],
+ marihuanaFilm: ["Wikipedia — Marihuana (1936 film)", "https://en.wikipedia.org/wiki/Marihuana_(1936_film)"],
+ shoulda: ["Wikipedia — She Shoulda Said No!", "https://en.wikipedia.org/wiki/She_Shoulda_Said_No!"],
+ bigsteal: ["American Film Institute Catalog — The Big Steal (1949), on Mitchum's sentence", "https://catalog.afi.com/Catalog/moviedetails/25852"],
+ code: ["Britannica — Motion Picture Production Code", "https://www.britannica.com/topic/Motion-Picture-Production-Code"],
+ caldwell: ["Wikipedia — Samuel R. Caldwell", "https://en.wikipedia.org/wiki/Samuel_R._Caldwell"],
+ mezzrow: ["Wikipedia — Mezz Mezzrow", "https://en.wikipedia.org/wiki/Mezz_Mezzrow"],
+ krupa: ["Wikipedia — Gene Krupa", "https://en.wikipedia.org/wiki/Gene_Krupa"],
+ boggs: ["Wikipedia — Boggs Act", "https://en.wikipedia.org/wiki/Boggs_Act"],
+ leary: ["Wikipedia — Leary v. United States", "https://en.wikipedia.org/wiki/Leary_v._United_States"],
+ salazar: ["Wikipedia — Leopoldo Salazar Viniegra", "https://en.wikipedia.org/wiki/Leopoldo_Salazar_Viniegra"],
+ campos: ["Isaac Campos — Reefer Madness Past and Present: Dr. Leopoldo Salazar Viniegra, Mexico, and the United States (2021)", ""],
+ ama: ["Wikipedia — Marihuana Tax Act of 1937, on the AMA's opposition", "https://en.wikipedia.org/wiki/Marihuana_Tax_Act_of_1937"],
+ ihdc: ["Wikipedia — Indian Hemp Drugs Commission", "https://en.wikipedia.org/wiki/Indian_Hemp_Drugs_Commission"],
+ legal: ["Wikipedia — Legal history of cannabis in the United States", "https://en.wikipedia.org/wiki/Legal_history_of_cannabis_in_the_United_States"],
+ siler: ["J. F. Siler and others — Mariajuana Smoking in Panama, The Military Surgeon (1933)", "https://www.druglibrary.net/schaffer/Library/studies/panama/panama1.htm"],
+ panama: ["Wikipedia — Cannabis in Panama", "https://en.wikipedia.org/wiki/Cannabis_in_Panama"],
+ uniform: ["Wikipedia — Uniform State Narcotic Drug Act", "https://en.wikipedia.org/wiki/Uniform_State_Narcotic_Drug_Act"],
+ league: ["Squaring a Circle: Cannabis and the Dubious Legacy of the League of Nations", ""],
+};
+
… 1294 more linessrc/pages/search.json.js +5 −1
Not shown: a picture, a generated file, or past the length shown per change.
src/pages/sitemap.xml.js +3 −0
Not shown: a picture, a generated file, or past the length shown per change.
src/styles/global.css +22 −1
Not shown: a picture, a generated file, or past the length shown per change.