diff --git a/src/App.tsx b/src/App.tsx index 163e9a6..25f6ca5 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -21,7 +21,6 @@ import { Drills } from "./pages/student/drills/page"; import { HardTestModules } from "./pages/student/hard-test-modules/page"; import { Analytics } from "./pages/student/Analytics"; import { QuestMap } from "./pages/student/QuestMap"; -import ErrorPage from "./pages/ErrorPage"; import { Register } from "./pages/auth/Register"; function App() { @@ -40,6 +39,7 @@ function App() { children: [ { element: , + children: [ { path: "home", diff --git a/src/components/FetchLessonPage.tsx b/src/components/FetchLessonPage.tsx index b78151a..9a70c2e 100644 --- a/src/components/FetchLessonPage.tsx +++ b/src/components/FetchLessonPage.tsx @@ -5,22 +5,16 @@ import { lazy, type ComponentType } from "react"; export type LessonId = // ---- EBRW ---- - | "ebrw-main-idea" - | "ebrw-explicit-meaning" + | "ebrw-words-in-context" + | "ebrw-text-structure-purpose" + | "ebrw-cross-text-connections" + | "ebrw-central-ideas-details" | "ebrw-inferences" - | "ebrw-graphic-displays" - | "ebrw-craft-structure" - | "ebrw-vocab-precise" - | "ebrw-vocab-meaning" - | "ebrw-expression-ideas" + | "ebrw-command-of-evidence" + | "ebrw-boundaries" + | "ebrw-form-structure-sense" | "ebrw-transitions" - | "ebrw-commas" - | "ebrw-semicolons-colons" - | "ebrw-dashes-apostrophes" - | "ebrw-subject-verb" - | "ebrw-pronouns" - | "ebrw-verbs" - | "ebrw-sentence-structure" + | "ebrw-rhetorical-synthesis" // ---- MATH ---- | "alg-linear-eq-1var" @@ -45,55 +39,45 @@ export type LessonId = | "geom-circles"; // ---- EBRW ---- -const EBRWMainIdea = lazy( - () => import("../pages/student/lessons/EBRWMainIdeaLesson"), +const EBRWWordsInContext = lazy( + () => import("../pages/student/lessons/EBRWWordsInContextLesson"), ); -const EBRWExplicitMeaning = lazy( - () => import("../pages/student/lessons/EBRWExplicitMeaningLesson"), + +const EBRWTextStructurePurpose = lazy( + () => import("../pages/student/lessons/EBRWTextStructurePurposeLesson"), ); + +const EBRWCrossText = lazy( + () => import("../pages/student/lessons/EBRWCrossTextLesson"), +); + +const EBRWCentralIdeas = lazy( + () => import("../pages/student/lessons/EBRWCentralIdeasLesson"), +); + const EBRWInferences = lazy( () => import("../pages/student/lessons/EBRWInferencesLesson"), ); -const EBRWGraphicDisplays = lazy( - () => import("../pages/student/lessons/EBRWGraphicDisplaysLesson"), + +const EBRWCommandEvidence = lazy( + () => import("../pages/student/lessons/EBRWCommandEvidenceLesson"), ); -const EBRWCraftStructure = lazy( - () => import("../pages/student/lessons/EBRWCraftStructureLesson"), + +const EBRWBoundaries = lazy( + () => import("../pages/student/lessons/EBRWBoundariesLesson"), ); -const EBRWVocabPrecise = lazy( - () => import("../pages/student/lessons/EBRWVocabPreciseLesson"), -); -const EBRWVocabMeaning = lazy( - () => import("../pages/student/lessons/EBRWVocabMeaningLesson"), -); -const EBRWExpressionIdeas = lazy( - () => import("../pages/student/lessons/EBRWExpressionIdeasLesson"), + +const EBRWFormStructureSense = lazy( + () => import("../pages/student/lessons/EBRWFormStructureSenseLesson"), ); + const EBRWTransitions = lazy( () => import("../pages/student/lessons/EBRWTransitionsLesson"), ); -const EBRWCommas = lazy( - () => import("../pages/student/lessons/EBRWCommasLesson"), -); -const EBRWSemicolonsColons = lazy( - () => import("../pages/student/lessons/EBRWSemicolonsColonsLesson"), -); -const EBRWDashesApostrophes = lazy( - () => import("../pages/student/lessons/EBRWDashesApostrophesLesson"), -); -const EBRWSubjectVerb = lazy( - () => import("../pages/student/lessons/EBRWSubjectVerbLesson"), -); -const EBRWPronouns = lazy( - () => import("../pages/student/lessons/EBRWPronounsLesson"), -); -const EBRWVerbs = lazy( - () => import("../pages/student/lessons/EBRWVerbsLesson"), -); -const EBRWSentenceStructure = lazy( - () => import("../pages/student/lessons/EBRWSentenceStructureLesson"), -); +const EBRWRhetoricalSynthesis = lazy( + () => import("../pages/student/lessons/EBRWRhetoricalSynthesisLesson"), +); // ---- MATH ---- const AlgLinearEq1Var = lazy( () => import("../pages/student/lessons/LinearEq1VarLesson"), @@ -158,24 +142,19 @@ const GeomCircles = lazy( // ---- Registry Map ---- export const LESSON_COMPONENT_MAP: Record = { - // EBRW - "ebrw-main-idea": EBRWMainIdea, - "ebrw-explicit-meaning": EBRWExplicitMeaning, + // ---- EBRW ---- + "ebrw-words-in-context": EBRWWordsInContext, + "ebrw-text-structure-purpose": EBRWTextStructurePurpose, + "ebrw-cross-text-connections": EBRWCrossText, + "ebrw-central-ideas-details": EBRWCentralIdeas, "ebrw-inferences": EBRWInferences, - "ebrw-graphic-displays": EBRWGraphicDisplays, - "ebrw-craft-structure": EBRWCraftStructure, - "ebrw-vocab-precise": EBRWVocabPrecise, - "ebrw-vocab-meaning": EBRWVocabMeaning, - "ebrw-expression-ideas": EBRWExpressionIdeas, + "ebrw-command-of-evidence": EBRWCommandEvidence, + "ebrw-boundaries": EBRWBoundaries, + "ebrw-form-structure-sense": EBRWFormStructureSense, "ebrw-transitions": EBRWTransitions, - "ebrw-commas": EBRWCommas, - "ebrw-semicolons-colons": EBRWSemicolonsColons, - "ebrw-dashes-apostrophes": EBRWDashesApostrophes, - "ebrw-subject-verb": EBRWSubjectVerb, - "ebrw-pronouns": EBRWPronouns, - "ebrw-verbs": EBRWVerbs, - "ebrw-sentence-structure": EBRWSentenceStructure, - // MATH + "ebrw-rhetorical-synthesis": EBRWRhetoricalSynthesis, + + // ---- MATH ---- "alg-linear-eq-1var": AlgLinearEq1Var, "alg-linear-eq-2var": AlgLinearEq2Var, "alg-linear-functions": AlgLinearFunctions, diff --git a/src/components/lessons/LessonShell.tsx b/src/components/lessons/LessonShell.tsx index 5b6644a..b06d373 100644 --- a/src/components/lessons/LessonShell.tsx +++ b/src/components/lessons/LessonShell.tsx @@ -109,7 +109,7 @@ export default function LessonShell({ const childArray = React.Children.toArray(children); return ( -
+
{/* ── Mobile toggle ── */} +
+ +
+ {cards.map((card, i) => { + const open = revealed.has(i); + return ( + + ); + })} +
+
+ ); +}; + +export default RevealCardGrid; diff --git a/src/components/lessons/useScrollReveal.ts b/src/components/lessons/useScrollReveal.ts new file mode 100644 index 0000000..381c1ab --- /dev/null +++ b/src/components/lessons/useScrollReveal.ts @@ -0,0 +1,36 @@ +import { useEffect } from 'react'; + +/** + * Observes all `.scroll-reveal` elements in the DOM and adds the `revealed` + * class when they scroll into view. Works with the stagger-1 … stagger-10 + * utility classes defined in index.css for sequenced entrance animations. + * + * Call once at the top of a lesson component: + * useScrollReveal(); + */ +export default function useScrollReveal() { + useEffect(() => { + const sel = [ + '.scroll-reveal:not(.revealed)', + '.scroll-reveal-left:not(.revealed)', + '.scroll-reveal-right:not(.revealed)', + '.scroll-reveal-scale:not(.revealed)', + ].join(','); + const els = document.querySelectorAll(sel); + if (!els.length) return; + + const obs = new IntersectionObserver( + entries => + entries.forEach(e => { + if (e.isIntersecting) { + e.target.classList.add('revealed'); + obs.unobserve(e.target); + } + }), + { threshold: 0.12, rootMargin: '0px 0px -60px 0px' }, + ); + + els.forEach(el => obs.observe(el)); + return () => obs.disconnect(); + }, []); +} diff --git a/src/data/rw/central-ideas-details.ts b/src/data/rw/central-ideas-details.ts index ecf328a..2eef08a 100644 --- a/src/data/rw/central-ideas-details.ts +++ b/src/data/rw/central-ideas-details.ts @@ -1,4 +1,4 @@ -import type { PracticeQuestion } from "../../types/lesson"; +import { type PracticeQuestion } from "../../types/lesson"; export const CENTRAL_IDEAS_EASY: PracticeQuestion[] = [ { diff --git a/src/data/rw/index.ts b/src/data/rw/index.ts index 67a452d..146df8a 100644 --- a/src/data/rw/index.ts +++ b/src/data/rw/index.ts @@ -151,8 +151,8 @@ export const RW_TOPICS: TopicRegistry = { id: "transitions", name: "Transitions", section: "rw", - category: "Standard English", - color: "purple", + category: "Expression of Ideas", + color: "rose", questions: { easy: TRANSITIONS_EASY, medium: TRANSITIONS_MEDIUM, diff --git a/src/pages/auth/Login.tsx b/src/pages/auth/Login.tsx index ce6f738..e9f08e0 100644 --- a/src/pages/auth/Login.tsx +++ b/src/pages/auth/Login.tsx @@ -2,191 +2,363 @@ import { useState, useEffect } from "react"; import type { FormEvent } from "react"; import { useNavigate, useLocation } from "react-router-dom"; import { useAuthStore } from "../../stores/authStore"; -import { Loader2, Mail, Lock } from "lucide-react"; +import { Loader2, Mail, Lock, Target, Clock, BarChart2 } from "lucide-react"; interface LocationState { from?: { pathname: string }; } -const DOTS = [ - { size: 12, color: "#f97316", top: "8%", left: "6%", delay: "0s" }, - { size: 7, color: "#a855f7", top: "22%", left: "3%", delay: "1.2s" }, - { size: 9, color: "#22c55e", top: "65%", left: "5%", delay: "0.6s" }, - { size: 8, color: "#f43f5e", top: "80%", left: "8%", delay: "2.1s" }, - { size: 12, color: "#3b82f6", top: "10%", right: "6%", delay: "1.8s" }, - { size: 7, color: "#eab308", top: "40%", right: "3%", delay: "0.9s" }, - { size: 10, color: "#a855f7", top: "72%", right: "5%", delay: "0.4s" }, - { size: 8, color: "#f97316", top: "55%", right: "8%", delay: "1.5s" }, -]; - const STYLES = ` @import url('https://fonts.googleapis.com/css2?family=Nunito:wght@400;700;800;900&family=Nunito+Sans:wght@400;600;700&display=swap'); - .lg-screen { + *, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; } + + .lg-root { min-height: 100vh; - background: #fffbf4; + display: flex; font-family: 'Nunito', sans-serif; + background: #fffbf4; + } + + /* ─── LEFT PANEL ─── */ + .lg-left { position: relative; + width: 50%; + min-height: 100vh; + background: linear-gradient(160deg, #060d1f 0%, #0f2044 40%, #0e3476 75%, #1a56c4 100%); + display: flex; + flex-direction: column; + align-items: flex-start; + justify-content: center; + padding: 3rem 3.5rem; overflow: hidden; + flex-shrink: 0; + } + + /* Animated grid */ + .lg-grid { + position: absolute; inset: 0; pointer-events: none; + background-image: + linear-gradient(rgba(99,179,255,0.06) 1px, transparent 1px), + linear-gradient(90deg, rgba(99,179,255,0.06) 1px, transparent 1px); + background-size: 52px 52px; + animation: gridScroll 25s linear infinite; + } + @keyframes gridScroll { + from { background-position: 0 0; } + to { background-position: 52px 52px; } + } + + /* Radial glow spots */ + .lg-glow { + position: absolute; pointer-events: none; border-radius: 50%; + filter: blur(60px); + } + .lg-glow-1 { width: 380px; height: 380px; background: #1d4ed8; opacity: 0.35; top: -120px; right: -80px; animation: glowPulse 8s ease-in-out infinite; } + .lg-glow-2 { width: 280px; height: 280px; background: #0ea5e9; opacity: 0.2; bottom: -60px; left: -60px; animation: glowPulse 10s ease-in-out infinite 2s; } + .lg-glow-3 { width: 200px; height: 200px; background: #f97316; opacity: 0.12; top: 55%; left: 55%; animation: glowPulse 12s ease-in-out infinite 1s; } + @keyframes glowPulse { + 0%,100% { transform: scale(1); opacity: 0.2; } + 50% { transform: scale(1.2); opacity: 0.35; } + } + + /* ── Floating score card ── */ + .lg-score-card { + position: absolute; + top: 9%; right: 6%; + width: 162px; + background: linear-gradient(135deg, rgba(255,255,255,0.11), rgba(255,255,255,0.05)); + border: 1px solid rgba(255,255,255,0.18); + border-radius: 20px; + padding: 1.1rem 1.2rem 1rem; + backdrop-filter: blur(16px); + box-shadow: 0 8px 32px rgba(0,0,0,0.35), inset 0 1px 0 rgba(255,255,255,0.15); + animation: floatA 6s ease-in-out infinite; + z-index: 2; + } + @keyframes floatA { + 0%,100% { transform: translateY(0) rotate(-1.5deg); } + 50% { transform: translateY(-14px) rotate(0.5deg); } + } + .lg-score-tag { + font-size: 0.58rem; font-weight: 800; letter-spacing: 0.14em; + text-transform: uppercase; color: #7dd3fc; margin-bottom: 0.5rem; + display: flex; align-items: center; gap: 0.3rem; + } + .lg-score-tag::before { content:''; width:6px;height:6px;border-radius:50%;background:#34d399;display:inline-block;box-shadow:0 0 6px #34d399; } + .lg-score-num { + font-size: 2.2rem; font-weight: 900; color: white; line-height: 1; margin-bottom: 0.2rem; + text-shadow: 0 2px 12px rgba(99,179,255,0.4); + } + .lg-score-delta { + font-family: 'Nunito Sans', sans-serif; + font-size: 0.7rem; font-weight: 700; color: #4ade80; + display: flex; align-items: center; gap: 0.2rem; margin-bottom: 0.75rem; + } + .lg-bars { + display: flex; align-items: flex-end; gap: 3px; height: 40px; + } + .lg-bar { + flex: 1; border-radius: 3px 3px 0 0; + animation: barGrow 1s cubic-bezier(0.34,1.56,0.64,1) both; + transform-origin: bottom; + } + @keyframes barGrow { + from { transform: scaleY(0); } + to { transform: scaleY(1); } + } + + /* ── Streak pill ── */ + .lg-streak { + position: absolute; + bottom: 12%; left: 5%; + background: linear-gradient(135deg, rgba(249,115,22,0.18), rgba(239,68,68,0.1)); + border: 1px solid rgba(249,115,22,0.4); + border-radius: 100px; + padding: 0.65rem 1.2rem; + backdrop-filter: blur(14px); + box-shadow: 0 4px 20px rgba(249,115,22,0.2), inset 0 1px 0 rgba(255,255,255,0.1); + display: flex; align-items: center; gap: 0.65rem; + animation: floatB 7s ease-in-out infinite 1s; + z-index: 2; + } + @keyframes floatB { + 0%,100% { transform: translateY(0) rotate(1deg); } + 50% { transform: translateY(-10px) rotate(-0.5deg); } + } + .lg-streak-fire { font-size: 1.5rem; filter: drop-shadow(0 0 8px #f97316); } + .lg-streak-text strong { display:block; font-size:0.85rem; font-weight:900; color:white; } + .lg-streak-text span { font-family:'Nunito Sans',sans-serif; font-size:0.68rem; font-weight:600; color:#fed7aa; } + + /* ── Questions badge ── */ + .lg-q-badge { + position: absolute; + bottom: 28%; right: 5%; + background: linear-gradient(135deg, rgba(139,92,246,0.18), rgba(99,102,241,0.1)); + border: 1px solid rgba(139,92,246,0.4); + border-radius: 16px; + padding: 0.7rem 1.05rem; + backdrop-filter: blur(14px); + box-shadow: 0 4px 20px rgba(139,92,246,0.2), inset 0 1px 0 rgba(255,255,255,0.1); + animation: floatA 9s ease-in-out infinite 0.5s; + z-index: 2; + } + .lg-q-badge p { font-family:'Nunito Sans',sans-serif; font-size:0.68rem; font-weight:700; color:#c4b5fd; margin-bottom:0.15rem; } + .lg-q-badge strong { font-size:1.35rem; font-weight:900; color:white; display:block; text-shadow:0 0 20px rgba(167,139,250,0.5); } + + /* ── Accuracy ring ── */ + .lg-ring-wrap { + position: absolute; + top: 52%; left: 6%; + width: 80px; height: 80px; + animation: floatB 10s ease-in-out infinite 0.8s; + z-index: 2; + } + .lg-ring-svg { width: 80px; height: 80px; transform: rotate(-90deg); } + .lg-ring-bg { fill: none; stroke: rgba(255,255,255,0.08); stroke-width: 5; } + .lg-ring-fill { fill: none; stroke: #34d399; stroke-width: 5; stroke-linecap: round; + stroke-dasharray: 188; stroke-dashoffset: 18; + animation: ringFill 1.8s ease both 0.3s; } + @keyframes ringFill { + from { stroke-dashoffset: 188; } + to { stroke-dashoffset: 18; } + } + .lg-ring-label { + position: absolute; inset: 0; + display: flex; flex-direction: column; align-items: center; justify-content: center; + } + .lg-ring-label strong { font-size: 0.95rem; font-weight: 900; color: white; line-height: 1; } + .lg-ring-label span { font-family:'Nunito Sans',sans-serif; font-size: 0.52rem; font-weight: 700; color: #7dd3fc; text-transform: uppercase; letter-spacing: 0.05em; } + + /* Scattered glitter dots */ + .lg-glitter { position: absolute; border-radius: 50%; pointer-events: none; animation: glitterFloat 8s ease-in-out infinite; } + @keyframes glitterFloat { + 0%,100% { transform: translateY(0) scale(1); opacity: 0.5; } + 50% { transform: translateY(-16px) scale(1.3); opacity: 0.9; } + } + + /* Stars */ + .lg-star { position: absolute; pointer-events: none; animation: starTwinkle 2.5s ease-in-out infinite; color: #fde68a; } + @keyframes starTwinkle { + 0%,100% { opacity: 0.4; transform: scale(0.9) rotate(0deg); } + 50% { opacity: 1; transform: scale(1.4) rotate(20deg); } + } + + /* Thin decorative rings */ + .lg-deco-ring { + position: absolute; border-radius: 50%; pointer-events: none; + border: 1.5px solid rgba(255,255,255,0.07); + animation: decoSpin 40s linear infinite; + } + @keyframes decoSpin { to { transform: rotate(360deg); } } + + /* Panel content */ + .lg-panel-content { + position: relative; z-index: 3; + display: flex; flex-direction: column; + align-items: flex-start; gap: 2rem; + width: 100%; + } + + .lg-panel-logo { display: flex; align-items: center; gap: 0.75rem; } + .lg-panel-logo-badge { + width: 46px; height: 46px; border-radius: 13px; + background: linear-gradient(135deg, #f97316, #ef4444); display: flex; align-items: center; justify-content: center; - padding: 2rem 1.25rem; + box-shadow: 0 5px 0 rgba(0,0,0,0.3), 0 8px 20px rgba(249,115,22,0.45); + font-size: 1.3rem; + } + .lg-panel-logo-text { font-size: 1.35rem; font-weight: 900; color: white; letter-spacing: -0.02em; } + + .lg-panel-headline { display: flex; flex-direction: column; gap: 0.6rem; } + .lg-panel-headline h2 { + font-size: 2.6rem; font-weight: 900; line-height: 1.1; + color: white; letter-spacing: -0.035em; + text-shadow: 0 4px 24px rgba(0,0,0,0.3); + } + .lg-panel-headline h2 span { + background: linear-gradient(90deg, #fbbf24 0%, #f97316 60%, #ef4444 100%); + -webkit-background-clip: text; -webkit-text-fill-color: transparent; + filter: drop-shadow(0 0 16px rgba(249,115,22,0.4)); + } + .lg-panel-headline p { + font-family: 'Nunito Sans', sans-serif; + font-size: 0.92rem; font-weight: 600; color: #93c5fd; line-height: 1.65; } - /* Blobs */ - .lg-blob { position:fixed;pointer-events:none;z-index:0; } - .lg-blob-1 { width:280px;height:280px;background:#fde68a;top:-100px;left:-100px;border-radius:60% 40% 70% 30%/50% 60% 40% 50%;animation:lgWobble1 14s ease-in-out infinite; } - .lg-blob-2 { width:220px;height:220px;background:#a5f3c0;bottom:-60px;left:4%;border-radius:40% 60% 30% 70%/60% 40% 60% 40%;animation:lgWobble2 16s ease-in-out infinite; } - .lg-blob-3 { width:250px;height:250px;background:#fbcfe8;top:10%;right:-70px;border-radius:70% 30% 50% 50%/40% 60% 40% 60%;animation:lgWobble1 18s ease-in-out infinite reverse; } - .lg-blob-4 { width:180px;height:180px;background:#bfdbfe;bottom:8%;right:0;border-radius:50% 50% 30% 70%/60% 40% 60% 40%;animation:lgWobble2 12s ease-in-out infinite; } - - @keyframes lgWobble1 { - 0%,100%{border-radius:60% 40% 70% 30%/50% 60% 40% 50%;transform:translate(0,0) rotate(0deg);} - 50%{border-radius:40% 60% 30% 70%/60% 40% 60% 40%;transform:translate(14px,18px) rotate(8deg);} + /* Stats row */ + .lg-stats { display: flex; gap: 0.75rem; width: 100%; } + .lg-stat { + flex: 1; + background: linear-gradient(135deg, rgba(255,255,255,0.09), rgba(255,255,255,0.04)); + border: 1px solid rgba(255,255,255,0.13); + border-radius: 18px; padding: 0.9rem 0.85rem; + backdrop-filter: blur(12px); + box-shadow: 0 4px 16px rgba(0,0,0,0.25), inset 0 1px 0 rgba(255,255,255,0.12); + display: flex; flex-direction: column; gap: 0.2rem; + animation: statSlide 0.5s ease both; } - @keyframes lgWobble2 { - 0%,100%{border-radius:40% 60% 30% 70%/60% 40% 60% 40%;transform:translate(0,0) rotate(0deg);} - 50%{border-radius:60% 40% 70% 30%/40% 60% 40% 60%;transform:translate(-12px,14px) rotate(-6deg);} + .lg-stat:nth-child(1) { animation-delay: 0.1s; } + .lg-stat:nth-child(2) { animation-delay: 0.2s; } + .lg-stat:nth-child(3) { animation-delay: 0.3s; } + @keyframes statSlide { + from { opacity: 0; transform: translateY(14px); } + to { opacity: 1; transform: translateY(0); } } + .lg-stat-icon { width: 28px; height: 28px; border-radius: 8px; display:flex;align-items:center;justify-content:center; margin-bottom:0.3rem; } + .lg-stat strong { font-size: 1.3rem; font-weight: 900; color: white; line-height: 1; } + .lg-stat span { font-family:'Nunito Sans',sans-serif; font-size:0.64rem; font-weight:600; color:#93c5fd; } - .lg-dot { position:fixed;border-radius:50%;pointer-events:none;z-index:0;opacity:0.28;animation:lgFloat 7s ease-in-out infinite; } - @keyframes lgFloat { - 0%,100%{transform:translateY(0) rotate(0deg);} - 50%{transform:translateY(-14px) rotate(180deg);} + /* Social proof */ + .lg-social { display: flex; align-items: center; gap: 0.75rem; } + .lg-avs { display: flex; } + .lg-av { + width: 30px; height: 30px; border-radius: 50%; + border: 2px solid #0f2044; margin-left: -8px; + display: flex; align-items: center; justify-content: center; + font-size: 0.6rem; font-weight: 800; color: white; + box-shadow: 0 2px 8px rgba(0,0,0,0.3); } + .lg-av:first-child { margin-left: 0; } + .lg-social p { font-family:'Nunito Sans',sans-serif; font-size:0.75rem; font-weight:700; color:#93c5fd; } + .lg-social p strong { color:#fbbf24; } - /* Card */ - .lg-card { + /* ─── RIGHT PANEL ─── */ + .lg-right { + flex: 1; + display: flex; align-items: center; justify-content: center; + padding: 3rem 4rem; + position: relative; overflow: hidden; + } + .lg-bg-dot { position:absolute;border-radius:50%;pointer-events:none;opacity:0.09;animation:bgFloat 10s ease-in-out infinite; } + @keyframes bgFloat { 0%,100%{transform:translateY(0);} 50%{transform:translateY(-14px);} } + + .lg-form-wrap { position: relative; z-index: 1; width: 100%; max-width: 400px; - background: white; border: 2.5px solid #f3f4f6; - border-radius: 28px; - box-shadow: 0 12px 40px rgba(0,0,0,0.08), 0 4px 12px rgba(0,0,0,0.04); - padding: 2.25rem 2rem 2rem; - display: flex; flex-direction: column; gap: 1.75rem; - animation: lgPopIn 0.5s cubic-bezier(0.34,1.56,0.64,1) both; + display: flex; flex-direction: column; gap: 2rem; + animation: formPop 0.55s cubic-bezier(0.34,1.56,0.64,1) both; } - @keyframes lgPopIn { - from { opacity:0; transform:scale(0.9) translateY(20px); } - to { opacity:1; transform:scale(1) translateY(0); } + @keyframes formPop { + from { opacity:0; transform:translateY(22px) scale(0.97); } + to { opacity:1; transform:translateY(0) scale(1); } } - /* Logo area */ - .lg-logo-wrap { - display: flex; flex-direction: column; align-items: center; gap: 0.85rem; - } - .lg-logo-badge { - width: 64px; height: 64px; border-radius: 20px; - background: linear-gradient(135deg, #a855f7, #7c3aed); - display: flex; align-items: center; justify-content: center; - box-shadow: 0 6px 0 #5b21b655, 0 10px 24px rgba(124,58,237,0.25); - font-size: 1.75rem; - animation: lgPopIn 0.5s cubic-bezier(0.34,1.56,0.64,1) 0.1s both; - } - .lg-title { - font-size: 1.5rem; font-weight: 900; color: #1e1b4b; - letter-spacing: -0.02em; text-align: center; - } - .lg-sub { - font-family: 'Nunito Sans', sans-serif; - font-size: 0.82rem; font-weight: 600; color: #9ca3af; - text-align: center; margin-top: -0.25rem; - } + .lg-form-header { display:flex;flex-direction:column;gap:0.4rem; } + .lg-form-header h1 { font-size:2rem;font-weight:900;color:#1e1b4b;letter-spacing:-0.03em;line-height:1.2; } + .lg-form-header p { font-family:'Nunito Sans',sans-serif;font-size:0.88rem;font-weight:600;color:#9ca3af; } - /* Form fields */ - .lg-fields { display: flex; flex-direction: column; gap: 1rem; } - - .lg-field { display: flex; flex-direction: column; gap: 0.4rem; } - .lg-label { - font-size: 0.72rem; font-weight: 800; letter-spacing: 0.1em; - text-transform: uppercase; color: #6b7280; - padding-left: 0.25rem; - } - .lg-input-wrap { position: relative; } - .lg-input-icon { - position: absolute; left: 0.85rem; top: 50%; - transform: translateY(-50%); pointer-events: none; color: #9ca3af; - transition: color 0.2s ease; - } + .lg-fields { display:flex;flex-direction:column;gap:1.1rem; } + .lg-field { display:flex;flex-direction:column;gap:0.4rem; } + .lg-label { font-size:0.7rem;font-weight:800;letter-spacing:0.1em;text-transform:uppercase;color:#6b7280;padding-left:0.2rem; } + .lg-input-wrap { position:relative; } + .lg-input-icon { position:absolute;left:0.9rem;top:50%;transform:translateY(-50%);pointer-events:none;color:#9ca3af;transition:color 0.2s; } .lg-input { - width: 100%; padding: 0.8rem 1rem 0.8rem 2.6rem; - background: #f9fafb; border: 2.5px solid #f3f4f6; - border-radius: 14px; - font-family: 'Nunito Sans', sans-serif; - font-size: 0.88rem; font-weight: 600; color: #1e1b4b; - outline: none; transition: all 0.2s ease; - box-sizing: border-box; + width:100%;padding:0.9rem 1rem 0.9rem 2.65rem; + background:#f9fafb;border:2.5px solid #f3f4f6;border-radius:14px; + font-family:'Nunito Sans',sans-serif;font-size:0.9rem;font-weight:600;color:#1e1b4b; + outline:none;transition:all 0.2s;box-sizing:border-box; } - .lg-input:focus { - background: white; border-color: #c4b5fd; - box-shadow: 0 0 0 3px rgba(168,85,247,0.1); - } - .lg-input:focus ~ .lg-input-icon { color: #a855f7; } - .lg-input:disabled { opacity: 0.5; cursor: not-allowed; } - .lg-input::placeholder { color: #d1d5db; } + .lg-input:focus { background:white;border-color:#93c5fd;box-shadow:0 0 0 3.5px rgba(59,130,246,0.1); } + .lg-input:disabled { opacity:0.5;cursor:not-allowed; } + .lg-input::placeholder { color:#d1d5db; } - /* Remember me */ - .lg-remember { - display: flex; align-items: center; gap: 0.5rem; - padding: 0 0.1rem; - } - .lg-checkbox { - width: 18px; height: 18px; border-radius: 6px; - accent-color: #a855f7; cursor: pointer; flex-shrink: 0; - } - .lg-remember-label { - font-family: 'Nunito Sans', sans-serif; - font-size: 0.8rem; font-weight: 600; color: #6b7280; - cursor: pointer; - } + .lg-remember { display:flex;align-items:center;gap:0.5rem;padding:0 0.1rem; } + .lg-checkbox { width:17px;height:17px;border-radius:5px;accent-color:#3b82f6;cursor:pointer;flex-shrink:0; } + .lg-remember-label { font-family:'Nunito Sans',sans-serif;font-size:0.8rem;font-weight:600;color:#6b7280;cursor:pointer; } - /* Error */ .lg-error { - background: #fff1f2; border: 2px solid #fecdd3; - border-radius: 14px; padding: 0.75rem 1rem; - font-family: 'Nunito Sans', sans-serif; - font-size: 0.82rem; font-weight: 700; color: #e11d48; - display: flex; align-items: center; gap: 0.5rem; + background:#fff1f2;border:2px solid #fecdd3;border-radius:14px;padding:0.8rem 1rem; + font-family:'Nunito Sans',sans-serif;font-size:0.82rem;font-weight:700;color:#e11d48; + display:flex;align-items:center;gap:0.5rem; } - /* Submit button */ .lg-btn { - width: 100%; padding: 0.95rem; - background: #f97316; color: white; border: none; - border-radius: 100px; cursor: pointer; - font-family: 'Nunito', sans-serif; font-size: 0.95rem; font-weight: 900; - display: flex; align-items: center; justify-content: center; gap: 0.5rem; - box-shadow: 0 6px 0 #c2560e, 0 8px 20px rgba(249,115,22,0.25); - transition: transform 0.1s ease, box-shadow 0.1s ease; + width:100%;padding:1rem;background:#f97316;color:white;border:none;border-radius:100px;cursor:pointer; + font-family:'Nunito',sans-serif;font-size:1rem;font-weight:900; + display:flex;align-items:center;justify-content:center;gap:0.5rem; + box-shadow:0 6px 0 #c2560e,0 10px 24px rgba(249,115,22,0.28); + transition:transform 0.1s,box-shadow 0.1s; } - .lg-btn:hover { transform:translateY(-2px); box-shadow:0 8px 0 #c2560e,0 12px 24px rgba(249,115,22,0.3); } - .lg-btn:active { transform:translateY(3px); box-shadow:0 3px 0 #c2560e; } - .lg-btn:disabled { - background: #e5e7eb; color: #9ca3af; - cursor: not-allowed; box-shadow: 0 4px 0 #d1d5db; - } - .lg-btn:disabled:hover { transform: none; box-shadow: 0 4px 0 #d1d5db; } + .lg-btn:hover { transform:translateY(-2px);box-shadow:0 8px 0 #c2560e,0 14px 28px rgba(249,115,22,0.32); } + .lg-btn:active { transform:translateY(3px);box-shadow:0 3px 0 #c2560e; } + .lg-btn:disabled { background:#e5e7eb;color:#9ca3af;cursor:not-allowed;box-shadow:0 4px 0 #d1d5db; } + .lg-btn:disabled:hover { transform:none;box-shadow:0 4px 0 #d1d5db; } - .lg-spinner { animation: lgSpin 0.8s linear infinite; } - @keyframes lgSpin { to { transform: rotate(360deg); } } + .lg-spinner { animation:spin 0.8s linear infinite; } + @keyframes spin { to { transform:rotate(360deg); } } - /* Footer hint */ - .lg-footer { - text-align: center; - font-family: 'Nunito Sans', sans-serif; - font-size: 0.75rem; font-weight: 600; color: #9ca3af; + .lg-footer { text-align:center;font-family:'Nunito Sans',sans-serif;font-size:0.75rem;font-weight:600;color:#9ca3af; } + .lg-signup-footer { text-align:center;font-family:'Nunito Sans',sans-serif;font-size:0.8rem;font-weight:600;color:#9ca3af; } + .lg-link { color:#f97316;font-weight:800;text-decoration:none;cursor:pointer; } + .lg-link:hover { color:#ea6c00; } + + @media (max-width: 860px) { + .lg-left { display:none; } + .lg-right { padding:2rem 1.5rem; } } - .rg-footer { - text-align: center; - font-family: 'Nunito Sans', sans-serif; - font-size: 0.78rem; font-weight: 600; color: #9ca3af; - } - .rg-link { - color: #a855f7; font-weight: 800; text-decoration: none; - transition: color 0.2s ease; - } - .rg-link:hover { color: #7c3aed; } `; +const BAR_HEIGHTS = [30, 50, 42, 75, 55, 88, 65]; +const BAR_COLORS = [ + "#60a5fa", + "#60a5fa", + "#7dd3fc", + "#38bdf8", + "#60a5fa", + "#7dd3fc", + "#38bdf8", +]; + +const AV_COLORS = [ + "linear-gradient(135deg,#3b82f6,#1d4ed8)", + "linear-gradient(135deg,#f97316,#ef4444)", + "linear-gradient(135deg,#22c55e,#15803d)", + "linear-gradient(135deg,#a855f7,#7c3aed)", + "linear-gradient(135deg,#eab308,#ca8a04)", +]; +const AV_INITIALS = ["SK", "NR", "TM", "AB", "PL"]; + export const Login = () => { const [email, setEmail] = useState(""); const [password, setPassword] = useState(""); @@ -195,13 +367,12 @@ export const Login = () => { const { login, isAuthenticated, isLoading, error, clearError } = useAuthStore(); - - const from = (location.state as LocationState)?.from?.pathname || "/student"; + const from = + (location.state as LocationState)?.from?.pathname || "/student/home"; useEffect(() => { if (isAuthenticated) navigate("/student/home", { replace: true }); }, [isAuthenticated, navigate]); - useEffect(() => { return () => clearError(); }, [clearError]); @@ -216,138 +387,323 @@ export const Login = () => { if (isAuthenticated) return null; return ( -
+
- {/* Blobs */} -
-
-
-
+ {/* ── LEFT PANEL ── */} +
+ {/* Background layers */} +
+
+
+
- {/* Dots */} - {DOTS.map((d, i) => ( -
- ))} - -
- {/* Logo + heading */} -
- EdBridge { - (e.target as HTMLImageElement).style.display = "none"; - }} + {/* Decorative spinning rings */} + {[ + { s: 260, t: "38%", l: "58%", mt: -130, ml: -130 }, + { + s: 380, + t: "42%", + l: "54%", + mt: -190, + ml: -190, + dir: "reverse" as const, + }, + ].map((r, i) => ( +
+ ))} -
-

Welcome back 👋

-

Sign in to continue your SAT prep

+ {/* Floating score card */} +
+

SAT Score

+

1480

+

▲ +120 pts this month

+
+ {BAR_HEIGHTS.map((h, i) => ( +
+ ))}
- {/* Fields */} -
- {/* Email */} -
- -
- - setEmail(e.target.value)} - disabled={isLoading} - /> -
+ {/* Accuracy ring */} +
+ + + + +
+ 94% + Accuracy
+
- {/* Password */} -
- -
- - setPassword(e.target.value)} - disabled={isLoading} - /> -
+ {/* Streak pill */} +
+ 🔥 +
+ 14-day streak! + Keep it going
+
- {/* Remember me */} -
- - -
+ {/* Questions badge */} +
+

Questions solved

+ 2,847 +
- {/* Error */} - {error && ( -
- ⚠️ {error} -
- )} + {/* Glitter dots */} + {[ + { s: 10, c: "#60a5fa", t: "13%", l: "58%", d: "0s", dur: "9s" }, + { s: 7, c: "#fbbf24", t: "70%", l: "70%", d: "1s", dur: "11s" }, + { s: 12, c: "#34d399", t: "38%", l: "7%", d: "0.4s", dur: "8s" }, + { s: 5, c: "#f472b6", t: "82%", l: "35%", d: "1.8s", dur: "13s" }, + { s: 8, c: "#a78bfa", t: "20%", l: "36%", d: "0.9s", dur: "10s" }, + { s: 6, c: "#fb923c", t: "62%", l: "80%", d: "1.3s", dur: "7s" }, + ].map((d, i) => ( +
+ ))} - {/* Submit */} - -
+ ★ + + ))} -

- By signing in you agree to Edbridge's Terms & Privacy Policy. -

-

- Don't have an account?{" "} - - Sign up - -

+ {/* Panel content */} +
+
+
📚
+ EdBridge +
+ +
+

+ Welcome +
+ back, +
+ champion. +

+

+ Your SAT goals are waiting. +
+ Pick up right where you left off. +

+
+ +
+ {[ + { + icon: , + bg: "linear-gradient(135deg,#3b82f6,#1d4ed8)", + val: "94%", + label: "Accuracy", + }, + { + icon: , + bg: "linear-gradient(135deg,#f97316,#ef4444)", + val: "47m", + label: "Daily study", + }, + { + icon: , + bg: "linear-gradient(135deg,#22c55e,#15803d)", + val: "+180", + label: "Score gain", + }, + ].map((s, i) => ( +
+
+ {s.icon} +
+ {s.val} + {s.label} +
+ ))} +
+ +
+
+ {AV_INITIALS.map((init, i) => ( +
+ {init} +
+ ))} +
+

+ 2,400+ students improved their scores +

+
+
+
+ + {/* ── RIGHT PANEL ── */} +
+ {[ + { s: 200, c: "#f97316", t: "4%", r: "4%", d: "0s", dur: "12s" }, + { s: 120, c: "#3b82f6", b: "8%", l: "2%", d: "1.5s", dur: "10s" }, + { s: 70, c: "#22c55e", t: "52%", r: "2%", d: "0.8s", dur: "8s" }, + ].map((d, i) => ( +
+ ))} + +
+
+

Welcome back 👋

+

Sign in to continue your SAT prep

+
+ +
+
+ +
+ + setEmail(e.target.value)} + disabled={isLoading} + /> +
+
+ +
+ +
+ + setPassword(e.target.value)} + disabled={isLoading} + /> +
+
+ +
+ + +
+ + {error && ( +
+ ⚠️ {error} +
+ )} + + +
+ +

+ By signing in you agree to EdBridge's Terms & Privacy Policy. +

+

+ Don't have an account?{" "} + navigate("/register")}> + Sign up + +

+
); diff --git a/src/pages/auth/Register.tsx b/src/pages/auth/Register.tsx index e6b5319..86602a5 100644 --- a/src/pages/auth/Register.tsx +++ b/src/pages/auth/Register.tsx @@ -2,207 +2,455 @@ import { useState } from "react"; import type { FormEvent } from "react"; import { useNavigate } from "react-router-dom"; import { useAuthStore } from "../../stores/authStore"; -import { Loader2, Mail, Lock, User, ImageIcon } from "lucide-react"; - -const DOTS = [ - { size: 12, color: "#f97316", top: "8%", left: "6%", delay: "0s" }, - { size: 7, color: "#a855f7", top: "22%", left: "3%", delay: "1.2s" }, - { size: 9, color: "#22c55e", top: "65%", left: "5%", delay: "0.6s" }, - { size: 8, color: "#f43f5e", top: "80%", left: "8%", delay: "2.1s" }, - { size: 12, color: "#3b82f6", top: "10%", right: "6%", delay: "1.8s" }, - { size: 7, color: "#eab308", top: "40%", right: "3%", delay: "0.9s" }, - { size: 10, color: "#a855f7", top: "72%", right: "5%", delay: "0.4s" }, - { size: 8, color: "#f97316", top: "55%", right: "8%", delay: "1.5s" }, -]; +import { + Loader2, + Mail, + Lock, + User, + ImageIcon, + BookOpen, + Star, + Zap, + Trophy, +} from "lucide-react"; const STYLES = ` @import url('https://fonts.googleapis.com/css2?family=Nunito:wght@400;700;800;900&family=Nunito+Sans:wght@400;600;700&display=swap'); - .rg-screen { + *, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; } + + .rg-root { min-height: 100vh; - background: #fffbf4; + display: flex; font-family: 'Nunito', sans-serif; + background: #fffbf4; + } + + /* ─── LEFT PANEL ─── */ + .rg-left { position: relative; + width: 50%; + min-height: 100vh; + background: linear-gradient(150deg, #1e1b4b 0%, #3b1d8a 50%, #6d28d9 100%); + display: flex; + flex-direction: column; + align-items: flex-start; + justify-content: center; + padding: 3rem 3.5rem; overflow: hidden; - display: flex; align-items: center; justify-content: center; - padding: 2rem 1.25rem; + flex-shrink: 0; } - /* Blobs */ - .rg-blob { position:fixed;pointer-events:none;z-index:0; } - .rg-blob-1 { width:280px;height:280px;background:#fde68a;top:-100px;left:-100px;border-radius:60% 40% 70% 30%/50% 60% 40% 50%;animation:rgWobble1 14s ease-in-out infinite; } - .rg-blob-2 { width:220px;height:220px;background:#a5f3c0;bottom:-60px;left:4%;border-radius:40% 60% 30% 70%/60% 40% 60% 40%;animation:rgWobble2 16s ease-in-out infinite; } - .rg-blob-3 { width:250px;height:250px;background:#fbcfe8;top:10%;right:-70px;border-radius:70% 30% 50% 50%/40% 60% 40% 60%;animation:rgWobble1 18s ease-in-out infinite reverse; } - .rg-blob-4 { width:180px;height:180px;background:#bfdbfe;bottom:8%;right:0;border-radius:50% 50% 30% 70%/60% 40% 60% 40%;animation:rgWobble2 12s ease-in-out infinite; } - - @keyframes rgWobble1 { - 0%,100%{border-radius:60% 40% 70% 30%/50% 60% 40% 50%;transform:translate(0,0) rotate(0deg);} - 50%{border-radius:40% 60% 30% 70%/60% 40% 60% 40%;transform:translate(14px,18px) rotate(8deg);} + /* Blobs inside left panel */ + .rg-panel-blob { + position: absolute; pointer-events: none; border-radius: 50%; + opacity: 0.18; } - @keyframes rgWobble2 { - 0%,100%{border-radius:40% 60% 30% 70%/60% 40% 60% 40%;transform:translate(0,0) rotate(0deg);} - 50%{border-radius:60% 40% 70% 30%/40% 60% 40% 60%;transform:translate(-12px,14px) rotate(-6deg);} + .rg-panel-blob-1 { + width: 420px; height: 420px; + background: #a855f7; + top: -140px; right: -100px; + animation: blobDrift1 16s ease-in-out infinite; + } + .rg-panel-blob-2 { + width: 300px; height: 300px; + background: #f97316; + bottom: -100px; left: -80px; + animation: blobDrift2 18s ease-in-out infinite; + } + .rg-panel-blob-3 { + width: 200px; height: 200px; + background: #22c55e; + top: 50%; left: 50%; + transform: translate(-50%, -50%); + animation: blobDrift1 14s ease-in-out infinite reverse; + } + @keyframes blobDrift1 { + 0%,100% { transform: translate(0,0) scale(1); } + 50% { transform: translate(24px, 32px) scale(1.08); } + } + @keyframes blobDrift2 { + 0%,100% { transform: translate(0,0) scale(1); } + 50% { transform: translate(-20px, -28px) scale(1.06); } } - .rg-dot { position:fixed;border-radius:50%;pointer-events:none;z-index:0;opacity:0.28;animation:rgFloat 7s ease-in-out infinite; } - @keyframes rgFloat { - 0%,100%{transform:translateY(0) rotate(0deg);} - 50%{transform:translateY(-14px) rotate(180deg);} + /* Floating decorative shapes */ + .rg-shape { + position: absolute; pointer-events: none; + animation: floatShape 8s ease-in-out infinite; + } + @keyframes floatShape { + 0%,100% { transform: translateY(0) rotate(0deg); } + 50% { transform: translateY(-16px) rotate(12deg); } } - /* Card */ - .rg-card { + /* Stars scattered */ + .rg-star { + position: absolute; pointer-events: none; + color: #fde68a; opacity: 0.55; + animation: twinkle 3s ease-in-out infinite; + } + @keyframes twinkle { + 0%,100% { opacity: 0.55; transform: scale(1); } + 50% { opacity: 0.9; transform: scale(1.3); } + } + + /* Left panel content */ + .rg-panel-content { position: relative; z-index: 1; - width: 100%; max-width: 400px; - background: white; border: 2.5px solid #f3f4f6; - border-radius: 28px; - box-shadow: 0 12px 40px rgba(0,0,0,0.08), 0 4px 12px rgba(0,0,0,0.04); - padding: 2.25rem 2rem 2rem; - display: flex; flex-direction: column; gap: 1.75rem; - animation: rgPopIn 0.5s cubic-bezier(0.34,1.56,0.64,1) both; - } - @keyframes rgPopIn { - from { opacity:0; transform:scale(0.9) translateY(20px); } - to { opacity:1; transform:scale(1) translateY(0); } + display: flex; flex-direction: column; + align-items: flex-start; gap: 2.5rem; + width: 100%; } - /* Logo area */ - .rg-logo-wrap { - display: flex; flex-direction: column; align-items: center; gap: 0.85rem; + .rg-panel-logo { + display: flex; align-items: center; gap: 0.75rem; } - .rg-title { - font-size: 1.5rem; font-weight: 900; color: #1e1b4b; - letter-spacing: -0.02em; text-align: center; + .rg-panel-logo-badge { + width: 48px; height: 48px; border-radius: 14px; + background: linear-gradient(135deg, #f97316, #ef4444); + display: flex; align-items: center; justify-content: center; + box-shadow: 0 6px 0 rgba(0,0,0,0.25), 0 8px 20px rgba(249,115,22,0.4); + font-size: 1.4rem; } - .rg-sub { + .rg-panel-logo-text { + font-size: 1.4rem; font-weight: 900; + color: white; letter-spacing: -0.02em; + } + + .rg-panel-headline { + display: flex; flex-direction: column; gap: 0.75rem; + } + .rg-panel-headline h2 { + font-size: 2.4rem; font-weight: 900; line-height: 1.15; + color: white; letter-spacing: -0.03em; + } + .rg-panel-headline h2 span { + background: linear-gradient(90deg, #fde68a, #f97316); + -webkit-background-clip: text; -webkit-text-fill-color: transparent; + } + .rg-panel-headline p { font-family: 'Nunito Sans', sans-serif; - font-size: 0.82rem; font-weight: 600; color: #9ca3af; - text-align: center; margin-top: -0.25rem; + font-size: 1rem; font-weight: 600; color: #c4b5fd; line-height: 1.6; } - /* Avatar preview */ - .rg-avatar-preview { - width: 56px; height: 56px; border-radius: 50%; + /* Feature pills */ + .rg-features { display: flex; flex-direction: column; gap: 0.85rem; } + .rg-feature { + display: flex; align-items: center; gap: 0.85rem; + background: rgba(255,255,255,0.07); + border: 1.5px solid rgba(255,255,255,0.12); + border-radius: 14px; padding: 0.85rem 1.1rem; + backdrop-filter: blur(8px); + animation: fadeSlideIn 0.5s ease both; + } + .rg-feature:nth-child(1) { animation-delay: 0.1s; } + .rg-feature:nth-child(2) { animation-delay: 0.2s; } + .rg-feature:nth-child(3) { animation-delay: 0.3s; } + @keyframes fadeSlideIn { + from { opacity: 0; transform: translateX(-16px); } + to { opacity: 1; transform: translateX(0); } + } + .rg-feature-icon { + width: 36px; height: 36px; border-radius: 10px; + display: flex; align-items: center; justify-content: center; + flex-shrink: 0; + } + .rg-feature-text strong { + display: block; font-size: 0.85rem; font-weight: 800; color: white; + } + .rg-feature-text span { + font-family: 'Nunito Sans', sans-serif; + font-size: 0.75rem; font-weight: 600; color: #a5b4fc; + } + + /* Social proof */ + .rg-social-proof { + display: flex; align-items: center; gap: 0.75rem; + padding: 0.1rem 0; + } + .rg-avatars { display: flex; } + .rg-av { + width: 30px; height: 30px; border-radius: 50%; + border: 2px solid #3b1d8a; + background: linear-gradient(135deg, #a855f7, #6d28d9); + margin-left: -8px; display: flex; align-items: center; + justify-content: center; font-size: 0.65rem; font-weight: 800; color: white; + } + .rg-av:first-child { margin-left: 0; } + .rg-social-proof p { + font-family: 'Nunito Sans', sans-serif; + font-size: 0.78rem; font-weight: 700; color: #c4b5fd; + } + .rg-social-proof p strong { color: #fde68a; } + + /* ─── RIGHT PANEL (form) ─── */ + .rg-right { + flex: 1; + display: flex; align-items: center; justify-content: center; + padding: 3rem 4rem; + position: relative; overflow: hidden; + } + + /* Subtle bg dots on right */ + .rg-bg-dot { + position: absolute; border-radius: 50%; pointer-events: none; opacity: 0.10; + animation: bgDotFloat 9s ease-in-out infinite; + } + @keyframes bgDotFloat { + 0%,100% { transform: translateY(0); } + 50% { transform: translateY(-12px); } + } + + .rg-form-wrap { + position: relative; z-index: 1; + width: 100%; max-width: 420px; + display: flex; flex-direction: column; gap: 2rem; + animation: formPopIn 0.55s cubic-bezier(0.34,1.56,0.64,1) both; + } + @keyframes formPopIn { + from { opacity: 0; transform: translateY(24px) scale(0.97); } + to { opacity: 1; transform: translateY(0) scale(1); } + } + + /* Form header */ + .rg-form-header { display: flex; flex-direction: column; gap: 0.4rem; } + .rg-form-header h1 { + font-size: 2rem; font-weight: 900; color: #1e1b4b; + letter-spacing: -0.03em; line-height: 1.2; + } + .rg-form-header p { + font-family: 'Nunito Sans', sans-serif; + font-size: 0.88rem; font-weight: 600; color: #9ca3af; + } + + /* Avatar row */ + .rg-avatar-row { + display: flex; align-items: center; gap: 1.1rem; + background: #f9fafb; border: 2.5px solid #f3f4f6; + border-radius: 18px; padding: 0.9rem 1.1rem; + transition: border-color 0.2s; + } + .rg-avatar-row:focus-within { border-color: #c4b5fd; background: white; } + .rg-avatar-ring { + width: 52px; height: 52px; border-radius: 50%; border: 2.5px dashed #e5e7eb; display: flex; align-items: center; justify-content: center; - overflow: hidden; background: #f9fafb; - transition: border-color 0.2s ease; - margin-top: 0.25rem; + overflow: hidden; background: white; flex-shrink: 0; + transition: border-color 0.25s, border-style 0.25s; } - .rg-avatar-preview.has-image { - border-style: solid; border-color: #c4b5fd; + .rg-avatar-ring.filled { border-style: solid; border-color: #a855f7; } + .rg-avatar-ring img { width: 100%; height: 100%; object-fit: cover; } + .rg-avatar-input-col { flex: 1; display: flex; flex-direction: column; gap: 0.2rem; } + .rg-avatar-label { + font-size: 0.68rem; font-weight: 800; letter-spacing: 0.1em; + text-transform: uppercase; color: #6b7280; } - .rg-avatar-preview img { - width: 100%; height: 100%; object-fit: cover; + .rg-avatar-input { + background: transparent; border: none; outline: none; + font-family: 'Nunito Sans', sans-serif; + font-size: 0.85rem; font-weight: 600; color: #1e1b4b; + width: 100%; + } + .rg-avatar-input::placeholder { color: #d1d5db; } + .rg-avatar-hint { + font-family: 'Nunito Sans', sans-serif; + font-size: 0.7rem; font-weight: 600; color: #c4b5fd; } - /* Form fields */ + /* Fields grid */ .rg-fields { display: flex; flex-direction: column; gap: 1rem; } + .rg-row { display: flex; gap: 1rem; } + .rg-row .rg-field { flex: 1; } .rg-field { display: flex; flex-direction: column; gap: 0.4rem; } .rg-label { - font-size: 0.72rem; font-weight: 800; letter-spacing: 0.1em; - text-transform: uppercase; color: #6b7280; - padding-left: 0.25rem; + font-size: 0.7rem; font-weight: 800; letter-spacing: 0.1em; + text-transform: uppercase; color: #6b7280; padding-left: 0.2rem; } .rg-input-wrap { position: relative; } .rg-input-icon { - position: absolute; left: 0.85rem; top: 50%; + position: absolute; left: 0.9rem; top: 50%; transform: translateY(-50%); pointer-events: none; color: #9ca3af; - transition: color 0.2s ease; + transition: color 0.2s; } .rg-input { - width: 100%; padding: 0.8rem 1rem 0.8rem 2.6rem; + width: 100%; padding: 0.85rem 1rem 0.85rem 2.6rem; background: #f9fafb; border: 2.5px solid #f3f4f6; border-radius: 14px; font-family: 'Nunito Sans', sans-serif; font-size: 0.88rem; font-weight: 600; color: #1e1b4b; - outline: none; transition: all 0.2s ease; - box-sizing: border-box; + outline: none; transition: all 0.2s; } .rg-input:focus { background: white; border-color: #c4b5fd; - box-shadow: 0 0 0 3px rgba(168,85,247,0.1); + box-shadow: 0 0 0 3.5px rgba(168,85,247,0.1); } - .rg-input:focus ~ .rg-input-icon { color: #a855f7; } + .rg-input:focus + .rg-input-icon { color: #a855f7; } .rg-input:disabled { opacity: 0.5; cursor: not-allowed; } .rg-input::placeholder { color: #d1d5db; } - /* Password strength */ - .rg-strength-bar { - display: flex; gap: 4px; margin-top: 0.35rem; padding: 0 0.1rem; - } + /* Strength */ + .rg-strength-bar { display: flex; gap: 5px; margin-top: 0.4rem; } .rg-strength-seg { flex: 1; height: 4px; border-radius: 999px; - background: #f3f4f6; transition: background 0.3s ease; + background: #f3f4f6; transition: background 0.3s; } - .rg-strength-seg.active-weak { background: #f43f5e; } - .rg-strength-seg.active-medium { background: #eab308; } - .rg-strength-seg.active-strong { background: #22c55e; } - .rg-strength-label { + .rg-strength-seg.weak { background: #f43f5e; } + .rg-strength-seg.medium { background: #eab308; } + .rg-strength-seg.strong { background: #22c55e; } + .rg-strength-hint { font-family: 'Nunito Sans', sans-serif; - font-size: 0.72rem; font-weight: 700; - padding: 0 0.1rem; margin-top: 0.15rem; + font-size: 0.7rem; font-weight: 700; margin-top: 0.2rem; padding-left: 0.1rem; color: #9ca3af; } - .rg-strength-label.weak { color: #f43f5e; } - .rg-strength-label.medium { color: #eab308; } - .rg-strength-label.strong { color: #22c55e; } + .rg-strength-hint.weak { color: #f43f5e; } + .rg-strength-hint.medium { color: #eab308; } + .rg-strength-hint.strong { color: #22c55e; } /* Error */ .rg-error { background: #fff1f2; border: 2px solid #fecdd3; - border-radius: 14px; padding: 0.75rem 1rem; + border-radius: 14px; padding: 0.8rem 1rem; font-family: 'Nunito Sans', sans-serif; font-size: 0.82rem; font-weight: 700; color: #e11d48; display: flex; align-items: center; gap: 0.5rem; } - /* Submit button */ + /* Submit */ .rg-btn { - width: 100%; padding: 0.95rem; + width: 100%; padding: 1rem; background: #a855f7; color: white; border: none; border-radius: 100px; cursor: pointer; - font-family: 'Nunito', sans-serif; font-size: 0.95rem; font-weight: 900; + font-family: 'Nunito', sans-serif; font-size: 1rem; font-weight: 900; display: flex; align-items: center; justify-content: center; gap: 0.5rem; - box-shadow: 0 6px 0 #7c3aed, 0 8px 20px rgba(168,85,247,0.25); - transition: transform 0.1s ease, box-shadow 0.1s ease; + box-shadow: 0 6px 0 #7c3aed, 0 10px 24px rgba(168,85,247,0.3); + transition: transform 0.1s, box-shadow 0.1s; + letter-spacing: 0.01em; } - .rg-btn:hover { transform:translateY(-2px); box-shadow:0 8px 0 #7c3aed,0 12px 24px rgba(168,85,247,0.3); } - .rg-btn:active { transform:translateY(3px); box-shadow:0 3px 0 #7c3aed; } + .rg-btn:hover { transform: translateY(-2px); box-shadow: 0 8px 0 #7c3aed, 0 14px 28px rgba(168,85,247,0.35); } + .rg-btn:active { transform: translateY(3px); box-shadow: 0 3px 0 #7c3aed; } .rg-btn:disabled { background: #e5e7eb; color: #9ca3af; cursor: not-allowed; box-shadow: 0 4px 0 #d1d5db; } .rg-btn:disabled:hover { transform: none; box-shadow: 0 4px 0 #d1d5db; } - .rg-spinner { animation: rgSpin 0.8s linear infinite; } - @keyframes rgSpin { to { transform: rotate(360deg); } } + .rg-spinner { animation: spin 0.8s linear infinite; } + @keyframes spin { to { transform: rotate(360deg); } } - /* Sign-in link */ - .rg-footer { + .rg-form-footer { text-align: center; font-family: 'Nunito Sans', sans-serif; - font-size: 0.78rem; font-weight: 600; color: #9ca3af; + font-size: 0.8rem; font-weight: 600; color: #9ca3af; } .rg-link { color: #a855f7; font-weight: 800; text-decoration: none; - transition: color 0.2s ease; } .rg-link:hover { color: #7c3aed; } + + /* Responsive */ + @media (max-width: 860px) { + .rg-left { display: none; } + .rg-right { padding: 2rem 1.5rem; } + } `; -function getPasswordStrength(password: string): 0 | 1 | 2 | 3 { - if (!password) return 0; - let score = 0; - if (password.length >= 8) score++; - if (/[A-Z]/.test(password) && /[a-z]/.test(password)) score++; - if (/[0-9]/.test(password) || /[^A-Za-z0-9]/.test(password)) score++; - return score as 0 | 1 | 2 | 3; +function getStrength(p: string): 0 | 1 | 2 | 3 { + if (!p) return 0; + let s = 0; + if (p.length >= 8) s++; + if (/[A-Z]/.test(p) && /[a-z]/.test(p)) s++; + if (/[0-9]/.test(p) || /[^A-Za-z0-9]/.test(p)) s++; + return s as 0 | 1 | 2 | 3; } +const S_LABEL = ["", "Weak", "Medium", "Strong"]; +const S_CLASS = ["", "weak", "medium", "strong"]; -const STRENGTH_LABELS = ["", "Weak", "Medium", "Strong"]; -const STRENGTH_CLASSES = ["", "weak", "medium", "strong"]; +const FEATURES = [ + { + icon: , + bg: "#a855f7", + title: "Adaptive Practice", + sub: "Questions tailored to your skill level", + }, + { + icon: , + bg: "#f97316", + title: "Instant Feedback", + sub: "Know exactly where you went wrong", + }, + { + icon: , + bg: "#22c55e", + title: "Score Tracking", + sub: "Watch your SAT score climb over time", + }, +]; + +const PANEL_DOTS = [ + { + size: 70, + color: "#f97316", + top: "15%", + left: "65%", + delay: "0s", + dur: "9s", + }, + { + size: 45, + color: "#22c55e", + top: "62%", + left: "10%", + delay: "1s", + dur: "11s", + }, + { + size: 30, + color: "#fde68a", + top: "35%", + left: "75%", + delay: "0.5s", + dur: "7s", + }, + { + size: 20, + color: "#a855f7", + top: "78%", + left: "50%", + delay: "2s", + dur: "13s", + }, +]; + +const BG_DOTS = [ + { + size: 180, + color: "#a855f7", + top: "5%", + right: "5%", + delay: "0s", + dur: "12s", + }, + { + size: 100, + color: "#f97316", + bottom: "10%", + left: "2%", + delay: "1.5s", + dur: "10s", + }, + { + size: 60, + color: "#22c55e", + top: "50%", + right: "3%", + delay: "0.8s", + dur: "8s", + }, +]; + +const INITIALS = ["JD", "AS", "MK", "RP", "LL"]; export const Register = () => { const [name, setName] = useState(""); @@ -214,12 +462,8 @@ export const Register = () => { const navigate = useNavigate(); const { register, isLoading, error, clearError } = useAuthStore(); - const strength = getPasswordStrength(password); - - const handleAvatarChange = (url: string) => { - setAvatarUrl(url); - setAvatarError(false); - }; + const strength = getStrength(password); + const isValid = name.trim() && email.trim() && password.length >= 6; const handleSubmit = async (e: FormEvent) => { e.preventDefault(); @@ -233,218 +477,318 @@ export const Register = () => { if (success) navigate("/student/home", { replace: true }); }; - const isValid = name.trim() && email.trim() && password.length >= 6; - return ( -
+
- {/* Blobs */} -
-
-
-
+ {/* ── LEFT PANEL ── */} +
+
+
+
- {/* Dots */} - {DOTS.map((d, i) => ( -
- ))} - -
- {/* Logo + heading */} -
- EdBridge { - (e.target as HTMLImageElement).style.display = "none"; - }} - /> - - {/* Avatar preview */} + {/* Decorative floating dots */} + {PANEL_DOTS.map((d, i) => (
- {avatarUrl && !avatarError ? ( - Avatar preview setAvatarError(true)} - /> - ) : ( - - )} + key={i} + className="rg-shape" + style={ + { + width: d.size, + height: d.size, + background: d.color, + borderRadius: "50%", + opacity: 0.12, + top: d.top, + left: d.left, + animationDelay: d.delay, + animationDuration: d.dur, + } as React.CSSProperties + } + /> + ))} + + {/* Stars */} + {[ + { top: "14%", left: "20%", size: 14, delay: "0s" }, + { top: "28%", left: "78%", size: 10, delay: "0.7s" }, + { top: "52%", left: "30%", size: 12, delay: "1.3s" }, + { top: "70%", left: "65%", size: 8, delay: "0.4s" }, + { top: "88%", left: "22%", size: 10, delay: "1.8s" }, + ].map((s, i) => ( + + ))} + + {/* Decorative ring shapes */} + {[ + { size: 90, top: "72%", left: "5%", delay: "0.2s", dur: "10s" }, + { size: 60, top: "8%", left: "55%", delay: "1.1s", dur: "13s" }, + ].map((r, i) => ( +
+ ))} + +
+ {/* Logo */} +
+
📚
+ EdBridge
-
-

Create account ✨

-

Join EdBridge and start your SAT prep

+ {/* Headline */} +
+

+ Ace the SAT. +
+ Start for free. +

+

+ Join thousands of students who improved their +
+ SAT scores with personalized practice. +

+
+ + {/* Feature pills */} +
+ {FEATURES.map((f, i) => ( +
+
+ {f.icon} +
+
+ {f.title} + {f.sub} +
+
+ ))} +
+ + {/* Social proof */} +
+
+ {INITIALS.map((s, i) => ( +
+ {s} +
+ ))} +
+

+ 2,400+ students already enrolled +

+
- {/* Fields */} -
- {/* Name */} -
- -
- - setName(e.target.value)} - disabled={isLoading} - autoComplete="off" - /> -
+ {/* ── RIGHT PANEL ── */} +
+ {BG_DOTS.map((d, i) => ( +
+ ))} + +
+ {/* Header */} +
+

Create your account ✨

+

Fill in the details below to get started

- {/* Email */} -
- -
- - setEmail(e.target.value)} - disabled={isLoading} - autoComplete="off" - /> + {/* Avatar URL row */} +
+
+ {avatarUrl && !avatarError ? ( + Avatar setAvatarError(true)} + /> + ) : ( + + )}
-
- - {/* Avatar URL */} -
-