From 77d6b56bcb60af68a186e8f98e57d04f8f6c6a36 Mon Sep 17 00:00:00 2001
From: "gpt-engineer-app[bot]"
<159125892+gpt-engineer-app[bot]@users.noreply.github.com>
Date: Sun, 7 Jun 2026 08:38:34 +0000
Subject: [PATCH 1/4] Changes
Co-authored-by: akshayamaram <141863638+akshayamaram@users.noreply.github.com>
---
src/lib/quotes.ts | 70 +++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 70 insertions(+)
create mode 100644 src/lib/quotes.ts
diff --git a/src/lib/quotes.ts b/src/lib/quotes.ts
new file mode 100644
index 0000000..cbfe93b
--- /dev/null
+++ b/src/lib/quotes.ts
@@ -0,0 +1,70 @@
+// daily quotes + milestone quotes. engineering / aerospace / stoic flavor.
+
+export const DAILY_QUOTES: string[] = [
+ "the engineer's first duty is to the truth, not to the deadline.",
+ "a problem well stated is a problem half solved.",
+ "every equation you understand is a small piece of the universe you own.",
+ "rockets don't care how you feel. show up anyway.",
+ "the day you stop learning is the day the field leaves you behind.",
+ "first principles. always.",
+ "you don't rise to your goals. you fall to your habits.",
+ "the gap between knowing and doing is closed only by reps.",
+ "drag is real. so is lift. choose what you generate today.",
+ "no one ever drowned in sweat.",
+ "small daily improvements compound into stunning results.",
+ "the syllabus is finite. your time is too. respect both.",
+ "discipline is choosing between what you want now and what you want most.",
+ "if you can't explain it simply, you don't understand it well enough.",
+ "study like the exam is tomorrow. live like it isn't.",
+ "the expert in anything was once a beginner who refused to quit.",
+ "you are not behind. you are exactly where consistency would have put you.",
+ "the only easy day was yesterday.",
+ "motivation gets you started. systems keep you going.",
+ "don't break the chain.",
+ "an hour of focused work beats a day of distracted study.",
+ "the calm sea never made a skilled sailor.",
+ "feynman: the first principle is that you must not fool yourself.",
+ "von braun: research is what i'm doing when i don't know what i'm doing.",
+ "kalam: dream is not what you see in sleep. it is what doesn't let you sleep.",
+ "edison: genius is one percent inspiration, ninety-nine percent perspiration.",
+ "every topic you skip today will find you in the exam hall.",
+ "you don't need more time. you need fewer tabs.",
+ "thrust over drag. effort over excuse.",
+ "the syllabus shrinks every time you sit down. open it.",
+];
+
+export const MILESTONE_QUOTES: Record<25 | 50 | 75 | 100, string[]> = {
+ 25: [
+ "a quarter of the way. the takeoff roll is the loudest part.",
+ "twenty-five percent. momentum is a quiet thing until it isn't.",
+ "the first quarter is the hardest. you cleared it.",
+ ],
+ 50: [
+ "halfway. the view from here is the rest of the climb.",
+ "fifty percent. you are no longer starting. you are continuing.",
+ "the second half is paid for by the first. keep going.",
+ ],
+ 75: [
+ "three quarters. the runway is behind you now.",
+ "seventy-five percent. closer to done than to start.",
+ "you are in the final stretch. don't decelerate.",
+ ],
+ 100: [
+ "complete. a section closed is a small forever.",
+ "one hundred percent. carry this feeling into the next.",
+ "done. now revise. then move.",
+ ],
+};
+
+// pick a deterministic daily quote so the same quote shows all day.
+export function getDailyQuote(date = new Date()): { quote: string; key: string } {
+ const key = `${date.getFullYear()}-${date.getMonth() + 1}-${date.getDate()}`;
+ let h = 0;
+ for (let i = 0; i < key.length; i++) h = (h * 31 + key.charCodeAt(i)) >>> 0;
+ return { quote: DAILY_QUOTES[h % DAILY_QUOTES.length], key };
+}
+
+export function pickMilestoneQuote(threshold: 25 | 50 | 75 | 100): string {
+ const arr = MILESTONE_QUOTES[threshold];
+ return arr[Math.floor(Math.random() * arr.length)];
+}
From bc8c9849ea9c3ded911ba7df95625e68f0598dec Mon Sep 17 00:00:00 2001
From: "gpt-engineer-app[bot]"
<159125892+gpt-engineer-app[bot]@users.noreply.github.com>
Date: Sun, 7 Jun 2026 08:39:02 +0000
Subject: [PATCH 2/4] Changes
Co-authored-by: akshayamaram <141863638+akshayamaram@users.noreply.github.com>
---
src/features/motivation/DailyMail.tsx | 80 +++++++++++++++++++++
src/features/motivation/MilestoneRibbon.tsx | 58 +++++++++++++++
src/features/motivation/PickupBanner.tsx | 41 +++++++++++
src/lib/storage.ts | 3 +
4 files changed, 182 insertions(+)
create mode 100644 src/features/motivation/DailyMail.tsx
create mode 100644 src/features/motivation/MilestoneRibbon.tsx
create mode 100644 src/features/motivation/PickupBanner.tsx
diff --git a/src/features/motivation/DailyMail.tsx b/src/features/motivation/DailyMail.tsx
new file mode 100644
index 0000000..93b5cf3
--- /dev/null
+++ b/src/features/motivation/DailyMail.tsx
@@ -0,0 +1,80 @@
+import { useEffect, useState } from "react";
+import { getDailyQuote } from "@/lib/quotes";
+import { STORAGE_KEYS } from "@/lib/storage";
+
+export function DailyMail() {
+ const [open, setOpen] = useState(false);
+ const [unread, setUnread] = useState(false);
+ const [data, setData] = useState<{ quote: string; key: string } | null>(null);
+
+ useEffect(() => {
+ if (typeof window === "undefined") return;
+ const d = getDailyQuote();
+ setData(d);
+ try {
+ const last = localStorage.getItem(STORAGE_KEYS.dailyMailOpened);
+ setUnread(last !== d.key);
+ } catch {
+ setUnread(true);
+ }
+ }, []);
+
+ const openMail = () => {
+ setOpen(true);
+ if (data) {
+ try {
+ localStorage.setItem(STORAGE_KEYS.dailyMailOpened, data.key);
+ } catch {}
+ setUnread(false);
+ }
+ };
+
+ return (
+ <>
+
+
+ {open && data && (
+