Files
dark-lord/app/sanctuary/page.tsx
drjones 2f928fbdc4 10x every page: real interactions, kill fake content, wire everything
- ChatWidget: remove illegal seeds, real localStorage per-handle chat,
  honest bot replies about market/forum/funds
- ForumBoard: wire to real forumState (loadForum/addThread/vote), kill
  fake stats and illegal seed posts
- Home page: privacy features list reflects reality, footer links real
- Links: kill all alert() calls, replace fake onions with real clearnet
  privacy resources + internal route grid
- Support: per-coin copied state, env-driven addresses, real BTC addr
- Inner circle: wire to AccountContext, tier system from LUX balance,
  remove hardcoded admin/shadow credentials and fake trading signals
- Drop box: real sealed-note localStorage system, honest about no
  anonymous upload capability, real file picker with receipt
- Messages: fully functional per-handle localStorage chat, AI-style
  contextual bot replies, clear history, honest about local storage
- Wallets: pivot from fake PayPal accounts to Digital Access Passes,
  wire Buy Now to cart via ShopProduct interface
- Testimonials: wire submit form to localStorage, interactive star
  rating 1-10, display submitted reviews above the fold
- Raffle: use real merchant BTC address, real per-handle entry storage,
  honest LUX-only prize disclaimer, fix 0x address
- Drops/Lotto: real number picker 1-49 with Quick Pick, ticket
  submission, match display against drawn numbers, demo disclaimer
- Sanctuary: real 4-4-6-2 breathing timer, meditation passage with
  timer, candle-lighting with localStorage notes
- Game: full playable Void Pong with canvas physics, CPU AI, scoring,
  rally counter, localStorage high score
- Security analysis: honest architecture breakdown with real grades,
  layer-by-layer analysis, practical OPSEC guide, fiction banner
- Trust: compute real scores from actual localStorage data (LUX,
  USD, forum posts, testimonials), FAQ accordion

Made-with: Cursor
2026-04-16 00:55:28 -07:00

289 lines
11 KiB
TypeScript

"use client";
import { useState, useEffect, useRef, useCallback } from "react";
import Link from "next/link";
import ThemedLayout from "@/components/layouts/ThemedLayout";
const BREATH_PHASES = [
{ label: "Inhale", duration: 4, color: "#d4af37" },
{ label: "Hold", duration: 4, color: "#a07820" },
{ label: "Exhale", duration: 6, color: "#7a5c10" },
{ label: "Rest", duration: 2, color: "#4a3c00" },
];
const TOTAL_CYCLE = BREATH_PHASES.reduce((s, p) => s + p.duration, 0);
const MEDITATIONS = [
"The signal exists whether or not you observe it. Observe it.",
"Your handle is not your identity. Your actions are.",
"Anonymity is not invisibility — it is the deliberate curation of what is seen.",
"Every encrypted packet is a sealed letter to yourself across time.",
"You are one hop in a circuit that spans the planet.",
"In the void between nodes, there is no latency. Only intention.",
"Trust no one node. Trust the protocol.",
"The archive remembers. The relay forgets. Choose which you are.",
];
const CANDLE_KEY = "cyberlux-candles-v1";
type CandleEntry = { ts: number; note?: string };
function loadCandles(): CandleEntry[] {
if (typeof window === "undefined") return [];
try { return JSON.parse(localStorage.getItem(CANDLE_KEY) ?? "[]") as CandleEntry[]; }
catch { return []; }
}
function addCandle(note?: string) {
const candles = loadCandles();
candles.push({ ts: Date.now(), note });
if (typeof window !== "undefined") {
localStorage.setItem(CANDLE_KEY, JSON.stringify(candles.slice(-50)));
}
return candles.length + 1;
}
export default function SanctuaryPage() {
const [tab, setTab] = useState<"breathe" | "meditate" | "candle">("breathe");
// Breathing exercise
const [breathing, setBreathing] = useState(false);
const [phase, setPhase] = useState(0);
const [phaseTime, setPhaseTime] = useState(0);
const breathRef = useRef<ReturnType<typeof setInterval> | null>(null);
// Meditation
const [meditationIdx, setMeditationIdx] = useState(0);
const [meditationSeconds, setMeditationSeconds] = useState(0);
const [meditating, setMeditating] = useState(false);
const medRef = useRef<ReturnType<typeof setInterval> | null>(null);
// Candle
const [candleNote, setCandleNote] = useState("");
const [candleLit, setCandleLit] = useState(false);
const [candleCount, setCandleCount] = useState(0);
useEffect(() => {
setCandleCount(loadCandles().length);
}, []);
const startBreathing = useCallback(() => {
setBreathing(true);
setPhase(0);
setPhaseTime(0);
if (breathRef.current) clearInterval(breathRef.current);
let elapsed = 0;
breathRef.current = setInterval(() => {
elapsed++;
let acc = 0;
for (let i = 0; i < BREATH_PHASES.length; i++) {
acc += BREATH_PHASES[i]!.duration;
if (elapsed % TOTAL_CYCLE < acc - (BREATH_PHASES[i]!.duration - 1) + (BREATH_PHASES[i]!.duration - 1)) {
const cyclePos = elapsed % TOTAL_CYCLE;
let phaseAcc = 0;
for (let j = 0; j < BREATH_PHASES.length; j++) {
if (cyclePos < phaseAcc + BREATH_PHASES[j]!.duration) {
setPhase(j);
setPhaseTime(cyclePos - phaseAcc);
break;
}
phaseAcc += BREATH_PHASES[j]!.duration;
}
break;
}
}
}, 1000);
}, []);
const stopBreathing = useCallback(() => {
setBreathing(false);
if (breathRef.current) clearInterval(breathRef.current);
}, []);
useEffect(() => () => { if (breathRef.current) clearInterval(breathRef.current); }, []);
const startMeditation = useCallback(() => {
setMeditating(true);
setMeditationSeconds(0);
setMeditationIdx(Math.floor(Math.random() * MEDITATIONS.length));
if (medRef.current) clearInterval(medRef.current);
medRef.current = setInterval(() => setMeditationSeconds((s) => s + 1), 1000);
}, []);
const stopMeditation = useCallback(() => {
setMeditating(false);
if (medRef.current) clearInterval(medRef.current);
}, []);
useEffect(() => () => { if (medRef.current) clearInterval(medRef.current); }, []);
const lightCandle = () => {
const count = addCandle(candleNote.trim() || undefined);
setCandleCount(count);
setCandleLit(true);
setCandleNote("");
setTimeout(() => setCandleLit(false), 5000);
};
const currentPhase = BREATH_PHASES[phase]!;
const circleScale = (() => {
if (!breathing) return 0.5;
const progress = phaseTime / currentPhase.duration;
if (phase === 0) return 0.5 + progress * 0.5;
if (phase === 1) return 1;
if (phase === 2) return 1 - progress * 0.5;
return 0.5;
})();
const formatMedSec = (s: number) =>
`${Math.floor(s / 60).toString().padStart(2, "0")}:${(s % 60).toString().padStart(2, "0")}`;
return (
<ThemedLayout theme="mystic" title="The Sanctuary">
<div className="mx-auto max-w-2xl px-4 py-12 text-center">
<h1 className="mb-2 font-serif text-4xl italic text-[#d4af37]">The Sanctuary</h1>
<p className="mb-10 text-sm text-[#d4af37]/60">A place of quiet. No tracking. No noise.</p>
{/* Tabs */}
<div className="mb-8 flex justify-center gap-1 rounded-full bg-black/30 p-1 border border-[#d4af37]/15">
{(["breathe", "meditate", "candle"] as const).map((t) => (
<button
key={t}
type="button"
onClick={() => setTab(t)}
className={`flex-1 rounded-full py-2 text-xs font-bold uppercase tracking-wider transition-all ${
tab === t ? "bg-[#d4af37]/20 text-[#d4af37]" : "text-[#d4af37]/40 hover:text-[#d4af37]/70"
}`}
>
{t === "breathe" ? "Breathing" : t === "meditate" ? "Meditation" : "Candles"}
</button>
))}
</div>
{/* Breathing */}
{tab === "breathe" && (
<div className="space-y-8">
<p className="text-sm text-[#d4af37]/70">
4-4-6-2 breathing pattern. Calms the nervous system in under two minutes.
</p>
<div className="relative flex h-48 w-48 mx-auto items-center justify-center">
<div
className="absolute inset-0 rounded-full border-2 border-[#d4af37]/30 transition-transform duration-1000"
style={{ transform: `scale(${circleScale})`, backgroundColor: `${currentPhase.color}18` }}
/>
<div className="relative text-center">
<div className="text-2xl font-bold text-[#d4af37]">
{breathing ? currentPhase.label : "Ready"}
</div>
{breathing && (
<div className="text-sm text-[#d4af37]/60">
{currentPhase.duration - phaseTime}s
</div>
)}
</div>
</div>
<div className="flex justify-center gap-3">
{BREATH_PHASES.map((p, i) => (
<div
key={p.label}
className={`rounded-full px-3 py-1 text-xs font-bold uppercase transition-all ${
breathing && phase === i
? "bg-[#d4af37]/20 text-[#d4af37]"
: "text-[#d4af37]/30"
}`}
>
{p.label} {p.duration}s
</div>
))}
</div>
<button
type="button"
onClick={breathing ? stopBreathing : startBreathing}
className="rounded-full border-2 border-[#d4af37]/40 px-8 py-3 text-sm font-bold uppercase text-[#d4af37] hover:bg-[#d4af37]/10 transition-all"
>
{breathing ? "Stop" : "Begin Session"}
</button>
</div>
)}
{/* Meditation */}
{tab === "meditate" && (
<div className="space-y-8">
<div className="min-h-[5rem] flex items-center justify-center">
<p className="max-w-md text-xl italic leading-relaxed text-[#d4af37]/80">
"{MEDITATIONS[meditationIdx]}"
</p>
</div>
{meditating && (
<div className="text-3xl font-mono text-[#d4af37]">{formatMedSec(meditationSeconds)}</div>
)}
<div className="flex justify-center gap-3">
<button
type="button"
onClick={meditating ? stopMeditation : startMeditation}
className="rounded-full border-2 border-[#d4af37]/40 px-8 py-3 text-sm font-bold uppercase text-[#d4af37] hover:bg-[#d4af37]/10 transition-all"
>
{meditating ? "End Session" : "Begin Meditation"}
</button>
{!meditating && (
<button
type="button"
onClick={() => setMeditationIdx((i) => (i + 1) % MEDITATIONS.length)}
className="rounded-full border border-[#d4af37]/20 px-5 py-3 text-xs text-[#d4af37]/50 hover:text-[#d4af37]/80 transition-all"
>
Next passage
</button>
)}
</div>
{!meditating && meditationSeconds > 0 && (
<p className="text-sm text-[#d4af37]/50">
Session: {formatMedSec(meditationSeconds)}
</p>
)}
</div>
)}
{/* Candle */}
{tab === "candle" && (
<div className="space-y-6">
<p className="text-sm text-[#d4af37]/60">
Light a candle. Add an optional note. Stored only in this browser.{" "}
<span className="text-[#d4af37]/40">{candleCount} lit so far.</span>
</p>
<div className="relative mx-auto flex h-32 w-12 flex-col items-center">
<div
className={`mb-1 h-8 w-2 rounded-full transition-all duration-700 ${
candleLit ? "bg-amber-300 shadow-[0_0_20px_#fbbf24,0_0_40px_#fbbf24] animate-pulse" : "bg-[#d4af37]/20"
}`}
/>
<div className="h-20 w-10 rounded-b bg-gradient-to-b from-[#d4af37]/30 to-[#d4af37]/10 border border-[#d4af37]/20" />
</div>
<textarea
value={candleNote}
onChange={(e) => setCandleNote(e.target.value)}
rows={3}
maxLength={200}
placeholder="Optional note for this candle… (stored locally, not sent anywhere)"
className="w-full rounded-xl border border-[#d4af37]/20 bg-black/30 px-4 py-3 text-sm text-[#d4af37]/80 focus:border-[#d4af37]/40 focus:outline-none resize-none placeholder:text-[#d4af37]/30"
/>
<button
type="button"
onClick={lightCandle}
className="rounded-full border-2 border-[#d4af37]/40 px-8 py-3 text-sm font-bold uppercase text-[#d4af37] hover:bg-[#d4af37]/10 transition-all"
>
{candleLit ? "✦ Candle Lit" : "Light a Candle"}
</button>
{candleLit && (
<p className="text-sm text-[#d4af37]/60">Candle #{candleCount} lit. It burns in this browser.</p>
)}
</div>
)}
<div className="mt-16 flex justify-center gap-6 text-xs text-[#d4af37]/30">
<Link href="/" className="hover:text-[#d4af37]/60">Hub</Link>
<Link href="/vault" className="hover:text-[#d4af37]/60">Vault</Link>
<Link href="/mixer" className="hover:text-[#d4af37]/60">Mixer</Link>
</div>
</div>
</ThemedLayout>
);
}