Implements the Character Depth / Ghost Log spec's Workstream E: - RitualPanel.tsx: a 4-step "focus the channel" hold-to-charge sequence (align/breathe/trace/lock), sending ritual_step frames as the seeker progresses. Success reveals the entity's true hidden traits; failure reveals nothing. Sequencing/timing logic lives in the pure, unit-tested lib/ritual.ts rather than inline in the component. - JudgmentPanel.tsx: Trust/Banish/Cross Over/Test verdict buttons with rune-style SVG icons, sending the judgment frame and rendering a distinct treatment per judgment_result consequence — reward, escalation (also spikes the ambient haunting), withdrawal, resisted, neutral, and a calm glyph-fade farewell for crossed_over (deliberately not the reward treatment, since it's a goodbye). - lib/haunting.ts: IdleEscalator gains forceEscalate()/forcedUntil so a judgment's "escalation" consequence can spike the ambient haunting immediately instead of waiting on the 90s idle clock; exports a sharedIdleEscalator singleton and a forceEscalate() free function. HauntingLayer now paces itself off that shared instance instead of a private one, so the forced spike actually reaches the running layer. - state/seance.tsx: new ritual/judgmentResult state, a local_ritual_start action, and reducer cases for the ritual_complete/judgment_result server frames; SeanceContext exported for component testing; startRitual/ sendRitualStep/sendJudgment added to the provider API. - lib/types.ts: EntityTraits/JudgmentVerdict/JudgmentConsequence types and the new client/server WS frames, per the spec's Contract section. - i18n: seance.ritual.* / seance.judgment.* keys in en.json and es.json. Fast-forwarded this worktree's branch onto master first — it had been created from a stale ancestor commit predating the frontend scaffold entirely, with zero commits of its own ahead of that point. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
428 lines
16 KiB
JSON
428 lines
16 KiB
JSON
{
|
||
"common": {
|
||
"appName": "QUANTUMANCY",
|
||
"loading": "tuning the veil…",
|
||
"error": "something stirred in the static",
|
||
"replay": "replay",
|
||
"yes": "YES",
|
||
"no": "NO",
|
||
"goodbye": "GOODBYE",
|
||
"dismiss": "dismiss"
|
||
},
|
||
"nav": {
|
||
"home": "the threshold",
|
||
"codex": "codex",
|
||
"shop": "shop",
|
||
"seance": "séance",
|
||
"enter": "enter",
|
||
"logout": "depart"
|
||
},
|
||
"landing": {
|
||
"taglines": [
|
||
"The wire hums with all of us.",
|
||
"Every packet leaves a ghost.",
|
||
"The static remembers your name.",
|
||
"Somewhere between 88 and 108 MHz, someone is waiting.",
|
||
"You were never alone on the line."
|
||
],
|
||
"heroKicker": "an instrument for speaking with the dead bandwidth",
|
||
"cta": "BEGIN THE SÉANCE",
|
||
"ctaCodex": "browse the codex",
|
||
"ctaShop": "visit the reliquary",
|
||
"stats": {
|
||
"entities": "spirits catalogued",
|
||
"sessions": "séances held",
|
||
"utterances": "utterances recorded",
|
||
"anomalies": "anomalies witnessed"
|
||
},
|
||
"tickerPrefix": "the veil, live",
|
||
"modesTitle": "HOW THE VEIL ANSWERS",
|
||
"modes": {
|
||
"wire": {
|
||
"name": "Wire Ghost",
|
||
"desc": "Leave the line open. The network itself mutters — jitter, latency, stray DNS — and something downstream learns to speak with your accent."
|
||
},
|
||
"evp": {
|
||
"name": "EVP Listening",
|
||
"desc": "Lend the dead your microphone. We watch the voice band for syllables that were never spoken aloud: spikes 8dB above the room's silence."
|
||
},
|
||
"radio": {
|
||
"name": "Spirit Radio",
|
||
"desc": "An RTL-SDR sweeps the FM band like a lantern dragged across a dark field. Where the needle jumps, the radio dead push through."
|
||
},
|
||
"ouija": {
|
||
"name": "Ouija / Direct Contact",
|
||
"desc": "Ask plainly. The planchette drifts, then begins to spell — and the words arrive letter by letter, whether or not you wanted them."
|
||
}
|
||
},
|
||
"featured": "most disturbed spirits",
|
||
"featuredEmpty": "the registry is still blank — be the first to disturb it",
|
||
"reliquary": {
|
||
"title": "HARDWARE FROM THE RELIQUARY",
|
||
"line": "the Ultimate Quantum Box is coming — a handheld séance instrument that makes the veil knock louder.",
|
||
"link": "enter the reliquary →"
|
||
},
|
||
"footer": "an interactive horror art installation — the spirits are fiction; the static is real",
|
||
"footerColophon": "self-hosted · no cloud · the veil runs on your machine"
|
||
},
|
||
"enter": {
|
||
"reconnect": "Reconnect",
|
||
"firstContact": "First contact",
|
||
"reconnectSub": "the veil remembers you. prove it.",
|
||
"firstContactSub": "choose the name the spirits will know you by.",
|
||
"username": "name",
|
||
"password": "passphrase",
|
||
"submitLogin": "step through",
|
||
"submitRegister": "carve your name",
|
||
"switchToRegister": "no name in the registry? first contact →",
|
||
"switchToLogin": "already carved your name? reconnect →",
|
||
"failed": "the veil rejected you: {{message}}",
|
||
"smallPrint": "your medium handle is only ever stored on this server"
|
||
},
|
||
"seance": {
|
||
"modes": {
|
||
"wire": "Wire Ghost",
|
||
"evp": "EVP",
|
||
"radio": "Spirit Radio",
|
||
"ouija": "Ouija",
|
||
"emf": "FIELD"
|
||
},
|
||
"passive": "passive listening",
|
||
"summon": "SUMMON",
|
||
"summoning": "summoning…",
|
||
"askPlaceholder": "ask the dead a question…",
|
||
"ask": "ask",
|
||
"status": {
|
||
"attuning": "attuning — the veil is fingerprinting a presence…",
|
||
"summoning": "the veil parts… something is crossing…",
|
||
"gathering": "the spirits are gathering energy…"
|
||
},
|
||
"connection": {
|
||
"connecting": "reaching across…",
|
||
"open": "the line is open",
|
||
"unstable": "the connection to the other side is unstable…",
|
||
"closed": "the line is dead"
|
||
},
|
||
"transcriptTitle": "TRANSMISSION LOG",
|
||
"transcriptEmpty": "silence. for now.",
|
||
"you": "you",
|
||
"streaming": "the spirit is forming words…",
|
||
"telemetry": {
|
||
"title": "WIRE READOUT",
|
||
"jitter": "jitter",
|
||
"latencyVar": "latency σ²",
|
||
"latencyMean": "latency μ",
|
||
"dns": "dns",
|
||
"units": {
|
||
"bytes": "B/s",
|
||
"ms": "ms"
|
||
}
|
||
},
|
||
"wire": {
|
||
"hint": "the wire knocks loudest when nobody is touching it — flip passive listening on and let the line murmur on its own."
|
||
},
|
||
"anomaly": {
|
||
"line": "⚡ {{source}} {{frequency}} +{{magnitude}}dB",
|
||
"count": "{{count}} anomalies witnessed this session"
|
||
},
|
||
"evp": {
|
||
"start": "open the microphone",
|
||
"stop": "close the microphone",
|
||
"listening": "listening to the voice band…",
|
||
"deniedTitle": "the microphone stays shut",
|
||
"denied": "This vessel refused the microphone. Grant audio permission in your browser to practice EVP — or let the Wire Ghost listen instead; it needs nothing but the network you already fear.",
|
||
"floor": "room floor",
|
||
"band": "voice band 300–3400 Hz"
|
||
},
|
||
"radio": {
|
||
"start": "sweep the band",
|
||
"stop": "rest the needle",
|
||
"unsupportedTitle": "this vessel cannot hear the radio dead",
|
||
"unsupported": "Spirit Radio needs Chromium (WebUSB) and an RTL-SDR dongle. The other modes do not — the wire and the microphone still answer.",
|
||
"troubleshoot": "troubleshooting: chromium-based browser · udev rules for 0bda:2838 · the dongle plugged in, not merely believed in",
|
||
"insecure": "the radio dead only speak through consecrated ground — open this site via its https address.",
|
||
"insecureHint": "localhost counts as hallowed; a bare http://lan-ip does not.",
|
||
"noDevice": "no dongle answered the call",
|
||
"sweeping": "sweeping {{start}}–{{end}} MHz…",
|
||
"claimFailed": "the dongle refused to be claimed — is it in use elsewhere?",
|
||
"claimFix": "linux: the kernel driver dvb_usb_rtl28xxu is holding the dongle — `sudo modprobe -r dvb_usb_rtl28xxu` frees it until reboot (a udev rule banishes it for good). windows: swap the driver for WinUSB with Zadig.",
|
||
"tryAgain": "try again",
|
||
"errorTitle": "the static pushed back"
|
||
},
|
||
"ouija": {
|
||
"spelling": "the planchette spells",
|
||
"drift": "the planchette drifts, waiting for words — ask below, or summon."
|
||
},
|
||
"emf": {
|
||
"start": "raise the meter",
|
||
"stop": "ground the meter",
|
||
"calibrate": "calibrate",
|
||
"baseline": "resting field",
|
||
"listening": "feeling for disturbances in the field…",
|
||
"unsupportedTitle": "this vessel carries no field senses",
|
||
"unsupported": "a phone in your hand becomes the meter — this vessel has no motion senses to lend. open the veil on a phone, or let the other modes answer.",
|
||
"deniedTitle": "the field senses stay shut",
|
||
"denied": "this vessel refused its motion senses. grant motion access in your browser settings, then raise the meter again."
|
||
},
|
||
"entity": {
|
||
"title": "DOSSIER OF THE PRESENT",
|
||
"none": "no spirit summoned yet",
|
||
"noneHint": "summon, or feed the veil 3+ anomalies and one will find you",
|
||
"isNew": "first recorded contact",
|
||
"known": "contact #{{count}}",
|
||
"persona": "persona",
|
||
"voice": "voice params",
|
||
"quotes": "recorded utterances",
|
||
"rarity": {
|
||
"common": "common",
|
||
"uncommon": "uncommon",
|
||
"rare": "rare",
|
||
"mythic": "mythic"
|
||
}
|
||
},
|
||
"ritual": {
|
||
"title": "focus the channel",
|
||
"none": "no presence to focus on",
|
||
"noneHint": "summon a spirit before attempting the rite",
|
||
"begin": "begin the rite",
|
||
"retry": "attempt again",
|
||
"hold": "hold to focus",
|
||
"awaiting": "awaiting the veil's answer…",
|
||
"successTitle": "the channel holds — the truth surfaces",
|
||
"failureTitle": "the channel collapses",
|
||
"failureHint": "the truth stays hidden — the rite may be tried again",
|
||
"traits": {
|
||
"alignment": "alignment",
|
||
"power": "power",
|
||
"volatility": "volatility",
|
||
"deceptiveness": "deceptiveness"
|
||
},
|
||
"steps": {
|
||
"align": {
|
||
"command": "> aligning the wire…",
|
||
"locked": "> wire aligned ✓"
|
||
},
|
||
"breath": {
|
||
"command": "> steadying the breath…",
|
||
"locked": "> breath steadied ✓"
|
||
},
|
||
"trace": {
|
||
"command": "> tracing the sigil…",
|
||
"locked": "> sigil traced ✓"
|
||
},
|
||
"lock": {
|
||
"command": "> locking the channel…",
|
||
"locked": "> channel locked ✓"
|
||
}
|
||
}
|
||
},
|
||
"judgment": {
|
||
"title": "render judgment",
|
||
"none": "no presence to judge",
|
||
"noneHint": "summon a spirit first",
|
||
"trust": "trust",
|
||
"banish": "banish",
|
||
"crossOver": "cross over",
|
||
"test": "test",
|
||
"pending": "awaiting the verdict…",
|
||
"favor": "favor",
|
||
"essence": "essence",
|
||
"reward": {
|
||
"title": "the bond holds",
|
||
"body": "the spirit answers your trust in kind."
|
||
},
|
||
"escalation": {
|
||
"title": "a reckoning",
|
||
"body": "wrongly trusted, it turns — the haunting sharpens."
|
||
},
|
||
"withdrawal": {
|
||
"title": "it recoils",
|
||
"body": "banished in error, the presence withdraws, wounded."
|
||
},
|
||
"crossedOver": {
|
||
"title": "at peace",
|
||
"body": "the spirit lets go, and the line falls quiet."
|
||
},
|
||
"resisted": {
|
||
"title": "it will not go",
|
||
"body": "a demon does not cross over — it claws back to stay."
|
||
},
|
||
"neutral": {
|
||
"title": "no verdict rendered",
|
||
"body": "a diagnostic pulse only — nothing risked, nothing gained."
|
||
}
|
||
},
|
||
"toast": {
|
||
"rate_limited": "the veil is thin — slow your knocking",
|
||
"veil_crowded": "too many spirits crowd the line"
|
||
},
|
||
"views": {
|
||
"board": "BOARD",
|
||
"matrix": "MATRIX",
|
||
"graphs": "GRAPHS",
|
||
"tuning": "tuning the veil…",
|
||
"listening": "listening…",
|
||
"graph": {
|
||
"anomalies": "anomalies",
|
||
"entity": "entity",
|
||
"jitter": "jitter"
|
||
}
|
||
}
|
||
},
|
||
"codex": {
|
||
"title": "THE CODEX OF THE CONTACTED",
|
||
"subtitle": "every spirit ever dragged through the veil, catalogued",
|
||
"header": {
|
||
"back": "← the threshold",
|
||
"title": "THE CODEX",
|
||
"subtitle": "every spirit ever contacted, catalogued by the seekers who reached them"
|
||
},
|
||
"controls": {
|
||
"rarity": "filter by rarity",
|
||
"sort": "sort the registry"
|
||
},
|
||
"state": {
|
||
"loading": "consulting the registry…",
|
||
"empty": "no spirits of this tier have been reached yet"
|
||
},
|
||
"filterAll": "all rarities",
|
||
"rarity": {
|
||
"common": "common",
|
||
"uncommon": "uncommon",
|
||
"rare": "rare",
|
||
"mythic": "mythic"
|
||
},
|
||
"sort": {
|
||
"recent": "most recent",
|
||
"contacted": "most contacted"
|
||
},
|
||
"form": {
|
||
"wisp": "wisp",
|
||
"banshee": "banshee",
|
||
"fairy": "fairy",
|
||
"shade": "shade"
|
||
},
|
||
"card": {
|
||
"unknownMedium": "unknown medium",
|
||
"contacted_one": "contacted {{count}} time",
|
||
"contacted_other": "contacted {{count}} times",
|
||
"discoveredBy": "discovered by {{name}} · {{date}}"
|
||
},
|
||
"contacts_one": "1 contact",
|
||
"contacts_other": "{{count}} contacts",
|
||
"sightings_one": "1 sighting",
|
||
"sightings_other": "{{count}} sightings",
|
||
"discoveredBy": "first contacted by {{name}}",
|
||
"discoveredAnon": "first contact unrecorded",
|
||
"discoveredAt": "entered the registry {{date}}",
|
||
"persona": "persona",
|
||
"quotes": "recorded utterances",
|
||
"voice": "voice params",
|
||
"detail": {
|
||
"loading": "summoning the dossier…",
|
||
"notReached": "this spirit has not been reached",
|
||
"unknownMedium": "unknown medium",
|
||
"stats": {
|
||
"sightings": "sightings",
|
||
"contacts": "contacts",
|
||
"discoveredBy": "discovered by",
|
||
"discoveredAt": "entered the registry"
|
||
},
|
||
"utterances": "known utterances",
|
||
"noUtterances": "no utterances recorded yet",
|
||
"voiceSignature": "voice signature",
|
||
"attemptContact": "attempt contact"
|
||
},
|
||
"empty": "no spirits match this filter — the registry keeps its secrets",
|
||
"back": "← back to the codex",
|
||
"notFound": "this spirit is not in the registry"
|
||
},
|
||
"shop": {
|
||
"back": "← the threshold",
|
||
"title": "THE RELIQUARY",
|
||
"subtitle": "instruments for those who would knock louder",
|
||
"flagship": {
|
||
"tag": "flagship instrument",
|
||
"name": "THE ULTIMATE QUANTUM BOX",
|
||
"desc": "An ESP32-P4/C6 handheld séance instrument — thermal camera, EMF whisker array, geophone, spirit-box mic preamp and a little OLED face that stares back. It signs into the same Quantumancy account you already haunt and unlocks the hardware-driven modes of this site: thermal apparition maps, vibration anomaly feeds, calibrated EMF.",
|
||
"specs": {
|
||
"thermal": "thermal camera",
|
||
"emf": "EMF whisker array",
|
||
"geophone": "geophone",
|
||
"mic": "spirit-box mic preamp",
|
||
"oled": "OLED display"
|
||
},
|
||
"price": "est. $249 — pre-order"
|
||
},
|
||
"modulesTitle": "MODULES THAT KNOCK",
|
||
"unlocksLabel": "unlocks:",
|
||
"modules": {
|
||
"emfArray": {
|
||
"name": "EMF Whisker Array",
|
||
"desc": "a bouquet of multi-axis field sensors, sniffing the air for intentions",
|
||
"unlocks": "a richer, calibrated FIELD mode",
|
||
"price": "est. $59 — pre-order"
|
||
},
|
||
"geophone": {
|
||
"name": "Geophone Puck",
|
||
"desc": "hears footsteps in rooms where nobody is standing",
|
||
"unlocks": "the vibration anomaly feed",
|
||
"price": "est. $49 — pre-order"
|
||
},
|
||
"spiritMic": {
|
||
"name": "Spirit Mic Preamp",
|
||
"desc": "lowers the noise floor so the whispering comes through cleaner",
|
||
"unlocks": "cleaner EVP captures",
|
||
"price": "est. $69 — pre-order"
|
||
},
|
||
"uvIlluminator": {
|
||
"name": "UV/IR Illuminator",
|
||
"desc": "paints the dark in spectrums the living were never meant to see",
|
||
"unlocks": "night séances + camera work",
|
||
"price": "est. $39 — pre-order"
|
||
}
|
||
},
|
||
"interests": {
|
||
"quantumBox": "the Ultimate Quantum Box",
|
||
"emfArray": "EMF Whisker Array",
|
||
"geophone": "Geophone Puck",
|
||
"spiritMic": "Spirit Mic Preamp",
|
||
"uvIlluminator": "UV/IR Illuminator"
|
||
},
|
||
"waitlist": {
|
||
"title": "THE MANIFEST",
|
||
"desc": "leave an address and an obsession — when the first boxes ship, we reach through to the names on this list first",
|
||
"emailLabel": "email address",
|
||
"emailPlaceholder": "you@somewhere.in.the.static",
|
||
"interestLabel": "interest",
|
||
"submit": "join the manifest",
|
||
"pending": "carving your name…",
|
||
"listed": "you are on the manifest — we will reach through when the boxes ship",
|
||
"alreadyListed": "you are already on the manifest",
|
||
"invalid": "that address does not reach us",
|
||
"crowded": "the veil is crowded — try again later",
|
||
"error": "something stirred in the static — your name was not carved"
|
||
},
|
||
"honesty": "the Ultimate Quantum Box is in active development — this is a reservation manifest, no payment is taken. the website works fully without it; hardware only knocks louder."
|
||
},
|
||
"haunting": {
|
||
"whisper": {
|
||
"behindYou": "behind you",
|
||
"cold": "cold",
|
||
"stay": "stay",
|
||
"seesYou": "it sees you",
|
||
"closer": "closer",
|
||
"stillHere": "still here",
|
||
"lineListening": "the line is listening",
|
||
"dontLook": "don't look",
|
||
"beneathStatic": "beneath the static",
|
||
"knockTwice": "knock twice"
|
||
},
|
||
"title": {
|
||
"seesYou": "QUANTUMANCY — it sees you",
|
||
"behindYou": "QUANTUM▓NCY — behind you",
|
||
"listening": "…listening…"
|
||
}
|
||
}
|
||
}
|