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
This commit is contained in:
@@ -1,32 +1,286 @@
|
||||
"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="text-center space-y-8">
|
||||
<h2 className="text-4xl font-serif italic mb-8">A Place for Quiet Reflection</h2>
|
||||
<img src="https://images.unsplash.com/photo-1518199266791-5375a83190b7?q=80&w=2070&auto=format&fit=crop" className="w-full h-96 object-cover rounded-full border-4 border-[#d4af37]/20 mx-auto" />
|
||||
|
||||
<div className="max-w-2xl mx-auto leading-loose text-lg">
|
||||
<p>
|
||||
In the chaos of the digital void, we offer a moment of stillness.
|
||||
No tracking. No noise. Just the hum of the server and the weight of your thoughts.
|
||||
</p>
|
||||
<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>
|
||||
|
||||
<div className="grid grid-cols-3 gap-4 mt-12">
|
||||
<div className="p-6 border border-[#d4af37]/10 hover:bg-[#d4af37]/5 transition-colors">
|
||||
<div className="text-2xl mb-2">🕯️</div>
|
||||
<div className="text-xs uppercase tracking-widest">Light a Candle</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>
|
||||
<div className="p-6 border border-[#d4af37]/10 hover:bg-[#d4af37]/5 transition-colors">
|
||||
<div className="text-2xl mb-2">📜</div>
|
||||
<div className="text-xs uppercase tracking-widest">Read the Scrolls</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>
|
||||
<div className="p-6 border border-[#d4af37]/10 hover:bg-[#d4af37]/5 transition-colors">
|
||||
<div className="text-2xl mb-2">🌑</div>
|
||||
<div className="text-xs uppercase tracking-widest">Enter the Void</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>
|
||||
|
||||
Reference in New Issue
Block a user