Website of the Studio for Advanced Research in Cybernetics (SARC). Static site built with Hugo Extended. Git + Markdown are the CMS.
See CLAUDE.md for the full project conventions — read it before
changing anything.
-
Hugo Extended
v0.164.0(pinned; theextendedbuild is required for image processing and asset bundling). Check withhugo version— the string must contain+extended.brew install hugo # macOS
make dev # hugo server with drafts (-D) + future posts, local dev
make build # production build (minified, correct baseURL) -> public/
make check # production build + required-output and link checks
make new-post SLUG=my-entry # new journal bundle from the archetype
make deploy # check, then push main; GitHub Actions publishes to Pages
content/— Markdown. Journal entries are leaf page bundles undercontent/journal/<year>/<slug>/and carry their own images/audio/diagrams.layouts/— the custom theme (plain HTML templates, no third-party theme).assets/css/— plain CSS, concatenated + minified through Hugo.assets/js/— minimal progressive-enhancement JS (the landing mark).assets/img/— source visual assets, incl. the SARC four-row mark.static/— passthrough files (robots.txt, favicon, …).
The homepage masthead renders the live SVG reconstruction of the four-row
mark — layouts/partials/mark.html, generated by scripts/generate-mark.py
(make mark) from the Nasalization letterforms in
design/fonts/Nasalization-Rg.otf. All sixteen letters (four rows, reflected
across the figure's centre in pairs) are independently transformable SVG
groups — no passive <use>; assets/js/mark.js turns one random letter 90°
at a time on a fixed cadence. It ships no font (glyphs are baked to paths),
degrades to a static readable state with no JS, and stays canonical under
prefers-reduced-motion.
The original raster (assets/img/rect15_larger.png) is preserved unchanged
and used as the social/Open-Graph preview image. See assets/img/README.md.
Beneath the mark sits one SARC-curated editorial quotation —
data/homepage_quotes.yaml, resolved by layouts/partials/homepage-quote.html.
This is homepage content, not the Library: quotes never become Library
entries and never touch Library front matter, filters, JSON, or random
selection. library_ref is an optional pointer into the Library (a
resolvable library.id) and is validated at build time
(homepage-quotes-validate.html) — an unresolvable reference fails the
build. Selection is session-stable (sessionStorage, revalidated against the
enabled set on every read) and never becomes URL state. The first enabled
quote is the deterministic no-JS/pre-JS fallback; assets/js/homepage-quote.js
is inlined (not <script src defer>) specifically so the session pick
replaces it before the fallback can visibly flash. See CLAUDE.md §
Homepage quote for the full editorial rules and the author/work/full/no-link
attribution rule.
The Library (/library/) is one unified catalog of entries — a growing
research collection and small knowledge graph, not a set of sections. Every
durable thing SARC wants to identify, annotate, connect, or point toward is an
entry (essays, books, manuals, people, groups, recordings, releases, websites,
systems, …). Type, subject, and access are metadata and filters, never shelves. The
landing shows a chance-picked "From the Library" entry, then the View
switch and Type/Subject filters (collapsed by default behind a native
disclosure), then the full ruled catalog. The controls render below the
chance panel but still define its field: the chance pick and the catalog
below it are both sampled/filtered from that exact same field (one
matching-set computation, not two) regardless of DOM order. A Catalog/Images view switch presents
the same filtered entries as a thumbnail-only grid instead of full records —
same images, same crop, same order, filters and URL state shared with Catalog.
Images is the default view (?view=catalog opts back into the ruled
records; an absent or invalid view resolves to Images). A third,
experimental Map view (?view=map) draws the same matching entries as a
force-directed diagram of explicit editorial relationships only —
creators[].ref and related[].ref, nothing inferred from shared subjects —
via assets/js/library-map.js; see CLAUDE.md § Library for the full
data-flow and what it deliberately doesn't do (no knowledge graph, no
similarity/recommendation logic, no clustering). The chance pick is
revalidated against the active filters/view on every change (kept if still
eligible, redrawn otherwise, restricted to image-bearing entries in Images
view) and is sessionStorage-only — it never becomes URL state. Requires JS;
Catalog with an unfiltered chance pick is the no-JS fallback. See CLAUDE.md
§ Library for the full behavior (shared thumbnail partial, accessibility,
chance-selection eligibility/revalidation). Section colour is Forest.
Add an entry (one archetype for everything):
hugo new --kind library-entry library/research/person/some-slug/index.md
Entries are stored under their Collection, then a public-type directory —
content/library/<collection>/<public-type>/<slug>/ (research is the
only Collection today; public types: person | group | organization | work | system | place | concept | event) — but always publish flat within
their Collection at /library/<collection>/<slug>/ (e.g.
/library/research/some-slug/); neither the Collection nor the type folder
appears past that in the URL. See docs/library-v2.md for the Library v2
architecture this reflects. Edit the front matter and set draft: false. Key fields:
library.id— stable, unique id (not the title/URL); relationships resolve through it.library.type— one controlled type (book | person | group | organization | manual | album | essay | website | system | …;personfor an individual,groupfor a band/ensemble,organizationfor an institution).library.sarc_work—trueif SARC made it (a filter axis, not a shelf). All validated againstdata/library.yaml.subjects— controlled terms (why it matters to SARC); power the filter.creators—{name, role, ref?}. Aref(another entry'slibrary.id) links the name and auto-adds a Works in the Library list on that entry.relatedare{ref, relation}editorial links.images— ordered; the first is the primary (list thumbnail + entry featured image). Live in the page bundle;altrequired. Optional per-imagecaption/credit/source/role/anchor/rights(anchor: Topcrops the thumbnail from the top when a centred crop cuts through a head). No hotlinking.creditandsourceare different claims:sourceis the exact page the image was obtained from/verified against (never a bare homepage or a platform name like "Bandcamp"/"Wikipedia");creditis who actually made or supplies it (a photographer, archive, or "SARC") — leave it absent rather than naming the site you found the image on. Per-imagerights.status(sarc-owned | public-domain | licensed | permitted | unknown— distinct from the hosted-filerights.statusbelow) records the image's copyright condition; a separateuse.basis(identification | editorial | promotional | fair-use | archival) plususe.noterecords why SARC is using it when rights aren't sarc-owned/public-domain/licensed/ permitted — kept apart so the schema never implies more legal certainty than it has. SeeCLAUDE.md§ Library for full examples andmake library-image-auditfor an offline report on every image's metadata.access—{label, kind, url|file}(many per entry).hosted-fileneeds a bundlefile; external kinds open in a new tab.url/fileare exclusive.rights.status(only for a hostedfile) —sarc-owned | public-domain | licensed | permitted | archival | review. A hosted file withreview/unset rights fails the build — keep it a draft.archival= good-faith hosting of a long-discontinued product's docs, taken down on request (not a public-domain claim). Don't fabricate rights.
Cross-site inclusion. A page elsewhere (e.g. a Systems manual) joins the
catalog with library: { include: true, id: … } — no copying, canonical URL
stays put. Moving a URL: add the old path under aliases:.
The research catalog is also emitted as /library/research/index.json
(/library/index.json is the Library root — currently a placeholder until
Phase 3 of the Library v2 migration lands, see docs/library-v2.md). See data/library.yaml
for the vocabularies and CLAUDE.md § Library for full conventions (graph,
images, random, filtering, rights, the private iCloud policy, why no database).
Hosted on GitHub Pages. Pushing to main triggers
.github/workflows/deploy.yml, which builds the site with the pinned Hugo
version and publishes it. make deploy runs make check first, then pushes
main (build/publish happens on the runner, not locally).
The custom domain (sarc.systems) is pinned by static/CNAME, and GitHub
issues/renews the HTTPS certificate. DNS + email live on Fastmail: the apex
A records point at GitHub Pages while MX/DKIM/SPF stay on Fastmail — never
touch the mail records when editing DNS. Deploy from main only; public/ is
gitignored and never hand-edited.