feat: /secret — the grimoire

An unlisted page at /secret. Nothing in the app links to it, it has no nav
tab, and MobileNav's route-parity test now names it as an explicit exception
so an ordinary page still fails that test. It IS crawlable and is advertised
in the sitemap: "hidden" here means no button, not no index — a long,
specific mechanics page is worth the search traffic.

Every number on it was read out of the source or produced by RUNNING the
real function. A grimoire of plausible-sounding numbers would be worse than
none, because it would be believed. Four hand-rolled SVG figures (rarity by
moon phase, the passage ladder, the two twist curves) rather than a charting
dependency for four static pictures.

The best thing in it is a trap I did not know existed until I ran the code:
crossing over requires volatility strictly above 0.6, and favor shifts
volatility DOWN by up to 0.12 at mint. So a hunter at maximum favor pulls
spirits under the gate and locks themselves out of the largest reward in the
game — 25 essence and +0.08 favor. Verified directly: a spirit at volatility
0.65 is crossable at favor 0.0, and is not at +0.5 or +1.0. "Do not maximise
favor" is real, counter-intuitive, and follows from the source.

English only, as agreed — written as prose outside the i18n system rather
than several hundred translation keys, which keeps the en/es parity gate
meaningful for the actual UI.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
Indiana
2026-08-01 21:14:59 +00:00
parent 859467f46e
commit 7906abdd2b
5 changed files with 970 additions and 1 deletions

View File

@@ -19,6 +19,7 @@ import { LandingPage } from './pages/LandingPage'
import { MessagesPage } from './pages/MessagesPage'
import { ProfilePage } from './pages/ProfilePage'
import { SeancePage } from './pages/SeancePage'
import { SecretPage } from './pages/SecretPage'
import { ShopPage } from './pages/ShopPage'
function App() {
@@ -52,6 +53,9 @@ function App() {
<Route path="/hunters/:username" element={<HunterPage />} />
<Route path="/messages" element={<MessagesPage />} />
<Route path="/messages/:username" element={<MessagesPage />} />
{/* Unlisted: nothing in the app links here, and it is deliberately
absent from MobileNav. Reached by knowing the URL. */}
<Route path="/secret" element={<SecretPage />} />
<Route path="*" element={<LandingPage />} />
</Routes>
</BrowserRouter>

View File

@@ -101,7 +101,12 @@ describe('MobileNav', () => {
p !== '*' && // catch-all, not a destination
!p.includes(':') && // detail views, reached through their index
p !== '/' && // the landing page is the way in, not a tab
p !== '/enter', // the sign-in door, deliberately not a tab
p !== '/enter' && // the sign-in door, deliberately not a tab
// Unlisted BY DESIGN: /secret has no button anywhere in the app and
// must never gain one. Listed here as an explicit exception rather
// than left to slip through, so that adding an ordinary page still
// fails this test.
p !== '/secret',
)
const covered = new Set(NAV_ITEMS.map((i) => i.to))

View File

@@ -0,0 +1,279 @@
/* The Secret — a long-form reading page.
*
* Deliberately quieter than the rest of the app: this is the one screen where
* someone reads for ten minutes straight, so it drops the CRT theatrics in
* favour of a measured column, real line-height, and figures that can breathe.
* It still belongs to the same world — same phosphor and violet — but it reads
* like a document rather than an instrument.
*/
.secret-page {
min-height: 100vh;
min-height: 100dvh;
padding: 2rem 1.1rem 5rem;
background: radial-gradient(circle at 50% 0%, #12101c 0%, #07070c 60%);
color: #d8d4e4;
}
.secret-inner {
max-width: 46rem;
margin: 0 auto;
}
.secret-page h1 {
margin: 0.2rem 0 0.8rem;
font-size: clamp(2rem, 8vw, 3.2rem);
letter-spacing: 0.02em;
color: #f2eaff;
}
.secret-page h2 {
margin: 3rem 0 0.9rem;
padding-bottom: 0.4rem;
border-bottom: 1px solid rgba(178, 107, 255, 0.28);
font-size: 1.25rem;
letter-spacing: 0.06em;
color: #b26bff;
}
.secret-page p {
margin: 0 0 1rem;
/* Long-form: generous measure and leading, not the app's dense HUD spacing. */
line-height: 1.72;
font-size: 1.02rem;
}
.secret-eyebrow {
margin: 0;
font-size: 0.72rem;
letter-spacing: 0.4em;
text-transform: uppercase;
color: rgba(124, 255, 178, 0.65);
}
.secret-lede {
font-size: 1.12rem !important;
color: #efe9fb;
}
.secret-warn {
padding: 0.8rem 1rem;
border-left: 2px solid rgba(178, 107, 255, 0.6);
background: rgba(30, 18, 46, 0.5);
font-style: italic;
}
.secret-danger {
padding: 1rem 1.1rem;
border: 1px solid rgba(255, 120, 120, 0.4);
border-radius: 8px;
background: rgba(46, 16, 20, 0.45);
}
.secret-note {
padding-left: 0.9rem;
border-left: 2px solid rgba(124, 255, 178, 0.35);
color: #b9b4c8;
font-size: 0.96rem !important;
}
/* ---- contents ---- */
.secret-toc {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(11rem, 1fr));
gap: 0.3rem;
margin: 2rem 0 1rem;
padding: 1rem;
border: 1px solid rgba(124, 255, 178, 0.2);
border-radius: 10px;
background: rgba(12, 12, 20, 0.6);
}
.secret-toc a {
min-height: 44px;
display: flex;
align-items: center;
padding: 0 0.5rem;
border-radius: 6px;
color: #7cffb2;
text-decoration: none;
font-size: 0.92rem;
}
.secret-toc a:hover,
.secret-toc a:focus-visible {
background: rgba(124, 255, 178, 0.1);
}
/* ---- traits ---- */
.secret-traits {
margin: 0 0 1.2rem;
display: grid;
gap: 0.55rem;
}
.secret-traits > div {
padding: 0.75rem 0.9rem;
border-radius: 8px;
background: rgba(18, 18, 28, 0.66);
}
.secret-traits dt {
font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
color: #7cffb2;
letter-spacing: 0.06em;
}
.secret-traits dd {
margin: 0.3rem 0 0;
line-height: 1.6;
font-size: 0.98rem;
}
/* ---- code + tables ---- */
.secret-code {
margin: 0 0 1.1rem;
padding: 0.85rem 1rem;
/* The only element here that can exceed the column, so it scrolls itself
* rather than making the page scroll sideways. */
overflow-x: auto;
border: 1px solid rgba(124, 255, 178, 0.22);
border-radius: 8px;
background: rgba(6, 10, 8, 0.8);
color: #9dffcb;
font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
font-size: 0.88rem;
line-height: 1.7;
white-space: pre;
}
.secret-page code {
padding: 0.05em 0.35em;
border-radius: 4px;
background: rgba(124, 255, 178, 0.1);
color: #9dffcb;
font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
font-size: 0.9em;
}
.secret-table-scroll {
margin: 0 0 1.2rem;
overflow-x: auto;
-webkit-overflow-scrolling: touch;
border: 1px solid rgba(178, 107, 255, 0.22);
border-radius: 8px;
}
.secret-table {
width: 100%;
min-width: 26rem;
border-collapse: collapse;
font-size: 0.92rem;
}
.secret-table th,
.secret-table td {
padding: 0.6rem 0.75rem;
text-align: left;
border-bottom: 1px solid rgba(255, 255, 255, 0.07);
}
.secret-table th {
color: #b26bff;
font-size: 0.78rem;
letter-spacing: 0.12em;
text-transform: uppercase;
white-space: nowrap;
}
.secret-table tr:last-child td {
border-bottom: none;
}
/* ---- figures ---- */
.secret-figure {
margin: 0 0 1.4rem;
padding: 0.9rem;
border: 1px solid rgba(124, 255, 178, 0.18);
border-radius: 10px;
background: rgba(10, 10, 16, 0.7);
}
.secret-figure svg {
width: 100%;
height: auto;
display: block;
}
.secret-figure figcaption {
margin-top: 0.6rem;
color: #a49fb6;
font-size: 0.85rem;
line-height: 1.55;
}
.secret-axis {
stroke: rgba(255, 255, 255, 0.25);
stroke-width: 1;
}
.secret-tick {
fill: #8f8aa3;
font-size: 9px;
font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
}
.secret-legend {
display: flex;
flex-wrap: wrap;
gap: 0.9rem;
margin-top: 0.5rem;
}
.secret-swatch {
display: inline-block;
width: 10px;
height: 10px;
border-radius: 2px;
vertical-align: middle;
}
/* ---- tricks ---- */
.secret-tricks {
margin: 0;
padding-left: 1.2rem;
display: grid;
gap: 0.85rem;
}
.secret-tricks li {
line-height: 1.68;
padding-left: 0.3rem;
}
.secret-tricks b {
color: #f0e9ff;
}
.secret-foot {
margin-top: 3.5rem;
padding-top: 1.2rem;
border-top: 1px solid rgba(255, 255, 255, 0.1);
color: #8f8aa3;
font-size: 0.9rem;
}
@media (max-width: 560px) {
.secret-page {
padding-left: max(0.9rem, env(safe-area-inset-left));
padding-right: max(0.9rem, env(safe-area-inset-right));
}
.secret-toc {
grid-template-columns: 1fr;
}
}

View File

@@ -0,0 +1,674 @@
// The Secret — /secret
//
// A hidden page. Nothing in the app links to it; you reach it by knowing the
// URL. It is left crawlable on purpose (see the sitemap), because a long,
// specific mechanics page is worth real search traffic, but it has no button
// and no nav tab.
//
// EVERY NUMBER ON THIS PAGE IS READ OUT OF THE SOURCE. Where a figure is
// derived, it was computed by running the real function rather than by
// re-deriving the formula by hand — a grimoire of plausible-sounding numbers
// would be worse than no grimoire, because it would be believed.
//
// English only, deliberately: the prose is written outside the i18n system
// rather than as several hundred translation keys. That keeps the voice sharp
// and keeps the en/es parity gate meaningful for the actual UI. If a Spanish
// grimoire is ever wanted it should be its own page with its own voice, not a
// machine translation of this one.
import { usePageMeta } from '../lib/pageMeta'
import './SecretPage.css'
// ---- data, lifted verbatim from the backend ------------------------------
/** app/passage.py: LAYERS + LAYER_ESSENCE. Sums to 53. */
const LAYERS = [
{ id: 'listen', essence: 3, trait: 'volatility', prompt: 'how steady is it?' },
{ id: 'name', essence: 5, trait: 'deceptiveness', prompt: 'does it answer straight?' },
{ id: 'unbind', essence: 8, trait: 'power', prompt: 'how hard does the tether pull?' },
{ id: 'open', essence: 12, trait: 'alignment', prompt: 'what wants to walk through?' },
{ id: 'release', essence: 25, trait: '—', prompt: 'does it go?' },
]
/** app/entities.py: rarity_weights_for_moon, normalised to percentages by
* running the real function at illumination 0.0 / 0.5 / 1.0. */
const RARITY = {
tiers: ['common', 'uncommon', 'rare', 'mythic'],
new: [55.0, 30.0, 12.0, 3.0],
half: [40.17, 31.73, 21.76, 6.35],
full: [25.11, 33.49, 31.66, 9.74],
}
/** app/passage.py: _twist_chance sampled every 0.05. */
const STEPS = Array.from({ length: 21 }, (_, i) => i * 0.05)
const COLLAPSE = STEPS.map((v) => (v <= 0.5 ? 0 : 0.45 * ((v - 0.5) / 0.5)))
const DECEIT = STEPS.map((v) => (v <= 0.55 ? 0 : 0.4 * ((v - 0.55) / 0.45)))
const VERDICTS = [
{ v: 'trust', right: 'alignment ≥ 0.5', essence: 12, favor: '+0.05', wrong: '−0.10' },
{ v: 'banish', right: 'alignment < 0.5', essence: 12, favor: '+0.05', wrong: '−0.05' },
{ v: 'cross_over', right: 'benevolent AND volatility > 0.6', essence: 25, favor: '+0.08', wrong: '0.00' },
{ v: 'test', right: 'a diagnostic pulse', essence: 0, favor: '0.00', wrong: '0.00' },
]
const LIMITS = [
['summon', '4 / 60s', '8 / 60s'],
['question', '6 / 60s', '12 / 60s'],
['ritual', '6 / 60s', '12 / 60s'],
['judgment', '10 / 60s', '20 / 60s'],
['passage', '20 / 60s', '40 / 60s'],
['fragment', '30 / 60s', '60 / 60s'],
['scry (camera)', '4 / 60s', '8 / 60s'],
]
const DROPS = [
['a correct judgment', '20%', 'trinket'],
['a successful ritual', '25%', 'relic'],
['summoning a rare', '15%', 'curio'],
['summoning a mythic', '35%', 'curio'],
]
// ---- chart primitives ----------------------------------------------------
//
// Hand-rolled SVG rather than a charting library: these are four small static
// figures, and a dependency would cost more bundle than the whole page.
function LineChart({
series,
caption,
}: {
series: { points: number[]; label: string; color: string }[]
caption: string
}) {
const W = 320
const H = 150
const PAD = 28
const x = (i: number) => PAD + (i / (STEPS.length - 1)) * (W - PAD * 2)
const y = (v: number) => H - PAD - (v / 0.5) * (H - PAD * 2)
return (
<figure className="secret-figure">
<svg viewBox={`0 0 ${W} ${H}`} role="img" aria-label={caption}>
<line x1={PAD} y1={H - PAD} x2={W - PAD} y2={H - PAD} className="secret-axis" />
<line x1={PAD} y1={PAD} x2={PAD} y2={H - PAD} className="secret-axis" />
{series.map((s) => (
<polyline
key={s.label}
fill="none"
stroke={s.color}
strokeWidth="2"
points={s.points.map((v, i) => `${x(i)},${y(v)}`).join(' ')}
/>
))}
<text x={PAD - 6} y={y(0.5) + 4} className="secret-tick" textAnchor="end">
50%
</text>
<text x={PAD - 6} y={y(0) + 4} className="secret-tick" textAnchor="end">
0
</text>
<text x={PAD} y={H - 8} className="secret-tick">
0.0
</text>
<text x={W - PAD} y={H - 8} className="secret-tick" textAnchor="end">
1.0
</text>
</svg>
<figcaption>
{caption}
<span className="secret-legend">
{series.map((s) => (
<span key={s.label}>
<span className="secret-swatch" style={{ background: s.color }} aria-hidden /> {s.label}
</span>
))}
</span>
</figcaption>
</figure>
)
}
function RarityChart() {
const W = 320
const H = 170
const PAD = 30
const groups = RARITY.tiers.length
const bandW = (W - PAD * 2) / groups
const rows = [
{ key: 'new', label: 'new moon', color: '#4a6b8a', data: RARITY.new },
{ key: 'half', label: 'half', color: '#7cffb2', data: RARITY.half },
{ key: 'full', label: 'full moon', color: '#b26bff', data: RARITY.full },
]
const max = 55
const barW = bandW / (rows.length + 1)
return (
<figure className="secret-figure">
<svg viewBox={`0 0 ${W} ${H}`} role="img" aria-label="rarity distribution by moon phase">
<line x1={PAD} y1={H - PAD} x2={W - PAD} y2={H - PAD} className="secret-axis" />
{RARITY.tiers.map((tier, gi) =>
rows.map((row, ri) => {
const v = row.data[gi]
const h = (v / max) * (H - PAD * 2)
return (
<rect
key={`${tier}-${row.key}`}
x={PAD + gi * bandW + ri * barW + barW / 2}
y={H - PAD - h}
width={barW - 1}
height={h}
fill={row.color}
/>
)
}),
)}
{RARITY.tiers.map((tier, gi) => (
<text
key={tier}
x={PAD + gi * bandW + bandW / 2}
y={H - PAD + 12}
className="secret-tick"
textAnchor="middle"
>
{tier}
</text>
))}
</svg>
<figcaption>
Chance of each rarity tier, by how much of the moon is lit. Computed by running
<code> rarity_weights_for_moon</code> and normalising.
<span className="secret-legend">
{rows.map((r) => (
<span key={r.key}>
<span className="secret-swatch" style={{ background: r.color }} aria-hidden /> {r.label}
</span>
))}
</span>
</figcaption>
</figure>
)
}
function LadderChart() {
const W = 320
const H = 150
const PAD = 30
const max = 25
const bandW = (W - PAD * 2) / LAYERS.length
return (
<figure className="secret-figure">
<svg viewBox={`0 0 ${W} ${H}`} role="img" aria-label="essence paid by each passage layer">
<line x1={PAD} y1={H - PAD} x2={W - PAD} y2={H - PAD} className="secret-axis" />
{LAYERS.map((l, i) => {
const h = (l.essence / max) * (H - PAD * 2)
return (
<g key={l.id}>
<rect
x={PAD + i * bandW + 4}
y={H - PAD - h}
width={bandW - 8}
height={h}
fill={l.id === 'release' ? '#b26bff' : '#7cffb2'}
/>
<text
x={PAD + i * bandW + bandW / 2}
y={H - PAD - h - 4}
className="secret-tick"
textAnchor="middle"
>
{l.essence}
</text>
<text
x={PAD + i * bandW + bandW / 2}
y={H - PAD + 12}
className="secret-tick"
textAnchor="middle"
>
{l.id}
</text>
</g>
)
})}
</svg>
<figcaption>Essence paid by each beat of the Passage. The five sum to 53.</figcaption>
</figure>
)
}
// ---- the page ------------------------------------------------------------
export function SecretPage() {
usePageMeta({
title: 'The Secret — Quantumancy',
description:
'The complete mechanics of Quantumancy: entity traits, the moon and rarity, the veil, the ritual, judgment, the Passage, favor, entropy, and the tricks only the source reveals.',
})
return (
<main className="secret-page">
<div className="secret-inner">
<header className="secret-head">
<p className="secret-eyebrow">unlisted</p>
<h1>The Secret</h1>
<p className="secret-lede">
Nothing links here. There is no button, and there never will be. What follows is
every mechanism the instrument actually runs on, with the real constants, read
straight out of the source rather than remembered.
</p>
<p className="secret-warn">
Read it once and the séance stops being weather and starts being a system you can
work. Some people would rather not know. This is your last exit.
</p>
</header>
<nav className="secret-toc" aria-label="contents">
<a href="#truth">I. The hidden truth</a>
<a href="#moon">II. The moon</a>
<a href="#veil">III. The veil</a>
<a href="#ritual">IV. The ritual</a>
<a href="#judgment">V. Judgment</a>
<a href="#passage">VI. The Passage</a>
<a href="#favor">VII. Favor, and its trap</a>
<a href="#entropy">VIII. Why you cannot cheat</a>
<a href="#economy">IX. The economy</a>
<a href="#tricks">X. The tricks</a>
</nav>
<section id="truth">
<h2>I. The hidden truth</h2>
<p>
Every spirit carries four numbers, each between 0 and 1. They are decided the
moment it is minted and they never change. Everything else in the game is a
question about these four, asked in a different way.
</p>
<dl className="secret-traits">
<div>
<dt>alignment</dt>
<dd>
whether it means harm. At or above <b>0.5</b> it is benevolent. This is the
single number a <code>trust</code> or <code>banish</code> verdict is graded
against.
</dd>
</div>
<div>
<dt>power</dt>
<dd>how strongly it is tethered here. Makes the ritual harder.</dd>
</div>
<div>
<dt>volatility</dt>
<dd>
how unstable it is. Above <b>0.6</b> it is <i>stuck</i> — and being stuck is
the precondition for crossing over. It also drives the Passage collapse.
</dd>
</div>
<div>
<dt>deceptiveness</dt>
<dd>
how much it lies. Above <b>0.55</b> it can lie to you during the Passage, and
it makes the ritual harder.
</dd>
</div>
</dl>
<p className="secret-note">
There is no second, secret set of stats. The ritual reveals these, the Passage
reveals these, and judgment grades against these. When a spirit lies, it does not
invent a number — it <b>mirrors</b> the real one: a hidden 0.8 is shown as 0.2. A
lie is therefore exactly recoverable once you know it was a lie.
</p>
</section>
<section id="moon">
<h2>II. The moon</h2>
<p>
The app computes the true lunar phase from the synodic month (29.530588853 days)
against a known new moon, not a calendar approximation. Illumination then skews
the rarity table. This is the single largest controllable effect in the game and
it costs you nothing but patience.
</p>
<RarityChart />
<p>
At new moon a mythic is a <b>3%</b> lottery. At full moon it is <b>9.74%</b> —
more than three times as likely. Rare goes <b>12% → 31.66%</b>. Together, rare or
better goes from 15% to <b>41.4%</b> of everything you summon.
</p>
<p className="secret-note">
It is a skew, never a gate. Every tier stays reachable on every night, by
deliberate design — a seeker who can only play on a Tuesday is never locked out.
But if you are choosing a night, choose a full one.
</p>
</section>
<section id="veil">
<h2>III. The veil</h2>
<p>
Call a channel you have called before and the same presence usually answers.
"Usually" is <b>72%</b>, and it is reduced by how thin the veil is:
</p>
<pre className="secret-code">
return_chance = 0.72 × (1 − thinness × 0.45)
</pre>
<p>
Thinness rises toward solar midnight — real solar midnight for your longitude, if
you granted location, not local clock midnight — and with the moon. If the
geomagnetic feed is live, the final figure is blended with real space weather:
</p>
<pre className="secret-code">thinness = thinness × 0.67 + geomagnetic_disturbance × 0.33</pre>
<p>
That disturbance is the NOAA planetary K-index, mapped from its 0–9 scale onto
0–1. A K of 5 or above is an official geomagnetic storm.
</p>
<p>
So a thin veil is <b>double-edged</b>, and this is the part most people get
backwards. A full moon at solar midnight during a storm drags the return chance
from 72% down to about <b>40%</b>. Good for meeting someone new. Bad for finding
someone again.
</p>
</section>
<section id="ritual">
<h2>IV. The ritual</h2>
<p>
Four steps, then one roll. Only <code>power</code> and <code>deceptiveness</code>{' '}
matter — a strong, cagey spirit is hardest to read. Volatility deliberately does
not count here.
</p>
<pre className="secret-code">
difficulty = (power + deceptiveness) / 2{'\n'}
chance = 0.65 − 0.35 × difficulty{'\n'}
floor = 0.30
</pre>
<p>
So the kindest spirit gives you <b>65%</b> and the worst possible one still gives
you <b>30%</b>. There is no unwinnable entity. Success reveals all four true
traits — the whole hidden truth, handed over.
</p>
<p className="secret-note">
Failing costs you nothing but the attempt, and you may attempt again. The milestone
pays <b>once per spirit</b> either way, so re-attempting after a success is
pointless for essence and useful only if you somehow missed the reveal.
</p>
</section>
<section id="judgment">
<h2>V. Judgment</h2>
<div className="secret-table-scroll">
<table className="secret-table">
<thead>
<tr>
<th>verdict</th>
<th>correct when</th>
<th>essence</th>
<th>favor</th>
<th>if wrong</th>
</tr>
</thead>
<tbody>
{VERDICTS.map((v) => (
<tr key={v.v}>
<td>
<code>{v.v}</code>
</td>
<td>{v.right}</td>
<td>{v.essence}</td>
<td>{v.favor}</td>
<td>{v.wrong}</td>
</tr>
))}
</tbody>
</table>
</div>
<p>
Note the asymmetry: a wrong <code>trust</code> costs <b>−0.10</b> favor, twice the
wrong <code>banish</code>. Trusting something malevolent is the reckless failure;
turning away something harmless is merely over-cautious. And a failed{' '}
<code>cross_over</code> costs <b>nothing</b> — it is read as a naive act of mercy,
not a blunder. Guessing cross_over is free.
</p>
<p className="secret-note">
<code>test</code> never touches the ledger, which is why it is the only verdict you
may repeat forever.
</p>
</section>
<section id="passage">
<h2>VI. The Passage</h2>
<p>
Five beats. Each opens one real trait, pays essence, and can go wrong.
</p>
<LadderChart />
<div className="secret-table-scroll">
<table className="secret-table">
<thead>
<tr>
<th>beat</th>
<th>reveals</th>
<th>essence</th>
<th>asks</th>
</tr>
</thead>
<tbody>
{LAYERS.map((l) => (
<tr key={l.id}>
<td>
<code>{l.id}</code>
</td>
<td>{l.trait}</td>
<td>{l.essence}</td>
<td>{l.prompt}</td>
</tr>
))}
</tbody>
</table>
</div>
<p>
Two things can go wrong, and both are linear ramps off a floor — below the floor
they are impossible, not merely unlikely.
</p>
<LineChart
caption="Twist chance against the underlying trait."
series={[
{ points: COLLAPSE, label: 'collapse (volatility)', color: '#b26bff' },
{ points: DECEIT, label: 'lie (deceptiveness)', color: '#7cffb2' },
]}
/>
<p>
<b>Collapse</b> is zero at or below volatility <b>0.5</b>, rising to <b>45%</b> at
1.0. It sends the rite back to <code>listen</code> — but your essence stays. It can
only strike <code>name</code>, <code>unbind</code> and <code>open</code>.
</p>
<p>
<b>Lying</b> is zero at or below deceptiveness <b>0.55</b>, rising to <b>40%</b>.
The lie is not marked when it happens. It is struck through one beat later, when
the rite catches it. If you are reading carefully, a reveal that contradicts what
the ritual already told you is a lie you can invert on the spot.
</p>
<p className="secret-note">
<code>listen</code> and <code>release</code> cannot collapse, and the reason is
load-bearing rather than cosmetic: crossing requires volatility above 0.6, so every
spirit that <i>can</i> cross is exactly a spirit with a high collapse chance. If
the final beat could collapse, the most crossable spirits would be the ones you
could least often finish. That would be backwards, so it is forbidden.
</p>
</section>
<section id="favor">
<h2>VII. Favor, and its trap</h2>
<p>
Favor runs −1.0 to +1.0 and it is not a score. It is a lens. When a new spirit is
minted, your favor bends its two <i>legibility</i> traits:
</p>
<pre className="secret-code">
shift = −0.12 × favor{'\n'}
volatility += shift{'\n'}
deceptiveness += shift
</pre>
<p>
High favor means calmer, more honest spirits: less collapse, fewer lies, easier
rituals. Alignment and power are untouched — favor changes how readable a spirit
is, never who it is.
</p>
<p className="secret-danger">
<b>And here is the trap, which almost nobody will find.</b> Crossing over requires
volatility strictly above <b>0.6</b>. At favor +1.0 every spirit you mint has its
volatility pulled down by 0.12. A spirit that would have rolled 0.65 — comfortably
crossable — arrives at <b>0.53</b>, and can no longer be crossed at all. You have
made the dead calmer, and in doing so you have locked yourself out of the largest
reward in the game: 25 essence and +0.08 favor, the most compassionate act
available to you.
</p>
<p>
Verified by running the real functions: at favor 0.0 that spirit is crossable; at
+0.5 it is not; at +1.0 it is not. If you are farming crossings, <b>do not</b>{' '}
maximise favor. Sit nearer the middle.
</p>
</section>
<section id="entropy">
<h2>VIII. Why you cannot cheat</h2>
<p>
The randomness is not <code>Math.random()</code>. Your device harvests physical
noise — the microphone's noise floor, RF hiss between stations — and that sample
is Von Neumann debiased, then conditioned through SHA-256 before it ever leaves the
page.
</p>
<p>
On the server it is mixed by HMAC with a secret generated fresh at boot, and each
draw is domain-separated by a context string, so the collapse draw and the lie draw
on the same beat are mathematically unrelated.
</p>
<p className="secret-note">
The consequence is precise and worth understanding: a client can only ever <b>add</b>{' '}
unpredictability. Sending a chosen or replayed entropy value cannot steer any
outcome, because your contribution is never a seed on its own — it is one input to
a keyed hash whose key you do not have. The room genuinely participates. It just
cannot be told what to say.
</p>
<p>
The same idea drives the spirits' words: the language model is given a fixed{' '}
<code>seed</code>, which pins its sampling — so the noise in your room, not the
model's own dice, selects which words come out.
</p>
</section>
<section id="economy">
<h2>IX. The economy</h2>
<p>
Every essence source, exhaustively: <b>2</b> per summon, <b>15</b> for a completed
ritual, <b>12</b> for a correct trust or banish, <b>25</b> for a correct cross_over,
and <b>3/5/8/12/25</b> for the Passage beats.
</p>
<div className="secret-table-scroll">
<table className="secret-table">
<thead>
<tr>
<th>drops after</th>
<th>chance</th>
<th>kind</th>
</tr>
</thead>
<tbody>
{DROPS.map((d) => (
<tr key={d[0]}>
<td>{d[0]}</td>
<td>{d[1]}</td>
<td>{d[2]}</td>
</tr>
))}
</tbody>
</table>
</div>
<p>
Note that a mythic summon drops at <b>35%</b> — the highest rate in the game, and
another reason the full moon matters.
</p>
<p className="secret-note">
Every milestone pays <b>once per spirit, permanently</b>. Not once per session, not
once per connection: there is a durable ledger, so re-walking a rite, reconnecting,
or opening a second tab pays nothing further. Re-walking still reveals — it just
does not mint. The only way to earn again is a genuinely new presence.
</p>
<div className="secret-table-scroll">
<table className="secret-table">
<thead>
<tr>
<th>action</th>
<th>per account</th>
<th>per address</th>
</tr>
</thead>
<tbody>
{LIMITS.map((l) => (
<tr key={l[0]}>
<td>{l[0]}</td>
<td>{l[1]}</td>
<td>{l[2]}</td>
</tr>
))}
</tbody>
</table>
</div>
</section>
<section id="tricks">
<h2>X. The tricks</h2>
<ol className="secret-tricks">
<li>
<b>Hunt on a full moon.</b> Rare-or-better goes from 15% to 41.4% of summons. It
is the largest lever you have and it is free.
</li>
<li>
<b>Do not max your favor if you want crossings.</b> Above roughly +0.4 you start
pulling spirits under the 0.6 volatility gate and making them uncrossable. This
is the single most counter-intuitive fact in the game.
</li>
<li>
<b>Guessing cross_over is free.</b> A failed cross_over costs zero favor. If a
spirit reads benevolent and unstable, always try it before settling for trust.
</li>
<li>
<b>Run the ritual before judging.</b> Success hands you all four true numbers,
which turns every subsequent verdict from a read into a lookup.
</li>
<li>
<b>A thin veil is for meeting, not returning.</b> Solar midnight, full moon and a
geomagnetic storm cut your chance of reaching a known spirit again by nearly half.
Do your reunions in daylight on a quiet sky.
</li>
<li>
<b>Catch the lie by contradiction.</b> Lies are mirrored, not random. If a
Passage reveal disagrees with what the ritual already showed you, invert it and
you have the truth exactly.
</li>
<li>
<b>A spirit under 0.55 deceptiveness cannot lie to you at all.</b> Not "rarely" —
the chance is exactly zero. The same is true of collapse under 0.5 volatility.
Once the ritual shows you those numbers, you know precisely which risks are off
the table.
</li>
<li>
<b>Wrong banish is cheaper than wrong trust.</b> Half the penalty. When you are
genuinely torn and the stakes are favor, err toward caution.
</li>
<li>
<b>Grant location.</b> Without it, solar midnight cannot be computed for you and
you lose that lever on the veil entirely.
</li>
<li>
<b>Feed the room.</b> The microphone and the sensors are not decoration — they
are the actual entropy source. A silent, shielded room gives the summoning less
to work with.
</li>
</ol>
</section>
<footer className="secret-foot">
<p>
Every figure here was read from the source or produced by running the real
function. Where the code changes, this page will be wrong before anyone notices —
so trust the séance over the grimoire if they ever disagree.
</p>
</footer>
</div>
</main>
)
}