Skip to content

BenMag-tech/iqlab

Folders and files

NameName
Last commit message
Last commit date

Latest commit

Β 

History

2 Commits
Β 
Β 
Β 
Β 

Repository files navigation

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}) => (

{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 (

πŸ”’
<div style={{fontFamily:"'DM Serif Display',serif",fontSize:18,marginBottom:6,textAlign:"center"}}>Premium Content
<div style={{fontSize:13,color:G.muted,textAlign:"center",maxWidth:220,lineHeight:1.6,marginBottom:20}}> Unlock downloads & full access for just GHβ‚΅{PLAN_PRICE}/term <button onClick={onUpgrade} style={{background:G.gold,color:"#08090d",border:"none",padding:"10px 28px",borderRadius:8,fontWeight:700,fontSize:14,cursor:"pointer"}}> Unlock with MoMo / Card ); }

// ───────────────────────────────────────────── // 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 &amp; 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 style={{minHeight:"100vh",display:"flex",flexDirection:"column",alignItems:"center",justifyContent:"center",textAlign:"center",padding:"100px 24px 60px",position:"relative",overflow:"hidden"}}> <div style={{position:"absolute",top:"25%",left:"50%",transform:"translateX(-50%)",width:700,height:400,background:"radial-gradient(ellipse,rgba(245,200,66,.12) 0%,transparent 70%)",pointerEvents:"none"}}/>

    <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}}>

<h1 style={{fontFamily:"'DM Serif Display',serif",fontSize:"clamp(36px,5vw,52px)",lineHeight:1.05,marginBottom:8}}>Browse all materials.

Free tier: browse & read. Premium: download everything.

{!isPaid&&<button onClick={onUpgrade} style={{background:"rgba(245,200,66,.1)",color:G.gold,border: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}}>

<div style={{display:"flex",alignItems:"center",gap:12,marginBottom:8}}> <span style={{background:"rgba(255,77,109,.15)",color:G.red,border:1px solid rgba(255,77,109,.3),borderRadius:6,padding:"3px 10px",fontSize:10,fontWeight:700,letterSpacing:2,fontFamily:"'JetBrains Mono',monospace"}}>ADMIN ONLY
<h1 style={{fontFamily:"'DM Serif Display',serif",fontSize:"clamp(28px,4vw,42px)"}}>Control Panel <Btn onClick={openUpload} style={{padding:"10px 24px"}}>+ Upload Resource

  {/* 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 &amp; 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"}}>

πŸ”
<h2 style={{fontFamily:"'DM Serif Display',serif",fontSize:28,marginBottom:8}}>Admin Access

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&&
Incorrect PIN
} <Btn onClick={submit} style={{width:"100%",padding:"13px",fontSize:15}}>Enter Dashboard ); }

// ───────────────────────────────────────────── // 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}/>}
</>

); }

About

No description, website, or topics provided.

Resources

Stars

Watchers

Forks

Packages

 
 
 

Contributors