mirror of
https://github.com/ramvignesh-b/pi-ku.git
synced 2026-05-04 00:56:34 +00:00
feat: add typography styles and complete about page
This commit is contained in:
Binary file not shown.
|
After Width: | Height: | Size: 118 KiB |
File diff suppressed because one or more lines are too long
|
After Width: | Height: | Size: 327 KiB |
@@ -1,17 +1,33 @@
|
||||
import { DotIcon } from "@phosphor-icons/react";
|
||||
import "@fontsource/knewave/400.css";
|
||||
|
||||
export default function Logo({ scale = 1, inline = false }) {
|
||||
if (inline) {
|
||||
// for span elements
|
||||
interface LogoProps {
|
||||
scale?: number;
|
||||
type?: "inline" | "mono" | "logo";
|
||||
}
|
||||
|
||||
export default function Logo({ scale = 1, type = "logo" }: LogoProps) {
|
||||
if (type === "inline") {
|
||||
return (
|
||||
<span className={"text-accent font-serif italic"}>
|
||||
<span
|
||||
className={
|
||||
"text-accent font-serif italic drop-shadow-xs drop-shadow-base-200/60 "
|
||||
}
|
||||
>
|
||||
Pi<span className="text-primary">.</span> Ku
|
||||
<span className="text-primary">.</span>
|
||||
</span>
|
||||
);
|
||||
}
|
||||
|
||||
if (type === "mono") {
|
||||
return (
|
||||
<span className="font-mono italic font-bold border-b-3 border-dashed border-stone-800/50">
|
||||
pi. ku.
|
||||
</span>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<div
|
||||
role="img"
|
||||
|
||||
@@ -68,3 +68,11 @@
|
||||
.glass-card {
|
||||
@apply bg-glass-bg backdrop-blur-xl border border-white/5 shadow-warm rounded-xl m-4;
|
||||
}
|
||||
|
||||
.ul-wavy {
|
||||
@apply decoration-primary/40 underline decoration-wavy underline-offset-4;
|
||||
}
|
||||
|
||||
a {
|
||||
@apply text-primary underline decoration-base-content/20 underline-offset-4 hover:decoration-primary/60 transition-colors;
|
||||
}
|
||||
|
||||
+750
-61
@@ -1,64 +1,425 @@
|
||||
import {
|
||||
ArrowArcLeftIcon,
|
||||
ArrowBendDownLeftIcon,
|
||||
ArrowBendDownRightIcon,
|
||||
ArrowRightIcon,
|
||||
CaretUpIcon,
|
||||
FlowerTulipIcon,
|
||||
GhostIcon,
|
||||
InfoIcon,
|
||||
LockLaminatedIcon,
|
||||
LockOpenIcon,
|
||||
PasswordIcon,
|
||||
PersonArmsSpreadIcon,
|
||||
PersonIcon,
|
||||
ScrollIcon,
|
||||
SmileyIcon,
|
||||
SparkleIcon,
|
||||
VaultIcon,
|
||||
} from "@phosphor-icons/react";
|
||||
import { ReactLenis } from "lenis/react";
|
||||
import { motion, useScroll, useTransform } from "motion/react";
|
||||
import { useRef } from "react";
|
||||
import { AnimatePresence, motion, useScroll, useTransform } from "motion/react";
|
||||
import { useRef, useState } from "react";
|
||||
import stamp from "../assets/envelope/stamp.png";
|
||||
import Logo from "../components/Logo.tsx";
|
||||
import { Modal } from "../components/ui/Modal";
|
||||
|
||||
import "@fontsource/kavivanar/index.css";
|
||||
import "@fontsource/space-mono/index.css";
|
||||
import "@fontsource/redacted-script/index.css";
|
||||
import "@fontsource/architects-daughter/index.css";
|
||||
import { useNavigate } from "react-router-dom";
|
||||
|
||||
export default function App() {
|
||||
const hSectionContainerRef = useRef(null);
|
||||
const { scrollYProgress } = useScroll({
|
||||
target: hSectionContainerRef,
|
||||
});
|
||||
function HorizontalScroll({ children }: { children: React.ReactNode }) {
|
||||
const ref = useRef(null);
|
||||
const { scrollYProgress } = useScroll({ target: ref });
|
||||
const x = useTransform(scrollYProgress, [0, 1], ["0%", "-50%"]);
|
||||
|
||||
return (
|
||||
<section ref={ref} className="relative h-[200dvh]">
|
||||
<div className="sticky top-0 flex h-screen w-screen items-center overflow-x-hidden">
|
||||
<motion.div style={{ x }} className="flex w-[200vw]">
|
||||
{children}
|
||||
</motion.div>
|
||||
</div>
|
||||
</section>
|
||||
);
|
||||
}
|
||||
|
||||
export default function About() {
|
||||
return (
|
||||
<ReactLenis root options={{ lerp: 0.1, duration: 1.5, smoothWheel: true }}>
|
||||
<div className={"flex flex-col"}>
|
||||
<section className="flex w-screen h-screen items-center text-5xl"></section>
|
||||
<div className="flex flex-col">
|
||||
<StorySection />
|
||||
|
||||
<section ref={hSectionContainerRef} className="relative h-[400dvh]">
|
||||
<div className="sticky top-0 flex h-screen w-screen items-center overflow-x-hidden">
|
||||
<motion.div
|
||||
style={{
|
||||
x: useTransform(scrollYProgress, [0, 1], ["0%", "-75%"]),
|
||||
}}
|
||||
className="flex w-[400vw] items-center justify-center"
|
||||
>
|
||||
<StorySection />
|
||||
<HorizontalScroll>
|
||||
<ForWhoSection />
|
||||
<ArchetypesSection />
|
||||
</HorizontalScroll>
|
||||
|
||||
<div className="flex h-screen w-screen items-center justify-center bg-neutral-700 text-6xl">
|
||||
<ForWhoSection />
|
||||
</div>
|
||||
<PrivacySection />
|
||||
|
||||
<div className="flex h-screen w-screen items-center justify-center bg-neutral-600 text-6xl">
|
||||
{/* zero knowledge */}
|
||||
</div>
|
||||
<HorizontalScroll>
|
||||
<SpecsSection />
|
||||
<OSSSection />
|
||||
</HorizontalScroll>
|
||||
|
||||
<div className="flex h-screen w-screen items-center justify-center bg-neutral-500 text-6xl">
|
||||
{/* OSS - code source, contributions, attribution */}
|
||||
</div>
|
||||
</motion.div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section className="flex h-screen items-center justify-center bg-neutral-900 text-5xl">
|
||||
{/* Start Writing */}
|
||||
</section>
|
||||
<AttributionSection />
|
||||
</div>
|
||||
</ReactLenis>
|
||||
);
|
||||
}
|
||||
|
||||
function PrivacySection() {
|
||||
return (
|
||||
<div className="flex flex-col min-h-dvh w-screen justify-center items-center py-18">
|
||||
<h1
|
||||
className={
|
||||
"relative tracking-tighter text-5xl md:text-8xl text-neutral-content/80 font-extrabold italic font-serif flex"
|
||||
}
|
||||
>
|
||||
The Promise
|
||||
<span className="absolute -translate-y-6 md:-translate-y-12 font-display italic text-4xl md:text-6xl text-success translate-x-6 md:translate-x-12 -rotate-6">
|
||||
privacy
|
||||
</span>
|
||||
<CaretUpIcon
|
||||
className="absolute translate-y-6 md:translate-y-12 translate-x-20 md:translate-x-36 text-neutral -rotate-6"
|
||||
weight="bold"
|
||||
/>
|
||||
</h1>
|
||||
<div className="flex flex-col items-center shrink-0 gap-8 max-w-11/12 w-200">
|
||||
<p className="text-xxs md:text-sm tracking-widester text-neutral-content/80 font-semibold uppercase mt-6">
|
||||
<span className="text-accent">Your letters.</span>{" "}
|
||||
<span className="text-error">Nobody else's.</span>
|
||||
</p>
|
||||
<p className="text-sm md:text-lg">
|
||||
When you write something here, it gets encrypted in your browser
|
||||
before anything leaves your device. What reaches the server isn't your
|
||||
letter. It's something unreadable — and the server has no way to
|
||||
change that, because the key never left you.
|
||||
</p>
|
||||
<figure className="diff aspect-3/4 touch-pan-y select-none">
|
||||
<div className="diff-item-1 z-1" role="img">
|
||||
<div className="bg-primary text-primary-content grid place-content-center text-sm md:gap-4">
|
||||
<div className="flex flex-col gap-2">
|
||||
<h1 className="text-3xl md:text-6xl uppercase font-bold tracking-widest mt-2 md:mt-8">
|
||||
you see
|
||||
</h1>
|
||||
<PasswordIcon
|
||||
className="text-neutral mx-auto -mb-3"
|
||||
size={32}
|
||||
/>
|
||||
<h2 className="text-xs md:text-sm tracking-widester text-center uppercase opacity-50">
|
||||
Your Password
|
||||
</h2>
|
||||
<p className="text-center md:text-2xl font-bold font-mono">
|
||||
<br />
|
||||
B@z1ng4A
|
||||
</p>
|
||||
</div>
|
||||
<div className="divider divider-neutral opacity-50 w-1/2 mx-auto">
|
||||
<LockOpenIcon size={48} />
|
||||
</div>
|
||||
<div className="flex flex-col items-center md:gap-2">
|
||||
<ScrollIcon
|
||||
className="text-neutral mx-auto md:-mb-3"
|
||||
size={32}
|
||||
/>
|
||||
<h2 className="text-xs md:text-sm tracking-widester text-center uppercase opacity-50">
|
||||
Your Letter
|
||||
</h2>
|
||||
<div className="p-6 bg-paper w-82 md:w-150 h-200 flex flex-col gap-4 text-xs md:text-lg overflow-hidden max-h-68 md:max-h-full">
|
||||
<p className="wrap-anywhere">Hello friend,</p>
|
||||
<p>I've never told this to anyone...</p>
|
||||
<p className="font-redact">
|
||||
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Ut
|
||||
semper, justo eget vehicula vestibulum, enim enim suscipit
|
||||
lectus, et sagittis nibh risus vel metus. Quisque eu ornare
|
||||
ante, et gravida mauris. Vivamus massa justo, sagittis non
|
||||
viverra sed, sodales non nisi. Nunc semper, massa a aliquet
|
||||
dictum, enim nisi malesuada orci, et elementum lectus turpis
|
||||
et velit. Nam vel felis vitae tortor dignissim malesuada.
|
||||
Nam suscipit, justo eu elementum pulvinar, magna sem tempor
|
||||
ex, vitae iaculis tellus odio non nisl. Duis dolor orci,
|
||||
viverra ut finibus sed, aliquet vitae tortor. Proin sodales
|
||||
ipsum ac ipsum hendrerit tempus. Nunc nec nibh nibh. Aenean
|
||||
consequat auctor posuere. Integer sed magna volutpat,
|
||||
efficitur nisl ut, dignissim neque. Vestibulum convallis nec
|
||||
dui a euismod. Duis dignissim magna in mattis pulvinar. Sed
|
||||
blandit nibh quis arcu ornare, sit amet fermentum nisi
|
||||
rhoncus.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="diff-item-2" role="img">
|
||||
<div className="bg-neutral-content bg-[url('https://www.transparenttextures.com/patterns/random-grey-variations.png')] text-primary-content grid place-content-center text-sm md:gap-4">
|
||||
<div className="flex flex-col gap-2">
|
||||
<h1 className="text-3xl md:text-6xl uppercase font-bold text-right tracking-widest mt-2 md:mt-8">
|
||||
server see
|
||||
</h1>
|
||||
<PasswordIcon
|
||||
className="text-neutral mx-auto -mb-3"
|
||||
size={32}
|
||||
/>
|
||||
<h2 className="text-xs md:text-sm tracking-widester text-center uppercase opacity-50">
|
||||
Your Password
|
||||
</h2>
|
||||
<p className="text-center md:text-2xl font-bold font-mono max-w-150 break-all">
|
||||
9e54d05f88bdd67a675b03bf1cd0a1647e2109b5aa18185ff6a9ba4c6959a19d
|
||||
</p>
|
||||
</div>
|
||||
<div className="divider divider-neutral opacity-50 w-1/2 mx-auto">
|
||||
<LockLaminatedIcon size={48} />
|
||||
</div>
|
||||
<div className="flex flex-col items-center md:gap-2">
|
||||
<ScrollIcon
|
||||
className="text-neutral mx-auto md:-mb-3"
|
||||
size={32}
|
||||
/>
|
||||
<h2 className="text-xs md:text-sm tracking-widester text-center uppercase opacity-50">
|
||||
Your Letter
|
||||
</h2>
|
||||
<div className="p-6 bg-paper w-82 md:w-150 h-200 text-xxs md:text-sm font-mono md:leading-loose overflow-hidden max-h-68 md:max-h-full">
|
||||
<p className="break-all">
|
||||
SZ0Mq9M9sCZsdDB8HGjk7JfWG56Kaot8Lgma74MCusDUYibUGoR7VviWgvc341pvFV9/IAyot9KtlDvwIX1ZmUw9Oh340JMaajRQ7iNgVjHgAwmJAr2cLbReNqlF6xzaf3mIYkiK9BXNQekk2h/9XufklsqoIXpaK1re7xWQ8mdddzy6z4EQFVH/Ev3np5ERW/ss7Z1kqYWUnANK7olWNL/7GgZmhU+L29rgbR52kcH9fng7gnEI3KEuISYExYCg81G1VaJYspkW3A4qwcet+jXdgmbKvkux5qNw6gyNi9d/YqKV7OUNrmoH190rHdJ5A7HOIv3/SvPhb3Zm4sNF5PcMxmhM0+T9m5PejV1GhV9bMBHbbgacay7hZJU3O0+q+7fBAE/+pqfvZdv78lLDFSdtHAXUpYOvHPrI5BNNwuS3T+FK1zjurLnUPThlOSYRICoZSUcxVswXz897PoRmFNNvbal0dpKUmCFrBwV5c/W3d1+iZor5msbm/JxpbNtys59e0StSTwHKsxvxm/rTuUAxWSOmzt13MDBxxd2zyVnX8rtQ7mEjMJ8IHHpvhKjONoa2S11VBJY68Ee1vNrw7htu+wajvmXhHAyfh1lYql8pu8VvPUG7leEQ9I0pMY35Y/C1cYCBLkDT5zf8NeZFtbp0BNgHd+QDVSFH+GSnvTskU2BCio3YE+zE6cDhvLUOMy3e5RAtPqsi5VzpEUcdCwph+Z+1pFlTxiEZ62i4wNpqw2lhS3b/E9ifJgnncSgRHLtfw/VxHZCRc4tBQ24xSZ507lSlQch+5lQeO7rx2htgd2D7aGNx/UN/xmeuEd4a28AxNOVS3uYh3wTDh8CSXyBRCRPxrANOV1ZBojdfK+v5fOJNPgDn3r5/pG80L3FTkecRB0zFuKNG8jIzi5ADx9k4SlhRNo17gPl2if8gRA6tzTae4kbzieG+woxhUWj/qvXg0MQmg59VTK2HHS34exdKDP9a561svlw+lJ2AtM1EL9srJk8i3kiyEPUeIlaLl3AfgbbSuC2RhlzFFAYuQ06rbsSvEoe4rrYeMXxL9jwVsXX0xrp8H25mOJu3ahn5pFYzADMSGf4L11H1vDArpefj/lW+8zcmogxxBktYYNF/qU4v+9367hp4MEn/84tQPpmb47TL+XpVnl9tQ3r9OfOaW3zX7NkWZbqoX7OgdgHOtTLP/euQujSs2MAzMO4BmbuCS7pR/GTZwDqF1sXiWAkunjo2qpKHieqlvSVmtwEhh6wsNwYTKEkddmTqvKSx0fHRvs3D9lMGJfg7wLSz/3Otx3G65tk9l/3B3r87qQTvbqXmcfnFdEIaR8mO/yMyCKnxtJkJb3lEzNUOrvnSxwL7Gyn54TLTWA==
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="diff-resizer"></div>
|
||||
</figure>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
function SpecsSection() {
|
||||
const [isModalOpen, setIsModalOpen] = useState(false);
|
||||
|
||||
return (
|
||||
<section className="flex flex-col min-h-dvh w-screen justify-center items-center py-18">
|
||||
<h1 className="relative tracking-tighter text-5xl md:text-8xl text-neutral-content/80 font-extrabold italic font-display z-10">
|
||||
S'more Specs
|
||||
</h1>
|
||||
<div className="flex flex-col items-start shrink-0 gap-6 max-w-11/12 w-200 mt-4 md:mt-12">
|
||||
<h2 className="text-xl md:text-3xl text-center mx-auto">
|
||||
<Logo type={"inline"} /> uses{" "}
|
||||
<span className="text-accent font-mono">Zero Knowledge</span>{" "}
|
||||
<span className="group ul-wavy font-mono text-primary">
|
||||
E
|
||||
<span className="hidden group-hover:inline group-focus-within:inline">
|
||||
nd
|
||||
</span>
|
||||
2
|
||||
<span className="hidden group-hover:inline group-focus-within:inline">
|
||||
|
||||
</span>
|
||||
E
|
||||
<span className="hidden group-hover:inline group-focus-within:inline">
|
||||
nd
|
||||
</span>
|
||||
<span className="hidden group-hover:inline group-focus-within:inline">
|
||||
<span>E</span>
|
||||
<span className="hidden group-hover:inline group-focus-within:inline">
|
||||
ncryption
|
||||
</span>
|
||||
</span>
|
||||
</span>{" "}
|
||||
with{" "}
|
||||
<span className="font-mono text-primary">Envelope Encryption</span>
|
||||
</h2>
|
||||
<p className="text-sm md:text-xl leading-relaxed">
|
||||
This means, both the encryption and decryption runs on your device, in
|
||||
your browser.
|
||||
<br />
|
||||
Every letter has a{" "}
|
||||
<span className="font-mono text-primary">unique key</span> which is
|
||||
derived from your original password.
|
||||
<br />
|
||||
Both the letter and the key are encrypted securely and sent to the
|
||||
server.
|
||||
<br />
|
||||
Now, the server holds{" "}
|
||||
<span className="text-primary font-bold">the envelope</span>,{" "}
|
||||
<span className="text-primary font-bold">the seal</span> and{" "}
|
||||
<span className="text-primary font-bold">another locked box</span>{" "}
|
||||
with a key inside that unseals your letter. But you,{" "}
|
||||
<span className="italic text-primary">only you</span>, hold the only
|
||||
thing that opens the box —{" "}
|
||||
<span className="font-mono text-accent">your password</span>.
|
||||
</p>
|
||||
<p className="text-sm md:text-xl text-right w-full flex items-center justify-end gap-4 leading-relaxed">
|
||||
Nothing on the server is readable without your actual password.
|
||||
<br />
|
||||
Even if someone were to breach in, all they'd find is encrypted noise.
|
||||
<VaultIcon size={48} weight="duotone" />
|
||||
</p>
|
||||
|
||||
<button
|
||||
type={"button"}
|
||||
className="btn btn-outline border-base-300 w-full justify-between font-medium opacity-80"
|
||||
onClick={() => setIsModalOpen(true)}
|
||||
>
|
||||
<span className="text-sm md:text-lg font-mono ul-wavy font-bold">
|
||||
Nerd Stuff
|
||||
</span>
|
||||
<ArrowRightIcon size={20} />
|
||||
</button>
|
||||
|
||||
<Modal isOpen={isModalOpen} onClose={() => setIsModalOpen(false)}>
|
||||
<div className="w-full bg-paper rounded-md p-6">
|
||||
<img src="/screenshots/e2e.svg" alt="pi ku e2e diagram" />
|
||||
</div>
|
||||
</Modal>
|
||||
|
||||
<p className="text-sm md:text-lg">
|
||||
This level of privacy comes with a catch.{" "}
|
||||
<span className="text-error font-bold">No password reset.</span>
|
||||
</p>
|
||||
<p className="text-sm md:text-lg alert alert-warning font-semibold">
|
||||
<InfoIcon weight="duotone" /> Your original password is never stored
|
||||
on the server. Which means if it's lost, the letters stay sealed
|
||||
forever.
|
||||
</p>
|
||||
</div>
|
||||
</section>
|
||||
);
|
||||
}
|
||||
|
||||
function OSSSection() {
|
||||
return (
|
||||
<section className="flex flex-col h-screen w-screen items-center justify-center py-18 gap-4">
|
||||
<h1
|
||||
className={
|
||||
"relative tracking-tighter text-4xl md:text-8xl text-neutral-content/80 font-extrabold italic font-serif text-center"
|
||||
}
|
||||
>
|
||||
<span className="hidden absolute -translate-y-24 translate-x-45 font-display text-3xl md:text-6xl opacity-70 rotate-8">
|
||||
only for
|
||||
<br />
|
||||
<span className="text-primary">your letters</span> <SmileyIcon />
|
||||
<ArrowArcLeftIcon className="inline rotate-45 -translate-y-8" />
|
||||
</span>
|
||||
<Logo type={"inline"} /> is{" "}
|
||||
<span className="line-through decoration-6 decoration-error">
|
||||
private
|
||||
</span>{" "}
|
||||
<span className="text-success">open source !</span>
|
||||
</h1>
|
||||
<div className="flex flex-col items-center shrink-0 max-w-11/12 w-200 gap-4 p-4 md:p-6">
|
||||
<p className="text-sm md:text-xl">
|
||||
<Logo type={"mono"} /> is fully open source. Every claim about privacy
|
||||
and encryption is publicly available in the code so you don't have to
|
||||
take anyone's word for it.
|
||||
</p>
|
||||
<p className="text-sm md:text-lg">
|
||||
You can also{" "}
|
||||
<span className="uppercase font-bold text-primary">Self-host</span>{" "}
|
||||
<Logo type={"inline"} /> in just 4 steps.
|
||||
</p>
|
||||
<div className="mockup-code w-full text-xs">
|
||||
<pre data-prefix="$">
|
||||
<code>git clone https://git.ramvignesh.dev/me/pi-ku.git</code>
|
||||
</pre>
|
||||
<pre data-prefix="$">
|
||||
<code>cd pi-ku</code>
|
||||
</pre>
|
||||
<pre data-prefix="$">
|
||||
<code>./scripts/setup.sh</code>
|
||||
</pre>
|
||||
<pre data-prefix="$">
|
||||
<code>./scripts/start.sh</code>
|
||||
</pre>
|
||||
</div>
|
||||
|
||||
<div className="flex flex-wrap gap-4 w-full items-center justify-center">
|
||||
<a
|
||||
href="https://git.ramvignesh.dev/me/pi-ku"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className="text-primary"
|
||||
>
|
||||
View on GitHub
|
||||
</a>
|
||||
<p className="text-xs md:text-base opacity-70">
|
||||
Found something to report or request?{" "}
|
||||
<a
|
||||
href="https://git.ramvignesh.dev/me/pi-ku/issues"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
>
|
||||
Please say so.
|
||||
</a>
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div className="divider opacity-30 my-0"></div>
|
||||
|
||||
<p className="text-xxs md:text-sm tracking-widester font-semibold uppercase text-accent">
|
||||
Built on the shoulders of open source.
|
||||
</p>
|
||||
|
||||
<p className="text-sm md:text-lg">
|
||||
<Logo type={"mono"} /> wouldn't exist without the work of people who
|
||||
chose to build in the open.
|
||||
</p>
|
||||
|
||||
<p className="text-sm md:text-lg">
|
||||
<a
|
||||
href="https://developer.mozilla.org/en-US/docs/Web/API/Web_Crypto_API"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
>
|
||||
Web Crypto API
|
||||
</a>{" "}
|
||||
— the backbone of everything promised. Browser-native
|
||||
cryptography that runs entirely on your device. Without it, none of
|
||||
the privacy here would be possible — or credible.
|
||||
</p>
|
||||
|
||||
<p className="text-sm md:text-lg">
|
||||
<a
|
||||
href="https://daisyui.com"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
>
|
||||
DaisyUI
|
||||
</a>{" "}
|
||||
·{" "}
|
||||
<a
|
||||
href="http://fabricjs.com"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
>
|
||||
Fabric.js
|
||||
</a>{" "}
|
||||
·{" "}
|
||||
<a
|
||||
href="https://phosphoricons.com"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
>
|
||||
Phosphor Icons
|
||||
</a>{" "}
|
||||
— the beautiful work by others that let me focus on the core
|
||||
experience.
|
||||
</p>
|
||||
|
||||
<p className="text-sm md:text-lg mt-4">
|
||||
Open source is what made this possible. It felt right to give it back
|
||||
the same way.
|
||||
</p>
|
||||
</div>
|
||||
</section>
|
||||
);
|
||||
}
|
||||
|
||||
function StorySection() {
|
||||
return (
|
||||
<div className="flex flex-col min-h-dvh w-screen items-center md:py-18 overscroll-contain">
|
||||
<div className="flex flex-col min-h-dvh w-screen justify-center items-center py-18">
|
||||
<h1
|
||||
className={
|
||||
"relative tracking-tighter text-5xl md:text-8xl text-neutral-content/80 font-extrabold italic font-serif"
|
||||
@@ -71,11 +432,11 @@ function StorySection() {
|
||||
<Logo />
|
||||
</div>
|
||||
<div className="flex ml-10 font-tamil text-2xl md:text-3xl group">
|
||||
<div className={"flex flex-col flex-wrap"}>
|
||||
<div className={"flex flex-col flex-wrap ul-wavy"}>
|
||||
பின்
|
||||
<span
|
||||
className={
|
||||
"font-sans transition-all duration-1000 opacity-0 group-hover:opacity-100 text-xxs tracking-widester uppercase text-neutral-content/90 mt-2"
|
||||
"font-sans transition-all duration-1000 opacity-0 group-hover:opacity-100 group-focus-within:opacity-100 text-xxs tracking-widester uppercase text-neutral-content/60 mt-2"
|
||||
}
|
||||
>
|
||||
after
|
||||
@@ -83,11 +444,11 @@ function StorySection() {
|
||||
</div>
|
||||
<ArrowBendDownLeftIcon className={"text-primary"} />
|
||||
<ArrowBendDownRightIcon className="ml-8 text-primary" />
|
||||
<div className={"flex flex-col flex-wrap group"}>
|
||||
<div className={"flex flex-col flex-wrap group ul-wavy"}>
|
||||
குறிப்பு
|
||||
<span
|
||||
className={
|
||||
"font-sans transition-all duration-1000 opacity-0 group-hover:opacity-100 text-xxs tracking-widest uppercase text-neutral-content/90 mt-2"
|
||||
"font-sans transition-all duration-1000 opacity-0 group-hover:opacity-100 group-focus-within:opacity-100 text-xxs tracking-[.2em] uppercase text-neutral-content/60 mt-2"
|
||||
}
|
||||
>
|
||||
note. remark.
|
||||
@@ -132,22 +493,34 @@ function StorySection() {
|
||||
"max-w-200 md:text-xl p-6 flex flex-col gap-4 md:gap-8 text-base-content/70 leading-relaxed"
|
||||
}
|
||||
>
|
||||
<p className={"hidden md:inline"}>
|
||||
<span className={"text-accent font-serif italic"}>pi. ku.</span> is
|
||||
an abbreviated transliteration of the Tamil word for{" "}
|
||||
<span className={"group italic text-primary font-serif inline"}>
|
||||
<p className={""}>
|
||||
<Logo type={"inline"} /> is an abbreviated transliteration of the
|
||||
Tamil word for{" "}
|
||||
<span
|
||||
className={
|
||||
"group italic text-primary font-serif inline underline decoration-dotted underline-offset-2 decoration-primary/40"
|
||||
}
|
||||
>
|
||||
P
|
||||
<span className={"text-neutral hidden group-hover:inline"}>
|
||||
<span
|
||||
className={
|
||||
"text-neutral hidden group-hover:inline group-focus-within:inline "
|
||||
}
|
||||
>
|
||||
ost
|
||||
</span>
|
||||
. S
|
||||
<span className={"text-neutral hidden group-hover:inline"}>
|
||||
<span
|
||||
className={
|
||||
"text-neutral hidden group-hover:inline group-focus-within:inline"
|
||||
}
|
||||
>
|
||||
cript
|
||||
</span>
|
||||
.
|
||||
</span>{" "}
|
||||
— the thing you add after you've already signed your name, what you
|
||||
write when you thought you were finished, but weren't.
|
||||
— the thing you add after you've already signed your name,
|
||||
what you write when you thought you were finished, but weren't.
|
||||
</p>
|
||||
<p>
|
||||
<span className={"font-medium text-primary"}>
|
||||
@@ -158,16 +531,18 @@ function StorySection() {
|
||||
change the subject. <br />
|
||||
Those words{" "}
|
||||
<span
|
||||
className={"blur-sm hover:blur-none transition-all duration-500"}
|
||||
className={
|
||||
"blur-sm hover:blur-none active:blur-none focus:blur-none focus:outline-none transition-all duration-500"
|
||||
}
|
||||
>
|
||||
don't just disappear. They
|
||||
</span>{" "}
|
||||
stay <span className={"text-primary font-hand"}>unsaid</span> — a
|
||||
quiet weight difficult to bear.
|
||||
stay <span className={"text-primary font-hand"}>unsaid</span>{" "}
|
||||
— a quiet weight difficult to bear.
|
||||
</p>
|
||||
<p className={"italic text-primary"}>And that's okay...</p>
|
||||
<p>
|
||||
<span className={"text-accent font-serif italic"}>pi. ku.</span>{" "}
|
||||
<Logo type={"inline"} />
|
||||
<span className={"text-primary"}>
|
||||
was built for putting that weight down.
|
||||
</span>
|
||||
@@ -182,23 +557,337 @@ function StorySection() {
|
||||
|
||||
function ForWhoSection() {
|
||||
return (
|
||||
<div className="flex flex-col h-screen w-screen items-center py-18">
|
||||
<div className="flex flex-col h-screen w-screen justify-center items-center py-18 bg-primary/80">
|
||||
<div className="max-w-4xl z-10">
|
||||
<h2 className="text-7xl md:text-9xl font-serif italic font-black tracking-tighter text-stone-900 leading-tightest mb-12">
|
||||
Who is <br /> this for?
|
||||
</h2>
|
||||
|
||||
<div className="space-y-6 max-w-200 p-4 text-base-200 text-xl md:text-2xl leading-relaxed">
|
||||
<p>
|
||||
<Logo type={"mono"} /> wasn't built for one kind of person, but a
|
||||
particular kind of feeling —
|
||||
<span className="italic font-serif text-stone-900">
|
||||
{" "}
|
||||
the one that lingers very quietly
|
||||
</span>{" "}
|
||||
— fragile, yet never breaks.
|
||||
</p>
|
||||
|
||||
<div className="pt-8 flex items-center gap-4">
|
||||
<span className="text-xs md:text-sm uppercase tracking-widest font-mono opacity-60">
|
||||
See if any of these feel too familiar to you
|
||||
</span>
|
||||
<div className="w-24 animate-pulse">
|
||||
<ArrowRightIcon size={24} />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="absolute right-0 top-1/2 -translate-y-1/2 translate-x-1/2 w-64 h-64 rounded-full bg-white/5 blur-3xl"></div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
function ArchetypesSection() {
|
||||
return (
|
||||
<div className="flex flex-col h-screen w-screen items-center justify-center py-18 bg-primary/80">
|
||||
<h1
|
||||
className={
|
||||
"relative tracking-tighter text-6xl md:text-8xl text-neutral-content/80 font-extrabold italic font-serif"
|
||||
"relative tracking-tighter text-5xl md:text-8xl text-base-300/80 font-extrabold italic font-serif"
|
||||
}
|
||||
>
|
||||
For Who
|
||||
The Archetypes
|
||||
</h1>
|
||||
<div className="flex flex-col items-center shrink-0">
|
||||
<div
|
||||
className={
|
||||
"max-w-200 text-xl p-6 flex flex-col gap-8 text-base-content/70 leading-relaxed"
|
||||
}
|
||||
>
|
||||
<p></p>
|
||||
<div className="flex flex-col items-center shrink-0 w-200 max-w-11/12 gap-2 md:gap-8 my-4">
|
||||
<div className="relative w-full">
|
||||
<details
|
||||
className="collapse shadow-xs glass opacity-75 open:opacity-100 text-base-300 peer"
|
||||
name="my-accordion-det-1"
|
||||
open
|
||||
>
|
||||
<summary className="collapse-title md:text-xl leading-tight font-hand flex items-center gap-4">
|
||||
<GhostIcon weight="duotone" className="text-accent" size={32} />{" "}
|
||||
To someone you can't reach anymore.
|
||||
</summary>
|
||||
<div className="collapse-content text-sm md:text-lg flex flex-col gap-4">
|
||||
<p>
|
||||
A person who left. A relationship that ended without a real
|
||||
ending. Someone who's still in your life but will never know
|
||||
what you felt. Some conversations just close before they're
|
||||
finished.
|
||||
<br />
|
||||
</p>
|
||||
<p className="font-serif font-medium opacity-70">
|
||||
Write the letter anyway. Keep it close.
|
||||
</p>
|
||||
</div>
|
||||
</details>
|
||||
<span className="absolute md:-right-8 md:-top-10 -top-4 -right-2 md:text-8xl text-6xl font-bold font-mono opacity-20 peer-open:opacity-60 pointer-events-none z-10 transition-all duration-500 rotate">
|
||||
01
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<div className="relative w-full">
|
||||
<details
|
||||
className="collapse shadow-xs glass opacity-75 open:opacity-100 text-base-300 peer"
|
||||
name="my-accordion-det-1"
|
||||
>
|
||||
<summary className="collapse-title text-lg md:text-xl leading-tight font-hand flex items-center gap-4">
|
||||
<FlowerTulipIcon
|
||||
weight="duotone"
|
||||
className="text-accent"
|
||||
size={32}
|
||||
/>{" "}
|
||||
To someone who's still here.
|
||||
</summary>
|
||||
<div className="collapse-content text-sm md:text-lg flex flex-col gap-4">
|
||||
<p>
|
||||
Not every letter is about distance. Sometimes you just need to
|
||||
say something properly — without a text thread, without
|
||||
the noise of a conversation already in motion. A letter slows it
|
||||
down.
|
||||
</p>
|
||||
<p className="font-serif font-medium opacity-70">
|
||||
Give people their due flowers while they can still smell them.
|
||||
</p>
|
||||
</div>
|
||||
</details>
|
||||
<span className="absolute md:-right-8 md:-top-10 -top-4 -right-2 md:text-8xl text-6xl font-bold font-mono opacity-20 peer-open:opacity-60 pointer-events-none z-10 transition-all duration-500">
|
||||
02
|
||||
</span>
|
||||
</div>
|
||||
<div className="relative w-full group">
|
||||
<details
|
||||
className="collapse shadow-xs glass opacity-75 open:opacity-100 text-base-300 peer"
|
||||
name="my-accordion-det-1"
|
||||
>
|
||||
<summary className="collapse-title text-lg md:text-xl leading-tight font-hand flex items-baseline gap-4">
|
||||
<div className="flex items-center">
|
||||
<PersonIcon
|
||||
weight="duotone"
|
||||
className="text-accent"
|
||||
size={14}
|
||||
/>{" "}
|
||||
<PersonArmsSpreadIcon
|
||||
weight="duotone"
|
||||
className="text-accent"
|
||||
size={24}
|
||||
/>
|
||||
</div>
|
||||
To yourself, further along.
|
||||
</summary>
|
||||
<div className="collapse-content text-sm md:text-lg flex flex-col gap-4">
|
||||
<p>
|
||||
Not a journal. Not a note-to-self. A proper letter — to
|
||||
whoever you'll be in a year, or five, or ten.
|
||||
<br />
|
||||
Ask yourself of the healed wounds, forgotten fears, or the
|
||||
things you finally learned to live with.
|
||||
</p>
|
||||
<p className="font-serif font-medium opacity-70">
|
||||
Set a date and let a letter surprise you when you've long
|
||||
forgotten writing it.
|
||||
</p>
|
||||
</div>
|
||||
</details>
|
||||
<span className="absolute md:-right-8 md:-top-10 -top-4 -right-2 md:text-8xl text-6xl font-bold font-mono opacity-20 peer-open:opacity-60 pointer-events-none z-10 transition-all duration-500">
|
||||
03
|
||||
</span>
|
||||
</div>
|
||||
<div className="relative w-full">
|
||||
<details
|
||||
className="collapse shadow-xs glass opacity-75 open:opacity-100 text-base-300 peer"
|
||||
name="my-accordion-det-1"
|
||||
>
|
||||
<summary className="collapse-title text-lg md:text-xl leading-tight font-hand flex items-center gap-4">
|
||||
<SparkleIcon weight="duotone" className="text-accent" size={32} />{" "}
|
||||
For liberation.
|
||||
</summary>
|
||||
<div className="collapse-content text-sm md:text-lg flex flex-col gap-4">
|
||||
<p>
|
||||
Some unsaid words just need to leave your headspace. There's no
|
||||
recipient, no subject line, no send button. Just the act of
|
||||
putting it somewhere outside of yourself. <br />
|
||||
That's sometimes enough.
|
||||
</p>
|
||||
<p className="font-serif font-medium opacity-70">
|
||||
Say it once. All of it. Then let it fade.
|
||||
</p>
|
||||
</div>
|
||||
</details>
|
||||
<span className="absolute md:-right-8 md:-top-10 -top-4 -right-2 md:text-8xl text-6xl font-bold font-mono opacity-20 peer-open:opacity-60 pointer-events-none z-10 transition-all duration-500">
|
||||
04
|
||||
</span>
|
||||
</div>
|
||||
<div className="flex items-center justify-center gap-2 group mt-12">
|
||||
<img
|
||||
src={stamp}
|
||||
alt="stamp"
|
||||
className="rotate-6 group-hover:rotate-0 group-focus-within:rotate-0 transition-all duration-1000"
|
||||
/>
|
||||
<p className="md:text-xl mt-4">
|
||||
If any of these felt familiar,
|
||||
<br />
|
||||
no matter how little,
|
||||
<br />
|
||||
this is for you.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
function AttributionSection() {
|
||||
const [hover, setHover] = useState<{
|
||||
visible: boolean;
|
||||
x: number;
|
||||
y: number;
|
||||
}>({ visible: false, x: 0, y: 0 });
|
||||
|
||||
const navigate = useNavigate();
|
||||
|
||||
return (
|
||||
<div className="flex flex-col min-h-screen w-screen items-center py-18">
|
||||
{/* Saajan hover image */}
|
||||
<AnimatePresence>
|
||||
{hover.visible && (
|
||||
<motion.img
|
||||
src="/saajan.png"
|
||||
alt="Saajan Fernandes from The Lunchbox, cutout"
|
||||
initial={{ opacity: 0, scale: 0.5 }}
|
||||
animate={{ opacity: 1, scale: 1 }}
|
||||
exit={{ opacity: 0, scale: 0.5 }}
|
||||
transition={{ duration: 0.25, ease: "easeOut" }}
|
||||
className="pointer-events-none fixed z-50 w-56 md:w-72 rounded-lg shadow-warm object-cover"
|
||||
style={{
|
||||
left: hover.x + 16,
|
||||
top: hover.y - 32,
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
</AnimatePresence>
|
||||
|
||||
<h1
|
||||
className={
|
||||
"relative tracking-tighter text-5xl md:text-8xl text-neutral-content/80 font-extrabold italic font-serif"
|
||||
}
|
||||
>
|
||||
Honest Speak
|
||||
</h1>
|
||||
<div className="flex flex-col items-center shrink-0">
|
||||
<div
|
||||
className={
|
||||
"max-w-200 m-2 md:m-8 text-sm md:text-lg px-4 md:px-8 py-6 md:py-12 flex flex-col gap-4 md:gap-8 text-base-100 leading-relaxed bg-paper font-mono tracking-tight"
|
||||
}
|
||||
>
|
||||
Hi.
|
||||
<p>Thank you so much for making it this far. Really.</p>
|
||||
<p>
|
||||
<Logo type={"inline"} /> took a while to exist.
|
||||
<br />
|
||||
This started as a{" "}
|
||||
<a
|
||||
href="https://cs50.harvard.edu/web/"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
>
|
||||
CS50W
|
||||
</a>{" "}
|
||||
capstone, one I kept postponing until I ran out of reasons not to.
|
||||
When I eventually sat down to build, I knew it had to be more than a
|
||||
deadline; it had to be something that outlasted the grade. I wanted
|
||||
to create a space for the feelings we usually keep to ourselves and
|
||||
every hour spent on it was worth it. I've shared the edges of{" "}
|
||||
<Logo type={"inline"} /> here, but the heart of it is best found by
|
||||
exploring it yourself.
|
||||
</p>
|
||||
<p>
|
||||
I kept coming back to{" "}
|
||||
<span
|
||||
role="tooltip"
|
||||
className="cursor-default ul-wavy text-accent"
|
||||
onMouseEnter={(e) =>
|
||||
setHover({
|
||||
visible: true,
|
||||
x: e.clientX,
|
||||
y: e.clientY,
|
||||
})
|
||||
}
|
||||
onMouseMove={(e) =>
|
||||
setHover((h) => ({
|
||||
...h,
|
||||
x: e.clientX,
|
||||
y: e.clientY,
|
||||
}))
|
||||
}
|
||||
onMouseLeave={() => setHover((h) => ({ ...h, visible: false }))}
|
||||
>
|
||||
Saajan
|
||||
</span>{" "}
|
||||
from{" "}
|
||||
<a
|
||||
href="https://www.imdb.com/title/tt2350496/"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
>
|
||||
The Lunchbox
|
||||
</a>{" "}
|
||||
—{" "}
|
||||
<span className="italic">
|
||||
one of the most subtle yet brilliant portrayals by Irrfan Khan
|
||||
</span>{" "}
|
||||
— the quiet emotional weight he carries throughout the film,
|
||||
going through the motions of a lonely life, until those letters
|
||||
arrive and something inside him finally loosens. Of course, the
|
||||
ending felt like a deep sigh of "it is what it is". But something
|
||||
about the act of writing and letting the unsaid out eased it, even
|
||||
briefly. I think about that a lot.
|
||||
</p>
|
||||
<p>
|
||||
There's a lot that goes{" "}
|
||||
<span className={"text-primary font-hand text-lg md:text-xl"}>
|
||||
unsaid
|
||||
</span>{" "}
|
||||
now. Not that people feel less or for the lack of time, but because
|
||||
the ways we reach each other have quietly changed. We're always
|
||||
reachable <span className="italic">digitally,</span> yet somehow the
|
||||
things that matter most end up staying inside.
|
||||
<br />
|
||||
Maybe writing will help with that. Maybe something about putting
|
||||
words somewhere deliberate makes them feel less like something
|
||||
you're carrying.
|
||||
</p>
|
||||
<p>Or maybe it won't, but it's worth a try.</p>
|
||||
<p>
|
||||
<Logo type={"inline"} /> is for that try. I hope it helps.
|
||||
</p>
|
||||
<p
|
||||
className={
|
||||
"text-right font-hand text-base-content text-lg md:text-xl"
|
||||
}
|
||||
>
|
||||
— Ram
|
||||
</p>
|
||||
</div>
|
||||
<blockquote className="text-primary/50 italic mt-8 md:mt-12 mx-auto border-l-primary/20 leading-relaxed border-l pl-4 max-w-11/12 text-lg">
|
||||
"I think we forget things if there is nobody to tell them."
|
||||
<span className="block mt-2 text-sm not-italic text-base-content/30 w-full text-right">
|
||||
~ Saajan Fernandes, <span className="italic">The Lunchbox</span>
|
||||
</span>
|
||||
</blockquote>
|
||||
</div>
|
||||
<div className="mt-40 mb-44 w-full justify-center flex">
|
||||
<button
|
||||
type={"button"}
|
||||
onClick={() => navigate("/onboard")}
|
||||
className="btn btn-primary btn-wide rounded-full px-14 font-mono"
|
||||
>
|
||||
Begin
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user