import { useState, useEffect, useRef } from "react";
// βββββββββββββββββββββββββββββββββββββββββββββ // CONSTANTS & DATA // βββββββββββββββββββββββββββββββββββββββββββββ
const ADMIN_PIN = "NSMQ2025"; // β CHANGE THIS to your secret PIN
const G = { bg:"#08090d", surface:"#0f1117", surface2:"#171b24", border:"#1e2435", gold:"#f5c842", teal:"#00d4aa", red:"#ff4d6d", blue:"#4d9fff", text:"#e8eaf0", muted:"#5a6380", green:"#64c864", purple:"#c864ff", };
const PLAN_PRICE = 20; // GHβ΅ per term
const INITIAL_USERS = [ { id:1, name:"Ama Korantema", email:"ama@presec.edu.gh", school:"PRESEC Legon", paid:true, joinDate:"2025-01-10", score:2840 }, { id:2, name:"Kwame Osei", email:"kwame@knust.edu.gh", school:"KNUST SHS", paid:true, joinDate:"2025-01-12", score:2711 }, { id:3, name:"Efua Mensah", email:"efua@giss.edu.gh", school:"GISS", paid:false, joinDate:"2025-01-15", score:980 }, { id:4, name:"Kofi Asante", email:"kofi@achi.edu.gh", school:"Achimota", paid:true, joinDate:"2025-01-18", score:2588 }, { id:5, name:"Abena Bonsu", email:"abena@wg.edu.gh", school:"Wesley Girls", paid:false, joinDate:"2025-01-20", score:430 }, { id:6, name:"Yaw Darko", email:"yaw@staugs.edu.gh", school:"St. Augustine's", paid:true, joinDate:"2025-01-22", score:2478 }, ];
const INITIAL_RESOURCES = [ { id:1, title:"Organic Chemistry Shortcuts", subject:"Chemistry", type:"Shortcut", premium:true, visible:true, downloads:342, uploadDate:"2025-01-05" }, { id:2, title:"Physics Equations Cheat Sheet", subject:"Physics", type:"Cheat Sheet", premium:true, visible:true, downloads:521, uploadDate:"2025-01-08" }, { id:3, title:"50 NSMQ Riddles + Answers", subject:"General", type:"Past Questions", premium:false, visible:true, downloads:718, uploadDate:"2025-01-10" }, { id:4, title:"Cell Biology Quick Notes", subject:"Biology", type:"Notes", premium:true, visible:true, downloads:289, uploadDate:"2025-01-12" }, { id:5, title:"Mental Math Speed Drills", subject:"Mathematics", type:"Practice", premium:true, visible:true, downloads:634, uploadDate:"2025-01-14" }, { id:6, title:"Periodic Table Trends", subject:"Chemistry", type:"Notes", premium:false, visible:true, downloads:412, uploadDate:"2025-01-16" }, { id:7, title:"Activity Series Mnemonic", subject:"Chemistry", type:"Shortcut", premium:false, visible:true, downloads:290, uploadDate:"2025-01-18" }, { id:8, title:"Speed Race Formula Pack", subject:"Mathematics", type:"Formula Sheet", premium:true, visible:false, downloads:0, uploadDate:"2025-01-20" }, ];
const SHORTCUTS = [ { id:1, subject:"chem", label:"Chemistry", title:"Activity Series Mnemonic", body:""Please Stop Calling Me A Zebra In Congo Mines Still" β K, Na, Ca, Mg, Al, Zn, Fe, Cu, Mn, Ag.", premium:false }, { id:2, subject:"phys", label:"Physics", title:"Snell's Law Fast Recall", body:"nβsinΞΈβ = nβsinΞΈβ. Denser medium β smaller angle, slower speed, shorter Ξ». Frequency NEVER changes.", premium:false }, { id:3, subject:"math", label:"Mathematics", title:"Log Laws in 10 Seconds", body:"log(ab)=loga+logb Β· log(a/b)=logaβlogb Β· log(aβΏ)=nΒ·loga. Base rule: log_b(b)=1, log_b(1)=0.", premium:true }, { id:4, subject:"bio", label:"Biology", title:"Mitosis Stages β PMAT", body:""People Meet And Talk" β Prophase, Metaphase, Anaphase, Telophase. Add Interphase before P.", premium:true }, { id:5, subject:"gen", label:"General", title:"7 SI Base Units", body:""King Henry Doesn't Mind Cold Kelvin Molly" β kg, m, s, A, K, mol, cd. Only 7 exist!", premium:false }, { id:6, subject:"chem", label:"Chemistry", title:"OIL RIG Redox Trick", body:"Oxidation Is Loss, Reduction Is Gain (of electrons). Group 1=+1, Group 2=+2, O=β2, H=+1 always.", premium:true }, ];
const QUIZ_BANK = [ { subject:"Chemistry", q:"What is the IUPAC name of CHββCHββCHββCOOH?", opts:["Propanoic acid","Butanoic acid","Pentanoic acid","Ethanoic acid"], ans:1 }, { subject:"Physics", q:"What is the SI unit of electric potential difference?", opts:["Ampere","Watt","Volt","Ohm"], ans:2 }, { subject:"Mathematics", q:"If logβ8 = x, what is the value of x?", opts:["2","3","4","8"], ans:1 }, { subject:"Biology", q:"Which organelle is called the 'powerhouse of the cell'?", opts:["Nucleus","Ribosome","Mitochondria","Golgi apparatus"], ans:2 }, { subject:"Chemistry", q:"What is the oxidation state of Mn in KMnOβ?", opts:["+5","+6","+7","+8"], ans:2 }, { subject:"Physics", q:"Which of the following is NOT a vector quantity?", opts:["Force","Velocity","Speed","Acceleration"], ans:2 }, { subject:"Mathematics", q:"What is the sum of interior angles of a hexagon?", opts:["540Β°","640Β°","720Β°","810Β°"], ans:2 }, { subject:"Biology", q:"The process by which plants make food using sunlight is called?", opts:["Respiration","Photosynthesis","Transpiration","Digestion"], ans:1 }, ];
// βββββββββββββββββββββββββββββββββββββββββββββ
// GLOBAL STYLES
// βββββββββββββββββββββββββββββββββββββββββββββ
const CSS = @import url('https://fonts.googleapis.com/css2?family=Bebas+Neue&family=DM+Serif+Display:ital@0;1&family=DM+Sans:wght@300;400;500;600;700&family=JetBrains+Mono:wght@400;700&display=swap'); *{margin:0;padding:0;box-sizing:border-box;} body{background:${G.bg};color:${G.text};font-family:'DM Sans',sans-serif;overflow-x:hidden;} ::-webkit-scrollbar{width:6px;} ::-webkit-scrollbar-track{background:${G.bg};} ::-webkit-scrollbar-thumb{background:${G.border};border-radius:3px;} input,select,textarea{font-family:'DM Sans',sans-serif;} @keyframes fadeUp{from{opacity:0;transform:translateY(20px)}to{opacity:1;transform:translateY(0)}} @keyframes pop{0%{transform:scale(.88);opacity:0}70%{transform:scale(1.03)}100%{transform:scale(1);opacity:1}} @keyframes pulse{0%,100%{opacity:1}50%{opacity:.3}} @keyframes marquee{from{transform:translateX(0)}to{transform:translateX(-50%)}} @keyframes shimmer{0%{background-position:-200% 0}100%{background-position:200% 0}} .fade-up{animation:fadeUp .5s both;} .pop{animation:pop .35s both;} .lock-overlay{position:absolute;inset:0;background:rgba(8,9,13,.85);backdrop-filter:blur(6px);display:flex;flex-direction:column;align-items:center;justify-content:center;border-radius:inherit;z-index:10;};
// βββββββββββββββββββββββββββββββββββββββββββββ // SMALL SHARED COMPONENTS // βββββββββββββββββββββββββββββββββββββββββββββ const SectionLabel = ({text}) => (
const Btn = ({children,onClick,variant="primary",style={}}) => {
const base = {border:"none",padding:"10px 24px",borderRadius:8,fontWeight:700,fontSize:14,cursor:"pointer",transition:"all .2s",fontFamily:"'DM Sans',sans-serif",...style};
const v = {
primary:{background:G.gold,color:"#08090d",...base},
ghost:{background:"transparent",color:G.text,border:1px solid ${G.border},...base},
danger:{background:"rgba(255,77,109,.15)",color:G.red,border:1px solid rgba(255,77,109,.3),...base},
success:{background:"rgba(0,212,170,.15)",color:G.teal,border:1px solid rgba(0,212,170,.3),...base},
};
return {children};
};
const Badge = ({label,color}) => (
<span style={{background:${color}22,color,border:1px solid ${color}44,borderRadius:4,padding:"2px 8px",fontSize:10,fontWeight:700,letterSpacing:1.2,textTransform:"uppercase",fontFamily:"'JetBrains Mono',monospace",whiteSpace:"nowrap"}}>{label}
);
const Toggle = ({on,onChange,label}) => ( <label style={{display:"flex",alignItems:"center",gap:8,cursor:"pointer",userSelect:"none",fontSize:13,color:G.muted}}> <div style={{width:36,height:20,borderRadius:10,background:on?G.teal:G.border,position:"relative",transition:"background .2s",flexShrink:0}}> <div style={{position:"absolute",top:3,left:on?17:3,width:14,height:14,borderRadius:"50%",background:"white",transition:"left .2s"}}/> <input type="checkbox" checked={on} onChange={e=>onChange(e.target.checked)} style={{position:"absolute",opacity:0,width:"100%",height:"100%",cursor:"pointer",margin:0}}/> {label} );
// βββββββββββββββββββββββββββββββββββββββββββββ // PAYWALL LOCK COMPONENT // βββββββββββββββββββββββββββββββββββββββββββββ function PaywallLock({onUpgrade}) { return (
// βββββββββββββββββββββββββββββββββββββββββββββ // PAYSTACK MODAL // βββββββββββββββββββββββββββββββββββββββββββββ function PaystackModal({onClose,onSuccess}) { const [step,setStep]=useState(1); const [name,setName]=useState(""); const [email,setEmail]=useState(""); const [phone,setPhone]=useState(""); const [method,setMethod]=useState("momo"); const [loading,setLoading]=useState(false);
const pay = () => { if(!name||!email||!phone){alert("Please fill all fields");return;} setLoading(true); // PRODUCTION: Replace with real Paystack popup // const handler = PaystackPop.setup({ // key: 'pk_live_YOUR_PAYSTACK_PUBLIC_KEY', // email, amount: PLAN_PRICE * 100, // currency: 'GHS', // ref: 'QV_' + Date.now(), // metadata: { name, phone, plan: 'premium_term' }, // callback: (response) => { onSuccess({name,email,phone}); }, // onClose: () => setLoading(false), // }); // handler.openIframe();
// Demo simulation:
setTimeout(()=>{setLoading(false);setStep(3);},2500);
};
return (
<div style={{position:"fixed",inset:0,background:"rgba(0,0,0,.9)",zIndex:9999,display:"flex",alignItems:"center",justifyContent:"center",backdropFilter:"blur(10px)",padding:24}} onClick={e=>e.target===e.currentTarget&&onClose()}>
<div className="pop" style={{background:G.surface,border:1px solid ${G.border},borderRadius:24,padding:"44px 40px",maxWidth:460,width:"100%",position:"relative"}}>
<button onClick={onClose} style={{position:"absolute",top:16,right:16,background:G.surface2,border:1px solid ${G.border},color:G.muted,width:30,height:30,borderRadius:8,cursor:"pointer",fontSize:14}}>β
{step===3 ? (
<div style={{textAlign:"center",padding:"20px 0"}}>
<div style={{fontSize:52,marginBottom:16}}>π</div>
<h3 style={{fontFamily:"'DM Serif Display',serif",fontSize:26,marginBottom:10}}>You're In!</h3>
<p style={{color:G.muted,marginBottom:8}}>Payment confirmed. Full access unlocked.</p>
<p style={{fontSize:13,color:G.muted,marginBottom:28}}>Welcome to QuizVault Premium π</p>
<Btn onClick={()=>{onSuccess({name,email,phone});onClose();}}>Start Learning β</Btn>
</div>
) : (
<>
<SectionLabel text="// UNLOCK PREMIUM" />
<h2 style={{fontFamily:"'DM Serif Display',serif",fontSize:26,marginBottom:6}}>GHβ΅{PLAN_PRICE} / term</h2>
<p style={{fontSize:14,color:G.muted,marginBottom:28,lineHeight:1.6}}>
Unlock all PDFs, notes, cheat sheets & unlimited downloads. Powered by Paystack.
</p>
{/* What you get */}
<div style={{background:G.surface2,border:`1px solid ${G.border}`,borderRadius:12,padding:20,marginBottom:24}}>
{["β
Download all PDFs & cheat sheets","β
Unlimited practice questions","β
Save shortcuts to your library","β
Full subject notes access","β
New uploads instantly available"].map(f=>(
<div key={f} style={{fontSize:13,color:G.muted,marginBottom:8}}>{f}</div>
))}
</div>
<div style={{display:"flex",flexDirection:"column",gap:12}}>
<input value={name} onChange={e=>setName(e.target.value)} placeholder="Your full name"
style={{background:G.surface2,border:`1px solid ${G.border}`,borderRadius:8,padding:"11px 14px",color:G.text,fontSize:14,outline:"none"}}/>
<input value={email} onChange={e=>setEmail(e.target.value)} placeholder="Email address"
style={{background:G.surface2,border:`1px solid ${G.border}`,borderRadius:8,padding:"11px 14px",color:G.text,fontSize:14,outline:"none"}}/>
<input value={phone} onChange={e=>setPhone(e.target.value)} placeholder="Phone (for MoMo)"
style={{background:G.surface2,border:`1px solid ${G.border}`,borderRadius:8,padding:"11px 14px",color:G.text,fontSize:14,outline:"none"}}/>
{/* Payment method */}
<div style={{display:"flex",gap:8}}>
{[["momo","π± MoMo"],["card","π³ Card"]].map(([v,l])=>(
<button key={v} onClick={()=>setMethod(v)} style={{flex:1,background:method===v?`${G.gold}18`:"transparent",border:`1px solid ${method===v?G.gold:G.border}`,color:method===v?G.gold:G.muted,padding:"10px",borderRadius:8,cursor:"pointer",fontSize:13,fontWeight:600,transition:"all .2s"}}>{l}</button>
))}
</div>
<button onClick={pay} disabled={loading} style={{background:loading?G.border:G.gold,color:loading?G.muted:"#08090d",border:"none",padding:"14px",borderRadius:9,fontWeight:700,fontSize:15,cursor:loading?"default":"pointer",transition:"all .2s"}}>
{loading ? "β³ Processing payment..." : `Pay GHβ΅${PLAN_PRICE} via Paystack`}
</button>
<div style={{textAlign:"center",fontSize:11,color:G.muted,marginTop:4}}>
π Secured by Paystack Β· MTN MoMo, Vodafone Cash, AirtelTigo, Visa/Mastercard
</div>
</div>
</>
)}
</div>
</div>
); }
// βββββββββββββββββββββββββββββββββββββββββββββ // UPLOAD MODAL // βββββββββββββββββββββββββββββββββββββββββββββ function UploadModal({onClose,onUpload}) { const [title,setTitle]=useState(""); const [subject,setSubject]=useState("Chemistry"); const [type,setType]=useState("Shortcut"); const [premium,setPremium]=useState(true); const [visible,setVisible]=useState(true); const [file,setFile]=useState(null); const [done,setDone]=useState(false); const fileRef=useRef();
const submit = () => { if(!title){alert("Add a title");return;} onUpload({title,subject,type,premium,visible,file}); setDone(true); setTimeout(onClose,2000); };
return (
<div style={{position:"fixed",inset:0,background:"rgba(0,0,0,.9)",zIndex:9999,display:"flex",alignItems:"center",justifyContent:"center",backdropFilter:"blur(10px)",padding:24}} onClick={e=>e.target===e.currentTarget&&onClose()}>
<div className="pop" style={{background:G.surface,border:1px solid ${G.border},borderRadius:24,padding:"44px 40px",maxWidth:500,width:"100%",position:"relative",maxHeight:"90vh",overflowY:"auto"}}>
<button onClick={onClose} style={{position:"absolute",top:16,right:16,background:G.surface2,border:1px solid ${G.border},color:G.muted,width:30,height:30,borderRadius:8,cursor:"pointer",fontSize:14}}>β
{done ? (
<div style={{textAlign:"center",padding:"30px 0"}}>
<div style={{fontSize:52,marginBottom:16}}>β
</div>
<h3 style={{fontFamily:"'DM Serif Display',serif",fontSize:26,marginBottom:8}}>Uploaded!</h3>
<p style={{color:G.muted}}>Resource is now live in the vault.</p>
</div>
) : (
<>
<SectionLabel text="// UPLOAD RESOURCE" />
<h2 style={{fontFamily:"'DM Serif Display',serif",fontSize:26,marginBottom:24}}>Add to the vault</h2>
<div style={{display:"flex",flexDirection:"column",gap:14}}>
<div>
<label style={{fontSize:11,color:G.muted,display:"block",marginBottom:5,fontFamily:"'JetBrains Mono',monospace",letterSpacing:1}}>TITLE *</label>
<input value={title} onChange={e=>setTitle(e.target.value)} placeholder="e.g. Organic Chemistry Shortcuts"
style={{width:"100%",background:G.surface2,border:`1px solid ${G.border}`,borderRadius:8,padding:"11px 14px",color:G.text,fontSize:14,outline:"none"}}/>
</div>
<div style={{display:"grid",gridTemplateColumns:"1fr 1fr",gap:12}}>
<div>
<label style={{fontSize:11,color:G.muted,display:"block",marginBottom:5,fontFamily:"'JetBrains Mono',monospace",letterSpacing:1}}>SUBJECT</label>
<select value={subject} onChange={e=>setSubject(e.target.value)} style={{width:"100%",background:G.surface2,border:`1px solid ${G.border}`,borderRadius:8,padding:"11px 14px",color:G.text,fontSize:14}}>
{["Chemistry","Physics","Biology","Mathematics","General"].map(s=><option key={s}>{s}</option>)}
</select>
</div>
<div>
<label style={{fontSize:11,color:G.muted,display:"block",marginBottom:5,fontFamily:"'JetBrains Mono',monospace",letterSpacing:1}}>TYPE</label>
<select value={type} onChange={e=>setType(e.target.value)} style={{width:"100%",background:G.surface2,border:`1px solid ${G.border}`,borderRadius:8,padding:"11px 14px",color:G.text,fontSize:14}}>
{["Shortcut","Full Notes","Cheat Sheet","Past Questions","Formula Sheet","Practice Problems"].map(t=><option key={t}>{t}</option>)}
</select>
</div>
</div>
{/* Admin controls */}
<div style={{background:G.surface2,border:`1px solid ${G.border}`,borderRadius:10,padding:16,display:"flex",flexDirection:"column",gap:12}}>
<div style={{fontSize:11,color:G.gold,fontFamily:"'JetBrains Mono',monospace",letterSpacing:1,marginBottom:2}}>// ADMIN CONTROLS</div>
<Toggle on={premium} onChange={setPremium} label={premium?"π Premium (paid only)":"π Free for everyone"} />
<Toggle on={visible} onChange={setVisible} label={visible?"π Visible to students":"π Hidden (draft)"} />
</div>
{/* File zone */}
<div onClick={()=>fileRef.current?.click()} style={{border:`2px dashed ${file?G.teal:G.border}`,borderRadius:12,padding:28,textAlign:"center",cursor:"pointer",transition:"all .2s",background:file?"rgba(0,212,170,.04)":"transparent"}}
onMouseEnter={e=>{if(!file)e.currentTarget.style.borderColor=G.gold;}}
onMouseLeave={e=>{if(!file)e.currentTarget.style.borderColor=G.border;}}>
<input ref={fileRef} type="file" style={{display:"none"}} onChange={e=>setFile(e.target.files[0])}/>
<div style={{fontSize:28,marginBottom:10}}>{file?"β
":"π"}</div>
<div style={{fontSize:14,fontWeight:600,marginBottom:4}}>{file?file.name:"Drop file or click to browse"}</div>
<div style={{fontSize:12,color:G.muted}}>PDF, DOCX, PNG, JPG, PPTX Β· Max 50MB</div>
</div>
<Btn onClick={submit} style={{padding:"14px",fontSize:15}}>Upload Resource</Btn>
</div>
</>
)}
</div>
</div>
); }
// βββββββββββββββββββββββββββββββββββββββββββββ // NAV // βββββββββββββββββββββββββββββββββββββββββββββ function Nav({page,setPage,isPaid,isAdmin,onUpgrade,openUpload}) { const [scrolled,setScrolled]=useState(false); useEffect(()=>{ const fn=()=>setScrolled(window.scrollY>20); window.addEventListener("scroll",fn);return()=>window.removeEventListener("scroll",fn); },[]);
return (
<nav style={{position:"fixed",top:0,left:0,right:0,zIndex:900,display:"flex",alignItems:"center",justifyContent:"space-between",padding:"0 32px",height:60,background:scrolled?"rgba(8,9,13,.95)":"transparent",backdropFilter:scrolled?"blur(20px)":"none",borderBottom:scrolled?1px solid ${G.border}:"1px solid transparent",transition:"all .3s"}}>
<span onClick={()=>setPage("Home")} style={{fontFamily:"'Bebas Neue',sans-serif",fontSize:26,letterSpacing:3,color:G.gold,cursor:"pointer"}}>
QUIZVAULT
{isPaid&&<span style={{marginLeft:8,background:"rgba(0,212,170,.15)",color:G.teal,border:1px solid rgba(0,212,170,.3),borderRadius:4,padding:"1px 6px",fontSize:9,fontWeight:700,letterSpacing:1.5,fontFamily:"'JetBrains Mono',monospace",verticalAlign:"middle"}}>PRO}
<div style={{display:"flex",gap:4}}>
{["Home","Resources","Practice","Shortcuts"].map(t=>(
<button key={t} onClick={()=>setPage(t)} style={{background:page===t?`${G.gold}18`:"transparent",border:page===t?`1px solid ${G.gold}44`:"1px solid transparent",color:page===t?G.gold:G.muted,padding:"6px 14px",borderRadius:8,fontSize:13,fontWeight:600,cursor:"pointer",transition:"all .2s"}}>
{t}
</button>
))}
{isAdmin&&<button onClick={()=>setPage("Admin")} style={{background:page==="Admin"?"rgba(255,77,109,.15)":"transparent",border:page==="Admin"?`1px solid rgba(255,77,109,.4)`:"1px solid transparent",color:page==="Admin"?G.red:G.muted,padding:"6px 14px",borderRadius:8,fontSize:13,fontWeight:600,cursor:"pointer"}}>β Admin</button>}
</div>
<div style={{display:"flex",gap:10,alignItems:"center"}}>
{!isPaid&&<button onClick={onUpgrade} style={{background:"rgba(245,200,66,.1)",color:G.gold,border:`1px solid rgba(245,200,66,.3)`,padding:"7px 16px",borderRadius:8,fontSize:12,fontWeight:700,cursor:"pointer"}}>π Go Premium</button>}
<button onClick={openUpload} style={{background:G.gold,color:"#08090d",border:"none",padding:"8px 18px",borderRadius:8,fontSize:13,fontWeight:700,cursor:"pointer"}}>+ Upload</button>
</div>
</nav>
); }
// βββββββββββββββββββββββββββββββββββββββββββββ // HOME PAGE // βββββββββββββββββββββββββββββββββββββββββββββ function HomePage({setPage,onUpgrade,isPaid}) { return (
<div className="fade-up" style={{display:"inline-flex",alignItems:"center",gap:8,background:"rgba(245,200,66,.1)",border:`1px solid rgba(245,200,66,.3)`,borderRadius:100,padding:"6px 16px",fontSize:12,fontWeight:600,letterSpacing:1.5,textTransform:"uppercase",color:G.gold,marginBottom:32}}>
<span style={{width:6,height:6,borderRadius:"50%",background:G.gold,animation:"pulse 2s infinite",display:"inline-block"}}/>
NSMQ Preparation Platform Β· Ghana π¬π
</div>
<h1 style={{fontFamily:"'Bebas Neue',sans-serif",fontSize:"clamp(64px,10vw,128px)",lineHeight:.9,letterSpacing:4,marginBottom:8,animation:"fadeUp .5s .1s both"}}>
MASTER THE <span style={{color:G.gold}}>QUIZ.</span><br/>BEAT THE BEST.
</h1>
<p style={{fontFamily:"'DM Serif Display',serif",fontStyle:"italic",fontSize:"clamp(17px,2.5vw,24px)",color:G.muted,marginBottom:24,animation:"fadeUp .5s .2s both"}}>Your shortcut to NSMQ glory</p>
<p style={{maxWidth:520,fontSize:16,lineHeight:1.75,color:"#7b849c",marginBottom:48,animation:"fadeUp .5s .3s both"}}>
Senior-curated shortcuts, cheat sheets, and learning tools. Free to browse, unlock everything for just <strong style={{color:G.gold}}>GHβ΅{PLAN_PRICE}/term</strong>.
</p>
<div style={{display:"flex",gap:14,flexWrap:"wrap",justifyContent:"center",animation:"fadeUp .5s .4s both"}}>
<Btn onClick={()=>setPage("Resources")} style={{padding:"14px 32px",fontSize:15}}>Browse Free Materials</Btn>
{!isPaid&&<button onClick={onUpgrade} style={{background:"transparent",color:G.gold,border:`1px solid rgba(245,200,66,.4)`,padding:"14px 32px",borderRadius:8,fontWeight:700,fontSize:15,cursor:"pointer"}}>π Unlock Premium β GHβ΅{PLAN_PRICE}</button>}
</div>
{/* Stats */}
<div style={{display:"flex",gap:56,marginTop:72,flexWrap:"wrap",justifyContent:"center",animation:"fadeUp .5s .5s both"}}>
{[["240+","Study Resources"],["5","Core Subjects"],["1,800+","Practice Questions"],["GHβ΅"+PLAN_PRICE,"Per Term"]].map(([n,l])=>(
<div key={l} style={{textAlign:"center"}}>
<div style={{fontFamily:"'Bebas Neue',sans-serif",fontSize:44,color:G.gold,letterSpacing:2,lineHeight:1}}>{n}</div>
<div style={{fontSize:11,color:G.muted,letterSpacing:1.2,textTransform:"uppercase",marginTop:4,fontFamily:"'JetBrains Mono',monospace"}}>{l}</div>
</div>
))}
</div>
</div>
{/* Marquee */}
<div style={{borderTop:`1px solid ${G.border}`,borderBottom:`1px solid ${G.border}`,overflow:"hidden",padding:"16px 0"}}>
<div style={{display:"flex",gap:48,animation:"marquee 22s linear infinite",whiteSpace:"nowrap"}}>
{[...Array(2)].flatMap(()=>["Chemistry","Physics","Biology","Mathematics","Speed Race","Past Questions","Riddles","True/False","General Knowledge"].map(s=>(
<span key={s+Math.random()} style={{display:"inline-flex",alignItems:"center",gap:8,fontSize:12,fontWeight:600,letterSpacing:1.5,textTransform:"uppercase",color:G.muted,flexShrink:0}}>
<span style={{width:4,height:4,borderRadius:"50%",background:G.gold,display:"inline-block"}}/>
{s}
</span>
)))}
</div>
</div>
{/* Free vs Premium */}
<div style={{maxWidth:900,margin:"0 auto",padding:"80px 24px"}}>
<SectionLabel text="// FREE VS PREMIUM"/>
<h2 style={{fontFamily:"'DM Serif Display',serif",fontSize:"clamp(30px,5vw,48px)",lineHeight:1.1,marginBottom:40}}>What do you get?</h2>
<div style={{display:"grid",gridTemplateColumns:"1fr 1fr",gap:24}}>
{/* Free */}
<div style={{background:G.surface,border:`1px solid ${G.border}`,borderRadius:20,padding:36}}>
<div style={{fontSize:11,color:G.muted,fontFamily:"'JetBrains Mono',monospace",letterSpacing:2,marginBottom:16}}>FREE TIER</div>
<h3 style={{fontFamily:"'DM Serif Display',serif",fontSize:24,marginBottom:24}}>Browse & Learn</h3>
{["β
Browse all shortcuts (read-only)","β
5 practice questions/day","β
View subject library","β
Free cheat sheets (selected)","β Cannot download PDFs","β No full notes access","β No cheat sheet downloads"].map(f=>(
<div key={f} style={{fontSize:14,color:f.startsWith("β")?G.muted:G.text,marginBottom:10,lineHeight:1.5}}>{f}</div>
))}
</div>
{/* Premium */}
<div style={{background:"linear-gradient(135deg,rgba(245,200,66,.08),rgba(0,212,170,.04))",border:`1px solid rgba(245,200,66,.3)`,borderRadius:20,padding:36,position:"relative"}}>
<div style={{position:"absolute",top:16,right:16}}><Badge label="RECOMMENDED" color={G.gold}/></div>
<div style={{fontSize:11,color:G.gold,fontFamily:"'JetBrains Mono',monospace",letterSpacing:2,marginBottom:16}}>PREMIUM β GHβ΅{PLAN_PRICE}/TERM</div>
<h3 style={{fontFamily:"'DM Serif Display',serif",fontSize:24,marginBottom:24,color:G.gold}}>Full Access</h3>
{["β
Download ALL PDFs & notes","β
Unlimited practice questions","β
Full cheat sheet downloads","β
Save shortcuts to library","β
New uploads instantly","β
Priority support","β
Exam season bonus packs"].map(f=>(
<div key={f} style={{fontSize:14,color:G.text,marginBottom:10,lineHeight:1.5}}>{f}</div>
))}
{!isPaid&&<Btn onClick={onUpgrade} style={{width:"100%",marginTop:20,padding:"13px",fontSize:15}}>Unlock Now β GHβ΅{PLAN_PRICE} β</Btn>}
{isPaid&&<div style={{marginTop:20,textAlign:"center",color:G.teal,fontSize:14,fontWeight:700}}>β You have full access!</div>}
</div>
</div>
</div>
</div>
); }
// βββββββββββββββββββββββββββββββββββββββββββββ // RESOURCES PAGE // βββββββββββββββββββββββββββββββββββββββββββββ function ResourcesPage({isPaid,onUpgrade,resources}) { const [filter,setFilter]=useState("All"); const [search,setSearch]=useState(""); const [downloaded,setDownloaded]=useState({});
const shown=resources.filter(r=>{ if(!r.visible)return false; const mf=filter==="All"||r.subject===filter; const ms=r.title.toLowerCase().includes(search.toLowerCase()); return mf&&ms; });
const subColors={Chemistry:G.teal,Physics:G.blue,Biology:G.green,Mathematics:G.gold,General:G.purple};
return ( <div style={{maxWidth:1200,margin:"0 auto",padding:"100px 24px 80px"}}> <div style={{display:"flex",alignItems:"flex-end",justifyContent:"space-between",flexWrap:"wrap",gap:24,marginBottom:48}}>
Free tier: browse & read. Premium: download everything.
1px solid rgba(245,200,66,.3),padding:"12px 24px",borderRadius:8,fontSize:14,fontWeight:700,cursor:"pointer"}}>π Unlock Downloads β GHβ΅{PLAN_PRICE}}
<div style={{position:"relative",marginBottom:24}}>
<span style={{position:"absolute",left:16,top:"50%",transform:"translateY(-50%)",fontSize:15,pointerEvents:"none"}}>π</span>
<input value={search} onChange={e=>setSearch(e.target.value)} placeholder="Search resources..."
style={{width:"100%",background:G.surface,border:`1px solid ${G.border}`,borderRadius:10,padding:"12px 16px 12px 44px",color:G.text,fontSize:15,outline:"none"}}/>
</div>
<div style={{display:"flex",gap:8,flexWrap:"wrap",marginBottom:36}}>
{["All","Chemistry","Physics","Biology","Mathematics","General"].map(f=>(
<button key={f} onClick={()=>setFilter(f)} style={{background:filter===f?G.gold:G.surface,color:filter===f?"#08090d":G.muted,border:`1px solid ${filter===f?G.gold:G.border}`,padding:"7px 18px",borderRadius:100,fontSize:13,fontWeight:600,cursor:"pointer",transition:"all .2s"}}>{f}</button>
))}
</div>
<div style={{display:"grid",gridTemplateColumns:"repeat(auto-fill,minmax(320px,1fr))",gap:20}}>
{shown.map(r=>(
<div key={r.id} style={{background:G.surface,border:`1px solid ${G.border}`,borderRadius:16,padding:28,transition:"all .25s",position:"relative",overflow:"hidden"}}
onMouseEnter={e=>{e.currentTarget.style.transform="translateY(-3px)";e.currentTarget.style.borderColor="rgba(245,200,66,.35)";}}
onMouseLeave={e=>{e.currentTarget.style.transform="";e.currentTarget.style.borderColor=G.border;}}>
<div style={{display:"flex",alignItems:"flex-start",justifyContent:"space-between",marginBottom:16}}>
<div style={{width:44,height:44,borderRadius:12,background:G.surface2,display:"flex",alignItems:"center",justifyContent:"center",fontSize:20}}>
{r.subject==="Chemistry"?"π§ͺ":r.subject==="Physics"?"β‘":r.subject==="Biology"?"πΏ":r.subject==="Mathematics"?"π":"π"}
</div>
<div style={{display:"flex",gap:6,alignItems:"center"}}>
{r.premium&&<Badge label="PREMIUM" color={G.gold}/>}
{!r.premium&&<Badge label="FREE" color={G.teal}/>}
</div>
</div>
<h3 style={{fontSize:16,fontWeight:700,marginBottom:8}}>{r.title}</h3>
<div style={{display:"flex",gap:6,marginBottom:16,flexWrap:"wrap"}}>
<Badge label={r.subject} color={subColors[r.subject]||G.muted}/>
<Badge label={r.type} color={G.blue}/>
</div>
<div style={{display:"flex",alignItems:"center",justifyContent:"space-between",paddingTop:14,borderTop:`1px solid ${G.border}`}}>
<span style={{fontSize:11,color:G.muted,fontFamily:"'JetBrains Mono',monospace"}}>{r.downloads+(downloaded[r.id]?1:0)} downloads</span>
{r.premium&&!isPaid ? (
<button onClick={onUpgrade} style={{background:"rgba(245,200,66,.1)",color:G.gold,border:`1px solid rgba(245,200,66,.3)`,padding:"6px 14px",borderRadius:7,fontSize:12,fontWeight:700,cursor:"pointer"}}>π Unlock</button>
) : (
<button onClick={()=>setDownloaded(d=>({...d,[r.id]:true}))} style={{background:downloaded[r.id]?`${G.teal}22`:G.gold,color:downloaded[r.id]?G.teal:"#08090d",border:downloaded[r.id]?`1px solid ${G.teal}`:"none",padding:"6px 16px",borderRadius:7,fontSize:12,fontWeight:700,cursor:"pointer"}}>
{downloaded[r.id]?"β Saved":"β¬ Download"}
</button>
)}
</div>
</div>
))}
</div>
</div>
); }
// βββββββββββββββββββββββββββββββββββββββββββββ // PRACTICE PAGE // βββββββββββββββββββββββββββββββββββββββββββββ function PracticePage({isPaid,onUpgrade}) { const [qIdx,setQIdx]=useState(0); const [selected,setSelected]=useState(null); const [score,setScore]=useState(0); const [total,setTotal]=useState(0); const [done,setDone]=useState(false); const [secs,setSecs]=useState(0); const FREE_LIMIT=5;
useEffect(()=>{const t=setInterval(()=>setSecs(s=>s+1),1000);return()=>clearInterval(t);},[]);
const fmt=s=>${Math.floor(s/60)}:${(s%60).toString().padStart(2,"0")};
const q=QUIZ_BANK[qIdx%QUIZ_BANK.length];
const hitLimit=!isPaid&&total>=FREE_LIMIT;
const pick=(i)=>{ if(selected!==null||hitLimit)return; setSelected(i); if(i===q.ans)setScore(s=>s+1); setTotal(t=>t+1); };
const next=()=>{ if(qIdx+1>=QUIZ_BANK.length){setDone(true);return;} setQIdx(i=>i+1);setSelected(null); };
const restart=()=>{setQIdx(0);setSelected(null);setScore(0);setTotal(0);setDone(false);setSecs(0);};
return (
<div style={{maxWidth:900,margin:"0 auto",padding:"100px 24px 80px"}}>
<h1 style={{fontFamily:"'DM Serif Display',serif",fontSize:"clamp(36px,5vw,52px)",lineHeight:1.05,marginBottom:12}}>Test yourself.
Time yourself.
{isPaid?"Unlimited NSMQ-style questions. Full access.":Free tier: ${FREE_LIMIT} questions/day. }
{!isPaid&&<span style={{color:G.gold,cursor:"pointer",fontWeight:600}} onClick={onUpgrade}>Unlock unlimited β}
<div style={{background:G.surface,border:`1px solid ${G.border}`,borderRadius:20,padding:36,position:"relative"}}>
{/* Free limit hit */}
{hitLimit&&!done&&(
<div style={{position:"absolute",inset:0,background:"rgba(8,9,13,.9)",backdropFilter:"blur(8px)",display:"flex",flexDirection:"column",alignItems:"center",justifyContent:"center",borderRadius:20,zIndex:10}}>
<div style={{fontSize:40,marginBottom:16}}>π</div>
<h3 style={{fontFamily:"'DM Serif Display',serif",fontSize:24,marginBottom:10}}>Daily limit reached</h3>
<p style={{color:G.muted,textAlign:"center",maxWidth:280,lineHeight:1.6,marginBottom:24}}>Free accounts get {FREE_LIMIT} questions/day. Upgrade for unlimited practice.</p>
<Btn onClick={onUpgrade} style={{padding:"12px 32px",fontSize:15}}>π Unlock Unlimited β GHβ΅{PLAN_PRICE}</Btn>
</div>
)}
{done ? (
<div style={{textAlign:"center",padding:"40px 20px"}}>
<div style={{fontSize:52,marginBottom:16}}>{score/total>=.8?"π":score/total>=.6?"πͺ":"π"}</div>
<h3 style={{fontFamily:"'DM Serif Display',serif",fontSize:26,marginBottom:10}}>Round Complete!</h3>
<p style={{color:G.muted,marginBottom:8}}>Score: <strong style={{color:G.gold,fontSize:20}}>{score}/{total}</strong></p>
<p style={{color:G.muted,fontSize:14,marginBottom:28}}>Time: {fmt(secs)} Β· {score/total>=.8?"Outstanding! π₯":score/total>=.6?"Good work!":"Keep practising!"}</p>
<Btn onClick={restart}>Try Again</Btn>
</div>
) : (
<>
{/* Progress */}
{!isPaid&&(
<div style={{marginBottom:20}}>
<div style={{display:"flex",justifyContent:"space-between",fontSize:12,color:G.muted,marginBottom:6,fontFamily:"'JetBrains Mono',monospace"}}>
<span>Questions used</span><span style={{color:total>=FREE_LIMIT?G.red:G.gold}}>{total}/{FREE_LIMIT} free</span>
</div>
<div style={{height:4,background:G.border,borderRadius:100,overflow:"hidden"}}>
<div style={{height:"100%",borderRadius:100,background:total>=FREE_LIMIT?G.red:G.gold,width:`${(total/FREE_LIMIT)*100}%`,transition:"width .3s"}}/>
</div>
</div>
)}
<div style={{background:G.surface2,border:`1px solid ${G.border}`,borderRadius:12,padding:24,marginBottom:20}}>
<div style={{fontFamily:"'JetBrains Mono',monospace",fontSize:10,letterSpacing:2,textTransform:"uppercase",color:G.gold,marginBottom:10}}>
{q.subject} Β· Question {qIdx+1} of {QUIZ_BANK.length}
</div>
<p style={{fontSize:15,lineHeight:1.65,fontWeight:500}}>{q.q}</p>
</div>
<div style={{display:"flex",flexDirection:"column",gap:10}}>
{q.opts.map((opt,i)=>{
let bg=G.surface2,border=G.border,color=G.text;
if(selected!==null){
if(i===q.ans){bg="rgba(0,212,170,.1)";border=G.teal;color=G.teal;}
else if(i===selected&&i!==q.ans){bg="rgba(255,77,109,.1)";border=G.red;color=G.red;}
}
return(
<button key={i} onClick={()=>pick(i)} style={{background:bg,border:`1px solid ${border}`,color,borderRadius:9,padding:"12px 16px",fontSize:14,cursor:selected!==null?"default":"pointer",transition:"all .2s",textAlign:"left",display:"flex",alignItems:"center",gap:12,fontFamily:"'DM Sans',sans-serif",fontWeight:500}}>
<span style={{width:24,height:24,borderRadius:6,background:G.border,display:"flex",alignItems:"center",justifyContent:"center",fontSize:11,fontWeight:700,fontFamily:"'JetBrains Mono',monospace",flexShrink:0}}>{"ABCD"[i]}</span>
{opt}
{selected!==null&&i===q.ans&&<span style={{marginLeft:"auto"}}>β</span>}
{selected!==null&&i===selected&&i!==q.ans&&<span style={{marginLeft:"auto"}}>β</span>}
</button>
);
})}
</div>
<div style={{display:"flex",justifyContent:"space-between",alignItems:"center",marginTop:24,paddingTop:16,borderTop:`1px solid ${G.border}`}}>
<div style={{fontFamily:"'JetBrains Mono',monospace",fontSize:12,color:G.red}}>β± {fmt(secs)}</div>
<div style={{display:"flex",alignItems:"center",gap:16}}>
<span style={{fontSize:12,color:G.muted}}>Score: <strong style={{color:G.gold}}>{score}/{total}</strong></span>
<button onClick={next} disabled={selected===null} style={{background:selected!==null?G.gold:G.surface2,color:selected!==null?"#08090d":G.muted,border:`1px solid ${selected!==null?G.gold:G.border}`,padding:"10px 24px",borderRadius:8,fontWeight:700,fontSize:14,cursor:selected!==null?"pointer":"default",transition:"all .2s"}}>Next β</button>
</div>
</div>
</>
)}
</div>
</div>
); }
// βββββββββββββββββββββββββββββββββββββββββββββ // SHORTCUTS PAGE // βββββββββββββββββββββββββββββββββββββββββββββ function ShortcutsPage({isPaid,onUpgrade}) { const [filter,setFilter]=useState("All"); const [saved,setSaved]=useState({}); const colors={chem:G.teal,phys:G.blue,math:G.gold,bio:G.green,gen:G.purple};
const shown=SHORTCUTS.filter(s=>filter==="All"||s.subject===filter);
return (
<div style={{maxWidth:1200,margin:"0 auto",padding:"100px 24px 80px"}}>
<h1 style={{fontFamily:"'DM Serif Display',serif",fontSize:"clamp(36px,5vw,52px)",lineHeight:1.05,marginBottom:12}}>Memory hacks
that actually work.
Free: read all. Premium: save to your library.
<div style={{display:"flex",gap:8,flexWrap:"wrap",marginBottom:36}}>
{[["All","All"],["chem","Chemistry"],["phys","Physics"],["math","Mathematics"],["bio","Biology"],["gen","General"]].map(([v,l])=>(
<button key={v} onClick={()=>setFilter(v)} style={{background:filter===v?G.gold:G.surface,color:filter===v?"#08090d":G.muted,border:`1px solid ${filter===v?G.gold:G.border}`,padding:"7px 18px",borderRadius:100,fontSize:13,fontWeight:600,cursor:"pointer",transition:"all .2s"}}>{l}</button>
))}
</div>
<div style={{display:"grid",gridTemplateColumns:"repeat(auto-fill,minmax(300px,1fr))",gap:20}}>
{shown.map(s=>(
<div key={s.id} style={{background:G.surface,border:`1px solid ${G.border}`,borderRadius:16,padding:28,transition:"all .25s"}}
onMouseEnter={e=>{e.currentTarget.style.transform="translateY(-3px)";e.currentTarget.style.borderColor="rgba(245,200,66,.4)";}}
onMouseLeave={e=>{e.currentTarget.style.transform="";e.currentTarget.style.borderColor=G.border;}}>
<div style={{display:"flex",alignItems:"flex-start",justifyContent:"space-between",marginBottom:14}}>
<span style={{background:`${colors[s.subject]||G.muted}18`,color:colors[s.subject]||G.muted,border:`1px solid ${colors[s.subject]||G.muted}33`,borderRadius:4,padding:"3px 10px",fontSize:10,fontWeight:700,letterSpacing:1.5,textTransform:"uppercase",fontFamily:"'JetBrains Mono',monospace"}}>{s.label}</span>
<span style={{fontSize:18}}>{s.subject==="chem"?"π§ͺ":s.subject==="phys"?"β‘":s.subject==="math"?"π":s.subject==="bio"?"πΏ":"π"}</span>
</div>
<h3 style={{fontSize:15,fontWeight:700,marginBottom:10}}>{s.title}</h3>
<p style={{fontSize:13,lineHeight:1.65,color:G.muted}}>{s.body}</p>
<div style={{display:"flex",alignItems:"center",justifyContent:"space-between",marginTop:20,paddingTop:14,borderTop:`1px solid ${G.border}`}}>
<Badge label={s.premium?"PREMIUM":"FREE"} color={s.premium?G.gold:G.teal}/>
{s.premium&&!isPaid ? (
<button onClick={onUpgrade} style={{background:"rgba(245,200,66,.1)",color:G.gold,border:`1px solid rgba(245,200,66,.3)`,padding:"5px 12px",borderRadius:7,fontSize:12,fontWeight:700,cursor:"pointer"}}>π Save</button>
) : (
<button onClick={()=>setSaved(v=>({...v,[s.id]:!v[s.id]}))} style={{background:saved[s.id]?`${G.teal}22`:G.gold,color:saved[s.id]?G.teal:"#08090d",border:saved[s.id]?`1px solid ${G.teal}`:"none",padding:"5px 14px",borderRadius:7,fontSize:12,fontWeight:700,cursor:"pointer",transition:"all .2s"}}>
{saved[s.id]?"β Saved":"Save"}
</button>
)}
</div>
</div>
))}
</div>
</div>
); }
// βββββββββββββββββββββββββββββββββββββββββββββ // ADMIN DASHBOARD // βββββββββββββββββββββββββββββββββββββββββββββ function AdminDashboard({resources,setResources,users,setUsers,openUpload}) { const [tab,setTab]=useState("overview");
const paidCount=users.filter(u=>u.paid).length; const revenue=paidCount*PLAN_PRICE; const visibleRes=resources.filter(r=>r.visible).length; const premiumRes=resources.filter(r=>r.premium).length;
const togglePremium=(id)=>setResources(prev=>prev.map(r=>r.id===id?{...r,premium:!r.premium}:r)); const toggleVisible=(id)=>setResources(prev=>prev.map(r=>r.id===id?{...r,visible:!r.visible}:r)); const deleteRes=(id)=>setResources(prev=>prev.filter(r=>r.id!==id)); const toggleUserPaid=(id)=>setUsers(prev=>prev.map(u=>u.id===id?{...u,paid:!u.paid}:u)); const deleteUser=(id)=>setUsers(prev=>prev.filter(u=>u.id!==id));
const tabs=[["overview","π Overview"],["resources","π Resources"],["users","π₯ Users"],["deploy","π Deploy"]];
return ( <div style={{maxWidth:1200,margin:"0 auto",padding:"100px 24px 80px"}}> {/* Header */} <div style={{display:"flex",alignItems:"center",justifyContent:"space-between",marginBottom:40,flexWrap:"wrap",gap:16}}>
1px solid rgba(255,77,109,.3),borderRadius:6,padding:"3px 10px",fontSize:10,fontWeight:700,letterSpacing:2,fontFamily:"'JetBrains Mono',monospace"}}>ADMIN ONLY
{/* Tabs */}
<div style={{display:"flex",gap:4,background:G.surface2,border:`1px solid ${G.border}`,borderRadius:12,padding:4,marginBottom:36,width:"fit-content",flexWrap:"wrap"}}>
{tabs.map(([v,l])=>(
<button key={v} onClick={()=>setTab(v)} style={{background:tab===v?G.gold:"transparent",color:tab===v?"#08090d":G.muted,border:"none",padding:"8px 18px",borderRadius:9,fontSize:13,fontWeight:700,cursor:"pointer",transition:"all .2s",fontFamily:"'DM Sans',sans-serif"}}>{l}</button>
))}
</div>
{/* ββ OVERVIEW ββ */}
{tab==="overview"&&(
<div className="fade-up">
<div style={{display:"grid",gridTemplateColumns:"repeat(auto-fit,minmax(200px,1fr))",gap:16,marginBottom:40}}>
{[
{label:"Total Users",value:users.length,color:G.blue,icon:"π₯"},
{label:"Paid Users",value:paidCount,color:G.teal,icon:"π³"},
{label:"Revenue (GHβ΅)",value:revenue,color:G.gold,icon:"π°"},
{label:"Resources",value:resources.length,color:G.purple,icon:"π"},
{label:"Visible",value:visibleRes,color:G.green,icon:"π"},
{label:"Premium Items",value:premiumRes,color:G.red,icon:"π"},
].map(s=>(
<div key={s.label} style={{background:G.surface,border:`1px solid ${G.border}`,borderRadius:14,padding:24}}>
<div style={{fontSize:22,marginBottom:8}}>{s.icon}</div>
<div style={{fontFamily:"'Bebas Neue',sans-serif",fontSize:38,color:s.color,letterSpacing:2,lineHeight:1}}>{s.value}</div>
<div style={{fontSize:12,color:G.muted,marginTop:4,fontFamily:"'JetBrains Mono',monospace",letterSpacing:.5}}>{s.label}</div>
</div>
))}
</div>
{/* Recent users */}
<h3 style={{fontFamily:"'DM Serif Display',serif",fontSize:22,marginBottom:20}}>Recent signups</h3>
<div style={{display:"flex",flexDirection:"column",gap:8}}>
{users.slice(0,5).map(u=>(
<div key={u.id} style={{background:G.surface,border:`1px solid ${G.border}`,borderRadius:10,padding:"14px 20px",display:"flex",alignItems:"center",gap:16,flexWrap:"wrap"}}>
<div style={{width:36,height:36,borderRadius:"50%",background:`linear-gradient(135deg,${G.gold},${G.teal})`,display:"flex",alignItems:"center",justifyContent:"center",fontSize:13,fontWeight:700,color:"#08090d",flexShrink:0}}>
{u.name.split(" ").map(n=>n[0]).join("").slice(0,2)}
</div>
<div style={{flex:1}}>
<div style={{fontSize:14,fontWeight:600}}>{u.name}</div>
<div style={{fontSize:12,color:G.muted}}>{u.school} Β· {u.email}</div>
</div>
<Badge label={u.paid?"PAID":"FREE"} color={u.paid?G.teal:G.muted}/>
<span style={{fontSize:12,color:G.muted,fontFamily:"'JetBrains Mono',monospace"}}>{u.joinDate}</span>
</div>
))}
</div>
</div>
)}
{/* ββ RESOURCES ββ */}
{tab==="resources"&&(
<div className="fade-up">
<div style={{display:"flex",justifyContent:"space-between",alignItems:"center",marginBottom:20,flexWrap:"wrap",gap:12}}>
<h3 style={{fontFamily:"'DM Serif Display',serif",fontSize:22}}>All Resources ({resources.length})</h3>
<div style={{fontSize:13,color:G.muted}}>Toggle visibility & premium status for each item</div>
</div>
<div style={{display:"flex",flexDirection:"column",gap:10}}>
{resources.map(r=>(
<div key={r.id} style={{background:G.surface,border:`1px solid ${r.visible?G.border:"rgba(255,77,109,.2)"}`,borderRadius:12,padding:"16px 20px",display:"flex",alignItems:"center",gap:16,flexWrap:"wrap",opacity:r.visible?1:.7}}>
<div style={{fontSize:20}}>{r.subject==="Chemistry"?"π§ͺ":r.subject==="Physics"?"β‘":r.subject==="Biology"?"πΏ":r.subject==="Mathematics"?"π":"π"}</div>
<div style={{flex:1,minWidth:160}}>
<div style={{fontSize:14,fontWeight:600}}>{r.title}</div>
<div style={{fontSize:12,color:G.muted,marginTop:2}}>{r.subject} Β· {r.type} Β· {r.downloads} downloads</div>
</div>
<div style={{display:"flex",gap:12,alignItems:"center",flexWrap:"wrap"}}>
<Toggle on={r.visible} onChange={()=>toggleVisible(r.id)} label={r.visible?"Visible":"Hidden"}/>
<Toggle on={r.premium} onChange={()=>togglePremium(r.id)} label={r.premium?"Premium":"Free"}/>
<button onClick={()=>deleteRes(r.id)} style={{background:"rgba(255,77,109,.1)",color:G.red,border:`1px solid rgba(255,77,109,.2)`,padding:"5px 12px",borderRadius:7,fontSize:12,fontWeight:600,cursor:"pointer"}}>Delete</button>
</div>
</div>
))}
</div>
</div>
)}
{/* ββ USERS ββ */}
{tab==="users"&&(
<div className="fade-up">
<div style={{display:"flex",justifyContent:"space-between",alignItems:"center",marginBottom:20,flexWrap:"wrap",gap:12}}>
<h3 style={{fontFamily:"'DM Serif Display',serif",fontSize:22}}>All Users ({users.length})</h3>
<div style={{fontSize:13,color:G.muted}}>Paid: {paidCount} Β· Free: {users.length-paidCount} Β· Revenue: GHβ΅{revenue}</div>
</div>
<div style={{display:"flex",flexDirection:"column",gap:10}}>
{users.map(u=>(
<div key={u.id} style={{background:G.surface,border:`1px solid ${G.border}`,borderRadius:12,padding:"16px 20px",display:"flex",alignItems:"center",gap:16,flexWrap:"wrap"}}>
<div style={{width:38,height:38,borderRadius:"50%",background:`linear-gradient(135deg,${G.gold},${G.teal})`,display:"flex",alignItems:"center",justifyContent:"center",fontSize:13,fontWeight:700,color:"#08090d",flexShrink:0}}>
{u.name.split(" ").map(n=>n[0]).join("").slice(0,2)}
</div>
<div style={{flex:1,minWidth:160}}>
<div style={{fontSize:14,fontWeight:600}}>{u.name}</div>
<div style={{fontSize:12,color:G.muted}}>{u.school} Β· {u.email}</div>
</div>
<div style={{fontSize:12,color:G.muted,fontFamily:"'JetBrains Mono',monospace"}}>Score: {u.score}</div>
<Badge label={u.paid?"PAID":"FREE"} color={u.paid?G.teal:G.muted}/>
<div style={{display:"flex",gap:8}}>
<button onClick={()=>toggleUserPaid(u.id)} style={{background:u.paid?"rgba(255,77,109,.1)":"rgba(0,212,170,.1)",color:u.paid?G.red:G.teal,border:`1px solid ${u.paid?"rgba(255,77,109,.3)":"rgba(0,212,170,.3)"}`,padding:"5px 12px",borderRadius:7,fontSize:12,fontWeight:600,cursor:"pointer"}}>
{u.paid?"Revoke Access":"Grant Access"}
</button>
<button onClick={()=>deleteUser(u.id)} style={{background:"rgba(255,77,109,.08)",color:G.red,border:`1px solid rgba(255,77,109,.15)`,padding:"5px 12px",borderRadius:7,fontSize:12,fontWeight:600,cursor:"pointer"}}>Remove</button>
</div>
</div>
))}
</div>
</div>
)}
{/* ββ DEPLOY GUIDE ββ */}
{tab==="deploy"&&(
<div className="fade-up">
<h3 style={{fontFamily:"'DM Serif Display',serif",fontSize:28,marginBottom:8}}>Go Live in 3 Steps</h3>
<p style={{color:G.muted,marginBottom:40,lineHeight:1.7}}>Follow this guide to put QuizVault online so any student in Ghana can find it on their phone.</p>
{[
{
step:"01", color:G.gold, title:"Deploy to Vercel (Free)",
content:[
"1. Go to github.com β Create new repo β upload the QuizVault.jsx file",
"2. Go to vercel.com β Sign up free β click 'New Project'",
"3. Connect your GitHub β select your repo β click Deploy",
"4. β
Your site is now live at: yourname.vercel.app",
"5. Optional: Buy quizvault.com.gh (~GHβ΅80/yr) at ghana.com",
" Then add it in Vercel β Settings β Domains",
]
},
{
step:"02", color:G.teal, title:"Connect Paystack (Payments)",
content:[
"1. Go to paystack.com β Sign up as a business",
"2. Complete Ghana KYC verification (Ghana Card + selfie)",
"3. Go to Settings β API Keys β copy your Live Public Key",
"4. In the code, find: pk_live_YOUR_PAYSTACK_PUBLIC_KEY",
"5. Replace it with your real key",
"6. Uncomment the PaystackPop block in PaystackModal β pay()",
"7. Add script tag: <script src='https://js.paystack.co/v1/inline.js'>",
"8. β
Students can now pay via MTN MoMo, Vodafone Cash, card",
]
},
{
step:"03", color:G.blue, title:"Connect Supabase (Database + File Storage)",
content:[
"1. Go to supabase.com β New project (free tier)",
"2. Create tables: users, resources, payments",
"3. Enable Storage β create bucket: 'resources' (public)",
"4. npm install @supabase/supabase-js",
"5. Add your Supabase URL + anon key to .env.local",
" NEXT_PUBLIC_SUPABASE_URL=...",
" NEXT_PUBLIC_SUPABASE_ANON_KEY=...",
"6. Replace mock data with real Supabase queries",
"7. β
Uploads, users, and payments all stored securely",
]
},
].map(({step,color,title,content})=>(
<div key={step} style={{background:G.surface,border:`1px solid ${G.border}`,borderRadius:16,padding:32,marginBottom:20}}>
<div style={{display:"flex",alignItems:"center",gap:16,marginBottom:20}}>
<div style={{fontFamily:"'Bebas Neue',sans-serif",fontSize:40,color,letterSpacing:2,lineHeight:1}}>{step}</div>
<h4 style={{fontFamily:"'DM Serif Display',serif",fontSize:22}}>{title}</h4>
</div>
<div style={{background:G.surface2,borderRadius:10,padding:20}}>
{content.map((line,i)=>(
<div key={i} style={{fontSize:13,color:line.startsWith("β
")?G.teal:line.startsWith(" ")?G.muted:G.text,marginBottom:10,lineHeight:1.6,fontFamily:line.includes("=")||line.includes("npm")||line.includes("pk_")||line.includes("NEXT_")?"'JetBrains Mono',monospace":"'DM Sans',sans-serif"}}>
{line}
</div>
))}
</div>
</div>
))}
<div style={{background:"rgba(245,200,66,.06)",border:`1px solid rgba(245,200,66,.2)`,borderRadius:14,padding:24,marginTop:24}}>
<div style={{fontSize:14,fontWeight:700,color:G.gold,marginBottom:10}}>π‘ Revenue Estimate</div>
<div style={{fontSize:14,color:G.muted,lineHeight:1.8}}>
50 students Γ GHβ΅{PLAN_PRICE} = <strong style={{color:G.gold}}>GHβ΅{50*PLAN_PRICE}/term</strong><br/>
100 students Γ GHβ΅{PLAN_PRICE} = <strong style={{color:G.gold}}>GHβ΅{100*PLAN_PRICE}/term</strong><br/>
200 students Γ GHβ΅{PLAN_PRICE} = <strong style={{color:G.gold}}>GHβ΅{200*PLAN_PRICE}/term</strong><br/>
Paystack charges ~1.5% + GHβ΅0.50 per transaction (Ghanaian cards/MoMo).
</div>
</div>
</div>
)}
</div>
); }
// βββββββββββββββββββββββββββββββββββββββββββββ // ADMIN LOGIN GATE // βββββββββββββββββββββββββββββββββββββββββββββ function AdminLoginGate({onSuccess}) { const [pin,setPin]=useState(""); const [err,setErr]=useState(false); const submit=()=>{ if(pin===ADMIN_PIN){onSuccess();} else{setErr(true);setTimeout(()=>setErr(false),1500);} }; return ( <div style={{maxWidth:400,margin:"160px auto",padding:24,textAlign:"center"}}>
Enter your admin PIN to continue.
<input value={pin} onChange={e=>setPin(e.target.value)} onKeyDown={e=>e.key==="Enter"&&submit()} type="password" placeholder="Enter PIN" style={{width:"100%",background:err?"rgba(255,77,109,.1)":G.surface,border:1px solid ${err?G.red:G.border},borderRadius:10,padding:"14px 18px",color:G.text,fontSize:16,outline:"none",textAlign:"center",letterSpacing:4,marginBottom:16}}/>
{err&&// βββββββββββββββββββββββββββββββββββββββββββββ // ROOT APP // βββββββββββββββββββββββββββββββββββββββββββββ export default function App() { const [page,setPage]=useState("Home"); const [isPaid,setIsPaid]=useState(false); const [isAdmin,setIsAdmin]=useState(false); const [adminAuthed,setAdminAuthed]=useState(false); const [showPaystack,setShowPaystack]=useState(false); const [showUpload,setShowUpload]=useState(false); const [resources,setResources]=useState(INITIAL_RESOURCES); const [users,setUsers]=useState(INITIAL_USERS);
// Secret admin access: click logo 5 times const logoClicks=useRef(0); const handleLogoClick=()=>{ logoClicks.current++; if(logoClicks.current>=5){setIsAdmin(true);setPage("Admin");logoClicks.current=0;} };
const handleUpload=(data)=>{ const newRes={id:Date.now(),title:data.title,subject:data.subject,type:data.type,premium:data.premium,visible:data.visible,downloads:0,uploadDate:new Date().toISOString().split("T")[0]}; setResources(prev=>[newRes,...prev]); };
const handleAdminPage=()=>{ if(!adminAuthed){setPage("AdminLogin");} else{setPage("Admin");} };
return ( <> <style>{CSS}</style>
<Nav
page={page} setPage={p=>{if(p==="Admin")handleAdminPage();else setPage(p);}}
isPaid={isPaid} isAdmin={isAdmin}
onUpgrade={()=>setShowPaystack(true)}
openUpload={()=>setShowUpload(true)}
/>
{/* Logo 5-click admin trick */}
<div style={{position:"fixed",top:0,left:0,width:120,height:60,zIndex:999,cursor:"default"}} onClick={handleLogoClick}/>
{page==="Home" && <HomePage setPage={setPage} onUpgrade={()=>setShowPaystack(true)} isPaid={isPaid}/>}
{page==="Resources" && <ResourcesPage isPaid={isPaid} onUpgrade={()=>setShowPaystack(true)} resources={resources}/>}
{page==="Practice" && <PracticePage isPaid={isPaid} onUpgrade={()=>setShowPaystack(true)}/>}
{page==="Shortcuts" && <ShortcutsPage isPaid={isPaid} onUpgrade={()=>setShowPaystack(true)}/>}
{page==="Admin" && adminAuthed && <AdminDashboard resources={resources} setResources={setResources} users={users} setUsers={setUsers} openUpload={()=>setShowUpload(true)}/>}
{page==="AdminLogin" && <AdminLoginGate onSuccess={()=>{setAdminAuthed(true);setPage("Admin");}}/>}
{/* Footer */}
<div style={{borderTop:`1px solid ${G.border}`,padding:"28px 36px",display:"flex",alignItems:"center",justifyContent:"space-between",flexWrap:"wrap",gap:16}}>
<span style={{fontFamily:"'Bebas Neue',sans-serif",fontSize:22,letterSpacing:3,color:G.gold}}>QUIZVAULT</span>
<span style={{fontSize:13,color:G.muted}}>Built for NSMQ champions π¬π Β· GHβ΅{PLAN_PRICE}/term</span>
<div style={{display:"flex",gap:20}}>
{["About","Contact","Privacy"].map(l=>(
<span key={l} style={{fontSize:13,color:G.muted,cursor:"pointer"}} onMouseEnter={e=>e.target.style.color=G.text} onMouseLeave={e=>e.target.style.color=G.muted}>{l}</span>
))}
</div>
</div>
{showPaystack&&<PaystackModal onClose={()=>setShowPaystack(false)} onSuccess={()=>{setIsPaid(true);setShowPaystack(false);}}/>}
{showUpload&&<UploadModal onClose={()=>setShowUpload(false)} onUpload={handleUpload}/>}
</>
); }