@titas_mallick/wedding-site-gen 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (141) hide show
  1. package/.eslintignore +20 -0
  2. package/.eslintrc.json +93 -0
  3. package/.recover +9 -0
  4. package/.vscode/settings.json +3 -0
  5. package/LICENSE +21 -0
  6. package/README.md +83 -0
  7. package/app/Neo-Lucentism/layout.tsx +7 -0
  8. package/app/Neo-Lucentism/page.tsx +259 -0
  9. package/app/couple/layout.tsx +7 -0
  10. package/app/couple/page.tsx +164 -0
  11. package/app/error.tsx +31 -0
  12. package/app/guestbook/page.tsx +470 -0
  13. package/app/invitation/[slug]/layout.tsx +36 -0
  14. package/app/invitation/[slug]/page.tsx +462 -0
  15. package/app/invitation/maker/auth.js +165 -0
  16. package/app/invitation/maker/dashboard.js +81 -0
  17. package/app/invitation/maker/guestAdder.js +204 -0
  18. package/app/invitation/maker/guestShower.js +287 -0
  19. package/app/invitation/maker/layout.tsx +11 -0
  20. package/app/invitation/maker/page.js +168 -0
  21. package/app/invitation/maker/rsvpViewer.js +122 -0
  22. package/app/layout.tsx +98 -0
  23. package/app/mark-the-dates/layout.tsx +7 -0
  24. package/app/mark-the-dates/page.tsx +196 -0
  25. package/app/memories/layout.tsx +7 -0
  26. package/app/memories/page.tsx +29 -0
  27. package/app/page.tsx +5 -0
  28. package/app/providers.tsx +33 -0
  29. package/app/sagun/layout.tsx +7 -0
  30. package/app/sagun/page.tsx +348 -0
  31. package/app/song-requests/page.tsx +354 -0
  32. package/app/sukanya/layout.tsx +7 -0
  33. package/app/sukanya/page.tsx +167 -0
  34. package/app/titas/layout.tsx +7 -0
  35. package/app/titas/page.tsx +175 -0
  36. package/app/travel-guide/page.tsx +400 -0
  37. package/app/updates/maker/page.js +323 -0
  38. package/app/updates/overlay/page.tsx +144 -0
  39. package/app/updates/page.js +207 -0
  40. package/cli.mjs +196 -0
  41. package/components/ConciergeBot.tsx +203 -0
  42. package/components/CountdownTimer.tsx +137 -0
  43. package/components/Gallery.tsx +372 -0
  44. package/components/LiveVideos.tsx +173 -0
  45. package/components/OurStory.tsx +160 -0
  46. package/components/certificate.jsx +300 -0
  47. package/components/counter.tsx +14 -0
  48. package/components/footer.tsx +89 -0
  49. package/components/hero.tsx +136 -0
  50. package/components/icons.tsx +283 -0
  51. package/components/importantNews.js +168 -0
  52. package/components/navbar.tsx +106 -0
  53. package/components/primitives.ts +53 -0
  54. package/components/sagun.js +22 -0
  55. package/components/theme-switch.tsx +81 -0
  56. package/components/updates.tsx +118 -0
  57. package/components/weddingcard.js +68 -0
  58. package/components/weddingcard2.js +58 -0
  59. package/config/firebase-admin.js +17 -0
  60. package/config/firebase.ts +36 -0
  61. package/config/fonts.ts +21 -0
  62. package/config/site.ts +74 -0
  63. package/next-env.d.ts +6 -0
  64. package/next.config.js +4 -0
  65. package/package.json +64 -0
  66. package/postcss.config.js +6 -0
  67. package/public/DCV.gif +0 -0
  68. package/public/DCV2.gif +0 -0
  69. package/public/DCV3.gif +0 -0
  70. package/public/Images/1.jpg +0 -0
  71. package/public/Images/11.jpg +0 -0
  72. package/public/Images/12.jpg +0 -0
  73. package/public/Images/13.jpg +0 -0
  74. package/public/Images/14.jpg +0 -0
  75. package/public/Images/15.jpg +0 -0
  76. package/public/Images/16.jpg +0 -0
  77. package/public/Images/17.jpg +0 -0
  78. package/public/Images/18.jpg +0 -0
  79. package/public/Images/19.jpg +0 -0
  80. package/public/Images/2.jpg +0 -0
  81. package/public/Images/20.jpg +0 -0
  82. package/public/Images/21.jpg +0 -0
  83. package/public/Images/22.jpg +0 -0
  84. package/public/Images/3.jpg +0 -0
  85. package/public/Images/4.jpg +0 -0
  86. package/public/Images/5.jpg +0 -0
  87. package/public/Images/6.jpg +0 -0
  88. package/public/Images/7.jpg +0 -0
  89. package/public/Images/8.jpg +0 -0
  90. package/public/Images/9.jpg +0 -0
  91. package/public/Images/9b.jpg +0 -0
  92. package/public/Images/Patipatra.jpeg +0 -0
  93. package/public/audio (1).mp3 +0 -0
  94. package/public/audio (2).mp3 +0 -0
  95. package/public/bride.jpg +0 -0
  96. package/public/corner1-01.svg +1 -0
  97. package/public/favicon.ico +0 -0
  98. package/public/groom.jpg +0 -0
  99. package/public/invite.png +0 -0
  100. package/public/love-birds.png +0 -0
  101. package/public/next.svg +1 -0
  102. package/public/pubqr.png +0 -0
  103. package/public/pw/001.jpg +0 -0
  104. package/public/pw/002.jpg +0 -0
  105. package/public/pw/003.jpg +0 -0
  106. package/public/pw/004.jpg +0 -0
  107. package/public/pw/005.jpg +0 -0
  108. package/public/pw/006.jpg +0 -0
  109. package/public/pw/007.jpg +0 -0
  110. package/public/pw/008.jpg +0 -0
  111. package/public/pw/009.jpg +0 -0
  112. package/public/pw/010.jpg +0 -0
  113. package/public/pw/011.jpg +0 -0
  114. package/public/pw/012.jpg +0 -0
  115. package/public/pw/013.jpg +0 -0
  116. package/public/pw/014.jpg +0 -0
  117. package/public/pw/015.jpg +0 -0
  118. package/public/pw/016.jpg +0 -0
  119. package/public/pw/017.jpg +0 -0
  120. package/public/pw/018.jpg +0 -0
  121. package/public/pw/019.jpg +0 -0
  122. package/public/pw/020.jpg +0 -0
  123. package/public/pw/021.jpg +0 -0
  124. package/public/pw/022.jpg +0 -0
  125. package/public/pw/023.jpg +0 -0
  126. package/public/pw/024.jpg +0 -0
  127. package/public/pw/025.jpg +0 -0
  128. package/public/pw/026.jpg +0 -0
  129. package/public/pw/027.jpg +0 -0
  130. package/public/pw/028.jpg +0 -0
  131. package/public/pw/029.jpg +0 -0
  132. package/public/pw/030.jpg +0 -0
  133. package/public/pw/031.jpg +0 -0
  134. package/public/pw/032.jpg +0 -0
  135. package/public/qr.png +0 -0
  136. package/public/vercel.svg +1 -0
  137. package/styles/globals.css +3 -0
  138. package/tailwind.config.js +51 -0
  139. package/tsconfig.json +45 -0
  140. package/tsconfig.tsbuildinfo +1 -0
  141. package/types/index.ts +5 -0
@@ -0,0 +1,354 @@
1
+ "use client";
2
+
3
+ import { useState, useEffect } from "react";
4
+ import { motion, AnimatePresence } from "framer-motion";
5
+ import {
6
+ Card,
7
+ CardBody,
8
+ Input,
9
+ Button,
10
+ addToast,
11
+ Divider,
12
+ Spinner,
13
+ Table,
14
+ TableHeader,
15
+ TableColumn,
16
+ TableBody,
17
+ TableRow,
18
+ TableCell,
19
+ Chip,
20
+ Tooltip,
21
+ Modal,
22
+ ModalContent,
23
+ ModalHeader,
24
+ ModalBody,
25
+ ModalFooter
26
+ } from "@heroui/react";
27
+ import {
28
+ getFirestore,
29
+ collection,
30
+ addDoc,
31
+ serverTimestamp,
32
+ onSnapshot,
33
+ query,
34
+ orderBy,
35
+ deleteDoc,
36
+ doc,
37
+ updateDoc
38
+ } from "firebase/firestore";
39
+ import { getAuth, onAuthStateChanged } from "firebase/auth";
40
+ import firebaseApp from "@/config/firebase";
41
+ import { fontCursive, fontSans, fontMono } from "@/config/fonts";
42
+ import { HeartFilledIcon } from "@/components/icons";
43
+
44
+ const db = getFirestore(firebaseApp());
45
+ const auth = getAuth(firebaseApp());
46
+
47
+ export default function SongRequestsPage() {
48
+ const [formData, setFormData] = useState({ name: "", song: "", artist: "", captcha: "" });
49
+ const [loading, setLoading] = useState(false);
50
+ const [user, setUser] = useState<any>(null);
51
+ const [requests, setRequests] = useState<any[]>([]);
52
+ const [listLoading, setListLoading] = useState(true);
53
+
54
+ // Delete Confirmation States
55
+ const [isDeleteModalOpen, setIsDeleteModalOpen] = useState(false);
56
+ const [requestToDelete, setRequestToDelete] = useState<string | null>(null);
57
+
58
+ useEffect(() => {
59
+ const unsubscribeAuth = onAuthStateChanged(auth, (currentUser) => {
60
+ setUser(currentUser);
61
+ });
62
+ return () => unsubscribeAuth();
63
+ }, []);
64
+
65
+ useEffect(() => {
66
+ const q = query(collection(db, "song_requests"), orderBy("createdAt", "desc"));
67
+ const unsubscribeData = onSnapshot(q, (snapshot) => {
68
+ const data = snapshot.docs.map((doc) => ({
69
+ id: doc.id,
70
+ ...doc.data(),
71
+ }));
72
+ setRequests(data);
73
+ setListLoading(false);
74
+ });
75
+ return () => unsubscribeData();
76
+ }, []);
77
+
78
+ const handleSubmit = async (e: React.FormEvent) => {
79
+ e.preventDefault();
80
+
81
+ if (formData.captcha.trim() !== "10") {
82
+ addToast({
83
+ title: "Security Check",
84
+ description: "Please answer the question correctly (Hint: 10 years!).",
85
+ color: "warning",
86
+ });
87
+ return;
88
+ }
89
+
90
+ if (!formData.name || !formData.song) return;
91
+
92
+ setLoading(true);
93
+ try {
94
+ await addDoc(collection(db, "song_requests"), {
95
+ ...formData,
96
+ status: "pending",
97
+ createdAt: serverTimestamp(),
98
+ });
99
+ setFormData({ name: "", song: "", artist: "", captcha: "" });
100
+ addToast({
101
+ title: "Song Requested!",
102
+ description: "We'll try our best to play your favorite track at the Reception.",
103
+ color: "success",
104
+ });
105
+ } catch (err) {
106
+ console.error("Song request error", err);
107
+ addToast({
108
+ title: "Error",
109
+ description: "Could not save your request. Please try again.",
110
+ color: "danger",
111
+ });
112
+ } finally {
113
+ setLoading(false);
114
+ }
115
+ };
116
+
117
+ const handleDeleteClick = (id: string) => {
118
+ setRequestToDelete(id);
119
+ setIsDeleteModalOpen(true);
120
+ };
121
+
122
+ const handleConfirmDelete = async () => {
123
+ if (!requestToDelete) return;
124
+ try {
125
+ await deleteDoc(doc(db, "song_requests", requestToDelete));
126
+ addToast({ title: "Deleted", description: "Request removed.", color: "success" });
127
+ } catch (err) {
128
+ addToast({ title: "Error", color: "danger" });
129
+ } finally {
130
+ setIsDeleteModalOpen(false);
131
+ setRequestToDelete(null);
132
+ }
133
+ };
134
+
135
+ const toggleStatus = async (id: string, currentStatus: string) => {
136
+ try {
137
+ await updateDoc(doc(db, "song_requests", id), {
138
+ status: currentStatus === "played" ? "pending" : "played"
139
+ });
140
+ addToast({ title: "Status Updated", color: "success" });
141
+ } catch (err) {
142
+ addToast({ title: "Error", color: "danger" });
143
+ }
144
+ };
145
+
146
+ const isAdmin = user?.email === "titas@titas.titas";
147
+
148
+ return (
149
+ <div className="min-h-screen pb-20 pt-10 px-4 max-w-6xl mx-auto flex flex-col items-center">
150
+ {/* Header */}
151
+ <motion.div
152
+ initial={{ opacity: 0, scale: 0.95 }}
153
+ animate={{ opacity: 1, scale: 1 }}
154
+ className="text-center space-y-6 mb-12"
155
+ >
156
+ <div className="bg-wedding-pink-100 dark:bg-wedding-pink-900/30 p-4 rounded-full w-fit mx-auto">
157
+ <svg width="32" height="32" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" className="text-wedding-pink-500">
158
+ <path d="M9 18V5l12-2v13"></path>
159
+ <circle cx="6" cy="18" r="3"></circle>
160
+ <circle cx="18" cy="16" r="3"></circle>
161
+ </svg>
162
+ </div>
163
+ <h1 className={`${fontCursive.className} text-5xl md:text-7xl text-wedding-pink-600 dark:text-wedding-pink-400 py-2`}>
164
+ Reception Playlist
165
+ </h1>
166
+ <p className="text-default-500 max-w-xl mx-auto italic">
167
+ &quot;Music is the soul of our celebration. Tell us which song makes you want to hit the dance floor!&quot;
168
+ </p>
169
+ </motion.div>
170
+
171
+ <div className="grid lg:grid-cols-2 gap-12 w-full items-start">
172
+ {/* Left: Request Form */}
173
+ <Card className="bg-white/80 dark:bg-zinc-900/80 backdrop-blur-xl border border-wedding-gold-200 dark:border-wedding-gold-800 shadow-2xl p-6 md:p-8 rounded-[40px]">
174
+ <h2 className={`${fontSans.className} text-2xl font-bold mb-6 text-default-800 dark:text-white`}>Request a Song</h2>
175
+ <form onSubmit={handleSubmit} className="space-y-6">
176
+ <Input
177
+ isRequired
178
+ label="Your Name"
179
+ labelPlacement="outside-top"
180
+ placeholder="Who are we dancing with?"
181
+ variant="bordered"
182
+ value={formData.name}
183
+ onChange={(e) => setFormData({ ...formData, name: e.target.value })}
184
+ classNames={{
185
+ label: "text-wedding-pink-600 font-bold",
186
+ input: "outline-none",
187
+ inputWrapper: "border-wedding-pink-100 focus-within:!border-wedding-pink-500 h-14"
188
+ }}
189
+ />
190
+ <div className="grid md:grid-cols-2 gap-4">
191
+ <Input
192
+ isRequired
193
+ label="Song Title"
194
+ labelPlacement="outside-top"
195
+ placeholder="E.g. Kesariya"
196
+ variant="bordered"
197
+ value={formData.song}
198
+ onChange={(e) => setFormData({ ...formData, song: e.target.value })}
199
+ classNames={{
200
+ label: "text-wedding-pink-600 font-bold",
201
+ input: "outline-none",
202
+ inputWrapper: "border-wedding-pink-100 focus-within:!border-wedding-pink-500 h-14"
203
+ }}
204
+ />
205
+ <Input
206
+ label="Artist (Optional)"
207
+ labelPlacement="outside-top"
208
+ placeholder="E.g. Arijit Singh"
209
+ variant="bordered"
210
+ value={formData.artist}
211
+ onChange={(e) => setFormData({ ...formData, artist: e.target.value })}
212
+ classNames={{
213
+ label: "text-wedding-pink-600 font-bold",
214
+ input: "outline-none",
215
+ inputWrapper: "border-wedding-pink-100 focus-within:!border-wedding-pink-500 h-14"
216
+ }}
217
+ />
218
+ </div>
219
+
220
+ <Divider className="opacity-50" />
221
+
222
+ <div className="p-4 bg-wedding-pink-50 dark:bg-wedding-pink-900/10 rounded-2xl border border-wedding-pink-100 dark:border-wedding-pink-800/30">
223
+ <p className="text-[10px] font-bold text-wedding-pink-600 dark:text-wedding-pink-400 uppercase tracking-widest mb-2">Bot Protection</p>
224
+ <Input
225
+ isRequired
226
+ label="How many years have we been together?"
227
+ labelPlacement="outside-top"
228
+ placeholder="Answer in numbers"
229
+ variant="underlined"
230
+ value={formData.captcha}
231
+ onChange={(e) => setFormData({ ...formData, captcha: e.target.value })}
232
+ classNames={{
233
+ label: "text-default-600 text-xs",
234
+ input: "text-center font-bold text-xl outline-none",
235
+ }}
236
+ />
237
+ </div>
238
+
239
+ <Button
240
+ type="submit"
241
+ isLoading={loading}
242
+ className="w-full bg-gradient-to-r from-wedding-pink-500 to-wedding-gold-500 text-white font-black h-14 text-lg shadow-xl shadow-wedding-pink-500/20"
243
+ >
244
+ Submit Song Request
245
+ </Button>
246
+ </form>
247
+ </Card>
248
+
249
+ {/* Right: Requests Feed */}
250
+ <div className="space-y-6">
251
+ <div className="flex items-center justify-between px-2">
252
+ <h2 className={`${fontSans.className} text-2xl font-bold text-default-800 dark:text-white`}>The Queue</h2>
253
+ <Chip color="secondary" variant="flat" size="sm" className={fontMono.className}>
254
+ {requests.length} Total
255
+ </Chip>
256
+ </div>
257
+
258
+ <div className="space-y-4 max-h-[600px] overflow-y-auto pr-2 custom-scrollbar">
259
+ {listLoading ? (
260
+ <div className="flex justify-center py-10"><Spinner color="danger" /></div>
261
+ ) : requests.length === 0 ? (
262
+ <p className="text-center py-10 text-default-400 italic font-medium bg-default-50 dark:bg-zinc-900/30 rounded-3xl border border-dashed border-default-200">The playlist is waiting for your touch!</p>
263
+ ) : (
264
+ <AnimatePresence>
265
+ {requests.map((item, i) => (
266
+ <motion.div
267
+ key={item.id}
268
+ initial={{ opacity: 0, x: 20 }}
269
+ animate={{ opacity: 1, x: 0 }}
270
+ transition={{ delay: i * 0.05 }}
271
+ >
272
+ <Card className={`border ${item.status === 'played' ? 'bg-default-50/50 dark:bg-zinc-900/20 opacity-60' : 'bg-white dark:bg-zinc-900 shadow-lg'} transition-all`}>
273
+ <CardBody className="p-4 flex flex-row items-center justify-between gap-4">
274
+ <div className="flex items-center gap-4 flex-1 overflow-hidden">
275
+ <div className={`w-10 h-10 rounded-full flex items-center justify-center flex-shrink-0 ${item.status === 'played' ? 'bg-green-100 text-green-600' : 'bg-wedding-pink-100 text-wedding-pink-600'}`}>
276
+ {item.status === 'played' ? (
277
+ <svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="3" strokeLinecap="round" strokeLinejoin="round"><polyline points="20 6 9 17 4 12"></polyline></svg>
278
+ ) : (
279
+ <svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2.5" strokeLinecap="round" strokeLinejoin="round"><path d="M9 18V5l12-2v13"></path><circle cx="6" cy="18" r="3"></circle></svg>
280
+ )}
281
+ </div>
282
+ <div className="flex flex-col overflow-hidden">
283
+ <p className={`font-bold truncate ${item.status === 'played' ? 'line-through text-default-400' : 'text-default-800 dark:text-white'}`}>{item.song}</p>
284
+ <p className="text-xs text-default-500 truncate italic">{item.artist || 'Unknown Artist'} • Requested by {item.name}</p>
285
+ </div>
286
+ </div>
287
+
288
+ {isAdmin && (
289
+ <div className="flex gap-1">
290
+ <Tooltip content={item.status === 'played' ? 'Mark Pending' : 'Mark Played'}>
291
+ <Button isIconOnly size="sm" variant="flat" color={item.status === 'played' ? 'warning' : 'success'} onPress={() => toggleStatus(item.id, item.status)}>
292
+ {item.status === 'played' ? '↺' : '✓'}
293
+ </Button>
294
+ </Tooltip>
295
+ <Tooltip content="Delete">
296
+ <Button isIconOnly size="sm" variant="flat" color="danger" onPress={() => handleDeleteClick(item.id)}>
297
+ âś•
298
+ </Button>
299
+ </Tooltip>
300
+ </div>
301
+ )}
302
+ </CardBody>
303
+ </Card>
304
+ </motion.div>
305
+ ))}
306
+ </AnimatePresence>
307
+ )}
308
+ </div>
309
+ </div>
310
+ </div>
311
+
312
+ <div className="mt-16 text-center text-default-400 space-y-2">
313
+ <HeartFilledIcon className="w-6 h-6 mx-auto text-wedding-pink-200" />
314
+ <p className={`${fontMono.className} text-[10px] uppercase tracking-widest`}>Let the music play</p>
315
+ </div>
316
+
317
+ {/* Delete Confirmation Modal */}
318
+ <Modal
319
+ isOpen={isDeleteModalOpen}
320
+ onOpenChange={setIsDeleteModalOpen}
321
+ backdrop="blur"
322
+ size="xs"
323
+ className="dark:bg-zinc-900 border border-default-100 dark:border-zinc-800"
324
+ >
325
+ <ModalContent>
326
+ {(onClose) => (
327
+ <>
328
+ <ModalBody className="pt-8 pb-4 text-center space-y-4">
329
+ <div className="w-16 h-16 bg-danger-50 dark:bg-danger-900/20 rounded-full flex items-center justify-center mx-auto text-danger animate-bounce">
330
+ <svg width="32" height="32" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
331
+ <polyline points="3 6 5 6 21 6"></polyline>
332
+ <path d="M19 6v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6m3 0V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2"></path>
333
+ </svg>
334
+ </div>
335
+ <div>
336
+ <h3 className="text-xl font-bold text-default-900 dark:text-white">Delete Request?</h3>
337
+ <p className="text-sm text-default-500 mt-2">Are you sure you want to remove this song from the queue?</p>
338
+ </div>
339
+ </ModalBody>
340
+ <ModalFooter className="flex-col gap-2 pb-8">
341
+ <Button color="danger" className="w-full font-bold h-12" onPress={handleConfirmDelete}>
342
+ Yes, Remove Request
343
+ </Button>
344
+ <Button variant="flat" className="w-full font-semibold h-12" onPress={onClose}>
345
+ Cancel
346
+ </Button>
347
+ </ModalFooter>
348
+ </>
349
+ )}
350
+ </ModalContent>
351
+ </Modal>
352
+ </div>
353
+ );
354
+ }
@@ -0,0 +1,7 @@
1
+ export default function SukanyaLayout({
2
+ children,
3
+ }: {
4
+ children: React.ReactNode;
5
+ }) {
6
+ return <div className="w-full">{children}</div>;
7
+ }
@@ -0,0 +1,167 @@
1
+ "use client";
2
+
3
+ import { Image, Button, Card, CardBody } from "@heroui/react";
4
+ import { Link } from "@heroui/link";
5
+
6
+ import { fontCursive, fontSans, fontMono } from "@/config/fonts";
7
+ import { HeartFilledIcon } from "@/components/icons";
8
+
9
+ export default function SukanyaPage() {
10
+ return (
11
+ <div className="w-full min-h-screen pb-20">
12
+ {/* Hero / Intro */}
13
+ <section className="relative py-16 md:py-24 px-4 bg-wedding-pink-50/50 dark:bg-wedding-pink-900/10 rounded-md">
14
+ <div className="container mx-auto max-w-6xl flex flex-col md:flex-row-reverse items-center gap-12">
15
+ {/* Image */}
16
+ <div className="w-full md:w-1/2 flex justify-center md:justify-start">
17
+ <div className="relative w-72 h-72 md:w-96 md:h-96">
18
+ <div className="absolute inset-0 bg-wedding-pink-200 dark:bg-wedding-pink-800 rounded-full blur-2xl opacity-50 transform translate-x-4 translate-y-4" />
19
+ <Image
20
+ alt="Sukanya Saha"
21
+ className="rounded-full object-cover w-full h-full border-4 border-white dark:border-default-100 shadow-2xl z-10"
22
+ src="/bride.jpg"
23
+ />
24
+ </div>
25
+ </div>
26
+
27
+ {/* Text */}
28
+ <div className="w-full md:w-1/2 text-center md:text-right space-y-6">
29
+ <h1
30
+ className={`${fontCursive.className} text-6xl md:text-7xl text-wedding-pink-600 dark:text-wedding-pink-400`}
31
+ >
32
+ Sukanya Saha
33
+ </h1>
34
+ <p
35
+ className={`${fontMono.className} text-xl md:text-2xl text-default-600 dark:text-white font-medium`}
36
+ >
37
+ Dedicated Educator &amp; Compassionate Mentor
38
+ </p>
39
+ <p
40
+ className={`${fontSans.className} text-lg text-default-700 dark:text-white leading-relaxed max-w-lg mx-auto md:ml-auto md:mr-0`}
41
+ >
42
+ A dedicated teacher with a strong academic background in Botany
43
+ and an unwavering passion for nurturing young learners.
44
+ </p>
45
+ </div>
46
+ </div>
47
+ </section>
48
+
49
+ {/* Content Grid */}
50
+ <section className="container mx-auto max-w-5xl px-4 py-16 space-y-16">
51
+ {/* About */}
52
+ <div className="grid md:grid-cols-12 gap-8 items-start">
53
+ <div className="md:col-span-4">
54
+ <h2
55
+ className={`${fontCursive.className} text-4xl text-wedding-pink-500 dark:text-wedding-pink-400 md:text-right`}
56
+ >
57
+ The Journey
58
+ </h2>
59
+ </div>
60
+ <div className="md:col-span-8 space-y-4 text-default-700 dark:text-white text-lg leading-relaxed">
61
+ <p>
62
+ I strive to foster curiosity, empathy, and excellence in the
63
+ classroom. My academic journey began at{" "}
64
+ <strong>Serampore Girls High School</strong> and continued with
65
+ Botany Honours at <strong>Raja Peary Mohan College</strong>. I
66
+ earned my Master&apos;s from{" "}
67
+ <strong>Lady Brabourne College</strong>, building a deep research
68
+ orientation.
69
+ </p>
70
+ <p>
71
+ Currently, I am a school teacher at{" "}
72
+ <strong>Rishra Narayana School</strong> and a teaching partner at{" "}
73
+ <strong>EugenicsErudite</strong>, where I emphasize conceptual
74
+ clarity and moral growth.
75
+ </p>
76
+ </div>
77
+ </div>
78
+
79
+ {/* Philosophy */}
80
+ <div className="grid md:grid-cols-12 gap-8 items-start">
81
+ <div className="md:col-span-4">
82
+ <h2
83
+ className={`${fontCursive.className} text-4xl text-wedding-pink-500 dark:text-wedding-pink-400 md:text-right`}
84
+ >
85
+ My Philosophy
86
+ </h2>
87
+ </div>
88
+ <div className="md:col-span-8 space-y-4 text-default-700 dark:text-white text-lg leading-relaxed">
89
+ <p>
90
+ Education is the most powerful tool for personal and social
91
+ transformation. I design my lessons to balance structure with
92
+ exploration, encouraging students to think independently and act
93
+ ethically in an ever-changing world.
94
+ </p>
95
+ </div>
96
+ </div>
97
+
98
+ {/* Interests */}
99
+ <div className="grid md:grid-cols-12 gap-8 items-start">
100
+ <div className="md:col-span-4">
101
+ <h2
102
+ className={`${fontCursive.className} text-4xl text-wedding-pink-500 dark:text-wedding-pink-400 md:text-right`}
103
+ >
104
+ Beyond Work
105
+ </h2>
106
+ </div>
107
+ <div className="md:col-span-8 space-y-4 text-default-700 dark:text-white text-lg leading-relaxed">
108
+ <p>
109
+ I have a keen interest in Indian classical literature, nature
110
+ walks, and visual arts. I often find inspiration in the harmony
111
+ between traditional wisdom and modern scientific thought.
112
+ </p>
113
+ <p>
114
+ Outside the classroom, I enjoy exploring plant biology and
115
+ creating learner-friendly resources while staying connected to my
116
+ cultural roots.
117
+ </p>
118
+ </div>
119
+ </div>
120
+
121
+ {/* Partner / Values */}
122
+ <div className="grid md:grid-cols-12 gap-8 items-start">
123
+ <div className="md:col-span-4">
124
+ <h2
125
+ className={`${fontCursive.className} text-4xl text-wedding-pink-500 dark:text-wedding-pink-400 md:text-right`}
126
+ >
127
+ Love & Life
128
+ </h2>
129
+ </div>
130
+ <div className="md:col-span-8 space-y-6 text-default-700 dark:text-white text-lg leading-relaxed">
131
+ <p>
132
+ I believe that marriage is a space for mutual upliftment, calm
133
+ companionship, and respect for each other&apos;s individuality.
134
+ Simplicity and emotional well-being are the values that shape my
135
+ daily life.
136
+ </p>
137
+
138
+ <Card className="bg-wedding-pink-50 dark:bg-wedding-pink-900/20 border-none shadow-sm mt-6">
139
+ <CardBody className="flex flex-col md:flex-row items-center gap-6 p-6">
140
+ <HeartFilledIcon className="text-wedding-pink-500 w-12 h-12 flex-shrink-0" />
141
+ <div>
142
+ <h3
143
+ className={`${fontCursive.className} text-2xl text-wedding-pink-600 dark:text-wedding-pink-300 mb-2`}
144
+ >
145
+ My Partner
146
+ </h3>
147
+ <p className="text-default-600 dark:text-white text-sm mb-4">
148
+ I found a partner in Titas—someone who is sincere,
149
+ intellectually inclined, and my steady companion.
150
+ </p>
151
+ <Button
152
+ as={Link}
153
+ className="bg-wedding-gold-100 text-wedding-gold-700 font-semibold hover:bg-wedding-gold-200 dark:bg-wedding-gold-500 dark:text-black dark:hover:bg-wedding-gold-400"
154
+ href="/titas"
155
+ size="sm"
156
+ >
157
+ Meet Titas
158
+ </Button>
159
+ </div>
160
+ </CardBody>
161
+ </Card>
162
+ </div>
163
+ </div>
164
+ </section>
165
+ </div>
166
+ );
167
+ }
@@ -0,0 +1,7 @@
1
+ export default function TitasLayout({
2
+ children,
3
+ }: {
4
+ children: React.ReactNode;
5
+ }) {
6
+ return <div className="w-full">{children}</div>;
7
+ }