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:
@@ -94,6 +94,13 @@ async def sitemap(db: AsyncSession = Depends(get_db)) -> Response:
|
|||||||
"<priority>0.9</priority></url>",
|
"<priority>0.9</priority></url>",
|
||||||
f"<url><loc>{base}/shop</loc><changefreq>weekly</changefreq>"
|
f"<url><loc>{base}/shop</loc><changefreq>weekly</changefreq>"
|
||||||
"<priority>0.7</priority></url>",
|
"<priority>0.7</priority></url>",
|
||||||
|
# /secret is UNLISTED, not private: nothing in the app links to it and
|
||||||
|
# it has no nav tab, so a seeker only finds it by being told. It is
|
||||||
|
# advertised here anyway because it is a long, specific mechanics page
|
||||||
|
# and search traffic is the whole reason it is crawlable — "hidden"
|
||||||
|
# here means "no button", not "no index".
|
||||||
|
f"<url><loc>{base}/secret</loc><changefreq>monthly</changefreq>"
|
||||||
|
"<priority>0.8</priority></url>",
|
||||||
]
|
]
|
||||||
for entity_id, discovered_at in entities:
|
for entity_id, discovered_at in entities:
|
||||||
loc = _xml_escape(f"{base}/codex/{entity_id}")
|
loc = _xml_escape(f"{base}/codex/{entity_id}")
|
||||||
|
|||||||
@@ -19,6 +19,7 @@ import { LandingPage } from './pages/LandingPage'
|
|||||||
import { MessagesPage } from './pages/MessagesPage'
|
import { MessagesPage } from './pages/MessagesPage'
|
||||||
import { ProfilePage } from './pages/ProfilePage'
|
import { ProfilePage } from './pages/ProfilePage'
|
||||||
import { SeancePage } from './pages/SeancePage'
|
import { SeancePage } from './pages/SeancePage'
|
||||||
|
import { SecretPage } from './pages/SecretPage'
|
||||||
import { ShopPage } from './pages/ShopPage'
|
import { ShopPage } from './pages/ShopPage'
|
||||||
|
|
||||||
function App() {
|
function App() {
|
||||||
@@ -52,6 +53,9 @@ function App() {
|
|||||||
<Route path="/hunters/:username" element={<HunterPage />} />
|
<Route path="/hunters/:username" element={<HunterPage />} />
|
||||||
<Route path="/messages" element={<MessagesPage />} />
|
<Route path="/messages" element={<MessagesPage />} />
|
||||||
<Route path="/messages/:username" 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 />} />
|
<Route path="*" element={<LandingPage />} />
|
||||||
</Routes>
|
</Routes>
|
||||||
</BrowserRouter>
|
</BrowserRouter>
|
||||||
|
|||||||
@@ -101,7 +101,12 @@ describe('MobileNav', () => {
|
|||||||
p !== '*' && // catch-all, not a destination
|
p !== '*' && // catch-all, not a destination
|
||||||
!p.includes(':') && // detail views, reached through their index
|
!p.includes(':') && // detail views, reached through their index
|
||||||
p !== '/' && // the landing page is the way in, not a tab
|
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))
|
const covered = new Set(NAV_ITEMS.map((i) => i.to))
|
||||||
|
|||||||
279
frontend/src/pages/SecretPage.css
Normal file
279
frontend/src/pages/SecretPage.css
Normal 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
674
frontend/src/pages/SecretPage.tsx
Normal file
674
frontend/src/pages/SecretPage.tsx
Normal 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>
|
||||||
|
)
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user