Add EMF field mode, Armory shop/waitlist, and ambient haunting layer

Three self-contained features, verified complete and cross-wired
end-to-end (audited: backend 54/54 tests, frontend 110/110 tests,
tsc --noEmit clean, i18n coverage script clean):

- EMF mode: DeviceMotion/DeviceOrientation-based field-meter sensing,
  a fifth séance channel alongside Wire/EVP/Radio/Ouija, with its own
  fragment prompt persona and full frontend gauge UI.
- Armory (shop/waitlist): pre-order capture page for the future
  Ultimate Quantum Box hardware line, rate-limited public endpoint,
  explicitly no payment collection.
- Haunting layer: ambient possession effects (dread-bed audio, title
  glitching, idle-paced whispers/manifests), respects
  prefers-reduced-motion, mounted once at the app root.

Plus WebUSB robustness fixes in lib/sdr.ts (Terratec vendor ID,
explicit selectConfiguration, isSecureContext gate).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013PphXq1s43DNRj1uWKGXof
This commit is contained in:
Indiana
2026-07-21 02:28:14 +00:00
parent c372427ced
commit 0756e677b9
33 changed files with 3148 additions and 48 deletions

View File

@@ -749,14 +749,16 @@
.evp-panel,
.radio-panel,
.ouija-panel,
.wire-panel {
.wire-panel,
.emf-panel {
display: flex;
flex-direction: column;
gap: 0.55rem;
}
.evp-scope,
.radio-waterfall {
.radio-waterfall,
.emf-gauge {
width: 100%;
border: 1px solid rgba(124, 255, 178, 0.18);
border-radius: 4px;
@@ -772,8 +774,13 @@
height: 110px;
}
.emf-gauge {
height: 140px;
}
.evp-readout,
.radio-readout {
.radio-readout,
.emf-readout {
display: flex;
justify-content: space-between;
align-items: baseline;
@@ -798,6 +805,70 @@
color: rgba(211, 233, 219, 0.75);
}
/* emf */
.emf-level {
color: #7cffb2;
font-size: 1.5rem;
text-shadow: 0 0 14px rgba(124, 255, 178, 0.65);
font-variant-numeric: tabular-nums;
}
.emf-baseline {
font-size: 0.72rem;
letter-spacing: 0.08em;
font-variant-numeric: tabular-nums;
}
.emf-listening {
margin: 0;
display: flex;
align-items: center;
gap: 0.5rem;
font-size: 0.76rem;
color: rgba(211, 233, 219, 0.75);
}
.emf-actions {
display: flex;
align-items: center;
gap: 0.6rem;
margin-top: 0.7rem;
}
.emf-actions .panel-action {
margin-top: 0;
}
/* smaller secondary control (calibrate, try again) */
.panel-subaction {
align-self: flex-start;
background: transparent;
border: 1px solid rgba(124, 255, 178, 0.3);
border-radius: 3px;
color: rgba(124, 255, 178, 0.75);
font-family: 'IBM Plex Mono', monospace;
font-size: 0.7rem;
letter-spacing: 0.12em;
text-transform: uppercase;
padding: 0.32rem 0.7rem;
cursor: pointer;
transition: box-shadow 150ms ease, color 150ms ease;
}
.panel-subaction:hover:not(:disabled) {
color: #7cffb2;
box-shadow: 0 0 10px rgba(124, 255, 178, 0.25);
}
.panel-subaction:disabled {
opacity: 0.35;
cursor: not-allowed;
}
.panel-notice .panel-subaction {
margin-top: 0.2rem;
}
.rec-dot {
width: 8px;
height: 8px;