Files
qtalker---/frontend/src/i18n/en.json
Indiana a52a781880 feat(frontend): ritual mini-game, judgment UI, and consequences (Workstream E)
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>
2026-07-23 11:29:29 +00:00

428 lines
16 KiB
JSON
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
{
"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…"
}
}
}