Loading the full README. The saved excerpt is shown below.
Jodith Miftah Tarot — a mobile-first tarot website, installable to the iPhone home
screen. Three reader personas, three services, the 22 Major Arcana. Every reading is
written by a model at request time, in Indonesian or English, in the voice of whichever
reader you picked.
www.jmtarot.site · v0.8.0 · Next.js 16 on Vercel sin1
A real draw against a real model: the question typed, three cards
taken from the fan, and the reading starting. Nothing here is a mockup.Read the full README ↗Jodith Miftah Tarot — a mobile-first tarot website, installable to the iPhone home screen. Three reader personas, three services, the 22 Major Arcana. Every reading is written by a model at request time, in Indonesian or English, in the voice of whichever reader you picked.
www.jmtarot.site · v0.8.0 · Next.js 16 on Vercel sin1
pick a reader ──► pick a service ──► draw 3 of 22 ──► POST /api/reading
│ │
your question ──┤ moderation gate (glm-4.5-flash, 1.5s)
│ │
the cards that keep ──┤ glm-4.6, streamed, four paragraphs
coming back to you │ │
▼ ▼
/history · /s/<slug> · the group chat
Every capture below is the running app at 390 x 844 — an iPhone 12/13/14/15 width —
at 2x, driven over CDP against npm run dev, a real Postgres and real z.ai calls. The
readings, the chat turns and the translations were all generated at the moment of
capture; the reader portraits and the card art are the only pre-existing images.
effectiveYesNo() decides
yes / no / unclear from the card and its orientation — reversal flips it — and the
prompt is handed the word. A question offering options ("ayam atau ikan?") gets the
same treatment from the other end: the model picks, and code validates the pick is
a slice of the question you typed.id and en. Switching does not regenerate
anything: it translates the stored prose, checks the card names came through
mechanically, and caches the result. Your question is never translated, on any surface./s/<12 chars> — no account, no indexing,
revocable, and it carries the language it was shared in. Re-sharing rotates the slug
within that language; revoking kills every language at once.Not built, deliberately: birth card, the once-per-day card lock, an About page. Card names stay English in both locales, and so do the readers'.
| Voice | Specialities | |
|---|---|---|
| Thessaly · The Grounded Guide | intuition plus logic, plain and lit | career decisions, direction, untangling a problem |
| Margaret · The Old Soul | long sentences, 1.3x everyone else's word budget | self-discovery, inner reflection, family |
| Adrian · The Modern Mystic | short, contractions, will make a joke | love, self-worth, short-term decisions |
Each carries a fixed gender in readers.json — Thessaly and Margaret female, Adrian
male — because a gender does not vary by locale but the words do. Each persona block in
the prompt carries one worked example paragraph, and that paragraph does more work than
the description: if the three ever stop being distinguishable with the names covered,
the fix is there and not in the code.
npm run smoke -- --all prints eighteen readings and ends with a blind read — three per
locale, names covered, key after forty blank lines. Guess. Three of three or the personas
need work.
This is the beginning of the README. Read the rest at the original. Read the full README ↗