Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/apursley2012/mystery-haunting/llms.txt

Use this file to discover all available pages before exploring further.

The Séance Circle page (route: /contact) transforms the humble contact form into a fully illustrated ouija board experience. The page title reads “SÉANCE CIRCLE” with the subtitle “Communicate with the developer.” The board itself is rendered on an antique parchment background (#d4c4a8) overlaid with a diagonal hatch pattern, bordered with an 8px #2d1b15 dark-wood edge, and it displays the classic ouija layout: an arc of 26 alphabet letters across the upper face, YES and NO in the corners, a row of digits 1234567890 in the middle, and GOODBYE at the bottom. As the visitor types their name into the form below, a teardrop-shaped planchette SVG glides across the board on a spring animation, pausing over each successive letter — channelling the séance energy of the message before it is sent into the void.

Form Fields

FieldLabelTypeRequired
nameYour Name (to guide the planchette)Text inputYes
messageYour MessageTextareaYes

Planchette Animation Mechanics

1

Map each typed character to an alphabet index

When the user types into the name field, the component reads the last character of the current value and looks it up in an A–Z array (index 0–25). This index determines which letter on the arc the planchette should travel toward next.
const lastChar = name.slice(-1).toUpperCase();
const alphabetIndex = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'.indexOf(lastChar);
2

Calculate letter position on the arc

Each letter’s position on the ouija arc is computed using a polar-to-Cartesian conversion. The 26 letters are distributed evenly across the arc, and a fixed radius of 150px from the arc’s origin determines how far from centre each letter sits. The y value is offset by +50 to shift the arc’s baseline downward into the board face.
const angle = (alphabetIndex / 25) * Math.PI - Math.PI / 2;
const x = Math.sin(angle) * 150;
const y = -Math.cos(angle) * 150 + 50;
3

Spring-animate the planchette to the target position

Framer Motion’s animate prop drives the planchette’s x, y, and rotate values toward the computed letter coordinates. A spring transition with stiffness: 100 gives the movement a weighted, otherworldly glide rather than a sharp mechanical jump.
<motion.div
  animate={{ x, y, rotate: angle * (180 / Math.PI) }}
  transition={{ type: 'spring', stiffness: 100 }}
>
  {/* Planchette SVG */}
</motion.div>
4

Slide down to GOODBYE on submit

When the form is submitted, the planchette animates to y: 150 — drifting downward toward the GOODBYE text at the bottom of the board — over 1.5 seconds with an ease-in-out curve, signalling that the message has departed.
<motion.div
  animate={status === 'summoning' ? { y: 150 } : {}}
  transition={{ duration: 1.5, ease: 'easeInOut' }}
>
  {/* Planchette SVG */}
</motion.div>

Form States

The form cycles through three states managed by a status variable in React state:
StateButton LabelBoard Behaviour
idleSEND INTO THE VOIDPlanchette tracks typed letters
summoningSUMMONING…Planchette slides to y:150 (GOODBYE)
sent(hidden)Board replaced by success message
When status reaches 'sent', the form is replaced with the success message:
MESSAGE RECEIVED The spirits will reply shortly.

Customization

The default handleSubmit function simulates a backend call with a setTimeout. Replace the timeout with a real API call to your preferred email delivery service before going live.
const handleSubmit = async (e) => {
  e.preventDefault();
  setStatus('summoning');
  // Replace this timeout with your actual API call:
  // await fetch('/api/contact', { method: 'POST', body: JSON.stringify({ name, message }) })
  setTimeout(() => {
    setStatus('sent');
  }, 1000);
};
Popular options include EmailJS (client-side, no backend required), Formspree (form endpoint hosting), and Resend (API-based transactional email). Each requires only minor changes to the fetch call above.
The default form has no backend — the submit handler uses a timeout simulation and does not send any data. Connect it to your preferred email service (EmailJS, Formspree, Resend, etc.) before deploying, or visitor messages will silently disappear into an actual void.

Build docs developers (and LLMs) love