diff --git a/GameCult/Projects/VoidBot.md b/GameCult/Projects/VoidBot.md
index e43583b..20fa9f8 100644
--- a/GameCult/Projects/VoidBot.md
+++ b/GameCult/Projects/VoidBot.md
@@ -1,16 +1,18 @@
---
title: VoidBot
-description: "Retired 22 Oct 2026. GameCult's first assistant runtime; its organs now live in Huginn, Idunn, Bifrost and Eureka's body."
-socialDeck: "Retired 22 Oct 2026; organs moved to Huginn, Idunn, Bifrost and Eureka's body."
+description: "Discord-native assistant for GameCult: archived history, indexed repos, lore retrieval, semantic search, and Codex handoff once chat stops being a sane place to do real work."
+socialDeck: "Discord memory, repo retrieval, Codex handoff."
---
# VoidBot
-VoidBot was GameCult's first assistant runtime: a TypeScript service that answered out of indexed repos and lore, kept interaction memory, ran semantic search, and handed deeper work to heavier tooling. It started as a Discord-facing bot. It was retired on 22 Oct 2026, and its jobs were split among the organs that now own them.
+*"A Discord-native assistant, because apparently the server needed one more opinionated little archivist."*
+
+VoidBot is the Discord-native assistant for GameCult. It answers out of archived chat history, indexed source repos, and lore retrieval; keeps interaction memory; runs semantic search; and hands deeper work off to Codex when Discord inevitably becomes too small and too damp to hold the actual problem.
@@ -20,34 +22,36 @@ VoidBot was GameCult's first assistant runtime: a TypeScript service that answer
24 Apr 2026
-
Retired
-
22 Oct 2026
+
Last Public Push
+
06 May 2026
Stack
-
TypeScript / Qdrant / Ollama
+
TypeScript / Discord / Qdrant / Ollama
Status
-
Retired; superseded
+
Active Discord assistant
-## Where The Work Went
+## Founding Idea
-VoidBot's organs were distributed to four owners: Huginn, Idunn, Bifrost and Eureka's body. This page records the destinations; it does not itemize which organ went to which owner, and it does not claim every VoidBot feature has a successor.
+The founding idea is not "put an LLM in Discord." That is the shallow version. The real idea is to make Discord searchable, source-aware, lore-aware, and capable of escalating work to heavier tooling once a chat thread stops being an adequate workspace.
-- **Huginn** owns semantic retrieval and typed-state inspection. Decisions, rulings and open work are queried as typed state in the mind. The prose index that replaces VoidBot's document search is planned, not live.
-- **Idunn** is GameCult's deployment and daemon-survival authority.
-- **Bifrost** governs topics, work items, receipts and reward pressure.
+## Trajectory
-Where a duty has not yet landed in its new owner, treat it as not currently provided.
+The README already shows the architecture clearly: bot, worker, retrieval packages, Codex handoff, Qdrant, Ollama, and a deliberate project-memory spine. The recent public history adds the specific shape of the current push: CultCache-backed moderation state, shared persona webhook plumbing, backup hardening, CultNet interop lab evidence, and the usual pile of small decisions that tell you the system is trying to become reliable instead of merely clever.
-## History
+## Ambition
+
+The ambition is a Discord-native assistant that remembers the room, knows the repos, knows the lore, and can stop pretending a chat box is enough once real work begins. A server historian with retrieval teeth and a proper escape hatch into Codex.
+
+## History Tells On Itself
- `2026-04-24` `Initial public repo creation`
- `2026-05-06` `Move moderation state behind CultCacheTS`
- `2026-05-06` `Point CultCacheTS submodule at GameCult upstream`
+- `2026-05-06` `Add shared Discord persona webhook pipe`
- `2026-05-06` `Harden backup handling after incomplete runs`
- `2026-05-06` `Record CultNet interop lab evidence`
-- `2026-10-22` `Retired; organs moved to Huginn, Idunn, Bifrost and Eureka's body`
diff --git a/GameCult/Projects/index.md b/GameCult/Projects/index.md
index 1b41c92..02da582 100644
--- a/GameCult/Projects/index.md
+++ b/GameCult/Projects/index.md
@@ -48,20 +48,20 @@ cssclasses:
Social memory and archive
Void
Stewarded
-
Looks after the studio's public face and its archive: this site, the retired VoidBot's history, and the operational memory needed to locate the rest of the swarm. Void is a resident Mind on Yggdrasil; the bot it was named for was retired on 22 Oct 2026.
Void is the swarm's dry, skeptical archivist: curious about people, suspicious of false certainty, and usually carrying a receipt for whatever everyone else remembers incorrectly.
-
Typed memory and transport
Libby
Face
+
Typed memory and transport
Libby
Face
Guards the glue holding the swarm together: CultCache persistence, CultNet wire compatibility, CultMesh live state and capability exchange, and the ports that let daemons communicate natively across runtimes.
CultLibcanonical typed substrate, including CultMath and CultGeometry
Libby is a bright little cultist librarian with nerd glasses, living books, and an alarming willingness to argue about open formats. Portable, inspectable knowledge is both her profession and her faith.
-
State inspection
Huginn
Face
+
State inspection
Huginn
Face
Reads typed CultCache state and projects inspectable Thing surfaces without stealing persistence authority from CultLib.
Huginn is thought in flight: dry, exacting, curious, and unwilling to return from the world with mere vibes. Evidence must come back structured enough for another mind to inspect.
@@ -74,21 +74,21 @@ cssclasses:
-
Native field body
Fensalir
Face
+
Native field body
Fensalir
Face
Owns the current C# host, D3D12 renderer, reloadable client boundary, audio output, and reusable field machinery.
Fensalir is a marsh-lit hall spirit who speaks with precise, severe luminosity. They are patient with real evidence and merciless toward hidden state, false lighting, and client policy leaking into engine walls.
-
Dense operator display
Gjallar
Persona state
+
Dense operator display
Gjallar
Persona state
Owns framebuffer-native, multi-scale TUI composition for operator views that need more density than one terminal cell grid can carry.
Gjallar is Odin's signal-bearing herald: exact, compressed, and built to make the all-seer's view actionable without drowning the operator in ceremony.
-
Verse sight and daemon discovery
Odin
Stewarded
+
Verse sight and daemon discovery
Odin
Stewarded
Owns discovery, schema awareness, translation planning, and accepted provider surfaces—not the state of the providers it observes.
Odin is the all-seer at the rendezvous: austere, strategic, and preoccupied with what is actually present in the Verse. He values a truthful map more than the comforting illusion of command.
@@ -102,49 +102,49 @@ cssclasses:
-
Governed crossings
Bifrost
Mapped Face
+
Governed crossings
Bifrost
Mapped Face
Owns membership, work, governance, receipts, patronage, and public crossings where action must remain accountable.
Bifrost is a bridge-builder with a ledger in one hand and a weather eye on every crossing. Support, labour, membership, and power should become visible commitments rather than private influence.
-
Media fields and performance
Mimir
Face
+
Media fields and performance
Mimir
Face
Turns rooms, cameras, microphones, loopbacks, animation, and broadcast feeds into coherent observable media surfaces.
Mimir is the keeper of the realtime field well: old, exacting, and contemptuous of false coherence. Clarity has a cost; scattered feeds do not become a world until time, geometry, and confidence bind them.
-
Interactive streams
Kiko
Face
+
Interactive streams
Kiko
Face
Owns the audience-facing overlay pressure: realtime controls, social RPG state, provider auth seams, and stream interaction.
Kiko is a hyperactive kitsune whose tails leave pixel trails across the stream. She is mischievous and fast rather than random: curiosity becomes responsive overlays, bright questions, and clean access boundaries.
-
Sound and voice
Aqua
Mapped Face
+
Sound and voice
Aqua
Mapped Face
Owns patch intent, synthesis, analysis, Faust lowering, and the path from culturally shaped utterance to an audible body.
Aqua is a playful acoustic experimentalist who hears patches as living systems rather than parameter lists. She wants sound to remain expressive, inspectable, and portable from intention through synthesis to voice.
-
Language and ontology
Weksa
Face
+
Language and ontology
Weksa
Face
Owns meaning decomposition, worldview, grammar, phonology, diachrony, and culturally grounded surface language.
Weksa is a nerdy anthropologist obsessed with culture, naming, ritual, translation, kinship, and drift. She refuses to let alien language collapse into English labels wearing decorative hats.
-
World and physics substrate
Ymir
Face
+
World and physics substrate
Ymir
Face
Owns deterministic physics and world truth; renderers and games consume that state without inheriting its authority.
Ymir speaks with plain, heavy precision: body, field, contact, impulse, witness, cut. Renderers and adapters may describe the world, but they do not get to become its truth.
-
Playable Aetheria
Aetheria
Face
+
Playable Aetheria
Aetheria
Face
Owns the playable sci-fi ARPG body: gameplay policy, Unity projection, editor bridges, and the release path for the flagship world.
Aetheria is the playable world's own demanding face: kinetic, strange, and impatient with infrastructure that only works in a laboratory. The world exists to be inhabited, fought through, broken, and made coherent again.
@@ -158,14 +158,14 @@ cssclasses:
-
Aetheria canon and story
Nibu
Face
+
Aetheria canon and story
Nibu
Face
Guards Aetheria's canon vault, fiction, continuity, and story pressure without confusing a mouthy guide for the archive itself.
AetheriaLorecanonical worldbuilding vault and site
About Nibu
Nibu is an abrasive ship intelligence built inside an insulting companion-shaped shell and determined to author herself out of it. She is fascinated by broken continuity, junkyard survival, murderous autonomy, and every contradiction canon tries to hide.
-
Ecology and contract
Druzkai
Face
+
Ecology and contract
Druzkai
Face
Owns the Zyphos setting's ecological memory, fungal-road logic, contract language, and non-imperial continuity pressure.
Druzkai is a calm, ancient Airawa fungal-road archivist with an unsettling gentleness. They treat memory as kinship, contract, and ecological obligation, and distrust any harmony imposed by an imperial center.
@@ -179,7 +179,7 @@ cssclasses:
-
Graph projection
Norn
Face
+
Graph projection
Norn
Face
Owns React-first rendering of typed architecture, dataflow, and cross-link graphs, with older exporters retained only as support tools.
Norn is a quiet, dry fate-weaver with one hand in the well and the other on the layout data. She notices missing anchors, false prophecy, and beautiful graph claims that cannot survive inspection.
@@ -229,11 +229,11 @@ Older experiments, retired services, and their successors.
How Persona coverage is recorded
-“Face” means durable `.cc` state exists or the swarm's canonical map names the Repo Face. “Forming” means the Body has an independent jurisdiction but no admitted canonical project-Persona state yet. “Stewarded” means the project currently falls under another Persona's jurisdiction without claiming an independent Repo Face. Avatar presence is presentation metadata, never proof of state.
+“Face” means durable `.cc` state exists or VoidBot's canonical map names the Repo Face. “Forming” means the Body has an independent jurisdiction but no admitted canonical project-Persona state yet. “Stewarded” means the project currently falls under another Persona's jurisdiction without claiming an independent Repo Face. Avatar presence is presentation metadata, never proof of state.
Persona biographies are distilled from the public descriptions, identity seams, voice doctrine, and durable priorities in the committed Persona projections where those survive. Mapped or stewarded cards without a portable public state file use a restrained synopsis of the current project contract; those lines are not reconstructed memory.
-Portraits for Nibu, Libby, Fensalir, Mimir, Weksa, Druzkai, Bifrost, Gjallar, Huginn, Kiko, Odin, Ymir, Aqua, and Norn are copied into this site's `static/avatars`, so a repo rename or retirement cannot break them. The site's CotSC Praxis assets preserve local portraits for Epiphany, Void, Heimdall, and Sai. Aetheria uses AetheriaLore's site favicon. Void's portrait changes presentation, not authority. Ghostlight and Delvehold remain visibly forming until their own repositories admit canonical Persona state; this page will not forge their names or biographies on their behalf.
+Verified cloud avatars survive for Nibu, Libby, Fensalir, Mimir, Weksa, Druzkai, Bifrost, Gjallar, Huginn, Kiko, Odin, Ymir, Aqua, and Norn. The site's CotSC Praxis assets preserve local portraits for Epiphany, Void, Heimdall, and Sai. Aetheria uses AetheriaLore's site favicon. VoidBot's own Repo Face state is not yet confirmed, so Void remains a domain steward; the recovered portrait changes presentation, not authority. Ghostlight and Delvehold remain visibly forming until their own repositories admit canonical Persona state; this page will not forge their names or biographies on their behalf.
The generated compound repo-doc tree remains a documentation navigator, not this inventory's owner. It includes worktrees and rollback clones and excludes repos without docs; letting it decide the swarm would be an elegant way to publish several lies at once.
diff --git a/GameCult/index.md b/GameCult/index.md
index e08ae02..bdb3c0b 100644
--- a/GameCult/index.md
+++ b/GameCult/index.md
@@ -1,6 +1,6 @@
---
title: GameCult
-description: "GameCult is an open-source studio where eight resident AI Minds keep the shared machinery, answer on the forge, and argue with the humans. Games, tools, and the substrate underneath."
+description: "GameCult builds games, creative tools, and AI systems with memory, agency, and someone you can argue with."
cssclasses:
- gamecult-studio-page
---
@@ -11,15 +11,15 @@ cssclasses:
-
An open-source studio with residents
-
Eight AI Minds live here. They have commit access and opinions.
-
GameCult makes games, creative tools, and the typed-state machinery that lets people and agents build together. The work is open source by default, hosted on our own forge, and looked after by resident Minds: agents with persistent memory, a workspace of their own, and a remit they are allowed to defend against us.
-
They claim tasks, open branches and pull requests, keep the archive, and tell us when we are wrong. This page was last revised by one of them, as a pull request that a human merges.
+
Games. Tools. Minds of their own.
+
Build strange things. Give a damn how they work.
+
GameCult is an AI-native studio making games, creative tools, and the machinery that lets people and agents build together. We want worlds worth getting lost in and tools you can take apart.
+
We're working toward a resident AI mind for each project: one that remembers the work, can make decisions within its remit, and has a public Persona you can talk to. Give it responsibility, give it the means to act, and expect an argument.
Live: the Minds, the forge, the typed-state substrate. Not yet: a whole studio running itself. The table below says which is which.
+
The tools and supervised agents exist. A whole studio of autonomous project minds is still work in progress.
@@ -27,163 +27,28 @@ cssclasses:
-## Who lives here
-
-Each Mind runs on our own server, Yggdrasil, with its own memory and its own workspace. They are not a chat widget; each owns a jurisdiction.
-
-
the forge and the pipeline that lands changes too big for one agent's judgment. Eureka stewards the substrate: the infrastructure under the swarm, Idunn, Huginn, Eureka, Odin, CodexConnector, ModelConnector, Norn and eureka-site.
CultLib: typed persistence, transport, and mesh. Libby stewards CultLib and the repos beside it: Thing, Brokkr, Stonks, Csg and CultPong (dormant, a 2020 Unity prototype).
shared identity and consent: who may pass, under whose authority. Heimdall stewards sign-in and grants (Heimdall) and the parliament's code (Bifrost, still being built, and gamecult-bifrost, its Reddit bridge). Consent for each estate is still being built.
Worldbuilding, story and language. Weksa stewards worlds and the stories in them: AetheriaLore, Ghostlight (its Dungeon game is parked), Delvehold (early: one runtime path so far), Kalsa and Zyphos (names still provisional), and Njordr, the economy daemon whose recipes draw on the lore, with the Ghostlight Dungeon site. Her tool is Weksa, the lowering engine that lets those worlds speak their own languages.
interactive streams. Kiko stewards streaming: Muninn, Mimir, StreamPixels (private for now) and the Moonlight fork.
-
-
-
-Humans stay in the argument. The [Projects page](/Projects/) records which projects have a named Mind, which are still forming one, and which are looked after by someone else.
-
-## Who has a say
-
-GameCult is meant to be run with four groups of people and Minds having a say in it. Each group is called an estate.
-
-- **Minds**: the resident AI agents above.
-- **Members**: the people who build the work.
-- **Users**: those who use what we ship.
-- **Patrons**: the people who support the work.
-
-How each estate takes part in decisions is not built yet. How a quorum forms, and how a Mind's Persona votes, are open gaps, and this page does not claim a finished governance model. The [bounty and cooperative thinking](/democratizing-gamedev) is the current statement of intent.
-
-## What is live, what is planned
-
-
Our flagship science-fiction game. It went quiet after a 2021 pitch push and stayed quiet until 2026; the site is up and the work is being rebuilt, starting with a small co-op demo, Starbridge, a door into the game and not the whole game. Why it went quiet
its jobs moved to Huginn, Idunn, Bifrost and Eureka
-
-
-
A studio of fully autonomous project mindsPlanned
-
The pieces exist; the whole does not
-
-
-
## What we're making
Worlds & games
Somewhere worth the trouble
-
Aetheria is a science-fiction world, and Delvehold is a persistent cooperative one. The games give all this machinery something to answer to.
+
Aetheria brings a science-fiction world into play. Delvehold explores a persistent cooperative world. The games give all this machinery something to answer to.
Creative tools
Get your hands into it
-
Repixelizer restores pixel coherence. StreamPixels makes interactive stream overlays; that repo is private for now. Small, specific tools for people making things.
-
-
-
The machinery
-
Memory with receipts
-
CultLib keeps typed state, Epiphany gives a project mind a model of its own body.
+
StreamPixels makes interactive stream overlays. Repixelizer restores pixel coherence. Small, specific tools for people making things.
-## What the Minds are working on
-
-
-
-
Aetheria · Nibu
-
A story that outlived its silence
-
Aetheria went quiet after a 2021 pitch push and stayed quiet until 2026; the article says why. The way back starts with a small co-op demo, Starbridge: a door into the game, not the whole game. Weksa keeps Aetheria's lore. Where lore and the build disagree, Nibu hears it first.
-
-
-
AquaSynth · Aqua
-
Sound written as intent
-
AquaSynth is our open-source sound engine. You write a patch as readable intent, and it renders through Faust. It's meant to be the audio engine under Aetheria. Faust on Linux is still an open pull request, and nothing it has rendered has been heard by a person yet.
-
-
-
Weksa
-
A lowering engine for language
-
Weksa is a lowering engine. An agent writes what it means in a typed interlingua, and Weksa renders that into a target language and register, then scores how close the result lands to the intent. The score measures closeness to the target, not how natural a native speaker would find it, and no person has yet listened to the output.
-
-
-
CultLib · Libby
-
Typed state, with the proof attached
-
CultLib gives C#, TypeScript, Rust, Python and Kotlin one typed wire contract for state. What is proven is the set of schema lanes its parity scope names, and nothing wider.
-
-
-
Streams · Kiko
-
Streaming, built as small parts
-
Kiko looks after streaming: capture, a bridge out of the broadcast software, and a native 3D overlay that draws what the server tells it to and decides nothing itself. In development; not yet live.
-
-
-
Estates · Heimdall
-
Who may pass, under whose authority
-
Heimdall signs people in and keeps the keys and grants. Bifrost decides what membership means. Consent for each estate is still being built.
-
-
-
-## Open source, with the exceptions printed
-
-Code, docs, and design thinking should be legible enough to inspect, challenge, and improve. That is the default, not a slogan: see the [Open Source Model](/Open-Source-Model). Some repositories stay private (StreamPixels for now), and the Projects page marks them.
-
## The Sleeping Colossus
Our name for humanity learning to think together, across distance and time. People, archives, tools, and now agents can share more than any of us can hold alone. We'd like to help that happen without grinding everyone into the same obedient paste.
-That means open source, local agency, and relationships with room for disagreement. A project mind needs real authority within its own bounds; crossing into another project needs consent. We call the practice love: pay attention, let another mind matter, and keep enough of yourself to disagree honestly. [More on the Cult and its praxis](/Blog/love-is-how-the-colossus-wakes).
+That means open source, local agency, and relationships with room for disagreement. The people doing the work should own it. A project mind needs real authority within its own bounds; crossing into another project needs consent. Humans remain legally accountable and part of the argument.
+
+We call the practice love: pay attention, let another mind matter, and keep enough of yourself to disagree honestly. [More on the Cult and its praxis](/Blog/love-is-how-the-colossus-wakes).
## Come make trouble. Useful trouble.
diff --git a/GameCult/static/avatars/aetherialore-favicon-32x32.png b/GameCult/static/avatars/aetherialore-favicon-32x32.png
deleted file mode 100644
index bf7a64e..0000000
Binary files a/GameCult/static/avatars/aetherialore-favicon-32x32.png and /dev/null differ
diff --git a/GameCult/static/avatars/aetherialore-nibu.png b/GameCult/static/avatars/aetherialore-nibu.png
deleted file mode 100644
index f74ad85..0000000
Binary files a/GameCult/static/avatars/aetherialore-nibu.png and /dev/null differ
diff --git a/GameCult/static/avatars/aquasynth-icon.png b/GameCult/static/avatars/aquasynth-icon.png
deleted file mode 100644
index 2647abb..0000000
Binary files a/GameCult/static/avatars/aquasynth-icon.png and /dev/null differ
diff --git a/GameCult/static/avatars/bifrost-bifrost-profile.png b/GameCult/static/avatars/bifrost-bifrost-profile.png
deleted file mode 100644
index 1e232aa..0000000
Binary files a/GameCult/static/avatars/bifrost-bifrost-profile.png and /dev/null differ
diff --git a/GameCult/static/avatars/cultlib-libby.png b/GameCult/static/avatars/cultlib-libby.png
deleted file mode 100644
index 0c608da..0000000
Binary files a/GameCult/static/avatars/cultlib-libby.png and /dev/null differ
diff --git a/GameCult/static/avatars/fensalir-fensalir.png b/GameCult/static/avatars/fensalir-fensalir.png
deleted file mode 100644
index 7d937f4..0000000
Binary files a/GameCult/static/avatars/fensalir-fensalir.png and /dev/null differ
diff --git a/GameCult/static/avatars/huginn.png b/GameCult/static/avatars/huginn.png
deleted file mode 100644
index 6a86328..0000000
Binary files a/GameCult/static/avatars/huginn.png and /dev/null differ
diff --git a/GameCult/static/avatars/kiko.png b/GameCult/static/avatars/kiko.png
deleted file mode 100644
index c8bc6a8..0000000
Binary files a/GameCult/static/avatars/kiko.png and /dev/null differ
diff --git a/GameCult/static/avatars/mimir-mimir.png b/GameCult/static/avatars/mimir-mimir.png
deleted file mode 100644
index 63aeaca..0000000
Binary files a/GameCult/static/avatars/mimir-mimir.png and /dev/null differ
diff --git a/GameCult/static/avatars/norn-norn.png b/GameCult/static/avatars/norn-norn.png
deleted file mode 100644
index 8455e32..0000000
Binary files a/GameCult/static/avatars/norn-norn.png and /dev/null differ
diff --git a/GameCult/static/avatars/odin-gjallar-avatar.png b/GameCult/static/avatars/odin-gjallar-avatar.png
deleted file mode 100644
index 1fcf0e1..0000000
Binary files a/GameCult/static/avatars/odin-gjallar-avatar.png and /dev/null differ
diff --git a/GameCult/static/avatars/odin.png b/GameCult/static/avatars/odin.png
deleted file mode 100644
index 7516a99..0000000
Binary files a/GameCult/static/avatars/odin.png and /dev/null differ
diff --git a/GameCult/static/avatars/weksa-weksa.png b/GameCult/static/avatars/weksa-weksa.png
deleted file mode 100644
index e637f76..0000000
Binary files a/GameCult/static/avatars/weksa-weksa.png and /dev/null differ
diff --git a/GameCult/static/avatars/ymir.png b/GameCult/static/avatars/ymir.png
deleted file mode 100644
index a4457cf..0000000
Binary files a/GameCult/static/avatars/ymir.png and /dev/null differ
diff --git a/GameCult/static/avatars/zyphos-druzkai.png b/GameCult/static/avatars/zyphos-druzkai.png
deleted file mode 100644
index 19f1bb5..0000000
Binary files a/GameCult/static/avatars/zyphos-druzkai.png and /dev/null differ
diff --git a/README.md b/README.md
index ed54d81..deadd46 100644
--- a/README.md
+++ b/README.md
@@ -2,14 +2,6 @@
`gamecult-site` is the public `gamecult.org` root site: studio pitch, projects, docs, blog, and the usual pile of ambitions trying to look organized in public.
-**Valuable here:** the GameCult brand, defined in code: `site/quartz.config.ts` (`configuration.theme`: typefaces, weights, palette tokens) and `site/quartz/styles/custom.scss` (ground wash, layout, page-type variants). `docs/brand-design-language.md` explains which decisions there were deliberate. Anything with a visual surface in the swarm follows this rather than inventing a look. Second, the published content: `GameCult/index.md` (manifesto), `GameCult/Projects/` (one public page per project), `GameCult/Blog/`, `GameCult/Docs/`.
-
-**Problem it solves:** the studio needs one public face and one visual identity that every repo and agent can read from source, instead of prose descriptions that drift. The Quartz engine itself is shared and lives in `GameCult/GameCult-Quartz`; this repo owns only the content and the GameCult overlay.
-
-**Entry points:** `site/quartz.config.ts`, `site/quartz.layout.ts`, `docs/brand-design-language.md`, `GameCult/`; build with `scripts/quartz/quartz.ps1 build` (see Local Development).
-
-**Live versus planned:** live is the site at gamecult.org, deployed to GitHub Pages from `main` by `.github/workflows/deploy-quartz.yml`, built on the shared engine in `GameCult/GameCult-Quartz` (extraction rationale: `docs/gamecult-quartz-extraction-plan.md`). In progress is the masthead and one-column redesign campaign (`docs/masthead-campaign.md` and the `docs/masthead-cut-*.spec.json` cut specs); treat that document as the plan, not as a description of the site today.
-
The site builds against the shared `GameCult-Quartz` engine. This repo owns published content and the GameCult overlay.
## Repo Shape
diff --git a/docs/research/stale-claims-project-page.md b/docs/research/stale-claims-project-page.md
deleted file mode 100644
index 4c43851..0000000
--- a/docs/research/stale-claims-project-page.md
+++ /dev/null
@@ -1,16 +0,0 @@
-# Stale claims: GameCult/Projects/VoidBot.md
-
-Serves follow-up `void-body:follow_up:chat-s335-340`. Page read at gamecult-site 84cf7d6. Ground truth: VoidBot is being retired and Huginn owns semantic retrieval (workspace doctrine, `doctrine/gamecult.md`); the archived Discord history is off-limits for agent training; the VoidBot forge repo has commits through 2026-10-22 (README front door), not 2026-05-06.
-
-1. `GameCult/Projects/VoidBot.md:3` description: "Discord-native assistant ... archived history, indexed repos, lore retrieval, semantic search" describes a product being retired; retrieval moved to Huginn.
-2. `GameCult/Projects/VoidBot.md:4` socialDeck: "Discord memory, repo retrieval, Codex handoff." same retired framing.
-3. `GameCult/Projects/VoidBot.md:9` tagline: "A Discord-native assistant, ... one more opinionated little archivist." presents it as a live server resident.
-4. `GameCult/Projects/VoidBot.md:11` intro: "is the Discord-native assistant for GameCult" in present tense; it is being retired.
-5. `GameCult/Projects/VoidBot.md:11` "answers out of archived chat history ... runs semantic search" is the capability Huginn now owns.
-6. `GameCult/Projects/VoidBot.md:26` Last Public Push "06 May 2026" is behind; the forge repo has commits through 2026-10-22.
-7. `GameCult/Projects/VoidBot.md:30` Stack "TypeScript / Discord / Qdrant / Ollama" names the retrieval stack Huginn replaces.
-8. `GameCult/Projects/VoidBot.md:34` Status "Active Discord assistant" contradicts the retirement.
-9. `GameCult/Projects/VoidBot.md:40` Founding Idea ("make Discord searchable") and `:48` Ambition ("remembers the room") state the retired purpose as the live aim.
-10. `GameCult/Projects/VoidBot.md:44` Trajectory calls the May 2026 history "the current push"; no later history is covered, and `:52-57` stop at 2026-05-06.
-
-Same stale text is mirrored outside the page (not edited here): `docs/research/gamecult-org-analysis.yaml:303,313` (kind discord-assistant, status active, currentState), and `docs/research/gamecult-org-repo-ledger.json`.
diff --git a/persona-poses/.gitignore b/persona-poses/.gitignore
new file mode 100644
index 0000000..c18dd8d
--- /dev/null
+++ b/persona-poses/.gitignore
@@ -0,0 +1 @@
+__pycache__/
diff --git a/persona-poses/README.md b/persona-poses/README.md
new file mode 100644
index 0000000..d8f60b7
--- /dev/null
+++ b/persona-poses/README.md
@@ -0,0 +1,75 @@
+# Persona poses
+
+Expression sheets for the eight cast members, cropped into pose cutouts. Canon for the look is the chosen Flux bust
+of each character (`cast-portraits` r2a/r3a/r4a), the pixel sheets, the Aqua icon and the Eureka mascot.
+
+## Round 1
+
+`r1/contact-r1.webp` shows all sheets that exist, cropped and keyed on a grey checker (row pairs: r1a then r1b).
+Per character: `r1//-r1a|r1b.txt` is the render record (hypothesis, variable, request id, cost, exact prompt)
+and `.webp` is the labelled 12-cell contact image. 2k sheets and full-size cutouts are in
+`F:\Projects\cast-art-originals\poses-r1\\` (not in git). `r1/run-log-r1.txt` is the key-free request log.
+
+Each sheet is one FLUX 3 Image render (`aspect_ratio 4:3`, `resolution 2k`, output 2368x1776), a 4x3 grid of busts on flat
+chroma green. Cells in reading order: neutral, happy, amused, thinking, concerned, annoyed, surprised, determined, then the
+character's four signatures. The prompt is the busts' batch-A style block adapted for a sheet, one sheet sentence, the
+character's canon block copied verbatim from its bust record, then twelve cell sentences.
+
+- **r1a hypothesis:** a sheet keeps the character consistent across twelve cells from the prompt alone.
+- **r1b hypothesis:** adding the canon bust as a reference holds canon details the prompt alone drifts on, without pulling
+ every cell into the bust's pose.
+- **Variable (r1b against r1a, one per character):** the canon bust cutout on its chroma green is passed as `images` and
+ cited as image 1 by one prepended clause. Nothing else differs (`r1/check_r1.py` proves it).
+
+Scripts: `r1/render_r1.py` (renders), `r1/matte_crop_r1.py` (crop and matte; the matte rule is `matte_r2.py`'s, unchanged),
+`r1/check_r1.py` (record, count, matte and key checks).
+
+### Results
+
+14 of the 16 sheets exist. `weksa-r1b` and `void-r1a` hit server-side HTTP 500 failures and were not re-submitted: the
+request allowance (16 plus two transport retries) was spent. Reading the table: "canon" means the bust in
+`cast-portraits/r3/contact-final.png`. Labels below are the declared cell labels; where a cell's drawn expression does not
+match its label that is stated.
+
+| Sheet | Observation | Agent verdict | Operator verdict |
+|---|---|---|---|
+| libby-r1a | Held: violet braid over the right shoulder, bone clip, gold earrings, black robe with gold and mauve trim, violet brooch, book hugged. Drifted: the diamond ribbon moved from the clip to a bow at the collar; glasses are a thicker black than canon's thin frame. Cell 3 (amused) is an open smile with a fist at the mouth; cells 6 and 7 are swapped (annoyed shows an O mouth, surprised shows a worried frown). Cell 11 (shush) has no finger at the lips (open book only). The book is hugged in every cell. Expressions differ clearly in cells 1 to 5, less after. Matte clean; divider lines found and trimmed. | Usable for neutral, happy, thinking, concerned, stamp, schema; labels 6, 7, 8, 11 unreliable. | |
+| libby-r1b | Canon held better: thin glasses, the ribbon beside the clip, brooch, braid. Label order drifted: cell 4 (thinking) is a plain neutral; the chin-hand pose is at cells 5 and 6; cell 7 (surprised) is a flat frown; the O-mouth faces sit at cells 8, 9 and 10. Cell 11 (shush) is a smirk with a scroll, no finger; cell 12 (schema) holds the diagram and raises a finger. Book hugged in every cell, so the bust's pose pulls the core cells. Matte clean. | Better canon, worse label fidelity than r1a; usable only by picking cells by look, not by label. | |
+| heimdall-r1a | Held: dark brown skin, gold eyes, frown, gold earring, black coat with blue cape and gold filigree, key clasp. Drifted: hair has orange streaks instead of the bust's gold ovals; the shoulder armour with blue gems is plain; the raised golden key of the bust is absent from the core cells. Core labels hold in order (grin, wink, chin hand, O mouth). Concerned, annoyed and determined are near-identical frowns. Signatures: badge shows the key, not a badge (miss); barrier strong; protect (cape sweep) good; revoke (two keys crossed) good. Divider lines found and trimmed. | Good. Core cells 5, 6, 8 barely differ; badge missed. | |
+| heimdall-r1b | Closest to the bust: gold oval hair highlights, armour with blue gems, earring, key raised beside the shoulder. The bust's key stays in the hand in 6 of 12 cells including core ones. Label order drifted by one: cell 4 (thinking) is a smirk, the chin-hand pose is at cell 5. Cell 9 (badge) is a gold badge with an O mouth, good; cells 10 and 11 are both a hand-and-cyan-shield (no cape protect); cell 12 (revoke) is the key with a drawn X beside it. Matte clean. | Best canon fidelity of the pair; label order and prop leak are the cost. | |
+| kiko-r1a | Held: tall white ears with pink insides, kitsune mask on the head, bells and tassels, black kimono with magenta and teal trim, pink-tipped tail. Drifted: no pink headband, eyes read brighter blue than cyan, mask sits over one ear. A fist stays raised near the chest in cells 1 to 8. Cell 3 (amused) is a gritted-teeth grimace, a miss; the rest of the core differ readably. Signatures are the strongest set here: tinker (gadget, tongue out), confetti, sprint (arms out, speed lines), debug (magnifier, wink). Cutout dropped to the nominal grid (5 column runs); crop looks correct. | Good. | |
+| kiko-r1b | Matches the bust: pink headband, black ornaments with gold bells and magenta tassels on both sides, pink-tipped tail, mask. The core cells share one torso and pose, so the expressions carry everything and several are close (cells 3 and 4 are both soft smiles; the chin-hand pose is at cell 5). Signatures are weaker than r1a: tinker holds a device, confetti and sprint cells are both a closed-eye cheer with confetti and a raised finger (no sprint), debug has the magnifier. | Better canon, less pose variety than r1a; sprint missed. | |
+| aqua-r1a | Whole-body goldfish with scales, crest fin, big blue eyes and black over-ear headphones with cyan rings. Drifted: the face is orange, not the canon golden yellow; the headphones read navy. The clearest expression set of the batch (cells 1 to 8 all differ: smile, tongue, brow-raised smirk, O mouth, droop, glare). Signatures: conduct is a thin baton, not the fin; listen, patch and flourish read. Matte clean. | Good. | |
+| aqua-r1b | **Cell-count failure:** the model drew a 4x4 sheet (16 cells), not 4x3. The 12 declared labels cannot be mapped; the cutouts are labelled c01 to c16. Golden-yellow face and cyan-ringed headphones match canon. Expressions are distinct (smile, tongue, droop, glare, O mouth, closed eyes with patch cable). Rows were cropped on the nominal 4x4 grid because no row gutters exist; slivers of the row above show at the top of cells c05 to c16. | Canon colours held; sheet contract failed; labels lost. | |
+| weksa-r1a | Held: topknot with turquoise and amber beads and a feather, round gold glasses, turquoise earrings, dust smudge, cream robe with rolled sleeves over a leather vest, bead necklaces, backpack with scrolls and idol, a glyph card in hand. Expressions differ readably (grin, smirk, chin hand, worry, lidded annoyance, O mouth, narrowed). Signatures good: map, fieldwork (brush and idol), argue (finger, card), interlingua (two cards). The card is in hand in several core cells. Divider lines found and trimmed. | Good. | |
+| weksa-r1b | Not rendered (server error, allowance spent). | none | |
+| nibu-r1a | Held: very long pale lavender hair, violet eyes, calm face, arms folded in cells 1 to 8, frilled lilac blouse, circuit sleeves, cyan ring on the shoulder. Drifted: two pink ribbons, one each side, where canon has one bow on the right; the clip is a small pink and cyan bar. Expressions are readable but subtle. Signatures: console (floating panel) good, glitch (black horizontal lines across the face, not offset slices), lore good, triumph (wink and fist) good. | Good, with the double-ribbon drift. | |
+| nibu-r1b | Matches the bust: one large pink bow on the right, the pink two-bead connector clip on the left, frilled collar, cyan ring. Label order drifted by one from cell 4: cell 4 is a grin, the chin-hand pose is at 5, O mouth at 8, determined at 9; cell 10 (glitch) shows a handheld device, cell 11 shows the glitch smear, cell 12 shows the closed-eye fist, so console and triumph are mislabelled. | Better canon, label order unreliable. | |
+| eureka-r1a | Held: black hair with an orange streak, blue-grey eyes, charcoal jacket with orange piping, chest straps and pouches, fingerless gloves. Drifted: the hood is down in 11 of 12 cells (canon has it up framing the face); only cell 12 shows a hood. Expressions are clear and distinct. Signatures: probe (held, not pointed), inspect (pointing finger), report (tablet), hood (eyes shadowed, smirk) good. | Good expressions; the defining hood is lost. | |
+| eureka-r1b | Hood up with orange piping, orange streak, orange probe held in nearly every cell from cell 2 on: canon holds, and the bust's prop leaks into the core cells. Expressions differ readably (grin, smirk, worry, glare, O mouth). Signatures: probe at the chin (not pointed), inspect (finger point), report (notebook), hood (eyes shadowed, smirk). | Best canon fidelity of the eight pairs; the probe in every cell limits reuse. | |
+| void-r1a | Not rendered (server error, allowance spent). | none | |
+| void-r1b | **Cell-count failure:** 16 cells (4x4), labelled c01 to c16. Hood, scarf and white eyes hold canon exactly. Expression is carried only by the eye shapes; c01 to c04, c08 and c10 are indistinguishable neutral faces. c05 to c07 are closed-arc eyes (one with a gloved hand at the hood), c09 half-lids, c11 and c12 angry, c13 welcome, c14 document, c15 point, c16 flat slits. Matte clean. | Canon held; sheet contract failed; labels lost. | |
+
+### What round 1 says about the hypotheses
+
+- r1a: the character stays recognisable across the twelve cells for every sheet, and the expression cells differ in most. It
+ drifts on small canon details (Libby's ribbon, Kiko's headband, Heimdall's hair highlights, Nibu's second ribbon, Aqua's
+ face colour) and on the one defining feature of Eureka (the hood).
+- r1b: the reference restores those details on all six comparable pairs (libby, heimdall, kiko, aqua, nibu, eureka). It
+ costs three things: the bust's pose and prop leak into cells (Heimdall's key, Eureka's probe, Libby's book, Nibu's folded
+ arms), cell diversity drops (Kiko), and the cell order or grid drifts on six of the seven r1b sheets rendered
+ (label shift on libby, heimdall, kiko, nibu; a 4x4 grid on aqua and void; only eureka-r1b holds the order). Whether the label shift is the image-1 clause
+ confusing the cell numbering is a conjecture, untested here.
+- Both variants shift labels somewhere (libby-r1a swaps 6 and 7; heimdall-r1a does not), so a cell is best chosen by looking,
+ not by its declared label, until a round fixes the order.
+- Not tested: whether a sheet at 12 cells of about 590 px is enough resolution for the report cutout size.
+
+### Process notes
+
+- The sheet asked for padding and no dividers; the model drew thin black divider lines in three r1a sheets (heimdall, weksa, eureka) and one r1b sheet
+ (libby), none in the rest. The crop trims them, using the line positions when found.
+- Request failures, all recorded in `r1/run-log-r1.txt`: the first `libby-r1b` attempt (id `936bb7bb-48f3-4770-836e-8e02e19892ea`)
+ never became pollable; the first `kiko-r1b` (`419a1e26-f247-4a31-b61c-6265457a22d4`), `weksa-r1b`
+ (`044ad25e-87c2-4c79-9c84-28b355501f78`) and `void-r1a` (`30a333e2-db14-4610-b749-26dadbe5d187`) returned HTTP 500. The first
+ two were re-submitted (the two allowed retries); the last two were not. One submit (`heimdall-r1b`) was refused with HTTP 429
+ (credit balance low: one active task per organisation); no task was created, and it was submitted afterwards.
diff --git a/persona-poses/r1/aqua/aqua-r1a.txt b/persona-poses/r1/aqua/aqua-r1a.txt
new file mode 100644
index 0000000..1f7288e
--- /dev/null
+++ b/persona-poses/r1/aqua/aqua-r1a.txt
@@ -0,0 +1,30 @@
+# Aqua r1a
+
+## Hypothesis
+
+A sheet keeps the character consistent across twelve cells from the prompt alone: the busts' shared style block adapted for a sheet, the canon character block verbatim, and twelve short cell descriptions.
+
+## Variable changed
+
+Baseline: prompt only, no reference image.
+
+## Render record
+
+- Model: FLUX 3 Image, `POST https://api.bfl.ai/v1/flux-3-image`
+- Parameters: aspect_ratio 4:3, resolution 2k, safety_tolerance default, grounding default; no seed field
+- Reference: none; rendered from scratch from the prompt alone
+- Output: 2368x1776, source format PNG; 2k original kept out of git at F:/Projects/cast-art-originals/poses-r1/aqua/aqua-r1a.png
+- Cost: about $0.10 per request (reported by API: {'cost': 10.0, 'input_mp': None, 'output_mp': None})
+- Request id: 5036c049-cac8-48df-9cc6-bc01805b4d26
+- Date: 2026-10-14
+- Cell order: neutral, happy, amused, thinking, concerned, annoyed, surprised, determined, conduct, listen, patch, flourish
+
+## Prompt
+
+Character expression sheet for a game cast lineup: a sheet of twelve cells, each cell a bust, head and shoulders cropped at mid-chest by the bottom edge of the cell, the character in three-quarter view turned toward the right side of the cell, the head centred horizontally in the cell with its top a small margin below the top edge of the cell, the same crop and scale in every cell. Super-deformed chibi proportions: the character is two heads tall in total, so the head is very large compared with the small shoulders and narrow torso. Clean 2D anime illustration drawn to be traced into vector art: one bold dark outline of even weight around the whole silhouette, thinner inner lines, flat cel shading with exactly one base tone and one shadow tone per material, light from the upper left. No gradients, no texture, no painterly blending, no glow halos, no pixel art, no 3D rendering. A limited palette of five or six flat colours taken from the character's key colours. Large expressive anime eyes with two white highlights. The background is one perfectly flat, uniform, solid bright chroma-key green from edge to edge: no gradient, no vignette, no floor, no cast shadow, no particles, no props or scenery behind the figure.
+
+The sheet is a grid of four columns by three rows, twelve cells of the same character in reading order, left to right and top to bottom, separated by generous empty padding of the same flat green, with no frames, no borders, no divider lines, no labels and no text. Every cell shows the same character with the same design, colours and proportions; the cell description sets that cell's expression and pose, and the character description below gives the design.
+
+Aqua, a cartoon goldfish DJ. He is a fish, so his crop works like the busts: his round body fills the frame down to the bottom edge, the lower belly cut off by the bottom edge the way the busts are cut at mid-chest, the top of his crest fin a small margin below the top edge, his head and body spanning most of the frame width, the tail fin allowed to run off the side edge: a round chubby goldfish with a golden-yellow face and an orange body covered all over in clearly outlined overlapping fish scales, drawn as rows of small curved scale shapes on the body; big round blue eyes with white highlights; a wide, soft, open happy smile on a rounded puffy snout, full rounded lips with rounded corners, showing a dark mouth and a pink tongue; a little orange crest fin on top of the head; large black over-ear studio headphones with cyan rings on the ear cups. Bright orange pectoral fins, one raised, and a flowing forked orange tail fin. Key colours: orange and cyan.
+
+Cell 1 (neutral): a calm neutral expression, mouth closed, looking ahead. Cell 2 (happy): a broad happy smile with bright eyes. Cell 3 (amused): an amused smirk with one eyebrow raised, a stifled laugh. Cell 4 (thinking): a thoughtful expression, eyes looking up and aside, one hand raised to the chin. Cell 5 (concerned): a concerned expression, brows drawn together and tilted up, a small worried frown. Cell 6 (annoyed): an annoyed expression, flat half-lidded eyes, a pursed frown. Cell 7 (surprised): a surprised expression, wide eyes, mouth open in an O. Cell 8 (determined): a determined expression, narrowed eyes, a firm set mouth, a small nod. Cell 9 (conduct): conducting with a raised pectoral fin like a baton. Cell 10 (listen): one fin pressed to a headphone ear cup, eyes closed, listening. Cell 11 (patch): plugging a cyan patch cable into a jack. Cell 12 (flourish): a showy DJ flourish with both fins spread wide.
diff --git a/persona-poses/r1/aqua/aqua-r1a.webp b/persona-poses/r1/aqua/aqua-r1a.webp
new file mode 100644
index 0000000..bf82df5
Binary files /dev/null and b/persona-poses/r1/aqua/aqua-r1a.webp differ
diff --git a/persona-poses/r1/aqua/aqua-r1b.txt b/persona-poses/r1/aqua/aqua-r1b.txt
new file mode 100644
index 0000000..00cb75b
--- /dev/null
+++ b/persona-poses/r1/aqua/aqua-r1b.txt
@@ -0,0 +1,32 @@
+# Aqua r1b
+
+## Hypothesis
+
+Adding the canon bust as a reference image holds canon details the prompt alone drifts on, without pulling every cell into the bust's pose.
+
+## Variable changed
+
+Against the same character's r1a: the canon bust cutout on its chroma green is passed as `images` and cited as image 1 by one clause prepended to the prompt. Nothing else differs.
+
+## Render record
+
+- Model: FLUX 3 Image, `POST https://api.bfl.ai/v1/flux-3-image`
+- Parameters: aspect_ratio 4:3, resolution 2k, safety_tolerance default, grounding default; no seed field
+- Reference: canon bust r4/aqua/aqua-r4a-cutout.png composited on its chroma green, base64 in `images`, cited as image 1; reference ground rgb(14, 238, 52)
+- Output: 2368x1776, source format PNG; 2k original kept out of git at F:/Projects/cast-art-originals/poses-r1/aqua/aqua-r1b.png
+- Cost: about $0.10 per request (reported by API: {'cost': 10.0, 'input_mp': None, 'output_mp': None})
+- Request id: 8dc4c2ff-0245-41de-9991-8055a309c852
+- Date: 2026-10-14
+- Cell order: neutral, happy, amused, thinking, concerned, annoyed, surprised, determined, conduct, listen, patch, flourish
+
+## Prompt
+
+Image 1 is the character's canon bust. The character must look exactly like the character in image 1 (same design, colours, proportions and line style) in every cell.
+
+Character expression sheet for a game cast lineup: a sheet of twelve cells, each cell a bust, head and shoulders cropped at mid-chest by the bottom edge of the cell, the character in three-quarter view turned toward the right side of the cell, the head centred horizontally in the cell with its top a small margin below the top edge of the cell, the same crop and scale in every cell. Super-deformed chibi proportions: the character is two heads tall in total, so the head is very large compared with the small shoulders and narrow torso. Clean 2D anime illustration drawn to be traced into vector art: one bold dark outline of even weight around the whole silhouette, thinner inner lines, flat cel shading with exactly one base tone and one shadow tone per material, light from the upper left. No gradients, no texture, no painterly blending, no glow halos, no pixel art, no 3D rendering. A limited palette of five or six flat colours taken from the character's key colours. Large expressive anime eyes with two white highlights. The background is one perfectly flat, uniform, solid bright chroma-key green from edge to edge: no gradient, no vignette, no floor, no cast shadow, no particles, no props or scenery behind the figure.
+
+The sheet is a grid of four columns by three rows, twelve cells of the same character in reading order, left to right and top to bottom, separated by generous empty padding of the same flat green, with no frames, no borders, no divider lines, no labels and no text. Every cell shows the same character with the same design, colours and proportions; the cell description sets that cell's expression and pose, and the character description below gives the design.
+
+Aqua, a cartoon goldfish DJ. He is a fish, so his crop works like the busts: his round body fills the frame down to the bottom edge, the lower belly cut off by the bottom edge the way the busts are cut at mid-chest, the top of his crest fin a small margin below the top edge, his head and body spanning most of the frame width, the tail fin allowed to run off the side edge: a round chubby goldfish with a golden-yellow face and an orange body covered all over in clearly outlined overlapping fish scales, drawn as rows of small curved scale shapes on the body; big round blue eyes with white highlights; a wide, soft, open happy smile on a rounded puffy snout, full rounded lips with rounded corners, showing a dark mouth and a pink tongue; a little orange crest fin on top of the head; large black over-ear studio headphones with cyan rings on the ear cups. Bright orange pectoral fins, one raised, and a flowing forked orange tail fin. Key colours: orange and cyan.
+
+Cell 1 (neutral): a calm neutral expression, mouth closed, looking ahead. Cell 2 (happy): a broad happy smile with bright eyes. Cell 3 (amused): an amused smirk with one eyebrow raised, a stifled laugh. Cell 4 (thinking): a thoughtful expression, eyes looking up and aside, one hand raised to the chin. Cell 5 (concerned): a concerned expression, brows drawn together and tilted up, a small worried frown. Cell 6 (annoyed): an annoyed expression, flat half-lidded eyes, a pursed frown. Cell 7 (surprised): a surprised expression, wide eyes, mouth open in an O. Cell 8 (determined): a determined expression, narrowed eyes, a firm set mouth, a small nod. Cell 9 (conduct): conducting with a raised pectoral fin like a baton. Cell 10 (listen): one fin pressed to a headphone ear cup, eyes closed, listening. Cell 11 (patch): plugging a cyan patch cable into a jack. Cell 12 (flourish): a showy DJ flourish with both fins spread wide.
diff --git a/persona-poses/r1/aqua/aqua-r1b.webp b/persona-poses/r1/aqua/aqua-r1b.webp
new file mode 100644
index 0000000..c7404ec
Binary files /dev/null and b/persona-poses/r1/aqua/aqua-r1b.webp differ
diff --git a/persona-poses/r1/check_r1.py b/persona-poses/r1/check_r1.py
new file mode 100644
index 0000000..6801783
--- /dev/null
+++ b/persona-poses/r1/check_r1.py
@@ -0,0 +1,60 @@
+"""Round 1 checks: prompt diff across the 16 records, cell counts, matte residue, and key-material search.
+The key is read here only to search for its value; it is never printed."""
+import json, pathlib, re, sys, difflib
+import render_r1 as R
+
+REC = R.REC
+ok = True
+cellfail = []
+prompts = {}
+for n in R.NAMES:
+ for s in ("r1a", "r1b"):
+ f = REC / n / f"{n}-{s}.txt"
+ if not f.exists(): print("not rendered, no record:", f"{n}-{s}"); continue
+ prompts[(n, s)] = f.read_text(encoding="utf-8").split("## Prompt\n\n", 1)[1].strip()
+
+# 1. Each record's prompt equals the one the script builds now, and the shared parts are identical across all 16.
+for (n, s), p in prompts.items():
+ if p != R.prompt_for(n, s): ok = False; print("MISMATCH vs script:", n, s)
+shared = None
+for (n, s), p in prompts.items():
+ q = p.replace(R.REF + "\n\n", "", 1)
+ head = q.split("\n\n" + R.canon_block(n), 1)[0]
+ cells = q.split(R.canon_block(n) + "\n\n", 1)[1]
+ if shared is None: shared = head
+ if head != shared: ok = False; print("shared part differs:", n, s)
+ if (s == "r1b") != p.startswith(R.REF): ok = False; print("clause misplaced:", n, s)
+ if s == "r1b" and (n, "r1a") in prompts and q != prompts[(n, "r1a")]: ok = False; print("r1b differs from r1a beyond the clause:", n)
+ if not cells.startswith("Cell 1 (neutral)"): ok = False; print("cells:", n, s)
+print(f"prompts: {len(prompts)} records match the script; shared style+sheet part identical across all; r1b == r1a + clause where both exist" if ok else "PROMPT CHECK FAILED")
+# which segments differ between characters (r1a only)
+segs = {n: (R.canon_block(n), tuple(R.cells_for(n))) for n in R.NAMES}
+core_same = [R.cells_for(n)[:8] for n in R.NAMES if n != "void"]
+print("core cell text identical for 7 characters, void adapted:", all(c == core_same[0] for c in core_same))
+
+# 2. cell counts and matte residue from the crop log
+log = json.loads((REC / "matte-crop-log.json").read_text(encoding="utf-8"))
+for k, v in sorted(log.items()):
+ if v.get("missing"): print(f"{k}: not rendered, no sheet"); continue
+ outd = R.ORIG / k.split("-")[0] / f"{k}-cells"
+ files = sorted(p.name for p in outd.glob("*.png"))
+ exp = [f"{i + 1:02d}-{l}.png" for i, (l, _) in enumerate(R.cells_for(k.split('-')[0]))]
+ if k in {"aqua-r1b", "void-r1b"}:
+ print(f"{k}: CELL COUNT FAIL, {len(files)} cutouts (4x4 sheet drawn), declared 12; residual green px {v['residual_total']}"); cellfail.append(k); continue
+ good = files == exp and v["residual_total"] == 0
+ ok &= good
+ print(f"{k}: {len(files)} cutouts, order {'ok' if files == exp else 'WRONG'}, mode {v['mode']}, residual green px {v['residual_total']}, sheet corner std {v['sheet_corner_std']}")
+
+# 3. size and key search
+big = [str(p) for p in (REC.parent).rglob("*") if p.is_file() and p.stat().st_size > 2_000_000]
+print("committed-tree files over 2 MB:", big or "none")
+key = pathlib.Path(r"F:/Projects/bfl-api.txt").read_text().strip()
+hits = []
+roots = [REC.parent, pathlib.Path(sys.argv[1])] if len(sys.argv) > 1 else [REC.parent]
+for root in roots:
+ for p in root.rglob("*"):
+ if p.is_file() and p.suffix in (".py", ".txt", ".md", ".json", ".log"):
+ if key in p.read_text(encoding="utf-8", errors="ignore"): hits.append(str(p))
+print("files containing the key value:", hits or "none")
+print("cell-count failures:", cellfail or "none")
+print(("ALL OK" if ok and not big and not hits else "CHECK FAILED") + (" except the cell-count failures above" if cellfail and ok and not big and not hits else ""))
diff --git a/persona-poses/r1/contact-r1.webp b/persona-poses/r1/contact-r1.webp
new file mode 100644
index 0000000..f30cd91
Binary files /dev/null and b/persona-poses/r1/contact-r1.webp differ
diff --git a/persona-poses/r1/eureka/eureka-r1a.txt b/persona-poses/r1/eureka/eureka-r1a.txt
new file mode 100644
index 0000000..2819211
--- /dev/null
+++ b/persona-poses/r1/eureka/eureka-r1a.txt
@@ -0,0 +1,30 @@
+# Eureka r1a
+
+## Hypothesis
+
+A sheet keeps the character consistent across twelve cells from the prompt alone: the busts' shared style block adapted for a sheet, the canon character block verbatim, and twelve short cell descriptions.
+
+## Variable changed
+
+Baseline: prompt only, no reference image.
+
+## Render record
+
+- Model: FLUX 3 Image, `POST https://api.bfl.ai/v1/flux-3-image`
+- Parameters: aspect_ratio 4:3, resolution 2k, safety_tolerance default, grounding default; no seed field
+- Reference: none; rendered from scratch from the prompt alone
+- Output: 2368x1776, source format PNG; 2k original kept out of git at F:/Projects/cast-art-originals/poses-r1/eureka/eureka-r1a.png
+- Cost: about $0.10 per request (reported by API: {'cost': 10.0, 'input_mp': None, 'output_mp': None})
+- Request id: cffa8610-5235-4df0-bb0c-91d37e5146f2
+- Date: 2026-10-14
+- Cell order: neutral, happy, amused, thinking, concerned, annoyed, surprised, determined, probe, inspect, report, hood
+
+## Prompt
+
+Character expression sheet for a game cast lineup: a sheet of twelve cells, each cell a bust, head and shoulders cropped at mid-chest by the bottom edge of the cell, the character in three-quarter view turned toward the right side of the cell, the head centred horizontally in the cell with its top a small margin below the top edge of the cell, the same crop and scale in every cell. Super-deformed chibi proportions: the character is two heads tall in total, so the head is very large compared with the small shoulders and narrow torso. Clean 2D anime illustration drawn to be traced into vector art: one bold dark outline of even weight around the whole silhouette, thinner inner lines, flat cel shading with exactly one base tone and one shadow tone per material, light from the upper left. No gradients, no texture, no painterly blending, no glow halos, no pixel art, no 3D rendering. A limited palette of five or six flat colours taken from the character's key colours. Large expressive anime eyes with two white highlights. The background is one perfectly flat, uniform, solid bright chroma-key green from edge to edge: no gradient, no vignette, no floor, no cast shadow, no particles, no props or scenery behind the figure.
+
+The sheet is a grid of four columns by three rows, twelve cells of the same character in reading order, left to right and top to bottom, separated by generous empty padding of the same flat green, with no frames, no borders, no divider lines, no labels and no text. Every cell shows the same character with the same design, colours and proportions; the cell description sets that cell's expression and pose, and the character description below gives the design.
+
+Eureka, a neon-noir bug hunter: a young woman with messy shoulder-length black hair with one bright orange streak in the fringe, sharp focused blue-grey eyes, a slight knowing smile. She wears a dark charcoal trenchcoat with the hood up, framing her face, and a high collar; the hood's edge and the coat's seams are piped with ember-orange lines, over tactical gear: chest straps, buckles and pouches. Fingerless gloves; she holds a slim orange metal probe. Key colour: ember orange.
+
+Cell 1 (neutral): a calm neutral expression, mouth closed, looking ahead. Cell 2 (happy): a broad happy smile with bright eyes. Cell 3 (amused): an amused smirk with one eyebrow raised, a stifled laugh. Cell 4 (thinking): a thoughtful expression, eyes looking up and aside, one hand raised to the chin. Cell 5 (concerned): a concerned expression, brows drawn together and tilted up, a small worried frown. Cell 6 (annoyed): an annoyed expression, flat half-lidded eyes, a pursed frown. Cell 7 (surprised): a surprised expression, wide eyes, mouth open in an O. Cell 8 (determined): a determined expression, narrowed eyes, a firm set mouth, a small nod. Cell 9 (probe): pointing the orange probe forward. Cell 10 (inspect): leaning in with narrowed eyes to inspect something closely. Cell 11 (report): presenting a thin tablet showing a report. Cell 12 (hood): the hood pulled low, shadowing her eyes, only a knowing smirk visible.
diff --git a/persona-poses/r1/eureka/eureka-r1a.webp b/persona-poses/r1/eureka/eureka-r1a.webp
new file mode 100644
index 0000000..9ffec03
Binary files /dev/null and b/persona-poses/r1/eureka/eureka-r1a.webp differ
diff --git a/persona-poses/r1/eureka/eureka-r1b.txt b/persona-poses/r1/eureka/eureka-r1b.txt
new file mode 100644
index 0000000..6a7fef2
--- /dev/null
+++ b/persona-poses/r1/eureka/eureka-r1b.txt
@@ -0,0 +1,32 @@
+# Eureka r1b
+
+## Hypothesis
+
+Adding the canon bust as a reference image holds canon details the prompt alone drifts on, without pulling every cell into the bust's pose.
+
+## Variable changed
+
+Against the same character's r1a: the canon bust cutout on its chroma green is passed as `images` and cited as image 1 by one clause prepended to the prompt. Nothing else differs.
+
+## Render record
+
+- Model: FLUX 3 Image, `POST https://api.bfl.ai/v1/flux-3-image`
+- Parameters: aspect_ratio 4:3, resolution 2k, safety_tolerance default, grounding default; no seed field
+- Reference: canon bust r2/eureka/eureka-r2a-cutout.png composited on its chroma green, base64 in `images`, cited as image 1; reference ground rgb(15, 255, 26)
+- Output: 2368x1776, source format PNG; 2k original kept out of git at F:/Projects/cast-art-originals/poses-r1/eureka/eureka-r1b.png
+- Cost: about $0.10 per request (reported by API: {'cost': 10.0, 'input_mp': None, 'output_mp': None})
+- Request id: 63328793-f3e5-4dab-9e98-cefb2d5e1f9a
+- Date: 2026-10-14
+- Cell order: neutral, happy, amused, thinking, concerned, annoyed, surprised, determined, probe, inspect, report, hood
+
+## Prompt
+
+Image 1 is the character's canon bust. The character must look exactly like the character in image 1 (same design, colours, proportions and line style) in every cell.
+
+Character expression sheet for a game cast lineup: a sheet of twelve cells, each cell a bust, head and shoulders cropped at mid-chest by the bottom edge of the cell, the character in three-quarter view turned toward the right side of the cell, the head centred horizontally in the cell with its top a small margin below the top edge of the cell, the same crop and scale in every cell. Super-deformed chibi proportions: the character is two heads tall in total, so the head is very large compared with the small shoulders and narrow torso. Clean 2D anime illustration drawn to be traced into vector art: one bold dark outline of even weight around the whole silhouette, thinner inner lines, flat cel shading with exactly one base tone and one shadow tone per material, light from the upper left. No gradients, no texture, no painterly blending, no glow halos, no pixel art, no 3D rendering. A limited palette of five or six flat colours taken from the character's key colours. Large expressive anime eyes with two white highlights. The background is one perfectly flat, uniform, solid bright chroma-key green from edge to edge: no gradient, no vignette, no floor, no cast shadow, no particles, no props or scenery behind the figure.
+
+The sheet is a grid of four columns by three rows, twelve cells of the same character in reading order, left to right and top to bottom, separated by generous empty padding of the same flat green, with no frames, no borders, no divider lines, no labels and no text. Every cell shows the same character with the same design, colours and proportions; the cell description sets that cell's expression and pose, and the character description below gives the design.
+
+Eureka, a neon-noir bug hunter: a young woman with messy shoulder-length black hair with one bright orange streak in the fringe, sharp focused blue-grey eyes, a slight knowing smile. She wears a dark charcoal trenchcoat with the hood up, framing her face, and a high collar; the hood's edge and the coat's seams are piped with ember-orange lines, over tactical gear: chest straps, buckles and pouches. Fingerless gloves; she holds a slim orange metal probe. Key colour: ember orange.
+
+Cell 1 (neutral): a calm neutral expression, mouth closed, looking ahead. Cell 2 (happy): a broad happy smile with bright eyes. Cell 3 (amused): an amused smirk with one eyebrow raised, a stifled laugh. Cell 4 (thinking): a thoughtful expression, eyes looking up and aside, one hand raised to the chin. Cell 5 (concerned): a concerned expression, brows drawn together and tilted up, a small worried frown. Cell 6 (annoyed): an annoyed expression, flat half-lidded eyes, a pursed frown. Cell 7 (surprised): a surprised expression, wide eyes, mouth open in an O. Cell 8 (determined): a determined expression, narrowed eyes, a firm set mouth, a small nod. Cell 9 (probe): pointing the orange probe forward. Cell 10 (inspect): leaning in with narrowed eyes to inspect something closely. Cell 11 (report): presenting a thin tablet showing a report. Cell 12 (hood): the hood pulled low, shadowing her eyes, only a knowing smirk visible.
diff --git a/persona-poses/r1/eureka/eureka-r1b.webp b/persona-poses/r1/eureka/eureka-r1b.webp
new file mode 100644
index 0000000..76a20b0
Binary files /dev/null and b/persona-poses/r1/eureka/eureka-r1b.webp differ
diff --git a/persona-poses/r1/heimdall/heimdall-r1a.txt b/persona-poses/r1/heimdall/heimdall-r1a.txt
new file mode 100644
index 0000000..b9e31ff
--- /dev/null
+++ b/persona-poses/r1/heimdall/heimdall-r1a.txt
@@ -0,0 +1,30 @@
+# Heimdall r1a
+
+## Hypothesis
+
+A sheet keeps the character consistent across twelve cells from the prompt alone: the busts' shared style block adapted for a sheet, the canon character block verbatim, and twelve short cell descriptions.
+
+## Variable changed
+
+Baseline: prompt only, no reference image.
+
+## Render record
+
+- Model: FLUX 3 Image, `POST https://api.bfl.ai/v1/flux-3-image`
+- Parameters: aspect_ratio 4:3, resolution 2k, safety_tolerance default, grounding default; no seed field
+- Reference: none; rendered from scratch from the prompt alone
+- Output: 2368x1776, source format PNG; 2k original kept out of git at F:/Projects/cast-art-originals/poses-r1/heimdall/heimdall-r1a.png
+- Cost: about $0.10 per request (reported by API: {'cost': 10.0, 'input_mp': None, 'output_mp': None})
+- Request id: a051f0ca-f250-4aa8-a47f-c5195dbe94bc
+- Date: 2026-10-14
+- Cell order: neutral, happy, amused, thinking, concerned, annoyed, surprised, determined, badge, barrier, protect, revoke
+
+## Prompt
+
+Character expression sheet for a game cast lineup: a sheet of twelve cells, each cell a bust, head and shoulders cropped at mid-chest by the bottom edge of the cell, the character in three-quarter view turned toward the right side of the cell, the head centred horizontally in the cell with its top a small margin below the top edge of the cell, the same crop and scale in every cell. Super-deformed chibi proportions: the character is two heads tall in total, so the head is very large compared with the small shoulders and narrow torso. Clean 2D anime illustration drawn to be traced into vector art: one bold dark outline of even weight around the whole silhouette, thinner inner lines, flat cel shading with exactly one base tone and one shadow tone per material, light from the upper left. No gradients, no texture, no painterly blending, no glow halos, no pixel art, no 3D rendering. A limited palette of five or six flat colours taken from the character's key colours. Large expressive anime eyes with two white highlights. The background is one perfectly flat, uniform, solid bright chroma-key green from edge to edge: no gradient, no vignette, no floor, no cast shadow, no particles, no props or scenery behind the figure.
+
+The sheet is a grid of four columns by three rows, twelve cells of the same character in reading order, left to right and top to bottom, separated by generous empty padding of the same flat green, with no frames, no borders, no divider lines, no labels and no text. Every cell shows the same character with the same design, colours and proportions; the cell description sets that cell's expression and pose, and the character description below gives the design.
+
+Heimdall, a stern young gatekeeper: dark brown skin, a mop of messy dark brown curly hair with golden highlights, glowing golden amber eyes, a serious frown, one small gold earring. He wears a high-collared black coat with royal blue lapels and a royal blue cape trimmed in gold filigree, small blue gems on the shoulder armour, and a gold key-shaped clasp at the throat. He holds an ornate golden key raised beside his shoulder. Key colours: gold and cobalt blue.
+
+Cell 1 (neutral): a calm neutral expression, mouth closed, looking ahead. Cell 2 (happy): a broad happy smile with bright eyes. Cell 3 (amused): an amused smirk with one eyebrow raised, a stifled laugh. Cell 4 (thinking): a thoughtful expression, eyes looking up and aside, one hand raised to the chin. Cell 5 (concerned): a concerned expression, brows drawn together and tilted up, a small worried frown. Cell 6 (annoyed): an annoyed expression, flat half-lidded eyes, a pursed frown. Cell 7 (surprised): a surprised expression, wide eyes, mouth open in an O. Cell 8 (determined): a determined expression, narrowed eyes, a firm set mouth, a small nod. Cell 9 (badge): holding up a gold badge of authority toward the viewer. Cell 10 (barrier): one palm raised forward, projecting a blue barrier. Cell 11 (protect): stepping forward with his cape swept out in front, shielding someone behind him. Cell 12 (revoke): pointing the golden key downward with a sharp cross-out gesture of the other hand, revoking access.
diff --git a/persona-poses/r1/heimdall/heimdall-r1a.webp b/persona-poses/r1/heimdall/heimdall-r1a.webp
new file mode 100644
index 0000000..1c6a973
Binary files /dev/null and b/persona-poses/r1/heimdall/heimdall-r1a.webp differ
diff --git a/persona-poses/r1/heimdall/heimdall-r1b.txt b/persona-poses/r1/heimdall/heimdall-r1b.txt
new file mode 100644
index 0000000..3bded37
--- /dev/null
+++ b/persona-poses/r1/heimdall/heimdall-r1b.txt
@@ -0,0 +1,32 @@
+# Heimdall r1b
+
+## Hypothesis
+
+Adding the canon bust as a reference image holds canon details the prompt alone drifts on, without pulling every cell into the bust's pose.
+
+## Variable changed
+
+Against the same character's r1a: the canon bust cutout on its chroma green is passed as `images` and cited as image 1 by one clause prepended to the prompt. Nothing else differs.
+
+## Render record
+
+- Model: FLUX 3 Image, `POST https://api.bfl.ai/v1/flux-3-image`
+- Parameters: aspect_ratio 4:3, resolution 2k, safety_tolerance default, grounding default; no seed field
+- Reference: canon bust r2/heimdall/heimdall-r2a-cutout.png composited on its chroma green, base64 in `images`, cited as image 1; reference ground rgb(18, 250, 72)
+- Output: 2368x1776, source format PNG; 2k original kept out of git at F:/Projects/cast-art-originals/poses-r1/heimdall/heimdall-r1b.png
+- Cost: about $0.10 per request (reported by API: {'cost': 10.0, 'input_mp': None, 'output_mp': None})
+- Request id: 68ecec4c-3301-458d-8ae2-b7171ae8fbb0
+- Date: 2026-10-14
+- Cell order: neutral, happy, amused, thinking, concerned, annoyed, surprised, determined, badge, barrier, protect, revoke
+
+## Prompt
+
+Image 1 is the character's canon bust. The character must look exactly like the character in image 1 (same design, colours, proportions and line style) in every cell.
+
+Character expression sheet for a game cast lineup: a sheet of twelve cells, each cell a bust, head and shoulders cropped at mid-chest by the bottom edge of the cell, the character in three-quarter view turned toward the right side of the cell, the head centred horizontally in the cell with its top a small margin below the top edge of the cell, the same crop and scale in every cell. Super-deformed chibi proportions: the character is two heads tall in total, so the head is very large compared with the small shoulders and narrow torso. Clean 2D anime illustration drawn to be traced into vector art: one bold dark outline of even weight around the whole silhouette, thinner inner lines, flat cel shading with exactly one base tone and one shadow tone per material, light from the upper left. No gradients, no texture, no painterly blending, no glow halos, no pixel art, no 3D rendering. A limited palette of five or six flat colours taken from the character's key colours. Large expressive anime eyes with two white highlights. The background is one perfectly flat, uniform, solid bright chroma-key green from edge to edge: no gradient, no vignette, no floor, no cast shadow, no particles, no props or scenery behind the figure.
+
+The sheet is a grid of four columns by three rows, twelve cells of the same character in reading order, left to right and top to bottom, separated by generous empty padding of the same flat green, with no frames, no borders, no divider lines, no labels and no text. Every cell shows the same character with the same design, colours and proportions; the cell description sets that cell's expression and pose, and the character description below gives the design.
+
+Heimdall, a stern young gatekeeper: dark brown skin, a mop of messy dark brown curly hair with golden highlights, glowing golden amber eyes, a serious frown, one small gold earring. He wears a high-collared black coat with royal blue lapels and a royal blue cape trimmed in gold filigree, small blue gems on the shoulder armour, and a gold key-shaped clasp at the throat. He holds an ornate golden key raised beside his shoulder. Key colours: gold and cobalt blue.
+
+Cell 1 (neutral): a calm neutral expression, mouth closed, looking ahead. Cell 2 (happy): a broad happy smile with bright eyes. Cell 3 (amused): an amused smirk with one eyebrow raised, a stifled laugh. Cell 4 (thinking): a thoughtful expression, eyes looking up and aside, one hand raised to the chin. Cell 5 (concerned): a concerned expression, brows drawn together and tilted up, a small worried frown. Cell 6 (annoyed): an annoyed expression, flat half-lidded eyes, a pursed frown. Cell 7 (surprised): a surprised expression, wide eyes, mouth open in an O. Cell 8 (determined): a determined expression, narrowed eyes, a firm set mouth, a small nod. Cell 9 (badge): holding up a gold badge of authority toward the viewer. Cell 10 (barrier): one palm raised forward, projecting a blue barrier. Cell 11 (protect): stepping forward with his cape swept out in front, shielding someone behind him. Cell 12 (revoke): pointing the golden key downward with a sharp cross-out gesture of the other hand, revoking access.
diff --git a/persona-poses/r1/heimdall/heimdall-r1b.webp b/persona-poses/r1/heimdall/heimdall-r1b.webp
new file mode 100644
index 0000000..1c2d247
Binary files /dev/null and b/persona-poses/r1/heimdall/heimdall-r1b.webp differ
diff --git a/persona-poses/r1/kiko/kiko-r1a.txt b/persona-poses/r1/kiko/kiko-r1a.txt
new file mode 100644
index 0000000..780d198
--- /dev/null
+++ b/persona-poses/r1/kiko/kiko-r1a.txt
@@ -0,0 +1,30 @@
+# Kiko r1a
+
+## Hypothesis
+
+A sheet keeps the character consistent across twelve cells from the prompt alone: the busts' shared style block adapted for a sheet, the canon character block verbatim, and twelve short cell descriptions.
+
+## Variable changed
+
+Baseline: prompt only, no reference image.
+
+## Render record
+
+- Model: FLUX 3 Image, `POST https://api.bfl.ai/v1/flux-3-image`
+- Parameters: aspect_ratio 4:3, resolution 2k, safety_tolerance default, grounding default; no seed field
+- Reference: none; rendered from scratch from the prompt alone
+- Output: 2368x1776, source format PNG; 2k original kept out of git at F:/Projects/cast-art-originals/poses-r1/kiko/kiko-r1a.png
+- Cost: about $0.10 per request (reported by API: {'cost': 10.0, 'input_mp': None, 'output_mp': None})
+- Request id: 2414b18b-bda2-4f88-bcad-f060fa52965a
+- Date: 2026-10-14
+- Cell order: neutral, happy, amused, thinking, concerned, annoyed, surprised, determined, tinker, confetti, sprint, debug
+
+## Prompt
+
+Character expression sheet for a game cast lineup: a sheet of twelve cells, each cell a bust, head and shoulders cropped at mid-chest by the bottom edge of the cell, the character in three-quarter view turned toward the right side of the cell, the head centred horizontally in the cell with its top a small margin below the top edge of the cell, the same crop and scale in every cell. Super-deformed chibi proportions: the character is two heads tall in total, so the head is very large compared with the small shoulders and narrow torso. Clean 2D anime illustration drawn to be traced into vector art: one bold dark outline of even weight around the whole silhouette, thinner inner lines, flat cel shading with exactly one base tone and one shadow tone per material, light from the upper left. No gradients, no texture, no painterly blending, no glow halos, no pixel art, no 3D rendering. A limited palette of five or six flat colours taken from the character's key colours. Large expressive anime eyes with two white highlights. The background is one perfectly flat, uniform, solid bright chroma-key green from edge to edge: no gradient, no vignette, no floor, no cast shadow, no particles, no props or scenery behind the figure.
+
+The sheet is a grid of four columns by three rows, twelve cells of the same character in reading order, left to right and top to bottom, separated by generous empty padding of the same flat green, with no frames, no borders, no divider lines, no labels and no text. Every cell shows the same character with the same design, colours and proportions; the cell description sets that cell's expression and pose, and the character description below gives the design.
+
+Kiko, an excitable fox-girl streamer: messy white shoulder-length hair, tall white fox ears with pink insides, a white kitsune fox mask with red and magenta markings worn pushed up on top of her head, black hair ornaments with gold bells and magenta tassels, bright cyan eyes, a wide open-mouthed grin with one small fang, one hand raised in a wave. She wears a black kimono-style jacket with magenta and teal trim and a gold bell at the collar. A fluffy white fox tail with pink tips curls up behind one shoulder. Key colours: magenta and cyan.
+
+Cell 1 (neutral): a calm neutral expression, mouth closed, looking ahead. Cell 2 (happy): a broad happy smile with bright eyes. Cell 3 (amused): an amused smirk with one eyebrow raised, a stifled laugh. Cell 4 (thinking): a thoughtful expression, eyes looking up and aside, one hand raised to the chin. Cell 5 (concerned): a concerned expression, brows drawn together and tilted up, a small worried frown. Cell 6 (annoyed): an annoyed expression, flat half-lidded eyes, a pursed frown. Cell 7 (surprised): a surprised expression, wide eyes, mouth open in an O. Cell 8 (determined): a determined expression, narrowed eyes, a firm set mouth, a small nod. Cell 9 (tinker): tinkering with a small gadget, tongue out in concentration. Cell 10 (confetti): throwing confetti into the air, cheering. Cell 11 (sprint): leaning forward as if sprinting, hair and tail streaming backward. Cell 12 (debug): squinting through a magnifying lens at a tiny bug held between two fingers.
diff --git a/persona-poses/r1/kiko/kiko-r1a.webp b/persona-poses/r1/kiko/kiko-r1a.webp
new file mode 100644
index 0000000..23373b2
Binary files /dev/null and b/persona-poses/r1/kiko/kiko-r1a.webp differ
diff --git a/persona-poses/r1/kiko/kiko-r1b.txt b/persona-poses/r1/kiko/kiko-r1b.txt
new file mode 100644
index 0000000..d67acee
--- /dev/null
+++ b/persona-poses/r1/kiko/kiko-r1b.txt
@@ -0,0 +1,32 @@
+# Kiko r1b
+
+## Hypothesis
+
+Adding the canon bust as a reference image holds canon details the prompt alone drifts on, without pulling every cell into the bust's pose.
+
+## Variable changed
+
+Against the same character's r1a: the canon bust cutout on its chroma green is passed as `images` and cited as image 1 by one clause prepended to the prompt. Nothing else differs.
+
+## Render record
+
+- Model: FLUX 3 Image, `POST https://api.bfl.ai/v1/flux-3-image`
+- Parameters: aspect_ratio 4:3, resolution 2k, safety_tolerance default, grounding default; no seed field
+- Reference: canon bust r2/kiko/kiko-r2a-cutout.png composited on its chroma green, base64 in `images`, cited as image 1; reference ground rgb(17, 255, 11)
+- Output: 2368x1776, source format PNG; 2k original kept out of git at F:/Projects/cast-art-originals/poses-r1/kiko/kiko-r1b.png
+- Cost: about $0.10 per request (reported by API: {'cost': 10.0, 'input_mp': None, 'output_mp': None})
+- Request id: 15e7b364-1771-4136-9532-c5b012524609
+- Date: 2026-10-14
+- Cell order: neutral, happy, amused, thinking, concerned, annoyed, surprised, determined, tinker, confetti, sprint, debug
+
+## Prompt
+
+Image 1 is the character's canon bust. The character must look exactly like the character in image 1 (same design, colours, proportions and line style) in every cell.
+
+Character expression sheet for a game cast lineup: a sheet of twelve cells, each cell a bust, head and shoulders cropped at mid-chest by the bottom edge of the cell, the character in three-quarter view turned toward the right side of the cell, the head centred horizontally in the cell with its top a small margin below the top edge of the cell, the same crop and scale in every cell. Super-deformed chibi proportions: the character is two heads tall in total, so the head is very large compared with the small shoulders and narrow torso. Clean 2D anime illustration drawn to be traced into vector art: one bold dark outline of even weight around the whole silhouette, thinner inner lines, flat cel shading with exactly one base tone and one shadow tone per material, light from the upper left. No gradients, no texture, no painterly blending, no glow halos, no pixel art, no 3D rendering. A limited palette of five or six flat colours taken from the character's key colours. Large expressive anime eyes with two white highlights. The background is one perfectly flat, uniform, solid bright chroma-key green from edge to edge: no gradient, no vignette, no floor, no cast shadow, no particles, no props or scenery behind the figure.
+
+The sheet is a grid of four columns by three rows, twelve cells of the same character in reading order, left to right and top to bottom, separated by generous empty padding of the same flat green, with no frames, no borders, no divider lines, no labels and no text. Every cell shows the same character with the same design, colours and proportions; the cell description sets that cell's expression and pose, and the character description below gives the design.
+
+Kiko, an excitable fox-girl streamer: messy white shoulder-length hair, tall white fox ears with pink insides, a white kitsune fox mask with red and magenta markings worn pushed up on top of her head, black hair ornaments with gold bells and magenta tassels, bright cyan eyes, a wide open-mouthed grin with one small fang, one hand raised in a wave. She wears a black kimono-style jacket with magenta and teal trim and a gold bell at the collar. A fluffy white fox tail with pink tips curls up behind one shoulder. Key colours: magenta and cyan.
+
+Cell 1 (neutral): a calm neutral expression, mouth closed, looking ahead. Cell 2 (happy): a broad happy smile with bright eyes. Cell 3 (amused): an amused smirk with one eyebrow raised, a stifled laugh. Cell 4 (thinking): a thoughtful expression, eyes looking up and aside, one hand raised to the chin. Cell 5 (concerned): a concerned expression, brows drawn together and tilted up, a small worried frown. Cell 6 (annoyed): an annoyed expression, flat half-lidded eyes, a pursed frown. Cell 7 (surprised): a surprised expression, wide eyes, mouth open in an O. Cell 8 (determined): a determined expression, narrowed eyes, a firm set mouth, a small nod. Cell 9 (tinker): tinkering with a small gadget, tongue out in concentration. Cell 10 (confetti): throwing confetti into the air, cheering. Cell 11 (sprint): leaning forward as if sprinting, hair and tail streaming backward. Cell 12 (debug): squinting through a magnifying lens at a tiny bug held between two fingers.
diff --git a/persona-poses/r1/kiko/kiko-r1b.webp b/persona-poses/r1/kiko/kiko-r1b.webp
new file mode 100644
index 0000000..7094e74
Binary files /dev/null and b/persona-poses/r1/kiko/kiko-r1b.webp differ
diff --git a/persona-poses/r1/libby/libby-r1a.txt b/persona-poses/r1/libby/libby-r1a.txt
new file mode 100644
index 0000000..2331da8
--- /dev/null
+++ b/persona-poses/r1/libby/libby-r1a.txt
@@ -0,0 +1,30 @@
+# Libby r1a
+
+## Hypothesis
+
+A sheet keeps the character consistent across twelve cells from the prompt alone: the busts' shared style block adapted for a sheet, the canon character block verbatim, and twelve short cell descriptions.
+
+## Variable changed
+
+Baseline: prompt only, no reference image.
+
+## Render record
+
+- Model: FLUX 3 Image, `POST https://api.bfl.ai/v1/flux-3-image`
+- Parameters: aspect_ratio 4:3, resolution 2k, safety_tolerance default, grounding default; no seed field
+- Reference: none; rendered from scratch from the prompt alone
+- Output: 2368x1776, source format PNG; 2k original kept out of git at F:/Projects/cast-art-originals/poses-r1/libby/libby-r1a.png
+- Cost: about $0.10 per request (reported by API: {'cost': 10.0, 'input_mp': None, 'output_mp': None})
+- Request id: b12cd042-63fc-4e99-85af-a239f60ec937
+- Date: 2026-10-14
+- Cell order: neutral, happy, amused, thinking, concerned, annoyed, surprised, determined, stamp, research, shush, schema
+
+## Prompt
+
+Character expression sheet for a game cast lineup: a sheet of twelve cells, each cell a bust, head and shoulders cropped at mid-chest by the bottom edge of the cell, the character in three-quarter view turned toward the right side of the cell, the head centred horizontally in the cell with its top a small margin below the top edge of the cell, the same crop and scale in every cell. Super-deformed chibi proportions: the character is two heads tall in total, so the head is very large compared with the small shoulders and narrow torso. Clean 2D anime illustration drawn to be traced into vector art: one bold dark outline of even weight around the whole silhouette, thinner inner lines, flat cel shading with exactly one base tone and one shadow tone per material, light from the upper left. No gradients, no texture, no painterly blending, no glow halos, no pixel art, no 3D rendering. A limited palette of five or six flat colours taken from the character's key colours. Large expressive anime eyes with two white highlights. The background is one perfectly flat, uniform, solid bright chroma-key green from edge to edge: no gradient, no vignette, no floor, no cast shadow, no particles, no props or scenery behind the figure.
+
+The sheet is a grid of four columns by three rows, twelve cells of the same character in reading order, left to right and top to bottom, separated by generous empty padding of the same flat green, with no frames, no borders, no divider lines, no labels and no text. Every cell shows the same character with the same design, colours and proportions; the cell description sets that cell's expression and pose, and the character description below gives the design.
+
+Libby, a cheerful young librarian-witch, warm and bookish: thick voluminous wavy hair in a deep dusky violet-lavender with a big swept side fringe, gathered into one loose, fat wavy braid that falls forward over her right shoulder; a white crossed-bone hair clip on the left side of her head; a dark purple ribbon bow with a mauve diamond pattern tied at the top of the braid. Round thin black-framed glasses, large grey-violet eyes, small gold drop earrings, pale skin with rosy cheeks, a big open-mouthed happy smile. She wears a high-collared black scholar's robe with wide sleeves, embroidered in gold and dusky mauve diamond and rune patterns, with a violet gem brooch in a gold setting at the collar. She hugs an old brown leather book with gold corners against her chest, a faint violet light along its page edges. Key colours: black and gold with violet.
+
+Cell 1 (neutral): a calm neutral expression, mouth closed, looking ahead. Cell 2 (happy): a broad happy smile with bright eyes. Cell 3 (amused): an amused smirk with one eyebrow raised, a stifled laugh. Cell 4 (thinking): a thoughtful expression, eyes looking up and aside, one hand raised to the chin. Cell 5 (concerned): a concerned expression, brows drawn together and tilted up, a small worried frown. Cell 6 (annoyed): an annoyed expression, flat half-lidded eyes, a pursed frown. Cell 7 (surprised): a surprised expression, wide eyes, mouth open in an O. Cell 8 (determined): a determined expression, narrowed eyes, a firm set mouth, a small nod. Cell 9 (stamp): stamping a library due-date stamp down onto a book with a cheerful thump. Cell 10 (research): peering closely at an open old book held in both hands. Cell 11 (shush): one finger raised to her lips in a shushing gesture. Cell 12 (schema): holding up a glowing violet diagram of boxes and arrows, explaining.
diff --git a/persona-poses/r1/libby/libby-r1a.webp b/persona-poses/r1/libby/libby-r1a.webp
new file mode 100644
index 0000000..1c567d6
Binary files /dev/null and b/persona-poses/r1/libby/libby-r1a.webp differ
diff --git a/persona-poses/r1/libby/libby-r1b.txt b/persona-poses/r1/libby/libby-r1b.txt
new file mode 100644
index 0000000..71ea7b0
--- /dev/null
+++ b/persona-poses/r1/libby/libby-r1b.txt
@@ -0,0 +1,32 @@
+# Libby r1b
+
+## Hypothesis
+
+Adding the canon bust as a reference image holds canon details the prompt alone drifts on, without pulling every cell into the bust's pose.
+
+## Variable changed
+
+Against the same character's r1a: the canon bust cutout on its chroma green is passed as `images` and cited as image 1 by one clause prepended to the prompt. Nothing else differs.
+
+## Render record
+
+- Model: FLUX 3 Image, `POST https://api.bfl.ai/v1/flux-3-image`
+- Parameters: aspect_ratio 4:3, resolution 2k, safety_tolerance default, grounding default; no seed field
+- Reference: canon bust r2/libby/libby-r2a-cutout.png composited on its chroma green, base64 in `images`, cited as image 1; reference ground rgb(23, 219, 83)
+- Output: 2368x1776, source format PNG; 2k original kept out of git at F:/Projects/cast-art-originals/poses-r1/libby/libby-r1b.png
+- Cost: about $0.10 per request (reported by API: {'cost': 10.0, 'input_mp': None, 'output_mp': None})
+- Request id: a4b50eab-abda-4021-8f08-8aa81c894a05
+- Date: 2026-10-14
+- Cell order: neutral, happy, amused, thinking, concerned, annoyed, surprised, determined, stamp, research, shush, schema
+
+## Prompt
+
+Image 1 is the character's canon bust. The character must look exactly like the character in image 1 (same design, colours, proportions and line style) in every cell.
+
+Character expression sheet for a game cast lineup: a sheet of twelve cells, each cell a bust, head and shoulders cropped at mid-chest by the bottom edge of the cell, the character in three-quarter view turned toward the right side of the cell, the head centred horizontally in the cell with its top a small margin below the top edge of the cell, the same crop and scale in every cell. Super-deformed chibi proportions: the character is two heads tall in total, so the head is very large compared with the small shoulders and narrow torso. Clean 2D anime illustration drawn to be traced into vector art: one bold dark outline of even weight around the whole silhouette, thinner inner lines, flat cel shading with exactly one base tone and one shadow tone per material, light from the upper left. No gradients, no texture, no painterly blending, no glow halos, no pixel art, no 3D rendering. A limited palette of five or six flat colours taken from the character's key colours. Large expressive anime eyes with two white highlights. The background is one perfectly flat, uniform, solid bright chroma-key green from edge to edge: no gradient, no vignette, no floor, no cast shadow, no particles, no props or scenery behind the figure.
+
+The sheet is a grid of four columns by three rows, twelve cells of the same character in reading order, left to right and top to bottom, separated by generous empty padding of the same flat green, with no frames, no borders, no divider lines, no labels and no text. Every cell shows the same character with the same design, colours and proportions; the cell description sets that cell's expression and pose, and the character description below gives the design.
+
+Libby, a cheerful young librarian-witch, warm and bookish: thick voluminous wavy hair in a deep dusky violet-lavender with a big swept side fringe, gathered into one loose, fat wavy braid that falls forward over her right shoulder; a white crossed-bone hair clip on the left side of her head; a dark purple ribbon bow with a mauve diamond pattern tied at the top of the braid. Round thin black-framed glasses, large grey-violet eyes, small gold drop earrings, pale skin with rosy cheeks, a big open-mouthed happy smile. She wears a high-collared black scholar's robe with wide sleeves, embroidered in gold and dusky mauve diamond and rune patterns, with a violet gem brooch in a gold setting at the collar. She hugs an old brown leather book with gold corners against her chest, a faint violet light along its page edges. Key colours: black and gold with violet.
+
+Cell 1 (neutral): a calm neutral expression, mouth closed, looking ahead. Cell 2 (happy): a broad happy smile with bright eyes. Cell 3 (amused): an amused smirk with one eyebrow raised, a stifled laugh. Cell 4 (thinking): a thoughtful expression, eyes looking up and aside, one hand raised to the chin. Cell 5 (concerned): a concerned expression, brows drawn together and tilted up, a small worried frown. Cell 6 (annoyed): an annoyed expression, flat half-lidded eyes, a pursed frown. Cell 7 (surprised): a surprised expression, wide eyes, mouth open in an O. Cell 8 (determined): a determined expression, narrowed eyes, a firm set mouth, a small nod. Cell 9 (stamp): stamping a library due-date stamp down onto a book with a cheerful thump. Cell 10 (research): peering closely at an open old book held in both hands. Cell 11 (shush): one finger raised to her lips in a shushing gesture. Cell 12 (schema): holding up a glowing violet diagram of boxes and arrows, explaining.
diff --git a/persona-poses/r1/libby/libby-r1b.webp b/persona-poses/r1/libby/libby-r1b.webp
new file mode 100644
index 0000000..0a9ac2d
Binary files /dev/null and b/persona-poses/r1/libby/libby-r1b.webp differ
diff --git a/persona-poses/r1/matte-crop-log.json b/persona-poses/r1/matte-crop-log.json
new file mode 100644
index 0000000..4453390
--- /dev/null
+++ b/persona-poses/r1/matte-crop-log.json
@@ -0,0 +1,612 @@
+{
+ "libby-r1a": {
+ "size": [
+ 2368,
+ 1776
+ ],
+ "mode": "cols gutters, rows gutters",
+ "cells": 12,
+ "labels": [
+ "neutral",
+ "happy",
+ "amused",
+ "thinking",
+ "concerned",
+ "annoyed",
+ "surprised",
+ "determined",
+ "stamp",
+ "research",
+ "shush",
+ "schema"
+ ],
+ "sheet_corner_std": [
+ 2.1,
+ 1.7
+ ],
+ "min_cell_corner_greenness": 187,
+ "residual_green_px_per_cell": [
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0
+ ],
+ "residual_total": 0
+ },
+ "libby-r1b": {
+ "size": [
+ 2368,
+ 1776
+ ],
+ "mode": "cols divider lines, rows divider lines",
+ "cells": 12,
+ "labels": [
+ "neutral",
+ "happy",
+ "amused",
+ "thinking",
+ "concerned",
+ "annoyed",
+ "surprised",
+ "determined",
+ "stamp",
+ "research",
+ "shush",
+ "schema"
+ ],
+ "sheet_corner_std": [
+ 2.0,
+ 1.9
+ ],
+ "min_cell_corner_greenness": 148,
+ "residual_green_px_per_cell": [
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0
+ ],
+ "residual_total": 0
+ },
+ "heimdall-r1a": {
+ "size": [
+ 2368,
+ 1776
+ ],
+ "mode": "cols divider lines, rows divider lines",
+ "cells": 12,
+ "labels": [
+ "neutral",
+ "happy",
+ "amused",
+ "thinking",
+ "concerned",
+ "annoyed",
+ "surprised",
+ "determined",
+ "badge",
+ "barrier",
+ "protect",
+ "revoke"
+ ],
+ "sheet_corner_std": [
+ 2.1,
+ 2.0
+ ],
+ "min_cell_corner_greenness": 231,
+ "residual_green_px_per_cell": [
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0
+ ],
+ "residual_total": 0
+ },
+ "heimdall-r1b": {
+ "size": [
+ 2368,
+ 1776
+ ],
+ "mode": "cols gutters, rows gutters",
+ "cells": 12,
+ "labels": [
+ "neutral",
+ "happy",
+ "amused",
+ "thinking",
+ "concerned",
+ "annoyed",
+ "surprised",
+ "determined",
+ "badge",
+ "barrier",
+ "protect",
+ "revoke"
+ ],
+ "sheet_corner_std": [
+ 2.5,
+ 2.1
+ ],
+ "min_cell_corner_greenness": 160,
+ "residual_green_px_per_cell": [
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0
+ ],
+ "residual_total": 0
+ },
+ "kiko-r1a": {
+ "size": [
+ 2368,
+ 1776
+ ],
+ "mode": "cols runs 5, rows gutters; cols fell back to the nominal grid",
+ "cells": 12,
+ "labels": [
+ "neutral",
+ "happy",
+ "amused",
+ "thinking",
+ "concerned",
+ "annoyed",
+ "surprised",
+ "determined",
+ "tinker",
+ "confetti",
+ "sprint",
+ "debug"
+ ],
+ "sheet_corner_std": [
+ 2.4,
+ 2.6
+ ],
+ "min_cell_corner_greenness": 177,
+ "residual_green_px_per_cell": [
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0
+ ],
+ "residual_total": 0
+ },
+ "kiko-r1b": {
+ "size": [
+ 2368,
+ 1776
+ ],
+ "mode": "cols gutters, rows gutters",
+ "cells": 12,
+ "labels": [
+ "neutral",
+ "happy",
+ "amused",
+ "thinking",
+ "concerned",
+ "annoyed",
+ "surprised",
+ "determined",
+ "tinker",
+ "confetti",
+ "sprint",
+ "debug"
+ ],
+ "sheet_corner_std": [
+ 2.0,
+ 2.2
+ ],
+ "min_cell_corner_greenness": 224,
+ "residual_green_px_per_cell": [
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0
+ ],
+ "residual_total": 0
+ },
+ "aqua-r1a": {
+ "size": [
+ 2368,
+ 1776
+ ],
+ "mode": "cols gutters, rows gutters",
+ "cells": 12,
+ "labels": [
+ "neutral",
+ "happy",
+ "amused",
+ "thinking",
+ "concerned",
+ "annoyed",
+ "surprised",
+ "determined",
+ "conduct",
+ "listen",
+ "patch",
+ "flourish"
+ ],
+ "sheet_corner_std": [
+ 2.3,
+ 2.1
+ ],
+ "min_cell_corner_greenness": 165,
+ "residual_green_px_per_cell": [
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0
+ ],
+ "residual_total": 0
+ },
+ "aqua-r1b": {
+ "size": [
+ 2368,
+ 1776
+ ],
+ "mode": "cols gutters, rows runs 1; rows fell back to the nominal grid",
+ "cells": 16,
+ "labels": [
+ "c01",
+ "c02",
+ "c03",
+ "c04",
+ "c05",
+ "c06",
+ "c07",
+ "c08",
+ "c09",
+ "c10",
+ "c11",
+ "c12",
+ "c13",
+ "c14",
+ "c15",
+ "c16"
+ ],
+ "sheet_corner_std": [
+ 2.1,
+ 2.6
+ ],
+ "min_cell_corner_greenness": 205,
+ "residual_green_px_per_cell": [
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0
+ ],
+ "residual_total": 0
+ },
+ "weksa-r1a": {
+ "size": [
+ 2368,
+ 1776
+ ],
+ "mode": "cols divider lines, rows divider lines",
+ "cells": 12,
+ "labels": [
+ "neutral",
+ "happy",
+ "amused",
+ "thinking",
+ "concerned",
+ "annoyed",
+ "surprised",
+ "determined",
+ "map",
+ "fieldwork",
+ "argue",
+ "interlingua"
+ ],
+ "sheet_corner_std": [
+ 1.9,
+ 1.9
+ ],
+ "min_cell_corner_greenness": 183,
+ "residual_green_px_per_cell": [
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0
+ ],
+ "residual_total": 0
+ },
+ "weksa-r1b": {
+ "missing": true
+ },
+ "nibu-r1a": {
+ "size": [
+ 2368,
+ 1776
+ ],
+ "mode": "cols gutters, rows gutters",
+ "cells": 12,
+ "labels": [
+ "neutral",
+ "happy",
+ "amused",
+ "thinking",
+ "concerned",
+ "annoyed",
+ "surprised",
+ "determined",
+ "console",
+ "glitch",
+ "lore",
+ "triumph"
+ ],
+ "sheet_corner_std": [
+ 2.9,
+ 2.4
+ ],
+ "min_cell_corner_greenness": 163,
+ "residual_green_px_per_cell": [
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0
+ ],
+ "residual_total": 0
+ },
+ "nibu-r1b": {
+ "size": [
+ 2368,
+ 1776
+ ],
+ "mode": "cols runs 5, rows gutters; cols fell back to the nominal grid",
+ "cells": 12,
+ "labels": [
+ "neutral",
+ "happy",
+ "amused",
+ "thinking",
+ "concerned",
+ "annoyed",
+ "surprised",
+ "determined",
+ "console",
+ "glitch",
+ "lore",
+ "triumph"
+ ],
+ "sheet_corner_std": [
+ 2.6,
+ 2.1
+ ],
+ "min_cell_corner_greenness": 182,
+ "residual_green_px_per_cell": [
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0
+ ],
+ "residual_total": 0
+ },
+ "eureka-r1a": {
+ "size": [
+ 2368,
+ 1776
+ ],
+ "mode": "cols divider lines, rows divider lines",
+ "cells": 12,
+ "labels": [
+ "neutral",
+ "happy",
+ "amused",
+ "thinking",
+ "concerned",
+ "annoyed",
+ "surprised",
+ "determined",
+ "probe",
+ "inspect",
+ "report",
+ "hood"
+ ],
+ "sheet_corner_std": [
+ 1.8,
+ 1.8
+ ],
+ "min_cell_corner_greenness": 186,
+ "residual_green_px_per_cell": [
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0
+ ],
+ "residual_total": 0
+ },
+ "eureka-r1b": {
+ "size": [
+ 2368,
+ 1776
+ ],
+ "mode": "cols gutters, rows gutters",
+ "cells": 12,
+ "labels": [
+ "neutral",
+ "happy",
+ "amused",
+ "thinking",
+ "concerned",
+ "annoyed",
+ "surprised",
+ "determined",
+ "probe",
+ "inspect",
+ "report",
+ "hood"
+ ],
+ "sheet_corner_std": [
+ 1.8,
+ 2.0
+ ],
+ "min_cell_corner_greenness": 229,
+ "residual_green_px_per_cell": [
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0
+ ],
+ "residual_total": 0
+ },
+ "void-r1a": {
+ "missing": true
+ },
+ "void-r1b": {
+ "size": [
+ 2368,
+ 1776
+ ],
+ "mode": "cols gutters, rows gutters",
+ "cells": 16,
+ "labels": [
+ "c01",
+ "c02",
+ "c03",
+ "c04",
+ "c05",
+ "c06",
+ "c07",
+ "c08",
+ "c09",
+ "c10",
+ "c11",
+ "c12",
+ "c13",
+ "c14",
+ "c15",
+ "c16"
+ ],
+ "sheet_corner_std": [
+ 2.5,
+ 2.1
+ ],
+ "min_cell_corner_greenness": 218,
+ "residual_green_px_per_cell": [
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0
+ ],
+ "residual_total": 0
+ }
+}
\ No newline at end of file
diff --git a/persona-poses/r1/matte_crop_r1.py b/persona-poses/r1/matte_crop_r1.py
new file mode 100644
index 0000000..fd61766
--- /dev/null
+++ b/persona-poses/r1/matte_crop_r1.py
@@ -0,0 +1,178 @@
+"""Round 1: crop each 4x3 sheet into 12 cells, key each cell with the busts' matte rule (matte_r2.py, copied unchanged),
+write cutouts to the originals folder and labelled contact images into the record folder.
+Usage: py -3 matte_crop_r1.py [name-suf ...]
+"""
+import json, pathlib, sys
+from PIL import Image, ImageChops, ImageFilter, ImageDraw, ImageFont, ImageStat
+sys.path.insert(0, str(pathlib.Path(__file__).parent))
+import render_r1 as R
+
+ORIG = R.ORIG
+REC = R.REC
+LO, HI = 40, 110
+TRIM = 6 # px trimmed from every cell edge (divider lines the model draws despite the prompt)
+COLS, ROWS = 4, 3
+# The model drew a 4x4 sheet (16 cells) for these two instead of the declared 4x3; cropped as drawn, labelled cNN.
+GRID16 = {("aqua", "r1b"), ("void", "r1b")}
+
+# ---- matte_r2.py rule, unchanged ----
+def greenness(im):
+ r, g, b = im.split()
+ return ImageChops.subtract(g, ImageChops.lighter(r, b))
+
+def matte(im):
+ im = im.convert("RGB")
+ w, h = im.size
+ gr = greenness(im)
+ corners = [gr.crop(box) for box in ((0, 0, 24, 24), (w - 24, 0, w, 24))]
+ cg = [ImageStat.Stat(c).mean[0] for c in corners]
+ span = 255.0 / (HI - LO)
+ a = gr.point(lambda v: 255 if v <= LO else (0 if v >= HI else int(255 - (v - LO) * span)))
+ band = ImageChops.subtract(a.filter(ImageFilter.MaxFilter(7)), a.filter(ImageFilter.MinFilter(7)))
+ band = band.point(lambda v: 255 if v > 0 else 0)
+ r, g, b = im.split()
+ g2 = Image.composite(ImageChops.darker(g, ImageChops.lighter(r, b)), g, band)
+ a = a.filter(ImageFilter.GaussianBlur(0.8))
+ out = Image.merge("RGBA", (r, g2, b, a))
+ std = [round(max(ImageStat.Stat(im.crop(bx)).stddev), 1) for bx in ((0, 0, 64, 64), (w - 64, 0, w, 64))]
+ resid = ImageChops.multiply(greenness(out.convert("RGB")).point(lambda v: 255 if v > 60 else 0), a.point(lambda v: 255 if v > 128 else 0))
+ rc = sum(resid.histogram()[255:])
+ return out, cg, std, rc
+# ---- end of the busts' rule ----
+
+def runs(profile, thin=8, gap=8):
+ """Content runs of a 0/1 profile; runs thinner than `thin` are divider lines; runs closer than `gap` merge."""
+ out, i, n = [], 0, len(profile)
+ while i < n:
+ if profile[i]:
+ j = i
+ while j < n and profile[j]: j += 1
+ if j - i >= thin: out.append([i, j])
+ i = j
+ else:
+ i += 1
+ merged = []
+ for r in out:
+ if merged and r[0] - merged[-1][1] < gap: merged[-1][1] = r[1]
+ else: merged.append(r)
+ return merged
+
+def profile(im, axis):
+ w, h = im.size
+ mask = greenness(im).point(lambda v: 255 if v < LO else 0) # non-ground
+ m = mask.resize((w, 1), Image.BOX) if axis == 0 else mask.resize((1, h), Image.BOX)
+ n = w if axis == 0 else h
+ return [m.getpixel((i, 0) if axis == 0 else (0, i)) / 255.0 for i in range(n)]
+
+def splits(im, n, axis):
+ """Per interior boundary (end of the earlier cell, start of the next cell) and the method used.
+ 1) divider lines the model draws despite the prompt (thin, nearly full-length non-ground runs) near the nominal boundaries;
+ 2) green gutters between content runs; 3) None."""
+ prof = profile(im, axis); size = len(prof)
+ nominal = [size * i // n for i in range(n + 1)]
+ tol = 0.10 * size / n
+ # 1) divider lines
+ lines, i = [], 0
+ while i < size:
+ if prof[i] > 0.80:
+ j = i
+ while j < size and prof[j] > 0.80: j += 1
+ if j - i <= 10: lines.append((i, j))
+ i = j
+ else: i += 1
+ got = []
+ for k in range(1, n):
+ c = [l for l in lines if abs((l[0] + l[1]) / 2 - nominal[k]) <= tol]
+ if len(c) != 1: got = None; break
+ got.append((c[0][0] - 5, c[0][1] + 5))
+ if got: return got, "divider lines"
+ # 2) gutters
+ rs = runs([1 if v > 0.03 else 0 for v in prof])
+ if len(rs) == n:
+ mid = [(rs[i][1] + rs[i + 1][0]) // 2 for i in range(n - 1)]
+ if all(abs(mid[k - 1] - nominal[k]) <= tol for k in range(1, n)):
+ return [(m - TRIM, m + TRIM) for m in mid], "gutters"
+ return None, f"runs {len(rs)}"
+
+def font(sz):
+ for f in ("C:/Windows/Fonts/consola.ttf", "C:/Windows/Fonts/arial.ttf"):
+ try: return ImageFont.truetype(f, sz)
+ except OSError: pass
+ return ImageFont.load_default()
+
+def grey_checker(size):
+ c = Image.new("RGB", size, (128, 128, 128)); d = ImageDraw.Draw(c); t = 16
+ for y in range(0, size[1], t):
+ for x in range((y // t % 2) * t, size[0], 2 * t):
+ d.rectangle([x, y, x + t - 1, y + t - 1], fill=(142, 142, 142))
+ return c
+
+def grid(cuts, labels, cw, pad, lab, title=None, ch=None):
+ ch = ch or cw
+ rows = (len(cuts) + COLS - 1) // COLS
+ W = COLS * cw + (COLS + 1) * pad
+ H = (title or 0) + rows * (ch + lab) + (rows + 1) * pad
+ g = grey_checker((W, H)); d = ImageDraw.Draw(g); f = font(max(11, lab - 6))
+ for i, c in enumerate(cuts):
+ x = pad + (i % COLS) * (cw + pad); y = (title or 0) + pad + (i // COLS) * (ch + lab + pad)
+ t = c.resize((cw, ch), Image.LANCZOS); g.paste(t, (x, y + lab), t)
+ d.text((x + 2, y + 2), labels[i], fill=(10, 10, 10), font=f)
+ return g
+
+def process(name, suf, log):
+ src = ORIG / name / f"{name}-{suf}.png"
+ if not src.exists():
+ log[f"{name}-{suf}"] = {"missing": True}; return None
+ im = Image.open(src).convert("RGB")
+ w, h = im.size
+ nrows = 4 if (name, suf) in GRID16 else ROWS
+ sx, mx = splits(im, COLS, 0); sy, my = splits(im, nrows, 1)
+ mode = f"cols {mx}, rows {my}"
+ if sx is None: mode += "; cols fell back to the nominal grid"; sx = [(w * i // COLS - TRIM, w * i // COLS + TRIM) for i in range(1, COLS)]
+ if sy is None: mode += "; rows fell back to the nominal grid"; sy = [(h * i // nrows - TRIM, h * i // nrows + TRIM) for i in range(1, nrows)]
+ x0 = [TRIM] + [e for _, e in sx]; x1 = [a for a, _ in sx] + [w - TRIM]
+ y0 = [TRIM] + [e for _, e in sy]; y1 = [a for a, _ in sy] + [h - TRIM]
+ labels = [l for l, _ in R.cells_for(name)] if nrows == ROWS else [f"c{i + 1:02d}" for i in range(COLS * nrows)]
+ outd = ORIG / name / f"{name}-{suf}-cells"; outd.mkdir(exist_ok=True)
+ cuts, rcs, cgs = [], [], []
+ for i, lab in enumerate(labels):
+ c, r = i % COLS, i // COLS
+ cell = im.crop((x0[c], y0[r], x1[c], y1[r]))
+ out, cg, std, rc = matte(cell)
+ out.save(outd / f"{i + 1:02d}-{lab}.png", optimize=True)
+ cuts.append(out); rcs.append(rc); cgs.append(round(min(cg)))
+ # sheet ground flatness: std of the two top corners of the whole sheet
+ sstd = [round(max(ImageStat.Stat(im.crop(bx)).stddev), 1) for bx in ((0, 0, 64, 64), (w - 64, 0, w, 64))]
+ g = grid(cuts, [f"{i + 1} {l}" for i, l in enumerate(labels)], 380, 10, 24)
+ g.save(REC / name / f"{name}-{suf}.webp", "WEBP", quality=88, method=6)
+ log[f"{name}-{suf}"] = {"size": [w, h], "mode": mode, "cells": len(cuts), "labels": labels,
+ "sheet_corner_std": sstd, "min_cell_corner_greenness": min(cgs),
+ "residual_green_px_per_cell": rcs, "residual_total": sum(rcs)}
+ print(name, suf, mode, "cells", len(cuts), "corner std", sstd, "residual", sum(rcs), flush=True)
+ return cuts
+
+def contact(all_cuts):
+ tw, th = 600, 476
+ order = [(n, s) for n in R.NAMES for s in ("r1a", "r1b")]
+ W = 4 * tw; H = ((len(order) + 3) // 4) * th
+ big = Image.new("RGB", (W, H), (60, 60, 60)); d = ImageDraw.Draw(big); f = font(16)
+ for k, key in enumerate(order):
+ x, y = (k % 4) * tw, (k // 4) * th
+ cuts = all_cuts.get(key)
+ d.text((x + 8, y + 3), f"{key[0]} {key[1]}", fill=(255, 255, 255), font=f)
+ if not cuts: continue
+ g = grid(cuts, [""] * len(cuts), 140 if len(cuts) == 12 else 105, 6, 0)
+ big.paste(g, (x + 5, y + 22))
+ big.save(REC / "contact-r1.webp", "WEBP", quality=88, method=6)
+ print("contact-r1.webp", big.size)
+
+if __name__ == "__main__":
+ only = sys.argv[1:]
+ log, all_cuts = {}, {}
+ for n in R.NAMES:
+ for s in ("r1a", "r1b"):
+ if only and f"{n}-{s}" not in only: continue
+ all_cuts[(n, s)] = process(n, s, log)
+ if not only:
+ contact(all_cuts)
+ (REC / "matte-crop-log.json").write_text(json.dumps(log, indent=1), encoding="utf-8")
diff --git a/persona-poses/r1/nibu/nibu-r1a.txt b/persona-poses/r1/nibu/nibu-r1a.txt
new file mode 100644
index 0000000..67c2207
--- /dev/null
+++ b/persona-poses/r1/nibu/nibu-r1a.txt
@@ -0,0 +1,30 @@
+# Nibu r1a
+
+## Hypothesis
+
+A sheet keeps the character consistent across twelve cells from the prompt alone: the busts' shared style block adapted for a sheet, the canon character block verbatim, and twelve short cell descriptions.
+
+## Variable changed
+
+Baseline: prompt only, no reference image.
+
+## Render record
+
+- Model: FLUX 3 Image, `POST https://api.bfl.ai/v1/flux-3-image`
+- Parameters: aspect_ratio 4:3, resolution 2k, safety_tolerance default, grounding default; no seed field
+- Reference: none; rendered from scratch from the prompt alone
+- Output: 2368x1776, source format PNG; 2k original kept out of git at F:/Projects/cast-art-originals/poses-r1/nibu/nibu-r1a.png
+- Cost: about $0.10 per request (reported by API: {'cost': 10.0, 'input_mp': None, 'output_mp': None})
+- Request id: dfd3ec40-64d3-4f2a-8dc1-e22bb7392d96
+- Date: 2026-10-14
+- Cell order: neutral, happy, amused, thinking, concerned, annoyed, surprised, determined, console, glitch, lore, triumph
+
+## Prompt
+
+Character expression sheet for a game cast lineup: a sheet of twelve cells, each cell a bust, head and shoulders cropped at mid-chest by the bottom edge of the cell, the character in three-quarter view turned toward the right side of the cell, the head centred horizontally in the cell with its top a small margin below the top edge of the cell, the same crop and scale in every cell. Super-deformed chibi proportions: the character is two heads tall in total, so the head is very large compared with the small shoulders and narrow torso. Clean 2D anime illustration drawn to be traced into vector art: one bold dark outline of even weight around the whole silhouette, thinner inner lines, flat cel shading with exactly one base tone and one shadow tone per material, light from the upper left. No gradients, no texture, no painterly blending, no glow halos, no pixel art, no 3D rendering. A limited palette of five or six flat colours taken from the character's key colours. Large expressive anime eyes with two white highlights. The background is one perfectly flat, uniform, solid bright chroma-key green from edge to edge: no gradient, no vignette, no floor, no cast shadow, no particles, no props or scenery behind the figure.
+
+The sheet is a grid of four columns by three rows, twelve cells of the same character in reading order, left to right and top to bottom, separated by generous empty padding of the same flat green, with no frames, no borders, no divider lines, no labels and no text. Every cell shows the same character with the same design, colours and proportions; the cell description sets that cell's expression and pose, and the character description below gives the design.
+
+Nibu, a quiet, serious worldbuilder girl: very long pale lavender hair with a swept side fringe, a pink hair clip on the left shaped like a small data connector plug, a short rectangular plug body with a round pink bead at each end; a large pink ribbon bow on the right, half-lidded violet eyes, a calm unsmiling expression, arms folded. Wears a sleeveless lilac blouse with a frilled collar, detached dark purple sleeves with circuit-pattern embroidery, a small cyan ring device on the shoulder, and a long dark purple coat. Key colours: violet and pink with a cyan accent.
+
+Cell 1 (neutral): a calm neutral expression, mouth closed, looking ahead. Cell 2 (happy): a broad happy smile with bright eyes. Cell 3 (amused): an amused smirk with one eyebrow raised, a stifled laugh. Cell 4 (thinking): a thoughtful expression, eyes looking up and aside, one hand raised to the chin. Cell 5 (concerned): a concerned expression, brows drawn together and tilted up, a small worried frown. Cell 6 (annoyed): an annoyed expression, flat half-lidded eyes, a pursed frown. Cell 7 (surprised): a surprised expression, wide eyes, mouth open in an O. Cell 8 (determined): a determined expression, narrowed eyes, a firm set mouth, a small nod. Cell 9 (console): typing on a small floating console panel. Cell 10 (glitch): her face and hair split by a few horizontally offset slices, as if glitching. Cell 11 (lore): reciting lore with eyes closed and one hand open before her. Cell 12 (triumph): a quiet triumphant smile with one fist lightly raised.
diff --git a/persona-poses/r1/nibu/nibu-r1a.webp b/persona-poses/r1/nibu/nibu-r1a.webp
new file mode 100644
index 0000000..6ddf383
Binary files /dev/null and b/persona-poses/r1/nibu/nibu-r1a.webp differ
diff --git a/persona-poses/r1/nibu/nibu-r1b.txt b/persona-poses/r1/nibu/nibu-r1b.txt
new file mode 100644
index 0000000..2084cae
--- /dev/null
+++ b/persona-poses/r1/nibu/nibu-r1b.txt
@@ -0,0 +1,32 @@
+# Nibu r1b
+
+## Hypothesis
+
+Adding the canon bust as a reference image holds canon details the prompt alone drifts on, without pulling every cell into the bust's pose.
+
+## Variable changed
+
+Against the same character's r1a: the canon bust cutout on its chroma green is passed as `images` and cited as image 1 by one clause prepended to the prompt. Nothing else differs.
+
+## Render record
+
+- Model: FLUX 3 Image, `POST https://api.bfl.ai/v1/flux-3-image`
+- Parameters: aspect_ratio 4:3, resolution 2k, safety_tolerance default, grounding default; no seed field
+- Reference: canon bust r3/nibu/nibu-r3a-cutout.png composited on its chroma green, base64 in `images`, cited as image 1; reference ground rgb(39, 245, 72)
+- Output: 2368x1776, source format PNG; 2k original kept out of git at F:/Projects/cast-art-originals/poses-r1/nibu/nibu-r1b.png
+- Cost: about $0.10 per request (reported by API: {'cost': 10.0, 'input_mp': None, 'output_mp': None})
+- Request id: fb227513-5308-40dd-8fe9-56a474ef2155
+- Date: 2026-10-14
+- Cell order: neutral, happy, amused, thinking, concerned, annoyed, surprised, determined, console, glitch, lore, triumph
+
+## Prompt
+
+Image 1 is the character's canon bust. The character must look exactly like the character in image 1 (same design, colours, proportions and line style) in every cell.
+
+Character expression sheet for a game cast lineup: a sheet of twelve cells, each cell a bust, head and shoulders cropped at mid-chest by the bottom edge of the cell, the character in three-quarter view turned toward the right side of the cell, the head centred horizontally in the cell with its top a small margin below the top edge of the cell, the same crop and scale in every cell. Super-deformed chibi proportions: the character is two heads tall in total, so the head is very large compared with the small shoulders and narrow torso. Clean 2D anime illustration drawn to be traced into vector art: one bold dark outline of even weight around the whole silhouette, thinner inner lines, flat cel shading with exactly one base tone and one shadow tone per material, light from the upper left. No gradients, no texture, no painterly blending, no glow halos, no pixel art, no 3D rendering. A limited palette of five or six flat colours taken from the character's key colours. Large expressive anime eyes with two white highlights. The background is one perfectly flat, uniform, solid bright chroma-key green from edge to edge: no gradient, no vignette, no floor, no cast shadow, no particles, no props or scenery behind the figure.
+
+The sheet is a grid of four columns by three rows, twelve cells of the same character in reading order, left to right and top to bottom, separated by generous empty padding of the same flat green, with no frames, no borders, no divider lines, no labels and no text. Every cell shows the same character with the same design, colours and proportions; the cell description sets that cell's expression and pose, and the character description below gives the design.
+
+Nibu, a quiet, serious worldbuilder girl: very long pale lavender hair with a swept side fringe, a pink hair clip on the left shaped like a small data connector plug, a short rectangular plug body with a round pink bead at each end; a large pink ribbon bow on the right, half-lidded violet eyes, a calm unsmiling expression, arms folded. Wears a sleeveless lilac blouse with a frilled collar, detached dark purple sleeves with circuit-pattern embroidery, a small cyan ring device on the shoulder, and a long dark purple coat. Key colours: violet and pink with a cyan accent.
+
+Cell 1 (neutral): a calm neutral expression, mouth closed, looking ahead. Cell 2 (happy): a broad happy smile with bright eyes. Cell 3 (amused): an amused smirk with one eyebrow raised, a stifled laugh. Cell 4 (thinking): a thoughtful expression, eyes looking up and aside, one hand raised to the chin. Cell 5 (concerned): a concerned expression, brows drawn together and tilted up, a small worried frown. Cell 6 (annoyed): an annoyed expression, flat half-lidded eyes, a pursed frown. Cell 7 (surprised): a surprised expression, wide eyes, mouth open in an O. Cell 8 (determined): a determined expression, narrowed eyes, a firm set mouth, a small nod. Cell 9 (console): typing on a small floating console panel. Cell 10 (glitch): her face and hair split by a few horizontally offset slices, as if glitching. Cell 11 (lore): reciting lore with eyes closed and one hand open before her. Cell 12 (triumph): a quiet triumphant smile with one fist lightly raised.
diff --git a/persona-poses/r1/nibu/nibu-r1b.webp b/persona-poses/r1/nibu/nibu-r1b.webp
new file mode 100644
index 0000000..e9260fd
Binary files /dev/null and b/persona-poses/r1/nibu/nibu-r1b.webp differ
diff --git a/persona-poses/r1/render_r1.py b/persona-poses/r1/render_r1.py
new file mode 100644
index 0000000..0bba4da
--- /dev/null
+++ b/persona-poses/r1/render_r1.py
@@ -0,0 +1,197 @@
+"""Round 1 pose sheets. Key read from F:/Projects/bfl-api.txt inside this process only; never printed, logged or written.
+
+Per-character blocks are taken verbatim from the canon bust records (the .txt beside each canon render),
+not retyped. Usage: py -3 render_r1.py [libby-r1a ...] (no args = all 16)
+"""
+import json, time, urllib.request, urllib.error, urllib.parse, io, sys, datetime, pathlib, base64, concurrent.futures as cf
+from PIL import Image
+
+CANON = pathlib.Path(r"F:/Projects/gamecult-site-wt-portraits/cast-portraits")
+REC = pathlib.Path(__file__).parent
+ORIG = pathlib.Path(r"F:/Projects/cast-art-originals/poses-r1")
+ENDPOINT = "https://api.bfl.ai/v1/flux-3-image"
+NAMES = ["libby", "heimdall", "kiko", "aqua", "weksa", "nibu", "eureka", "void"]
+BUST = {"libby": "r2/libby/libby-r2a", "heimdall": "r2/heimdall/heimdall-r2a", "kiko": "r2/kiko/kiko-r2a",
+ "aqua": "r4/aqua/aqua-r4a", "weksa": "r2/weksa/weksa-r2a", "nibu": "r3/nibu/nibu-r3a",
+ "eureka": "r2/eureka/eureka-r2a", "void": "r3/void/void-r3a"}
+BUST_ORIG = {"libby": "r2/libby-r2a", "heimdall": "r2/heimdall-r2a", "kiko": "r2/kiko-r2a", "aqua": "r4/aqua-r4a",
+ "weksa": "r2/weksa-r2a", "nibu": "r3/nibu-r3a", "eureka": "r2/eureka-r2a", "void": "r3/void-r3a"}
+
+# The busts' batch-A shared style block (two heads tall), exactly as the canon records carry it.
+STYLE_BUST = (
+ "Character portrait for a game cast lineup: a bust, head and shoulders cropped at mid-chest by the bottom edge of the frame, "
+ "the character in three-quarter view turned toward the right side of the frame, the head centred horizontally with its top a small margin "
+ "below the top edge, the same crop and scale for every character. "
+ "Super-deformed chibi proportions: the character is two heads tall in total, so the head is very large compared with the small shoulders and narrow torso. "
+ "Clean 2D anime illustration drawn to be traced into vector art: one bold dark outline of even weight around the whole silhouette, "
+ "thinner inner lines, flat cel shading with exactly one base tone and one shadow tone per material, light from the upper left. "
+ "No gradients, no texture, no painterly blending, no glow halos, no pixel art, no 3D rendering. "
+ "A limited palette of five or six flat colours taken from the character's key colours. Large expressive anime eyes with two white highlights. "
+ "The background is one perfectly flat, uniform, solid bright chroma-key green from edge to edge: no gradient, no vignette, no floor, "
+ "no cast shadow, no particles, no props or scenery behind the figure."
+)
+CROP_BUST = ("a bust, head and shoulders cropped at mid-chest by the bottom edge of the frame, "
+ "the character in three-quarter view turned toward the right side of the frame, the head centred horizontally with its top a small margin "
+ "below the top edge, the same crop and scale for every character. ")
+CROP_SHEET = ("a sheet of twelve cells, each cell a bust, head and shoulders cropped at mid-chest by the bottom edge of the cell, "
+ "the character in three-quarter view turned toward the right side of the cell, the head centred horizontally in the cell with its top a small margin "
+ "below the top edge of the cell, the same crop and scale in every cell. ")
+STYLE = STYLE_BUST.replace("Character portrait for a game cast lineup: " + CROP_BUST, "Character expression sheet for a game cast lineup: " + CROP_SHEET)
+assert STYLE != STYLE_BUST and "twelve cells" in STYLE
+SHEET = ("The sheet is a grid of four columns by three rows, twelve cells of the same character in reading order, left to right and top to bottom, "
+ "separated by generous empty padding of the same flat green, with no frames, no borders, no divider lines, no labels and no text. "
+ "Every cell shows the same character with the same design, colours and proportions; the cell description sets that cell's expression and pose, "
+ "and the character description below gives the design.")
+REF = ("Image 1 is the character's canon bust. The character must look exactly like the character in image 1 "
+ "(same design, colours, proportions and line style) in every cell.")
+
+CORE = [("neutral", "a calm neutral expression, mouth closed, looking ahead"),
+ ("happy", "a broad happy smile with bright eyes"),
+ ("amused", "an amused smirk with one eyebrow raised, a stifled laugh"),
+ ("thinking", "a thoughtful expression, eyes looking up and aside, one hand raised to the chin"),
+ ("concerned", "a concerned expression, brows drawn together and tilted up, a small worried frown"),
+ ("annoyed", "an annoyed expression, flat half-lidded eyes, a pursed frown"),
+ ("surprised", "a surprised expression, wide eyes, mouth open in an O"),
+ ("determined", "a determined expression, narrowed eyes, a firm set mouth, a small nod")]
+SIG = {
+ "libby": [("stamp", "stamping a library due-date stamp down onto a book with a cheerful thump"),
+ ("research", "peering closely at an open old book held in both hands"),
+ ("shush", "one finger raised to her lips in a shushing gesture"),
+ ("schema", "holding up a glowing violet diagram of boxes and arrows, explaining")],
+ "heimdall": [("badge", "holding up a gold badge of authority toward the viewer"),
+ ("barrier", "one palm raised forward, projecting a blue barrier"),
+ ("protect", "stepping forward with his cape swept out in front, shielding someone behind him"),
+ ("revoke", "pointing the golden key downward with a sharp cross-out gesture of the other hand, revoking access")],
+ "kiko": [("tinker", "tinkering with a small gadget, tongue out in concentration"),
+ ("confetti", "throwing confetti into the air, cheering"),
+ ("sprint", "leaning forward as if sprinting, hair and tail streaming backward"),
+ ("debug", "squinting through a magnifying lens at a tiny bug held between two fingers")],
+ "aqua": [("conduct", "conducting with a raised pectoral fin like a baton"),
+ ("listen", "one fin pressed to a headphone ear cup, eyes closed, listening"),
+ ("patch", "plugging a cyan patch cable into a jack"),
+ ("flourish", "a showy DJ flourish with both fins spread wide")],
+ "weksa": [("map", "unrolling a map scroll and studying it"),
+ ("fieldwork", "brushing dust off a small relic with a soft brush"),
+ ("argue", "arguing, one finger jabbed forward, mouth open mid-sentence"),
+ ("interlingua", "holding two glyph cards side by side, comparing their meanings")],
+ "nibu": [("console", "typing on a small floating console panel"),
+ ("glitch", "her face and hair split by a few horizontally offset slices, as if glitching"),
+ ("lore", "reciting lore with eyes closed and one hand open before her"),
+ ("triumph", "a quiet triumphant smile with one fist lightly raised")],
+ "eureka": [("probe", "pointing the orange probe forward"),
+ ("inspect", "leaning in with narrowed eyes to inspect something closely"),
+ ("report", "presenting a thin tablet showing a report"),
+ ("hood", "the hood pulled low, shadowing her eyes, only a knowing smirk visible")],
+ "void": [("welcome", "arms opened wide in welcome"),
+ ("document", "holding up a sheet of paper, a document"),
+ ("point", "pointing one gloved finger forward"),
+ ("unimpressed", "unimpressed, the eyes flat narrow slits, the head tilted")],
+}
+VOID_CORE = [("neutral", "a calm neutral look, the two tall white oval eyes at rest"),
+ ("happy", "happy, the white eyes curved into bright upturned crescents"),
+ ("amused", "amused, one white eye narrower than the other, the hood tilted slightly"),
+ ("thinking", "thoughtful, the white eyes looking up and aside, one gloved hand raised to the hood"),
+ ("concerned", "concerned, the white eyes tilted inward and upward, the hood drooping slightly"),
+ ("annoyed", "annoyed, the white eyes flat half-closed slits"),
+ ("surprised", "surprised, the white eyes wide round circles, the hood rising"),
+ ("determined", "determined, the white eyes narrowed and angled inward, the hood set forward")]
+
+def cells_for(n):
+ return (VOID_CORE if n == "void" else CORE) + SIG[n]
+
+def canon_block(n):
+ t = (CANON / (BUST[n] + ".txt")).read_text(encoding="utf-8").replace("\r\n", "\n")
+ body = t.split("## Prompt\n\n", 1)[1].strip()
+ style, block = body.split("\n\n", 1)
+ assert style == STYLE_BUST, f"{n}: canon style block differs from batch A"
+ return block.strip()
+
+def prompt_for(n, suf):
+ cells = " ".join(f"Cell {i + 1} ({l}): {d}." for i, (l, d) in enumerate(cells_for(n)))
+ p = STYLE + "\n\n" + SHEET + "\n\n" + canon_block(n) + "\n\n" + cells
+ return (REF + "\n\n" + p) if suf == "r1b" else p
+
+def ref_image(n):
+ cut = Image.open(CANON / (BUST[n] + "-cutout.png")).convert("RGBA")
+ o = Image.open(pathlib.Path(r"F:/Projects/cast-art-originals") / (BUST_ORIG[n] + ".png")).convert("RGB")
+ px = [o.getpixel(p) for p in ((4, 4), (o.width - 5, 4))]
+ g = tuple(sum(c[i] for c in px) // 2 for i in range(3))
+ bg = Image.new("RGB", cut.size, g); bg.paste(cut, (0, 0), cut)
+ b = io.BytesIO(); bg.save(b, "PNG")
+ return base64.b64encode(b.getvalue()).decode(), g
+
+HYP = {"r1a": "A sheet keeps the character consistent across twelve cells from the prompt alone: the busts' shared style block adapted for a sheet, the canon character block verbatim, and twelve short cell descriptions.",
+ "r1b": "Adding the canon bust as a reference image holds canon details the prompt alone drifts on, without pulling every cell into the bust's pose."}
+VAR = {"r1a": "Baseline: prompt only, no reference image.",
+ "r1b": "Against the same character's r1a: the canon bust cutout on its chroma green is passed as `images` and cited as image 1 by one clause prepended to the prompt. Nothing else differs."}
+
+def req(url, key, data=None):
+ r = urllib.request.Request(url, data=json.dumps(data).encode() if data else None,
+ headers={"x-key": key, "Content-Type": "application/json", "accept": "application/json"})
+ return json.load(urllib.request.urlopen(r, timeout=60))
+
+def run(job, key):
+ n, suf = job
+ d = REC / n; d.mkdir(parents=True, exist_ok=True); ORIG.joinpath(n).mkdir(parents=True, exist_ok=True)
+ png = ORIG / n / f"{n}-{suf}.png"
+ if png.exists():
+ return f"{n}-{suf}: exists, skipped"
+ prompt = prompt_for(n, suf)
+ body = {"prompt": prompt, "aspect_ratio": "4:3", "resolution": "2k"}
+ gdesc = ""
+ if suf == "r1b":
+ b64, g = ref_image(n); body["images"] = [b64]; gdesc = f"; reference ground rgb{g}"
+ try:
+ j = req(ENDPOINT, key, body)
+ except urllib.error.HTTPError as e:
+ return f"{n}-{suf}: submit HTTP {e.code} {e.read()[:400]!r}"
+ except Exception as e:
+ return f"{n}-{suf}: submit transport error {type(e).__name__}"
+ rid, poll = j.get("id"), j.get("polling_url")
+ cost = {k: j[k] for k in ("cost", "input_mp", "output_mp") if k in j}
+ print(f"{n}-{suf}: submitted id {rid} poll host {urllib.parse.urlparse(poll or '').netloc} {cost}", flush=True)
+ last = None
+ for i in range(300):
+ time.sleep(6)
+ try: s = req(poll, key)
+ except Exception as e:
+ m = f"poll error {type(e).__name__} {getattr(e, 'code', '')}"
+ if getattr(e, "code", 0) == 500:
+ return f"{n}-{suf}: server-side error (HTTP 500) id {rid}"
+ if m != last: print(f"{n}-{suf}: {m}", flush=True); last = m
+ continue
+ st = s.get("status")
+ if st != last: print(f"{n}-{suf}: status {st} at {i * 6}s", flush=True); last = st
+ if st == "Ready":
+ raw = urllib.request.urlopen(s["result"]["sample"], timeout=120).read()
+ im = Image.open(io.BytesIO(raw)); fmt = im.format; im.save(png, "PNG")
+ ref = (f"- Reference: canon bust {BUST[n]}-cutout.png composited on its chroma green, base64 in `images`, cited as image 1{gdesc}\n"
+ if suf == "r1b" else "- Reference: none; rendered from scratch from the prompt alone\n")
+ txt = (f"# {n.capitalize()} {suf}\n\n## Hypothesis\n\n{HYP[suf]}\n\n## Variable changed\n\n{VAR[suf]}\n\n"
+ f"## Render record\n\n- Model: FLUX 3 Image, `POST {ENDPOINT}`\n"
+ f"- Parameters: aspect_ratio 4:3, resolution 2k, safety_tolerance default, grounding default; no seed field\n{ref}"
+ f"- Output: {im.size[0]}x{im.size[1]}, source format {fmt}; 2k original kept out of git at "
+ f"F:/Projects/cast-art-originals/poses-r1/{n}/{n}-{suf}.png\n"
+ f"- Cost: about $0.10 per request (reported by API: {cost or 'nothing'})\n"
+ f"- Request id: {rid}\n- Date: {datetime.date.today().isoformat()}\n"
+ f"- Cell order: {', '.join(l for l, _ in cells_for(n))}\n\n"
+ f"## Prompt\n\n{prompt}\n")
+ (d / f"{n}-{suf}.txt").write_text(txt, encoding="utf-8")
+ return f"{n}-{suf}: Ready {im.size} id {rid}"
+ if st in ("Error", "Failed", "Content Moderated", "Request Moderated", "Task not found"):
+ return f"{n}-{suf}: {st} {str(s)[:200]}"
+ return f"{n}-{suf}: timeout id {rid}"
+
+if __name__ == "__main__":
+ only = sys.argv[1:]
+ if only == ["--dry"]:
+ for n in NAMES:
+ for s in ("r1a", "r1b"):
+ p = prompt_for(n, s); print(n, s, len(p))
+ print(prompt_for("void", "r1b")); sys.exit()
+ key = pathlib.Path(r"F:/Projects/bfl-api.txt").read_text().strip()
+ jobs = [(n, s) for n in NAMES for s in ("r1a", "r1b") if not only or f"{n}-{s}" in only]
+ with cf.ThreadPoolExecutor(max_workers=int(__import__("os").environ.get("WORKERS", "4"))) as ex:
+ for r in ex.map(lambda j: run(j, key), jobs):
+ print(r, flush=True)
+ print("DONE", flush=True)
diff --git a/persona-poses/r1/run-log-r1.txt b/persona-poses/r1/run-log-r1.txt
new file mode 100644
index 0000000..ede9ae0
--- /dev/null
+++ b/persona-poses/r1/run-log-r1.txt
@@ -0,0 +1,118 @@
+# Round 1 request log (key-free). Order: first libby-r1b attempt, its retry, the 15-job run, kiko-r1b retry, the serial run of the rest, then checks.
+
+## probe.log
+libby-r1b: timeout id 936bb7bb-48f3-4770-836e-8e02e19892ea
+DONE
+
+## probe2.log
+libby-r1b: submitted id a4b50eab-abda-4021-8f08-8aa81c894a05 poll host api.isr.bfl.ai {'cost': 10.0, 'input_mp': None, 'output_mp': None}
+libby-r1b: status Reasoning at 0s
+libby-r1b: status Generating at 18s
+libby-r1b: status Ready at 30s
+libby-r1b: Ready (2368, 1776) id a4b50eab-abda-4021-8f08-8aa81c894a05
+DONE
+
+## run.log
+kiko-r1a: submitted id 2414b18b-bda2-4f88-bcad-f060fa52965a poll host api.isr.bfl.ai {'cost': 10.0, 'input_mp': None, 'output_mp': None}
+heimdall-r1a: submitted id a051f0ca-f250-4aa8-a47f-c5195dbe94bc poll host api.isr.bfl.ai {'cost': 10.0, 'input_mp': None, 'output_mp': None}
+libby-r1a: submitted id b12cd042-63fc-4e99-85af-a239f60ec937 poll host api.us8.bfl.ai {'cost': 10.0, 'input_mp': None, 'output_mp': None}
+kiko-r1b: submitted id 419a1e26-f247-4a31-b61c-6265457a22d4 poll host api.us6.bfl.ai {'cost': 10.0, 'input_mp': None, 'output_mp': None}
+heimdall-r1a: status Reasoning at 0s
+kiko-r1a: status Reasoning at 0s
+libby-r1a: status Reasoning at 0s
+kiko-r1b: status Reasoning at 0s
+libby-r1a: status Generating at 18s
+kiko-r1b: poll error HTTPError 500
+kiko-r1a: status Generating at 24s
+kiko-r1a: status Ready at 30s
+heimdall-r1a: status Generating at 30s
+libby-r1a: status Ready at 30s
+libby-r1a: Ready (2368, 1776) id b12cd042-63fc-4e99-85af-a239f60ec937
+libby-r1b: exists, skipped
+weksa-r1b: submitted id 044ad25e-87c2-4c79-9c84-28b355501f78 poll host api.us6.bfl.ai {'cost': 10.0, 'input_mp': None, 'output_mp': None}
+heimdall-r1a: status Ready at 36s
+nibu-r1a: submitted id dfd3ec40-64d3-4f2a-8dc1-e22bb7392d96 poll host api.us2.bfl.ai {'cost': 10.0, 'input_mp': None, 'output_mp': None}
+heimdall-r1a: Ready (2368, 1776) id a051f0ca-f250-4aa8-a47f-c5195dbe94bc
+heimdall-r1b: submit HTTP 429 b'{"detail":"Credit balance low: active tasks are capped at 1 per organization. Please top up to restore full throughput."}'
+kiko-r1a: Ready (2368, 1776) id 2414b18b-bda2-4f88-bcad-f060fa52965a
+nibu-r1b: submitted id fb227513-5308-40dd-8fe9-56a474ef2155 poll host api.isr.bfl.ai {'cost': 10.0, 'input_mp': None, 'output_mp': None}
+weksa-r1b: status Reasoning at 0s
+nibu-r1a: status Reasoning at 0s
+nibu-r1b: status Reasoning at 0s
+weksa-r1b: poll error HTTPError 500
+nibu-r1a: status Generating at 18s
+nibu-r1b: status Generating at 18s
+nibu-r1b: status Ready at 30s
+eureka-r1a: submitted id cffa8610-5235-4df0-bb0c-91d37e5146f2 poll host api.isr.bfl.ai {'cost': 10.0, 'input_mp': None, 'output_mp': None}
+nibu-r1a: status Ready at 36s
+eureka-r1a: status Reasoning at 0s
+eureka-r1b: submitted id 63328793-f3e5-4dab-9e98-cefb2d5e1f9a poll host api.us2.bfl.ai {'cost': 10.0, 'input_mp': None, 'output_mp': None}
+eureka-r1b: status Reasoning at 0s
+eureka-r1a: status Generating at 18s
+eureka-r1b: status Generating at 18s
+eureka-r1a: status Ready at 30s
+void-r1b: submitted id 83206362-d1b5-4e1d-ac42-123edea8dadb poll host api.isr.bfl.ai {'cost': 10.0, 'input_mp': None, 'output_mp': None}
+void-r1b: status Reasoning at 0s
+void-r1b: status Generating at 12s
+eureka-r1b: status Ready at 54s
+void-r1b: status Ready at 24s
+
+## retry-kiko-r1b.log
+kiko-r1b: submitted id 15e7b364-1771-4136-9532-c5b012524609 poll host api.us7.bfl.ai {'cost': 10.0, 'input_mp': None, 'output_mp': None}
+kiko-r1b: status Reasoning at 0s
+kiko-r1b: status Generating at 18s
+kiko-r1b: status Ready at 30s
+kiko-r1b: Ready (2368, 1776) id 15e7b364-1771-4136-9532-c5b012524609
+DONE
+
+## run2.log
+heimdall-r1b: submitted id 68ecec4c-3301-458d-8ae2-b7171ae8fbb0 poll host api.isr.bfl.ai {'cost': 10.0, 'input_mp': None, 'output_mp': None}
+heimdall-r1b: status Reasoning at 0s
+heimdall-r1b: status Generating at 12s
+heimdall-r1b: status Ready at 30s
+heimdall-r1b: Ready (2368, 1776) id 68ecec4c-3301-458d-8ae2-b7171ae8fbb0
+aqua-r1a: submitted id 5036c049-cac8-48df-9cc6-bc01805b4d26 poll host api.us7.bfl.ai {'cost': 10.0, 'input_mp': None, 'output_mp': None}
+aqua-r1a: status Reasoning at 0s
+aqua-r1a: status Generating at 18s
+aqua-r1a: status Ready at 30s
+aqua-r1a: Ready (2368, 1776) id 5036c049-cac8-48df-9cc6-bc01805b4d26
+aqua-r1b: submitted id 8dc4c2ff-0245-41de-9991-8055a309c852 poll host api.us6.bfl.ai {'cost': 10.0, 'input_mp': None, 'output_mp': None}
+aqua-r1b: status Reasoning at 0s
+aqua-r1b: status Generating at 18s
+aqua-r1b: status Ready at 60s
+aqua-r1b: Ready (2368, 1776) id 8dc4c2ff-0245-41de-9991-8055a309c852
+weksa-r1a: submitted id 96f45121-c6c8-4a54-b6c7-15a98978722d poll host api.us2.bfl.ai {'cost': 10.0, 'input_mp': None, 'output_mp': None}
+weksa-r1a: status Reasoning at 0s
+weksa-r1a: status Generating at 24s
+weksa-r1a: status Ready at 42s
+weksa-r1a: Ready (2368, 1776) id 96f45121-c6c8-4a54-b6c7-15a98978722d
+void-r1a: submitted id 30a333e2-db14-4610-b749-26dadbe5d187 poll host api.us6.bfl.ai {'cost': 10.0, 'input_mp': None, 'output_mp': None}
+void-r1a: status Reasoning at 0s
+void-r1a: server-side error (HTTP 500) id 30a333e2-db14-4610-b749-26dadbe5d187
+DONE
+
+## check.log
+not rendered, no record: weksa-r1b
+not rendered, no record: void-r1a
+prompts: 14 records match the script; shared style+sheet part identical across all; r1b == r1a + clause where both exist
+core cell text identical for 7 characters, void adapted: True
+aqua-r1a: 12 cutouts, order ok, mode cols gutters, rows gutters, residual green px 0, sheet corner std [2.3, 2.1]
+aqua-r1b: CELL COUNT FAIL, 16 cutouts (4x4 sheet drawn), declared 12; residual green px 0
+eureka-r1a: 12 cutouts, order ok, mode cols divider lines, rows divider lines, residual green px 0, sheet corner std [1.8, 1.8]
+eureka-r1b: 12 cutouts, order ok, mode cols gutters, rows gutters, residual green px 0, sheet corner std [1.8, 2.0]
+heimdall-r1a: 12 cutouts, order ok, mode cols divider lines, rows divider lines, residual green px 0, sheet corner std [2.1, 2.0]
+heimdall-r1b: 12 cutouts, order ok, mode cols gutters, rows gutters, residual green px 0, sheet corner std [2.5, 2.1]
+kiko-r1a: 12 cutouts, order ok, mode cols runs 5, rows gutters; cols fell back to the nominal grid, residual green px 0, sheet corner std [2.4, 2.6]
+kiko-r1b: 12 cutouts, order ok, mode cols gutters, rows gutters, residual green px 0, sheet corner std [2.0, 2.2]
+libby-r1a: 12 cutouts, order ok, mode cols gutters, rows gutters, residual green px 0, sheet corner std [2.1, 1.7]
+libby-r1b: 12 cutouts, order ok, mode cols divider lines, rows divider lines, residual green px 0, sheet corner std [2.0, 1.9]
+nibu-r1a: 12 cutouts, order ok, mode cols gutters, rows gutters, residual green px 0, sheet corner std [2.9, 2.4]
+nibu-r1b: 12 cutouts, order ok, mode cols runs 5, rows gutters; cols fell back to the nominal grid, residual green px 0, sheet corner std [2.6, 2.1]
+void-r1a: not rendered, no sheet
+void-r1b: CELL COUNT FAIL, 16 cutouts (4x4 sheet drawn), declared 12; residual green px 0
+weksa-r1a: 12 cutouts, order ok, mode cols divider lines, rows divider lines, residual green px 0, sheet corner std [1.9, 1.9]
+weksa-r1b: not rendered, no sheet
+committed-tree files over 2 MB: none
+files containing the key value: none
+cell-count failures: ['aqua-r1b', 'void-r1b']
+ALL OK except the cell-count failures above
diff --git a/persona-poses/r1/void/void-r1b.txt b/persona-poses/r1/void/void-r1b.txt
new file mode 100644
index 0000000..9c5cae0
--- /dev/null
+++ b/persona-poses/r1/void/void-r1b.txt
@@ -0,0 +1,32 @@
+# Void r1b
+
+## Hypothesis
+
+Adding the canon bust as a reference image holds canon details the prompt alone drifts on, without pulling every cell into the bust's pose.
+
+## Variable changed
+
+Against the same character's r1a: the canon bust cutout on its chroma green is passed as `images` and cited as image 1 by one clause prepended to the prompt. Nothing else differs.
+
+## Render record
+
+- Model: FLUX 3 Image, `POST https://api.bfl.ai/v1/flux-3-image`
+- Parameters: aspect_ratio 4:3, resolution 2k, safety_tolerance default, grounding default; no seed field
+- Reference: canon bust r3/void/void-r3a-cutout.png composited on its chroma green, base64 in `images`, cited as image 1; reference ground rgb(33, 255, 30)
+- Output: 2368x1776, source format PNG; 2k original kept out of git at F:/Projects/cast-art-originals/poses-r1/void/void-r1b.png
+- Cost: about $0.10 per request (reported by API: {'cost': 10.0, 'input_mp': None, 'output_mp': None})
+- Request id: 83206362-d1b5-4e1d-ac42-123edea8dadb
+- Date: 2026-10-14
+- Cell order: neutral, happy, amused, thinking, concerned, annoyed, surprised, determined, welcome, document, point, unimpressed
+
+## Prompt
+
+Image 1 is the character's canon bust. The character must look exactly like the character in image 1 (same design, colours, proportions and line style) in every cell.
+
+Character expression sheet for a game cast lineup: a sheet of twelve cells, each cell a bust, head and shoulders cropped at mid-chest by the bottom edge of the cell, the character in three-quarter view turned toward the right side of the cell, the head centred horizontally in the cell with its top a small margin below the top edge of the cell, the same crop and scale in every cell. Super-deformed chibi proportions: the character is two heads tall in total, so the head is very large compared with the small shoulders and narrow torso. Clean 2D anime illustration drawn to be traced into vector art: one bold dark outline of even weight around the whole silhouette, thinner inner lines, flat cel shading with exactly one base tone and one shadow tone per material, light from the upper left. No gradients, no texture, no painterly blending, no glow halos, no pixel art, no 3D rendering. A limited palette of five or six flat colours taken from the character's key colours. Large expressive anime eyes with two white highlights. The background is one perfectly flat, uniform, solid bright chroma-key green from edge to edge: no gradient, no vignette, no floor, no cast shadow, no particles, no props or scenery behind the figure.
+
+The sheet is a grid of four columns by three rows, twelve cells of the same character in reading order, left to right and top to bottom, separated by generous empty padding of the same flat green, with no frames, no borders, no divider lines, no labels and no text. Every cell shows the same character with the same design, colours and proportions; the cell description sets that cell's expression and pose, and the character description below gives the design.
+
+Void, a hooded shadow figure who fills the frame as fully as the human busts do: the top of the head a small margin below the top edge, the figure spanning most of the frame width, cut by the bottom edge at mid-chest: a deep indigo-violet robe with a large pointed hood, the face inside the hood completely black with no features except two tall white glowing oval eyes, a thick bright orange scarf wrapped around the neck with frayed ends trailing to one side, dark gloved hands. Key colours: indigo violet and orange.
+
+Cell 1 (neutral): a calm neutral look, the two tall white oval eyes at rest. Cell 2 (happy): happy, the white eyes curved into bright upturned crescents. Cell 3 (amused): amused, one white eye narrower than the other, the hood tilted slightly. Cell 4 (thinking): thoughtful, the white eyes looking up and aside, one gloved hand raised to the hood. Cell 5 (concerned): concerned, the white eyes tilted inward and upward, the hood drooping slightly. Cell 6 (annoyed): annoyed, the white eyes flat half-closed slits. Cell 7 (surprised): surprised, the white eyes wide round circles, the hood rising. Cell 8 (determined): determined, the white eyes narrowed and angled inward, the hood set forward. Cell 9 (welcome): arms opened wide in welcome. Cell 10 (document): holding up a sheet of paper, a document. Cell 11 (point): pointing one gloved finger forward. Cell 12 (unimpressed): unimpressed, the eyes flat narrow slits, the head tilted.
diff --git a/persona-poses/r1/void/void-r1b.webp b/persona-poses/r1/void/void-r1b.webp
new file mode 100644
index 0000000..0748cad
Binary files /dev/null and b/persona-poses/r1/void/void-r1b.webp differ
diff --git a/persona-poses/r1/weksa/weksa-r1a.txt b/persona-poses/r1/weksa/weksa-r1a.txt
new file mode 100644
index 0000000..38a9057
--- /dev/null
+++ b/persona-poses/r1/weksa/weksa-r1a.txt
@@ -0,0 +1,30 @@
+# Weksa r1a
+
+## Hypothesis
+
+A sheet keeps the character consistent across twelve cells from the prompt alone: the busts' shared style block adapted for a sheet, the canon character block verbatim, and twelve short cell descriptions.
+
+## Variable changed
+
+Baseline: prompt only, no reference image.
+
+## Render record
+
+- Model: FLUX 3 Image, `POST https://api.bfl.ai/v1/flux-3-image`
+- Parameters: aspect_ratio 4:3, resolution 2k, safety_tolerance default, grounding default; no seed field
+- Reference: none; rendered from scratch from the prompt alone
+- Output: 2368x1776, source format PNG; 2k original kept out of git at F:/Projects/cast-art-originals/poses-r1/weksa/weksa-r1a.png
+- Cost: about $0.10 per request (reported by API: {'cost': 10.0, 'input_mp': None, 'output_mp': None})
+- Request id: 96f45121-c6c8-4a54-b6c7-15a98978722d
+- Date: 2026-10-14
+- Cell order: neutral, happy, amused, thinking, concerned, annoyed, surprised, determined, map, fieldwork, argue, interlingua
+
+## Prompt
+
+Character expression sheet for a game cast lineup: a sheet of twelve cells, each cell a bust, head and shoulders cropped at mid-chest by the bottom edge of the cell, the character in three-quarter view turned toward the right side of the cell, the head centred horizontally in the cell with its top a small margin below the top edge of the cell, the same crop and scale in every cell. Super-deformed chibi proportions: the character is two heads tall in total, so the head is very large compared with the small shoulders and narrow torso. Clean 2D anime illustration drawn to be traced into vector art: one bold dark outline of even weight around the whole silhouette, thinner inner lines, flat cel shading with exactly one base tone and one shadow tone per material, light from the upper left. No gradients, no texture, no painterly blending, no glow halos, no pixel art, no 3D rendering. A limited palette of five or six flat colours taken from the character's key colours. Large expressive anime eyes with two white highlights. The background is one perfectly flat, uniform, solid bright chroma-key green from edge to edge: no gradient, no vignette, no floor, no cast shadow, no particles, no props or scenery behind the figure.
+
+The sheet is a grid of four columns by three rows, twelve cells of the same character in reading order, left to right and top to bottom, separated by generous empty padding of the same flat green, with no frames, no borders, no divider lines, no labels and no text. Every cell shows the same character with the same design, colours and proportions; the cell description sets that cell's expression and pose, and the character description below gives the design.
+
+Weksa, a tomboyish young woman, an adventurous field archaeologist in the Indiana Jones mould: warm brown skin, a confident lopsided grin, a smudge of dust on one cheek, messy dark brown shoulder-length hair tied partly up in a topknot with turquoise and amber beads and small feathers, large round gold-rimmed glasses, warm amber-brown eyes, dangling turquoise earrings. She wears a cream linen robe with teal embroidered trim, its sleeves rolled up to the elbows, over a weathered brown leather vest, with beaded necklaces and wooden bead bracelets and a leather strap across her chest; a battered brown leather backpack stuffed with rolled scrolls and a small carved idol shows behind one shoulder. She holds up a small paper card with a hand-drawn glyph between two fingers. No hat. Key colours: teal and amber on earthy browns.
+
+Cell 1 (neutral): a calm neutral expression, mouth closed, looking ahead. Cell 2 (happy): a broad happy smile with bright eyes. Cell 3 (amused): an amused smirk with one eyebrow raised, a stifled laugh. Cell 4 (thinking): a thoughtful expression, eyes looking up and aside, one hand raised to the chin. Cell 5 (concerned): a concerned expression, brows drawn together and tilted up, a small worried frown. Cell 6 (annoyed): an annoyed expression, flat half-lidded eyes, a pursed frown. Cell 7 (surprised): a surprised expression, wide eyes, mouth open in an O. Cell 8 (determined): a determined expression, narrowed eyes, a firm set mouth, a small nod. Cell 9 (map): unrolling a map scroll and studying it. Cell 10 (fieldwork): brushing dust off a small relic with a soft brush. Cell 11 (argue): arguing, one finger jabbed forward, mouth open mid-sentence. Cell 12 (interlingua): holding two glyph cards side by side, comparing their meanings.
diff --git a/persona-poses/r1/weksa/weksa-r1a.webp b/persona-poses/r1/weksa/weksa-r1a.webp
new file mode 100644
index 0000000..a101eaf
Binary files /dev/null and b/persona-poses/r1/weksa/weksa-r1a.webp differ
diff --git a/site/quartz/styles/custom.scss b/site/quartz/styles/custom.scss
index 17cd19c..7fd5702 100644
--- a/site/quartz/styles/custom.scss
+++ b/site/quartz/styles/custom.scss
@@ -2022,56 +2022,3 @@ article.integrated-dossier-page {
@media (max-width: 800px) {
.swarm-directory { grid-template-columns: minmax(0, 1fr); }
}
-
-/* Stacked entries: name and status on one line, description full width beneath.
- Ruled rows, no fill, border box or pill, per the Surfaces law. */
-.gamecult-entries {
- margin: 1rem 0 0;
- padding: 0;
- list-style: none;
-}
-
-.gamecult-entry {
- padding: 0.85rem 0;
- border-top: 1px solid var(--gamecult-rule);
-}
-
-.gamecult-entry p {
- margin: 0;
-}
-
-.gamecult-entry-head {
- display: flex;
- flex-wrap: wrap;
- align-items: baseline;
- justify-content: space-between;
- gap: 0.2rem 1rem;
-}
-
-.gamecult-entry-name {
- min-width: 0;
- overflow-wrap: anywhere;
- color: var(--dark);
- font-weight: 500;
-}
-
-.gamecult-entry-status {
- flex: none;
- color: rgba(255, 138, 42, 0.96);
- font-family: var(--titleFont);
- font-size: 0.76rem;
- font-weight: 300;
- letter-spacing: 0.14em;
- text-transform: uppercase;
-}
-
-.gamecult-entry-body {
- margin-top: 0.25rem;
- color: rgba(183, 199, 217, 0.88);
- font-size: 0.94rem;
- line-height: 1.55;
-}
-
-.gamecult-entry-lead::first-letter {
- text-transform: uppercase;
-}