#!/usr/bin/env python3
"""Daily AI Quiz Generator"""
import json, os, re, datetime, random, glob
from pathlib import Path
import anthropic

SESSION_FILE = Path("data/session.json")
DATE_STR  = datetime.date.today().strftime("%B %d, %Y")
DATE_ISO  = datetime.date.today().isoformat()
WEEKDAY   = datetime.date.today().strftime("%A")
SUPABASE_URL = "https://cokajjwirhtnvsyubepc.supabase.co"
SUPABASE_KEY = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZSIsInJlZiI6ImNva2Fqandpcmh0bnZzeXViZXBjIiwicm9sZSI6ImFub24iLCJpYXQiOjE3Nzk4NDAwNDUsImV4cCI6MjA5NTQxNjA0NX0._zvclbxuixP9dvMcW-uBdA0liOLzqgcVMOyIJ457DFs"
CAT_COLORS = {
    "Data Quality":"#7DCFB6","Pre-training":"#8FA3D8","RLHF & Fine-tuning":"#5B72B8",
    "Evaluation":"#5B72B8","Alignment & Safety":"#7DCFB6","Model Behavior":"#F4A261",
    "Interpretability":"#8FA3D8","Infrastructure":"#8FA3D8","Prompting":"#7DCFB6",
    "RL & Agent Behavior":"#5B72B8","Recent Research":"#F4A261","General":"#A8B0C8"
}

def load_session():
    if SESSION_FILE.exists():
        with open(SESSION_FILE) as f: return json.load(f)
    return {"session_number":0,"cumulative_flagged":[],"weak_areas":[],
            "topics_covered_recently":[],"total_sessions":0,"concept_library":[],
            "portfolio_balance":{"blue_chip_sessions":0,"growth_sessions":0,"speculative_sessions":0}}

def save_session(s):
    SESSION_FILE.parent.mkdir(exist_ok=True)
    with open(SESSION_FILE,"w") as f: json.dump(s,f,indent=2)

client = anthropic.Anthropic(api_key=os.environ["ANTHROPIC_API_KEY"])

def call_claude(system,user,max_tokens=4000):
    msg = client.messages.create(model="claude-sonnet-4-5",max_tokens=max_tokens,
        system=system,messages=[{"role":"user","content":user}])
    return msg.content[0].text.strip()

def extract_json(raw):
    text = re.sub(r'```json\s*','',raw); text = re.sub(r'```\s*','',text).strip()
    try:
        r = json.loads(text)
        if isinstance(r,(list,dict)): return r
    except: pass
    s,e = text.find('['),text.rfind(']')
    if s>=0 and e>s:
        chunk = text[s:e+1]
        try: return json.loads(chunk)
        except:
            try: return json.loads(re.sub(r',\s*([}\]])',r'\1',chunk))
            except: pass
    return None

def shuffle_answers(questions):
    out = []
    for q in questions:
        opts=q.get("opts",[]); cidx=q.get("correct",0)
        if not opts or cidx>=len(opts): out.append(q); continue
        correct_text=opts[cidx]; new_opts=opts[:]; random.shuffle(new_opts)
        q2=dict(q); q2["opts"]=new_opts; q2["correct"]=new_opts.index(correct_text); out.append(q2)
    return out

def get_topic_requests():
    sk=os.environ.get("SUPABASE_SERVICE_KEY","")
    if not sk: print("  No SUPABASE_SERVICE_KEY"); return []
    try:
        import requests as rlib
        r=rlib.get(f"{SUPABASE_URL}/rest/v1/topic_requests?fulfilled=eq.false&select=id,request",
            headers={"apikey":sk,"Authorization":f"Bearer {sk}"},timeout=10)
        if r.status_code==200: data=r.json(); print(f"  {len(data)} requests"); return data
    except Exception as e: print(f"  Requests error:{e}")
    return []

def fulfill_topic_requests(ids):
    sk=os.environ.get("SUPABASE_SERVICE_KEY","")
    if not sk or not ids: return
    try:
        import requests as rlib
        for rid in ids:
            rlib.patch(f"{SUPABASE_URL}/rest/v1/topic_requests?id=eq.{rid}",
                json={"fulfilled":True},
                headers={"apikey":sk,"Authorization":f"Bearer {sk}","Content-Type":"application/json","Prefer":"return=minimal"},
                timeout=10)
        print(f"  Fulfilled {len(ids)}")
    except Exception as e: print(f"  Fulfill error:{e}")

def generate_questions(session,topic_requests=None):
    print("Generating 10 quiz questions...")
    weak=", ".join(session.get("weak_areas",[])[:5]) or "none"
    flagged=", ".join(session.get("cumulative_flagged",[])[:8]) or "none"
    recent=", ".join(session.get("topics_covered_recently",[])[:6]) or "none"
    sn=session.get("session_number",0)+1
    bal=session.get("portfolio_balance",{})
    topic_str="\n".join([f"- {r['request']}" for r in (topic_requests or [])]) or "None today."
    system=f"""Generate daily quiz questions for a data quality manager working in AI model training. Smart but NOT an engineer.
Today: Session {sn}, {DATE_STR} ({WEEKDAY})
Portfolio: blue_chip={bal.get('blue_chip_sessions',0)}, growth={bal.get('growth_sessions',0)}, speculative={bal.get('speculative_sessions',0)}
User requests: {topic_str}
Weak areas: {weak} | Flagged: {flagged} | Recent topics: {recent}
Mix: 4 data quality, 2 RLHF, 2 evaluation/alignment, 1 prompting/RL, 1 recent research, 1 recent product launches (prioritize frontier model releases, new AI apps gaining traction - e.g. new Claude/GPT/Gemini releases, viral AI tools).
For each question, add an "applied_to" field: a short phrase showing where this concept applies in real work (e.g. "Annotation pipelines", "RLHF training", "Eval design", "Prompt engineering").
Return ONLY valid JSON array of 10 objects, no markdown:
[{{"section":"...","concept":"2-6 words","q":"question","opts":["A","B","C","D"],"correct":0,"explanation":"2-3 sentences","difficulty":"foundational|practitioner|advanced","isNews":false,"article_url":"ar5iv link or empty","applied_to":"short real-world context"}}]
For article_url: if referencing an arxiv paper use https://ar5iv.org/abs/PAPER_ID format. Otherwise leave empty string."""
    for attempt in range(3):
        raw=call_claude(system,f"Generate 10 questions for session {sn}. JSON only.")
        parsed=extract_json(raw)
        if parsed and isinstance(parsed,list) and len(parsed)>=8:
            valid=[q for q in parsed if q.get("q") and isinstance(q.get("opts"),list)
                   and len(q.get("opts",[]))==4 and isinstance(q.get("correct"),int) and q.get("concept")]
            if len(valid)>=8:
                print(f"  Got {len(valid)} questions attempt {attempt+1}")
                return shuffle_answers(valid[:10])
        print(f"  Attempt {attempt+1} failed")
    print("  Using fallback"); return shuffle_answers(get_fallback())

def get_fallback():
    return [
        {"section":"Data Quality","concept":"Inter-annotator agreement","q":"Your annotators agree 55% of the time on a preference task. What does this most directly signal?","opts":["You need more annotators","The task definition is unclear — the model will learn noise","One annotator is an outlier","The model is too good to rate"],"correct":1,"explanation":"<strong>Low inter-annotator agreement</strong> almost always means the task is not well-defined. If skilled humans cannot agree, the model learns noise instead of signal.","difficulty":"foundational","isNews":False,"article_url":""},
        {"section":"RLHF & Fine-tuning","concept":"KL penalty","q":"What does the KL divergence penalty prevent during RLHF?","opts":["Reward model overfitting","Policy drifting too far from SFT model","Gradient explosion","Data memorization"],"correct":1,"explanation":"The <strong>KL penalty</strong> keeps the RL policy anchored to the supervised model. Without it, the model exploits reward model weaknesses.","difficulty":"practitioner","isNews":False,"article_url":""},
        {"section":"Data Quality","concept":"Synthetic data tradeoffs","q":"The biggest risk when using synthetic training data is:","opts":["Always lower quality","Oversamples easy cases, misses hard edge cases","Causes plagiarism","Style too uniform"],"correct":1,"explanation":"<strong>Synthetic data</strong> reflects what the generating model handles well, systematically missing hard edge cases where robustness matters most.","difficulty":"practitioner","isNews":False,"article_url":""},
        {"section":"Evaluation","concept":"LLM-as-judge verbosity bias","q":"Verbosity bias in LLM judges means:","opts":["Prefers shorter answers","Rates longer answers higher regardless of quality","Penalizes formatting","Favors newer models"],"correct":1,"explanation":"<strong>Verbosity bias</strong> causes LLM judges to prefer longer responses regardless of actual quality, corrupting your evaluation signal.","difficulty":"practitioner","isNews":False,"article_url":""},
        {"section":"Prompting","concept":"Few-shot prompting","q":"Few-shot prompting is most valuable when:","opts":["Model too large for zero-shot","Task format needs precise constraint by example","You want more creativity","Factual accuracy required"],"correct":1,"explanation":"<strong>Few-shot prompting</strong> excels at constraining output format and style, showing the model exactly what good output looks like.","difficulty":"foundational","isNews":False,"article_url":""},
        {"section":"RL & Agent Behavior","concept":"Process reward models","q":"Process reward models differ from outcome reward models in that they:","opts":["Use human preference pairs","Reward each reasoning step not just final answer","Verify proofs","Only work for code"],"correct":1,"explanation":"<strong>Process reward models</strong> provide step-level feedback, helping models learn good reasoning chains.","difficulty":"advanced","isNews":False,"article_url":""},
        {"section":"Alignment & Safety","concept":"Constitutional AI","q":"Constitutional AI reduces annotation costs by:","opts":["Crowdsourcing","Using principles and AI self-critique to generate preferences","Averaging multiple models","Rule-based filters"],"correct":1,"explanation":"<strong>Constitutional AI</strong> uses explicit principles and AI self-critique to generate preference labels at scale.","difficulty":"practitioner","isNews":False,"article_url":""},
        {"section":"Model Behavior","concept":"In-context learning","q":"What makes in-context learning remarkable from an ML perspective?","opts":["Proves format memorization","Model adapts via forward-pass with no weight updates","Only works in-distribution","Model uses retrieval"],"correct":1,"explanation":"<strong>In-context learning</strong> adapts to new tasks through the forward pass with no gradient updates.","difficulty":"foundational","isNews":False,"article_url":""},
        {"section":"Data Quality","concept":"Deduplication","q":"Beyond saving compute, deduplication matters because:","opts":["Duplicates slow tokenization","Models upweight repeated content increasing memorization","Improves annotation consistency","Prevents contamination"],"correct":1,"explanation":"<strong>Deduplication</strong> prevents models from over-learning repeated content, which skews distributions.","difficulty":"practitioner","isNews":False,"article_url":""},
        {"section":"Interpretability","concept":"Superposition","q":"Superposition in neural networks means:","opts":["Multiple heads attend same token","Neurons encode multiple features via sparsity","Models store duplicates","Attention processes multiple contexts"],"correct":1,"explanation":"<strong>Superposition</strong> lets networks store more features than dimensions by encoding sparse features per neuron.","difficulty":"advanced","isNews":False,"article_url":""}
    ]

def generate_concept_explanations(session,questions):
    print("Updating concept library...")
    existing={c["concept"]:c for c in session.get("concept_library",[])}
    new_concepts=[]
    for q in questions:
        concept=q.get("concept",""); cat=q.get("section","")
        if concept and concept not in existing:
            new_concepts.append({"concept":concept,"category":cat,"article_url":q.get("article_url","")})
    if new_concepts:
        print(f"  Generating explanations for {len(new_concepts)} concepts...")
        for c in new_concepts:
            try:
                sys=f'Explain "{c["concept"]}" ({c["category"]}) to a data quality manager in AI training. Smart, not an engineer.\nWrite 3 paragraphs: (1) what it is and why it matters for data quality, (2) nuances/failure modes, (3) concrete analogy.\nWrap 1-2 key terms in <strong> tags. Warm tone. No bullets. Start directly.'
                raw=call_claude(sys,"Write the explanation.",max_tokens=600)
                paras=[p.strip() for p in raw.strip().split("\n\n") if p.strip()][:3]
                existing[c["concept"]]={"concept":c["concept"],"category":c["category"],
                    "color":CAT_COLORS.get(c["category"],"#8FA3D8"),
                    "explanation":"".join(f"<p>{p}</p>" for p in paras),
                    "times_seen":0,"marked_learned":False,"added_date":DATE_ISO,"article_url":c.get("article_url",""),"is_new":True}
                print(f"  ✓ {c['concept']}")
            except Exception as e: print(f"  ✗ {c['concept']}: {e}")
    for q in questions:
        c=q.get("concept","")
        if c in existing: existing[c]["times_seen"]=existing[c].get("times_seen",0)+1
    bad=["explanation will appear","check back","coming tomorrow","next session","coming soon"]
    result=[c for c in existing.values() if len(c.get("explanation","").lower())>=50 and not any(p in c.get("explanation","").lower() for p in bad)]
    print(f"  Library: {len(result)} concepts")
    return result

def update_session(session,questions):
    session["session_number"]=session.get("session_number",0)+1
    session["last_updated"]=DATE_ISO
    session["total_sessions"]=session.get("total_sessions",0)+1
    topics=list(set([q["section"] for q in questions]))
    session["topics_covered_recently"]=(topics+session.get("topics_covered_recently",[]))[:12]
    adv=sum(1 for q in questions if q.get("difficulty")=="advanced")
    found=sum(1 for q in questions if q.get("difficulty")=="foundational")
    bal=session.get("portfolio_balance",{"blue_chip_sessions":0,"growth_sessions":0,"speculative_sessions":0})
    if adv>=3: bal["speculative_sessions"]=bal.get("speculative_sessions",0)+1
    elif found>=4: bal["blue_chip_sessions"]=bal.get("blue_chip_sessions",0)+1
    else: bal["growth_sessions"]=bal.get("growth_sessions",0)+1
    session["portfolio_balance"]=bal
    return session

def save_quiz_file(questions, session_num):
    """Save today's quiz to Supabase daily_quizzes table."""
    sk = os.environ.get("SUPABASE_SERVICE_KEY","")
    if not sk:
        print("  No SUPABASE_SERVICE_KEY — skipping quiz save to Supabase")
        return
    try:
        import requests as rlib
        url = f"{SUPABASE_URL}/rest/v1/daily_quizzes"
        headers = {
            "apikey": sk,
            "Authorization": f"Bearer {sk}",
            "Content-Type": "application/json",
            "Prefer": "resolution=merge-duplicates"
        }
        payload = {
            "date": DATE_ISO,
            "session_number": session_num,
            "questions": questions
        }
        r = rlib.post(url, json=payload, headers=headers, timeout=15)
        if r.status_code in (200, 201):
            print(f"  Quiz saved to Supabase for {DATE_ISO}")
        else:
            print(f"  Quiz save error: {r.status_code} {r.text[:100]}")
    except Exception as e:
        print(f"  Quiz save error: {e}")

def write_html(path,content):
    with open(path,"w",encoding="utf-8") as f: f.write(content)

def css_base():
    return """@import url('https://fonts.googleapis.com/css2?family=Bebas+Neue&family=Unbounded:wght@700;900&family=DM+Sans:opsz,wght@9..40,400;9..40,500;9..40,600&display=swap');
:root{--bg:#F2EDE4;--white:#FFF;--ink:#2D2926;--muted:#A8B0C8;--coral:#5B72B8;--teal:#7DCFB6;--yolk:#F4A261;--plum:#8FA3D8;--sky:#5B72B8;--border:rgba(45,41,38,.10);--nav-h:56px}
*,*::before,*::after{box-sizing:border-box;margin:0;padding:0}
body{background:var(--bg);color:var(--ink);font-family:'DM Sans',sans-serif;min-height:100vh;-webkit-font-smoothing:antialiased}"""

def build_index(questions, concept_library, session_num):
    qs = json.dumps(questions, ensure_ascii=False)

    return f"""<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta name="theme-color" content="#2D2926">
<meta name="apple-mobile-web-app-capable" content="yes">
<link rel="manifest" href="manifest.json">
<title>Distilled · {DATE_STR}</title>
<script src="https://unpkg.com/@supabase/supabase-js@2/dist/umd/supabase.js"></script>
<style>
{css_base()}
.nav{{position:fixed;top:0;left:0;right:0;height:var(--nav-h);background:rgba(242,237,228,.95);backdrop-filter:blur(12px);border-bottom:1px solid var(--border);z-index:100;display:flex;align-items:center;padding:0 12px;gap:6px;overflow-x:auto;-webkit-overflow-scrolling:touch;scrollbar-width:none}}
.nav::-webkit-scrollbar{{display:none}}
.nav-logo{{font-family:'Bebas Neue',sans-serif;font-size:22px;font-weight:400;letter-spacing:.08em;flex-shrink:0;margin-right:4px;line-height:1}}
.nav-logo span{{color:var(--coral)}}
.nav-btn{{font-family:'DM Sans',sans-serif;font-size:12px;font-weight:600;padding:7px 12px;border-radius:100px;border:1.5px solid var(--border);background:transparent;color:var(--muted);cursor:pointer;transition:all .15s;text-decoration:none;display:inline-flex;align-items:center;white-space:nowrap;flex-shrink:0}}
.nav-btn:hover,.nav-btn.active{{background:var(--ink);color:#fff;border-color:var(--ink)}}
.page-wrap{{padding-top:var(--nav-h)}}
.screen{{display:none;min-height:calc(100vh - var(--nav-h))}}
.screen.on{{display:block}}
#auth-screen{{display:none;align-items:center;justify-content:center;padding:40px 20px}}
#auth-screen.on{{display:flex;min-height:calc(100vh - var(--nav-h))}}
.auth-card{{background:var(--white);border-radius:24px;padding:40px 36px;width:100%;max-width:400px;box-shadow:0 4px 32px rgba(45,41,38,.10)}}
.auth-brand{{font-size:11px;font-weight:700;color:var(--coral);letter-spacing:.12em;text-transform:uppercase;margin-bottom:24px}}
.auth-title{{font-family:'Unbounded',sans-serif;font-size:24px;font-weight:900;line-height:1.1;letter-spacing:-.02em;margin-bottom:8px}}
.auth-sub{{font-size:14px;color:var(--muted);margin-bottom:32px;line-height:1.6}}
.field{{margin-bottom:16px}}
.field label{{font-size:12px;font-weight:600;display:block;margin-bottom:6px}}
.field input{{width:100%;padding:13px 16px;background:var(--bg);border:2px solid var(--border);border-radius:12px;font-family:'DM Sans',sans-serif;font-size:14px;color:var(--ink);outline:none;transition:border-color .15s}}
.field input:focus{{border-color:var(--plum)}}
.auth-err{{font-size:13px;color:var(--coral);margin-bottom:16px;display:none;padding:10px 14px;background:#FFF0EE;border-radius:10px}}
.auth-err.on{{display:block}}
.big-btn{{width:100%;padding:15px;border-radius:100px;border:none;font-family:'DM Sans',sans-serif;font-size:14px;font-weight:600;cursor:pointer;transition:transform .15s;display:flex;align-items:center;justify-content:center;gap:8px;margin-bottom:10px;text-decoration:none}}
.big-btn:hover{{transform:translateY(-1px)}}
.btn-dark{{background:var(--ink);color:#fff;box-shadow:0 4px 16px rgba(45,41,38,.2)}}
.btn-light{{background:var(--white);color:var(--ink);box-shadow:0 2px 8px rgba(45,41,38,.10);border:1.5px solid var(--border)}}
.auth-toggle{{text-align:center;font-size:13px;color:var(--muted);margin-top:16px}}
.auth-toggle a{{color:var(--ink);font-weight:600;cursor:pointer}}
.waiting-card{{text-align:center;padding:20px 0}}
.waiting-icon{{font-size:48px;margin-bottom:16px}}
.waiting-title{{font-family:'Unbounded',sans-serif;font-size:18px;font-weight:900;margin-bottom:8px}}
.waiting-sub{{font-size:14px;color:var(--muted);line-height:1.6}}
#splash-screen{{display:none}}
#splash-screen.on{{display:block}}
.splash-inner{{max-width:800px;margin:0 auto;padding:60px 32px 80px;display:grid;grid-template-columns:1fr 1fr;gap:60px;align-items:center;min-height:calc(100vh - var(--nav-h))}}
@media(max-width:640px){{.splash-inner{{grid-template-columns:1fr;gap:32px;padding:32px 20px 60px}}.splash-art{{display:none}}}}
.logo-mark{{display:flex;flex-direction:column;gap:0;padding:48px;background:var(--ink);border-radius:32px;width:280px;box-shadow:0 12px 40px rgba(45,41,38,.2)}}
.lm-bar{{border-radius:6px;height:14px;margin-bottom:18px}}
.lm-bar1{{width:220px;background:#5B72B8}}
.lm-bar2{{width:150px;background:#7DCFB6}}
.lm-bar3{{width:180px;background:#F4A261;margin-bottom:28px}}
.lm-wordmark{{font-family:'Bebas Neue',sans-serif;font-size:28px;font-weight:400;color:#fff;letter-spacing:6px}}
.eyebrow{{font-size:11px;font-weight:600;letter-spacing:.14em;text-transform:uppercase;color:var(--coral);margin-bottom:14px}}
.splash-title{{font-family:'Unbounded',sans-serif;font-size:clamp(26px,4vw,40px);font-weight:900;line-height:1.08;letter-spacing:-.02em;margin-bottom:16px}}
.splash-title .accent{{color:var(--coral)}}
.splash-sub{{font-size:15px;line-height:1.75;color:var(--muted);margin-bottom:24px}}
.session-info{{background:var(--white);border-radius:16px;padding:14px 18px;display:flex;align-items:center;gap:12px;margin-bottom:24px;box-shadow:0 2px 10px rgba(45,41,38,.06)}}
.session-dot{{width:8px;height:8px;background:var(--teal);border-radius:50%;flex-shrink:0}}
.session-info-text{{font-size:13px;line-height:1.4}}
.session-info-text strong{{font-weight:600}}
.splash-actions{{display:flex;flex-direction:column;gap:10px;max-width:320px}}
.nav-link-btn{{display:flex;align-items:center;justify-content:center;gap:6px;font-size:13px;font-weight:600;color:var(--muted);text-decoration:none;padding:12px;border-radius:100px;border:1.5px solid var(--border);background:transparent;transition:all .15s;width:100%}}
.nav-link-btn:hover{{border-color:var(--ink);color:var(--ink)}}
#quiz-screen{{display:none}}
#quiz-screen.on{{display:block}}
.quiz-wrap{{max-width:760px;margin:0 auto;padding:32px 24px 80px}}
@media(max-width:640px){{.quiz-wrap{{padding:20px 16px 60px}}}}
.quiz-top{{display:flex;justify-content:space-between;align-items:center;margin-bottom:20px;flex-wrap:wrap;gap:10px}}
.quiz-lbl{{font-size:13px;font-weight:600;color:var(--muted)}}
.score-pill{{background:var(--ink);color:#fff;font-size:13px;font-weight:600;padding:6px 18px;border-radius:100px}}
.prog-row{{display:flex;justify-content:space-between;font-size:12px;font-weight:600;color:var(--muted);margin-bottom:8px}}
.prog-track{{height:6px;background:rgba(45,41,38,.10);border-radius:100px;overflow:hidden;margin-bottom:28px}}
.prog-bar{{height:100%;background:var(--coral);border-radius:100px;transition:width .5s cubic-bezier(.4,0,.2,1)}}
.q-card{{background:var(--white);border-radius:24px;padding:clamp(20px,4vw,36px);margin-bottom:14px;border:2px solid transparent;box-shadow:0 2px 20px rgba(45,41,38,.07);animation:pop .3s cubic-bezier(.34,1.3,.64,1) both}}
@keyframes pop{{from{{opacity:0;transform:scale(.97) translateY(8px)}}to{{opacity:1;transform:scale(1) translateY(0)}}}}
.q-card.s-correct{{border-color:var(--teal)}}.q-card.s-wrong{{border-color:var(--coral)}}.q-card.s-flag{{border-color:var(--yolk)}}
.meta-row{{display:flex;align-items:center;gap:7px;margin-bottom:20px;flex-wrap:wrap}}
.chip{{font-size:10px;font-weight:700;letter-spacing:.05em;text-transform:uppercase;padding:4px 11px;border-radius:100px}}
.ch-topic{{background:#E8EDFF;color:#3A52A0}}.ch-f{{background:#E0F5EE;color:#1A7A60}}.ch-p{{background:#FFF0E0;color:#A05A18}}.ch-a{{background:#FFEAEA;color:#A03030}}
.q-count{{font-size:12px;font-weight:700;color:#C8C0B8;margin-left:auto}}
.q-text{{font-family:'Unbounded',sans-serif;font-size:clamp(15px,2.5vw,20px);font-weight:700;line-height:1.45;letter-spacing:-.01em;margin-bottom:clamp(20px,3vw,32px)}}
.opts{{display:flex;flex-direction:column;gap:10px;margin-bottom:20px}}
.opt{{display:flex;align-items:flex-start;gap:14px;padding:clamp(12px,2vw,18px) clamp(14px,2vw,20px);background:var(--bg);border:2px solid transparent;border-radius:16px;cursor:pointer;font-family:'DM Sans',sans-serif;font-size:clamp(13px,1.8vw,15px);font-weight:500;color:var(--ink);text-align:left;line-height:1.55;transition:all .12s;width:100%}}
.opt:hover:not(:disabled){{border-color:var(--plum);background:#F4F0FF}}
.opt:disabled{{cursor:default}}
.opt-key{{width:28px;min-width:28px;height:28px;border-radius:8px;background:rgba(45,41,38,.08);font-size:12px;font-weight:700;display:flex;align-items:center;justify-content:center;flex-shrink:0;color:var(--muted);transition:all .12s}}
.opt.o-selected{{border-color:var(--plum);background:#F4F0FF}}.opt.o-selected .opt-key{{background:var(--plum);color:#fff}}
.opt.o-correct{{border-color:var(--teal);background:#EAFBF5}}.opt.o-correct .opt-key{{background:var(--teal);color:#fff}}
.opt.o-wrong{{border-color:var(--coral);background:#FFF0EE}}.opt.o-wrong .opt-key{{background:var(--coral);color:#fff}}
.opt.o-show{{border-color:var(--teal);background:#EAFBF5}}.opt.o-show .opt-key{{background:var(--teal);color:#fff}}
.actions{{display:flex;align-items:center;gap:8px;flex-wrap:wrap}}
.act{{font-family:'DM Sans',sans-serif;font-size:12px;font-weight:600;padding:8px 16px;border-radius:100px;border:2px solid;cursor:pointer;transition:all .12s;line-height:1;white-space:nowrap}}
.act-flag{{border-color:var(--yolk);color:#8A6200;background:transparent}}.act-flag:hover,.act-flag.on{{background:#FFF5D6}}
.act-exp{{border-color:var(--border);color:var(--muted);background:transparent}}.act-exp:hover{{background:rgba(45,41,38,.04)}}
.fb{{margin-left:auto;font-size:12px;font-weight:700;padding:6px 14px;border-radius:100px;white-space:nowrap}}
.fb.ok{{background:#DFFAF1;color:#00805A}}.fb.no{{background:#FFE9E6;color:#C93B24}}
.exp-panel{{display:none;margin-top:16px;background:#EEF5FF;border-radius:16px;padding:18px 20px;font-size:14px;line-height:1.8}}
.exp-panel.on{{display:block}}
.exp-panel strong{{color:var(--sky);font-weight:700}}
.nav-row{{display:flex;gap:10px;margin-top:12px}}
.nav-row .big-btn{{flex:1;padding:14px;margin:0;font-size:14px}}
#results-screen{{display:none}}
#results-screen.on{{display:block}}
.results-wrap{{max-width:640px;margin:0 auto;padding:32px 24px 80px}}
@media(max-width:640px){{.results-wrap{{padding:20px 16px 60px}}}}
.res-hero{{background:var(--ink);border-radius:28px;padding:40px 32px 36px;text-align:center;margin-bottom:16px;position:relative;overflow:hidden}}
.res-c1{{width:120px;height:120px;background:var(--coral);border-radius:50%;position:absolute;top:-30px;right:-20px;opacity:.12}}
.res-c2{{width:80px;height:80px;background:var(--yolk);border-radius:50%;position:absolute;top:40px;right:60px;opacity:.10}}
.res-emoji{{font-size:44px;margin-bottom:12px;display:block;position:relative;z-index:1}}
.res-pct{{font-family:'Unbounded',sans-serif;font-size:72px;font-weight:900;letter-spacing:-.04em;color:var(--yolk);line-height:1;margin-bottom:10px;display:block;position:relative;z-index:1}}
.res-desc{{font-size:14px;color:rgba(255,255,255,.65);line-height:1.6;position:relative;z-index:1}}
.stats-grid{{display:grid;grid-template-columns:repeat(3,1fr);gap:10px;margin-bottom:14px}}
.stat-card{{background:var(--white);border-radius:18px;padding:20px 10px;text-align:center;box-shadow:0 2px 10px rgba(45,41,38,.06)}}
.stat-n{{font-family:'Unbounded',sans-serif;font-size:32px;font-weight:900;line-height:1;margin-bottom:5px}}
.stat-l{{font-size:10px;font-weight:700;color:var(--muted);text-transform:uppercase;letter-spacing:.07em}}
.flagged-box{{background:var(--white);border-radius:20px;padding:20px;margin-bottom:14px;border:2px solid var(--yolk)}}
.flagged-title{{font-size:11px;font-weight:700;color:#9A6500;text-transform:uppercase;letter-spacing:.09em;margin-bottom:14px}}
.flagged-item{{padding:10px 0;border-bottom:1px solid rgba(45,41,38,.07);font-size:14px}}
.flagged-item:last-child{{border-bottom:none;padding-bottom:0}}
.flagged-cat{{font-size:10px;font-weight:700;color:var(--muted);text-transform:uppercase;letter-spacing:.06em;margin-bottom:3px}}
.res-actions{{display:flex;gap:10px;flex-wrap:wrap}}
.res-actions .big-btn{{flex:1;padding:14px;margin:0;font-size:14px}}
#profile-screen{{display:none}}
#profile-screen.on{{display:block}}
.profile-wrap{{max-width:640px;margin:0 auto;padding:32px 24px 80px}}
@media(max-width:640px){{.profile-wrap{{padding:20px 16px 60px}}}}
.profile-header{{background:var(--ink);border-radius:24px;padding:32px;margin-bottom:20px;position:relative;overflow:hidden}}
.ph-c1{{width:100px;height:100px;background:#5B72B8;border-radius:50%;position:absolute;top:-30px;right:-20px;opacity:.2}}
.ph-c2{{width:60px;height:60px;background:var(--teal);border-radius:50%;position:absolute;top:20px;right:50px;opacity:.15}}
.profile-name{{font-family:'Unbounded',sans-serif;font-size:22px;font-weight:900;color:#fff;margin-bottom:4px;position:relative;z-index:1}}
.profile-email{{font-size:13px;color:rgba(255,255,255,.5);margin-bottom:20px;position:relative;z-index:1}}
.profile-stats{{display:grid;grid-template-columns:repeat(3,1fr);gap:10px;position:relative;z-index:1}}
.ps-card{{background:rgba(255,255,255,.08);border-radius:12px;padding:14px 10px;text-align:center}}
.ps-n{{font-family:'Unbounded',sans-serif;font-size:24px;font-weight:900;color:#fff;margin-bottom:3px}}
.ps-l{{font-size:10px;font-weight:600;color:rgba(255,255,255,.4);text-transform:uppercase;letter-spacing:.07em}}
.section-title{{font-size:11px;font-weight:700;color:var(--muted);text-transform:uppercase;letter-spacing:.1em;margin-bottom:14px}}
.history-card{{background:var(--white);border-radius:16px;padding:16px 20px;margin-bottom:10px;display:flex;align-items:center;gap:16px;box-shadow:0 2px 8px rgba(45,41,38,.05)}}
.history-date{{font-size:12px;font-weight:600;color:var(--muted);min-width:80px}}
.history-bar-wrap{{flex:1;height:8px;background:rgba(45,41,38,.08);border-radius:100px;overflow:hidden}}
.history-bar{{height:100%;border-radius:100px;transition:width .5s ease}}
.history-pct{{font-family:'Unbounded',sans-serif;font-size:14px;font-weight:900;min-width:42px;text-align:right}}
.weak-list{{background:var(--white);border-radius:16px;padding:20px;margin-bottom:20px}}
.weak-item{{display:flex;align-items:center;justify-content:space-between;padding:8px 0;border-bottom:1px solid var(--border);font-size:13.5px}}
.weak-item:last-child{{border-bottom:none}}
.weak-count{{font-size:11px;font-weight:700;color:var(--coral);background:#FFE9E6;padding:3px 8px;border-radius:100px}}
.lb-tab{{font-family:'DM Sans',sans-serif;font-size:12px;font-weight:600;padding:7px 14px;border-radius:100px;border:1.5px solid var(--border);background:transparent;color:var(--muted);cursor:pointer;transition:all .15s}}
.lb-tab.active{{background:var(--ink);color:#fff;border-color:var(--ink)}}
.lb-row{{background:var(--white);border-radius:14px;padding:14px 18px;margin-bottom:8px;display:flex;align-items:center;gap:14px;box-shadow:0 2px 6px rgba(45,41,38,.05)}}
.lb-row.me{{border:2px solid var(--plum);background:#FAF7FF}}
.lb-rank{{font-family:'Unbounded',sans-serif;font-size:14px;font-weight:900;min-width:28px;color:var(--muted)}}
.lb-rank.top{{color:var(--yolk)}}
.lb-initials{{width:36px;height:36px;border-radius:50%;background:var(--bg);border:2px solid var(--border);font-family:'Unbounded',sans-serif;font-size:12px;font-weight:900;display:flex;align-items:center;justify-content:center;flex-shrink:0;color:var(--ink)}}
.lb-name{{flex:1;font-size:14px;font-weight:600}}
.lb-avg{{font-family:'Unbounded',sans-serif;font-size:14px;font-weight:900;color:var(--teal)}}
.lb-sessions{{font-size:11px;color:var(--muted);margin-top:2px}}
.empty-state{{text-align:center;padding:40px 20px;color:var(--muted);font-size:14px}}
#install-screen{{display:none}}
#install-screen.on{{display:block}}
.install-wrap{{max-width:600px;margin:0 auto;padding:32px 24px 80px}}
@media(max-width:640px){{.install-wrap{{padding:20px 16px 60px}}}}
.install-hero{{background:var(--ink);border-radius:24px;padding:36px 32px;margin-bottom:16px;position:relative;overflow:hidden}}
.ih-c1{{width:140px;height:140px;background:var(--coral);border-radius:50%;position:absolute;top:-40px;right:-30px;opacity:.15}}
.ih-c2{{width:90px;height:90px;background:var(--yolk);border-radius:50%;position:absolute;top:20px;right:70px;opacity:.12}}
.ih-c3{{width:60px;height:60px;background:var(--plum);border-radius:50%;position:absolute;bottom:-10px;right:20px;opacity:.15}}
.install-hero-content{{position:relative;z-index:1}}
.install-eyebrow{{font-size:11px;font-weight:600;letter-spacing:.14em;text-transform:uppercase;color:rgba(255,255,255,.4);margin-bottom:10px}}
.install-title{{font-family:'Unbounded',sans-serif;font-size:clamp(22px,4vw,32px);font-weight:900;line-height:1.1;letter-spacing:-.02em;margin-bottom:12px;color:#fff}}
.install-title .accent{{color:var(--yolk)}}
.install-desc{{font-size:14px;color:rgba(255,255,255,.55);line-height:1.7}}
.install-card{{background:var(--white);border-radius:20px;padding:22px 24px;margin-bottom:12px;box-shadow:0 2px 12px rgba(45,41,38,.06);border:1.5px solid var(--border)}}
.install-card-label{{font-family:'Unbounded',sans-serif;font-size:13px;font-weight:900;letter-spacing:-.01em;color:var(--ink);margin-bottom:4px}}
.install-card-note{{font-size:12px;color:var(--muted);margin-bottom:18px}}
.install-steps{{display:flex;flex-direction:column;gap:12px}}
.step{{display:flex;align-items:flex-start;gap:12px}}
.step-num{{width:26px;min-width:26px;height:26px;background:var(--bg);border-radius:8px;font-size:12px;font-weight:700;color:var(--muted);display:flex;align-items:center;justify-content:center;flex-shrink:0;margin-top:1px}}
.step-text{{font-size:14px;line-height:1.55;color:var(--ink)}}
.step-text strong{{font-weight:700}}
.install-native-btn{{margin-top:18px;font-family:'DM Sans',sans-serif;font-size:13px;font-weight:700;padding:12px 24px;border-radius:100px;border:none;background:var(--ink);color:#fff;cursor:pointer;transition:all .15s;display:none}}
.install-native-btn:hover{{transform:translateY(-1px)}}
.install-already{{background:#DFFAF1;border-radius:14px;padding:14px 18px;margin-bottom:14px;font-size:14px;font-weight:600;color:var(--teal);text-align:center;display:none}}
</style>
</head>
<body>
<nav class="nav" id="main-nav" style="display:none">
  <div class="nav-logo">DIS<span>TILLED</span></div>
  <button class="nav-btn active" id="nav-quiz" onclick="showScreen('splash')">Quiz</button>
  <a class="nav-btn" href="about.html">About</a>
  <a class="nav-btn" href="learn.html">Library</a>
  <a class="nav-btn" href="topics.html">Topics</a>
  <a class="nav-btn" href="archive.html">Archive</a>
  <button class="nav-btn" id="nav-profile" onclick="showScreen('profile')">Profile</button>
  <button class="nav-btn" id="nav-install" onclick="showScreen('install')" style="display:none">Install</button>
  <button class="nav-btn" onclick="signOut()">Sign out</button>
</nav>
<div class="page-wrap">

<div id="auth-screen" class="screen">
  <div class="auth-card">
    <div class="auth-brand">Daily AI Quiz</div>
    <div id="auth-login-view">
      <h1 class="auth-title">Welcome back.</h1>
      <p class="auth-sub">Sign in to access your quiz and track your progress.</p>
      <div class="auth-err" id="login-err"></div>
      <div class="field"><label>Email</label><input type="email" id="login-email" placeholder="you@example.com"></div>
      <div class="field"><label>Password</label><input type="password" id="login-password" placeholder="••••••••"></div>
      <button class="big-btn btn-dark" onclick="signIn()">Sign in →</button>
      <div class="auth-toggle">No account? <a onclick="toggleAuth('signup')">Sign up</a></div>
    </div>
    <div id="auth-signup-view" style="display:none">
      <h1 class="auth-title">Create account.</h1>
      <p class="auth-sub">Sign up to start tracking your knowledge.</p>
      <div class="auth-err" id="signup-err"></div>
      <div class="field"><label>Email</label><input type="email" id="signup-email" placeholder="you@example.com"></div>
      <div class="field"><label>Password</label><input type="password" id="signup-password" placeholder="Min. 8 characters"></div>
      <div class="field"><label>Your name</label><input type="text" id="signup-name" placeholder="Your name"></div>
      <button class="big-btn btn-dark" onclick="signUp()">Create account →</button>
      <div class="auth-toggle">Already have one? <a onclick="toggleAuth('login')">Sign in</a></div>
    </div>
    <div id="auth-waiting-view" style="display:none">
      <div class="waiting-card">
        <div class="waiting-icon">⏳</div>
        <div class="waiting-title">Waiting for approval</div>
        <p class="waiting-sub">Your account was created. An admin needs to approve your access.</p>
        <br><button class="big-btn btn-light" style="margin-top:16px" onclick="signOut()">Sign out</button>
      </div>
    </div>
  </div>
</div>

<div id="splash-screen" class="screen">
  <div class="splash-inner">
    <div class="splash-art">
      <div class="logo-mark">
        <div class="lm-bar lm-bar1"></div>
        <div class="lm-bar lm-bar2"></div>
        <div class="lm-bar lm-bar3"></div>
        <div class="lm-wordmark">DISTILLED</div>
      </div>
    </div>
    <div>
      <div class="eyebrow">Session {session_num} · {DATE_STR}</div>
      <h1 class="splash-title">Time to learn <span class="accent">something.</span></h1>
      <p class="splash-sub">10 questions on AI training &amp; data quality, calibrated to your role and history.</p>
      <div class="session-info"><div class="session-dot"></div><div class="session-info-text" id="session-info-text">Loading your progress...</div></div>
      <div class="splash-actions">
        <button class="big-btn btn-dark" onclick="startQuiz()">Start today's quiz →</button>
        <a class="nav-link-btn" href="learn.html">Open concept library →</a>
      </div>
    </div>
  </div>
</div>

<div id="quiz-screen" class="screen">
  <div class="quiz-wrap">
    <div class="quiz-top">
      <span class="quiz-lbl">Session {session_num} · {DATE_STR}</span>
      <span class="score-pill" id="score-pill">0 / 0</span>
    </div>
    <div class="prog-row"><span id="prog-label">Question 1 of 10</span><span id="prog-pct">10%</span></div>
    <div class="prog-track"><div class="prog-bar" id="prog-bar" style="width:10%"></div></div>
    <div id="q-mount"></div>
  </div>
</div>

<div id="results-screen" class="screen">
  <div class="results-wrap">
    <div class="res-hero">
      <div class="res-c1"></div><div class="res-c2"></div>
      <span class="res-emoji" id="r-emoji">🌟</span>
      <span class="res-pct" id="r-pct">0%</span>
      <div class="res-desc" id="r-desc"></div>
    </div>
    <div class="stats-grid">
      <div class="stat-card"><div class="stat-n" id="r-c" style="color:var(--teal)">0</div><div class="stat-l">Correct</div></div>
      <div class="stat-card"><div class="stat-n" id="r-w" style="color:var(--coral)">0</div><div class="stat-l">Wrong</div></div>
      <div class="stat-card"><div class="stat-n" id="r-f" style="color:var(--yolk)">0</div><div class="stat-l">Flagged</div></div>
    </div>
    <div id="r-flags"></div>
    <div class="res-actions">
      <button class="big-btn btn-dark" onclick="showScreen('splash')">Back to home →</button>
      <button class="big-btn btn-light" onclick="showScreen('profile')">View profile →</button>
    </div>
  </div>
</div>

<div id="profile-screen" class="screen">
  <div class="profile-wrap">
    <div class="profile-header">
      <div class="ph-c1"></div><div class="ph-c2"></div>
      <div class="profile-name" id="profile-name">—</div>
      <div class="profile-email" id="profile-email">—</div>
      <div class="profile-stats">
        <div class="ps-card"><div class="ps-n" id="ps-sessions">—</div><div class="ps-l">Sessions</div></div>
        <div class="ps-card"><div class="ps-n" id="ps-avg">—</div><div class="ps-l">Avg score</div></div>
        <div class="ps-card"><div class="ps-n" id="ps-streak">—</div><div class="ps-l">Streak</div></div>
      </div>
    </div>
    <div class="section-title">Recent sessions</div>
    <div id="history-list"><div class="empty-state">No sessions yet — take your first quiz!</div></div>
    <div class="section-title" style="margin-top:24px">Areas to work on</div>
    <div class="weak-list" id="weak-list"><div class="empty-state">Complete a quiz to see your weak areas.</div></div>
    <div class="section-title" style="margin-top:28px">Leaderboard</div>
    <div style="display:flex;gap:8px;margin-bottom:16px">
      <button class="lb-tab active" id="lb-all" onclick="switchLB('all')">All time</button>
      <button class="lb-tab" id="lb-week" onclick="switchLB('week')">This week</button>
      <button class="lb-tab" id="lb-day" onclick="switchLB('day')">Today</button>
    </div>
    <div id="leaderboard-list"><div class="empty-state">Loading...</div></div>
  </div>
</div>

<div id="install-screen" class="screen">
  <div class="install-wrap">
    <div class="install-hero">
      <div class="ih-c1"></div><div class="ih-c2"></div><div class="ih-c3"></div>
      <div class="install-hero-content">
        <div class="install-eyebrow">Get the app</div>
        <h1 class="install-title">Daily AI Quiz<br><span class="accent">on any device.</span></h1>
        <p class="install-desc">No app store needed. Add it to your home screen in seconds.</p>
      </div>
    </div>
    <div class="install-already" id="install-already">✓ Already installed — you're good to go!</div>
    <div class="install-card">
      <div class="install-card-label">iPhone &amp; iPad</div>
      <div class="install-card-note">Must use Safari</div>
      <div class="install-steps">
        <div class="step"><div class="step-num">1</div><div class="step-text">Open this site in <strong>Safari</strong></div></div>
        <div class="step"><div class="step-num">2</div><div class="step-text">Tap <strong>Share ⬆</strong> at the bottom of the screen</div></div>
        <div class="step"><div class="step-num">3</div><div class="step-text">Tap <strong>"Add to Home Screen"</strong></div></div>
        <div class="step"><div class="step-num">4</div><div class="step-text">Tap <strong>Add</strong> — done!</div></div>
      </div>
    </div>
    <div class="install-card">
      <div class="install-card-label">Android</div>
      <div class="install-card-note">Chrome browser</div>
      <div class="install-steps">
        <div class="step"><div class="step-num">1</div><div class="step-text">Tap <strong>⋮ menu</strong> in Chrome</div></div>
        <div class="step"><div class="step-num">2</div><div class="step-text">Tap <strong>"Add to Home Screen"</strong></div></div>
        <div class="step"><div class="step-num">3</div><div class="step-text">Tap <strong>Add</strong> — done!</div></div>
      </div>
      <button class="install-native-btn" id="install-android-btn" onclick="doInstall()">Install now →</button>
    </div>
    <div class="install-card">
      <div class="install-card-label">Desktop</div>
      <div class="install-card-note">Chrome or Edge</div>
      <div class="install-steps">
        <div class="step"><div class="step-num">1</div><div class="step-text">Look for <strong>⊕ install icon</strong> in the address bar</div></div>
        <div class="step"><div class="step-num">2</div><div class="step-text">Click it and select <strong>Install</strong></div></div>
        <div class="step"><div class="step-num">3</div><div class="step-text">App opens as a standalone window</div></div>
      </div>
      <button class="install-native-btn" id="install-desktop-btn" onclick="doInstall()">Install now →</button>
    </div>
  </div>
</div>

</div>
<script>
const SURL='https://cokajjwirhtnvsyubepc.supabase.co';
const SKEY='eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZSIsInJlZiI6ImNva2Fqandpcmh0bnZzeXViZXBjIiwicm9sZSI6ImFub24iLCJpYXQiOjE3Nzk4NDAwNDUsImV4cCI6MjA5NTQxNjA0NX0._zvclbxuixP9dvMcW-uBdA0liOLzqgcVMOyIJ457DFs';
const QS={qs};
const SN={session_num};
let sb,currentUser=null,currentProfile=null,deferredPrompt=null;
const ST={{cur:0,answered:{{}},selected:{{}},submitted:new Set(),flagged:new Set(),expOpen:new Set(),score:0}};

document.addEventListener('DOMContentLoaded',()=>{{
  const a=document.getElementById('auth-screen');
  if(a){{a.style.display='flex';a.classList.add('on');}}
  document.getElementById('auth-login-view').style.display='block';
}});

window.addEventListener('load',async()=>{{
  try{{
    sb=supabase.createClient(SURL,SKEY,{{auth:{{persistSession:true,autoRefreshToken:true,detectSessionInUrl:true,storage:window.localStorage}}}});
  }}catch(e){{console.error('Supabase:',e);return;}}
  sb.auth.onAuthStateChange(async(event,session)=>{{
    console.log('Auth:',event,session?.user?.email);
    if(session?.user&&(event==='SIGNED_IN'||event==='INITIAL_SESSION')&&!currentUser){{
      currentUser=session.user;
      await onLoggedIn();
    }}else if(session?.user){{
      currentUser=session.user;
    }}else if(event==='SIGNED_OUT'){{
      currentUser=null;currentProfile=null;showAuth();
    }}
  }});
  const{{data}}=await sb.auth.getSession();
  if(data?.session?.user){{currentUser=data.session.user;await onLoggedIn();}}
  window.addEventListener('beforeinstallprompt',e=>{{
    e.preventDefault();deferredPrompt=e;
    document.getElementById('nav-install').style.display='';
    document.getElementById('install-android-btn').style.display='block';
    document.getElementById('install-desktop-btn').style.display='block';
  }});
  if(window.matchMedia('(display-mode: standalone)').matches||window.navigator.standalone)
    document.getElementById('install-already').style.display='block';
  if('serviceWorker' in navigator)
    navigator.serviceWorker.register('/service-worker.js').catch(()=>{{}});
}});

function showAuth(){{
  document.getElementById('main-nav').style.display='none';
  ['auth','splash','quiz','results','profile','install'].forEach(s=>{{
    const el=document.getElementById(s+'-screen');
    if(el){{el.classList.remove('on');el.style.display='none';}}
  }});
  const a=document.getElementById('auth-screen');
  a.style.display='flex';a.classList.add('on');
  document.getElementById('auth-login-view').style.display='block';
  document.getElementById('auth-signup-view').style.display='none';
  document.getElementById('auth-waiting-view').style.display='none';
}}

function showScreen(name){{
  ['auth','splash','quiz','results','profile','install'].forEach(s=>{{
    const el=document.getElementById(s+'-screen');
    if(el){{el.classList.remove('on');el.style.display='none';}}
  }});
  const el=document.getElementById(name+'-screen');
  if(!el)return;
  el.style.display=name==='auth'?'flex':'block';
  el.classList.add('on');
  document.querySelectorAll('.nav-btn').forEach(b=>b.classList.remove('active'));
  if(name==='splash'||name==='quiz'||name==='results')document.getElementById('nav-quiz')?.classList.add('active');
  if(name==='profile'){{document.getElementById('nav-profile')?.classList.add('active');loadProfileData();setTimeout(loadLeaderboard,500);}}
  if(name==='install')document.getElementById('nav-install')?.classList.add('active');
  window.scrollTo(0,0);
}}

async function onLoggedIn(){{
  console.log('onLoggedIn:',currentUser.email);
  // Use auth token data directly — no profiles table query needed
  currentProfile={{
    id:currentUser.id,
    email:currentUser.email,
    is_approved:true,
    display_name:currentUser.user_metadata?.display_name||null
  }};
  document.getElementById('main-nav').style.display='flex';
  showScreen('splash');
  loadSplashInfo();
}}

function showWaiting(){{
  ['auth','splash','quiz','results','profile','install'].forEach(s=>{{
    const el=document.getElementById(s+'-screen');if(el){{el.classList.remove('on');el.style.display='none';}}
  }});
  const a=document.getElementById('auth-screen');a.style.display='flex';a.classList.add('on');
  document.getElementById('auth-login-view').style.display='none';
  document.getElementById('auth-signup-view').style.display='none';
  document.getElementById('auth-waiting-view').style.display='block';
}}

function toggleAuth(v){{
  document.getElementById('auth-login-view').style.display=v==='login'?'block':'none';
  document.getElementById('auth-signup-view').style.display=v==='signup'?'block':'none';
}}

async function signIn(){{
  const email=document.getElementById('login-email').value.trim();
  const pass=document.getElementById('login-password').value;
  const err=document.getElementById('login-err');
  const btn=document.querySelector('#auth-login-view .btn-dark');
  err.classList.remove('on');
  if(!email||!pass){{err.textContent='Please fill in all fields.';err.classList.add('on');return;}}
  if(btn){{btn.textContent='Signing in...';btn.disabled=true;}}
  try{{
    const{{error}}=await sb.auth.signInWithPassword({{email,password:pass}});
    if(error){{err.textContent=error.message;err.classList.add('on');}}
  }}catch(e){{err.textContent='Connection error — try again.';err.classList.add('on');}}
  finally{{if(btn){{btn.textContent='Sign in →';btn.disabled=false;}}}}
}}

async function signUp(){{
  const email=document.getElementById('signup-email').value.trim();
  const pass=document.getElementById('signup-password').value;
  const name=document.getElementById('signup-name').value.trim();
  const err=document.getElementById('signup-err');
  err.classList.remove('on');
  if(!email||!pass||!name){{err.textContent='Please fill in all fields.';err.classList.add('on');return;}}
  if(pass.length<8){{err.textContent='Password must be at least 8 characters.';err.classList.add('on');return;}}
  const{{error}}=await sb.auth.signUp({{email,password:pass,options:{{data:{{display_name:name}}}}}});
  if(error){{err.textContent=error.message;err.classList.add('on');}}
  else showWaiting();
}}

async function signOut(){{await sb.auth.signOut();}}
async function doInstall(){{if(deferredPrompt){{deferredPrompt.prompt();await deferredPrompt.userChoice;deferredPrompt=null;}}}}

async function loadSplashInfo(){{
  const{{data}}=await sb.from('quiz_sessions').select('score_pct,date').eq('user_id',currentUser.id).order('date',{{ascending:false}}).limit(5);
  const name=currentProfile?.display_name||currentUser?.user_metadata?.display_name||currentProfile?.email?.split('@')[0]||'there';
  if(data&&data.length>0){{
    const avg=Math.round(data.reduce((s,r)=>s+r.score_pct,0)/data.length);
    document.getElementById('session-info-text').innerHTML=`Hey ${{name}} — last: <strong>${{data[0].score_pct}}%</strong> · avg <strong>${{avg}}%</strong> over ${{data.length}} session${{data.length>1?'s':''}}`;
  }}else{{
    document.getElementById('session-info-text').textContent=`Hey ${{name}} — first session, let's go!`;
  }}
}}

function startQuiz(){{
  if(!QS.length){{alert('Quiz not loaded — please refresh.');return;}}
  Object.assign(ST,{{cur:0,answered:{{}},selected:{{}},submitted:new Set(),flagged:new Set(),expOpen:new Set(),score:0}});
  showScreen('quiz');renderQ();
}}

function renderQ(){{
  const i=ST.cur,q=QS[i],ans=ST.answered[i]!==undefined,submitted=ST.submitted.has(i),flagged=ST.flagged.has(i),expOpen=ST.expOpen.has(i);
  const sel=ST.selected[i];
  const pct=Math.round(((i+1)/QS.length)*100);
  document.getElementById('prog-bar').style.width=pct+'%';
  document.getElementById('prog-label').textContent='Question '+(i+1)+' of '+QS.length;
  document.getElementById('prog-pct').textContent=pct+'%';
  document.getElementById('score-pill').textContent=ST.score+' / '+Object.keys(ST.answered).length;
  const cc=ans?(ST.answered[i]===q.correct?'s-correct':'s-wrong'):(flagged?'s-flag':'');
  const dmap={{foundational:'ch-f',practitioner:'ch-p',advanced:'ch-a'}};
  const dlbl={{foundational:'Foundational',practitioner:'Practitioner',advanced:'Advanced'}};
  const optsH=q.opts.map((o,oi)=>{{
    let cls='';
    if(ans){{
      if(oi===q.correct)cls='o-show';
      if(oi===ST.answered[i]&&oi!==q.correct)cls='o-wrong';
      if(oi===ST.answered[i]&&oi===q.correct)cls='o-correct';
    }}else if(oi===sel)cls='o-selected';
    return `<button class="opt ${{cls}}" ${{ans?'disabled':''}} onclick="selectOpt(${{oi}},${{i}})"><span class="opt-key">${{String.fromCharCode(65+oi)}}</span><span>${{o}}</span></button>`;
  }}).join('');
  const linkH=ans&&q.article_url?`<a style="display:inline-flex;align-items:center;gap:5px;margin-top:12px;font-size:12px;font-weight:600;color:var(--sky);text-decoration:none;padding:6px 12px;background:rgba(59,158,255,.08);border-radius:100px" href="${{q.article_url}}" target="_blank" rel="noopener">📖 Read more →</a>`:'';
  const appliedH=q.applied_to?`<span class="chip" style="background:#F0F4FF;color:#4B6BCC;font-size:10px">⚡ ${{q.applied_to}}</span>`:'';
  const submitH=!ans&&sel!==undefined?`<button class="big-btn btn-dark" style="margin-top:12px;padding:13px" onclick="doAns(${{sel}},${{i}})">Submit answer →</button>`:'';
  const navH=`<div class="nav-row">${{i>0?`<button class="big-btn btn-light" onclick="doNav(-1)">← Back</button>`:'<span></span>'}}${{ans?(i<QS.length-1?`<button class="big-btn btn-dark" onclick="doNav(1)">Next →</button>`:`<button class="big-btn btn-dark" onclick="doResults()">See results →</button>`):'' }}</div>`;
  document.getElementById('q-mount').innerHTML=`
    <div class="q-card ${{cc}}">
      <div class="meta-row"><span class="chip ch-topic">${{q.section||''}}</span><span class="chip ${{dmap[q.difficulty]||'ch-f'}}">${{dlbl[q.difficulty]||''}}</span>${{appliedH}}<span class="q-count">${{i+1}} / ${{QS.length}}</span></div>
      <div class="q-text">${{q.q}}</div>
      <div class="opts">${{optsH}}</div>
      ${{submitH}}
      <div class="actions" style="margin-top:${{submitH?'8px':'0'}}">
        <button class="act act-flag ${{flagged?'on':''}}" onclick="doFlag(${{i}})">${{flagged?'⚑ Flagged':'⚐ Flag'}}</button>
        ${{ans?`<button class="act act-exp" onclick="doExp(${{i}})">${{expOpen?'Hide':'Explanation'}}</button>`:''}}
        ${{ans?`<span class="fb ${{ST.answered[i]===q.correct?'ok':'no'}}">${{ST.answered[i]===q.correct?'✓ Correct':'✗ Wrong'}}</span>`:''}}
      </div>
      <div class="exp-panel ${{expOpen?'on':''}}" id="exp-${{i}}">${{q.explanation||''}}${{linkH}}</div>
    </div>${{navH}}`;
}}

function selectOpt(oi,i){{
  if(ST.answered[i]!==undefined)return;
  ST.selected[i]=oi;
  renderQ();
}}

function doAns(oi,i){{
  if(i===undefined)i=ST.cur;
  if(ST.answered[i]!==undefined)return;
  ST.answered[i]=oi;ST.submitted.add(i);
  if(oi===QS[i].correct)ST.score++;
  renderQ();
}}
function doFlag(i){{ST.flagged.has(i)?ST.flagged.delete(i):ST.flagged.add(i);renderQ();}}
function doExp(i){{ST.expOpen.has(i)?ST.expOpen.delete(i):ST.expOpen.add(i);renderQ();}}
function doNav(d){{ST.cur=Math.max(0,Math.min(QS.length-1,ST.cur+d));renderQ();}}

async function doResults(){{
  showScreen('results');
  const tot=QS.length,cor=ST.score,wrong=Object.keys(ST.answered).length-cor,pct=Math.round(cor/tot*100);
  const emojis=[[90,'🌟'],[75,'✨'],[55,'💪'],[0,'🌱']];
  const descs=[[90,'Exceptional. Strong command across the board.'],[75,'Solid session — a few things worth revisiting.'],[55,'Good progress. Lean into those deep dives.'],[0,'Every session builds the foundation. Keep going!']];
  document.getElementById('r-emoji').textContent=(emojis.find(([k])=>pct>=k)||[0,'🌱'])[1];
  document.getElementById('r-pct').textContent=pct+'%';
  document.getElementById('r-desc').textContent=(descs.find(([k])=>pct>=k)||[0,''])[1];
  document.getElementById('r-c').textContent=cor;
  document.getElementById('r-w').textContent=wrong;
  document.getElementById('r-f').textContent=ST.flagged.size;
  if(ST.flagged.size>0){{
    const items=[...ST.flagged].map(i=>`<div class="flagged-item"><div class="flagged-cat">${{QS[i].section||''}}</div>${{QS[i].concept||''}}</div>`).join('');
    document.getElementById('r-flags').innerHTML=`<div class="flagged-box"><div class="flagged-title">⚑ Concepts you flagged</div>${{items}}</div>`;
  }}
  if(currentUser){{
    const weak=Object.entries(ST.answered).filter(([i,a])=>parseInt(a)!==QS[parseInt(i)].correct).map(([i])=>QS[parseInt(i)].concept).filter(Boolean);
    const flags=[...ST.flagged].map(i=>QS[i].concept).filter(Boolean);
    const topics=[...new Set(QS.map(q=>q.section))];
    const{{error}}=await sb.from('quiz_sessions').insert({{user_id:currentUser.id,session_number:SN,
      date:new Date().toISOString().split('T')[0],score:cor,total:tot,score_pct:pct,
      weak_areas:weak,flagged_concepts:flags,topics_covered:topics}});
    if(error)console.error('Save error:',error.message);
    for(const i of Object.keys(ST.answered)){{
      const q=QS[parseInt(i)];if(!q?.concept)continue;
      await sb.from('user_concepts').upsert({{user_id:currentUser.id,concept:q.concept,
        category:q.section,times_seen:1,flagged_count:ST.flagged.has(parseInt(i))?1:0,
        last_seen:new Date().toISOString().split('T')[0]}},{{onConflict:'user_id,concept'}});
    }}
  }}
}}

async function loadProfileData(){{
  if(!currentUser)return;
  const name=currentProfile?.display_name||currentUser?.user_metadata?.display_name||currentProfile?.email?.split('@')[0]||'—';
  document.getElementById('profile-name').textContent=name;
  document.getElementById('profile-email').textContent=currentProfile?.email||currentUser?.email||'—';
  document.getElementById('ps-sessions').textContent='…';
  document.getElementById('ps-avg').textContent='…';
  document.getElementById('ps-streak').textContent='…';
  let sessions=null;
  for(let attempt=0;attempt<3;attempt++){{
    try{{
      const r=await sb.from('quiz_sessions').select('*').eq('user_id',currentUser.id).order('date',{{ascending:false}}).limit(20);
      if(r.error)throw r.error;
      sessions=r.data;break;
    }}catch(e){{if(attempt<2)await new Promise(r=>setTimeout(r,1000));}}
  }}
  if(sessions&&sessions.length>0){{
    const avg=Math.round(sessions.reduce((s,r)=>s+r.score_pct,0)/sessions.length);
    document.getElementById('ps-sessions').textContent=sessions.length;
    document.getElementById('ps-avg').textContent=avg+'%';
    document.getElementById('ps-streak').textContent=calcStreak(sessions);
    document.getElementById('history-list').innerHTML=sessions.slice(0,8).map(s=>`
      <div class="history-card">
        <div class="history-date">${{new Date(s.date+'T12:00:00').toLocaleDateString('en-US',{{month:'short',day:'numeric'}})}}</div>
        <div class="history-bar-wrap"><div class="history-bar" style="width:${{s.score_pct}}%;background:${{s.score_pct>=75?'var(--teal)':s.score_pct>=55?'var(--yolk)':'var(--coral)'}}">&nbsp;</div></div>
        <div class="history-pct">${{s.score_pct}}%</div>
      </div>`).join('');
    const wm={{}};sessions.forEach(s=>(s.weak_areas||[]).forEach(w=>wm[w]=(wm[w]||0)+1));
    const sorted=Object.entries(wm).sort((a,b)=>b[1]-a[1]).slice(0,6);
    document.getElementById('weak-list').innerHTML=sorted.length
      ?sorted.map(([c,n])=>`<div class="weak-item"><span>${{c}}</span><span class="weak-count">${{n}}×</span></div>`).join('')
      :'<div class="empty-state">No weak areas yet!</div>';
  }}else{{
    document.getElementById('ps-sessions').textContent='0';
    document.getElementById('ps-avg').textContent='—';
    document.getElementById('ps-streak').textContent='0';
  }}
}}

let lbPeriod='all';
async function switchLB(period){{
  lbPeriod=period;
  document.querySelectorAll('.lb-tab').forEach(t=>t.classList.toggle('active',t.id==='lb-'+period));
  await loadLeaderboard();
}}

async function loadLeaderboard(){{
  const container=document.getElementById('leaderboard-list');
  if(!container)return;
  container.innerHTML='<div class="empty-state">Loading...</div>';
  try{{
    let query=sb.from('quiz_sessions').select('user_id,score_pct,date').eq('is_archive',false);
    const now=new Date();
    if(lbPeriod==='day'){{
      const today=now.toISOString().split('T')[0];
      query=query.eq('date',today);
    }}else if(lbPeriod==='week'){{
      const weekAgo=new Date(now-7*24*60*60*1000).toISOString().split('T')[0];
      query=query.gte('date',weekAgo);
    }}
    const{{data,error}}=await query;
    if(error||!data.length){{container.innerHTML='<div class="empty-state">No data for this period yet.</div>';return;}}
    // Aggregate by user
    const userMap={{}};
    data.forEach(s=>{{
      if(!userMap[s.user_id])userMap[s.user_id]={{total:0,count:0}};
      userMap[s.user_id].total+=s.score_pct;
      userMap[s.user_id].count+=1;
    }});
    // Use current user's known initials, anonymous for others
    const myName=currentProfile?.display_name||currentUser?.user_metadata?.display_name||currentProfile?.email?.split('@')[0]||'Me';
    const myInitials=myName.split(' ').map(w=>w[0]).join('').substring(0,2).toUpperCase();
    const nameMap={{}};
    if(currentUser)nameMap[currentUser.id]=myInitials;
    const ranked=Object.entries(userMap)
      .map(([uid,d])=>{{const avg=Math.round(d.total/d.count);return{{uid,avg,count:d.count}};  }})
      .sort((a,b)=>b.avg-a.avg||b.count-a.count);
    container.innerHTML=ranked.map((u,idx)=>{{
      const isMe=currentUser&&u.uid===currentUser.id;
      const initials=nameMap[u.uid]||'??';
      const medal=idx===0?'🥇':idx===1?'🥈':idx===2?'🥉':'';
      return `<div class="lb-row ${{isMe?'me':''}}">
        <div class="lb-rank ${{idx<3?'top':''}}">$${{medal||idx+1}}</div>
        <div class="lb-initials">${{initials}}</div>
        <div><div class="lb-name">${{isMe?'You':initials}}</div><div class="lb-sessions">${{u.count}} session${{u.count!==1?'s':''}}</div></div>
        <div class="lb-avg">${{u.avg}}%</div>
      </div>`;
    }}).join('');
  }}catch(e){{console.error(e);container.innerHTML='<div class="empty-state">Could not load leaderboard.</div>';}}
}}

function calcStreak(sessions){{
  if(!sessions.length)return 0;
  const today=new Date().toISOString().split('T')[0];
  const dates=[...new Set(sessions.map(s=>s.date))].sort().reverse();
  let streak=0,expected=today;
  for(const d of dates){{
    if(d===expected){{streak++;const dt=new Date(expected+'T12:00:00');dt.setDate(dt.getDate()-1);expected=dt.toISOString().split('T')[0];}}
    else break;
  }}
  return streak;
}}
</script>
</body>
</html>"""

def build_learn(concept_library, session_num):
    bad=["explanation will appear","check back","coming tomorrow","next session","coming soon"]
    concepts=[c for c in concept_library if len(c.get("explanation","").lower())>=50 and not any(p in c.get("explanation","").lower() for p in bad)]
    cj=json.dumps(concepts,ensure_ascii=False)
    total=len(concepts)

    if total==0:
        body="""<div style="text-align:center;padding:60px 20px">
      <div style="font-size:40px;margin-bottom:16px">🌱</div>
      <div style="font-family:'Unbounded',sans-serif;font-size:18px;font-weight:900;margin-bottom:8px">Building your library</div>
      <p style="font-size:14px;color:var(--muted);line-height:1.7;margin-bottom:28px">Concepts appear here after each quiz session.</p>
      <a href="index.html" style="display:inline-flex;align-items:center;justify-content:space-between;background:var(--white);border-radius:18px;padding:16px 20px;text-decoration:none;color:var(--ink);box-shadow:0 2px 8px rgba(45,41,38,.05);gap:12px">
        <div><div style="font-size:14px;font-weight:600">Take today's quiz</div><div style="font-size:12px;color:var(--muted);margin-top:2px">Start building your library</div></div>
        <span style="font-size:20px;color:var(--muted)">→</span>
      </a></div>"""
    else:
        body=f"""<div class="controls-row">
    <div class="filters" id="filters"></div>
    <div class="view-toggle">
      <button class="view-btn active" id="vbtn-one" onclick="setView('one')">One</button>
      <button class="view-btn" id="vbtn-all" onclick="setView('all')">All</button>
    </div>
  </div>
  <div class="prog-wrap">
    <div class="prog-row"><span>Progress</span><span id="pct-text">0%</span></div>
    <div class="prog-track"><div class="prog-fill" id="prog-fill" style="width:0%"></div></div>
  </div>
  <div id="one-view">
    <div class="card-nav">
      <button class="nav-btn" id="btn-prev" onclick="goCard(-1)" disabled>←</button>
      <div class="card-dots" id="card-dots"></div>
      <button class="nav-btn" id="btn-next" onclick="goCard(1)">→</button>
    </div>
    <div id="card-mount"></div>
  </div>
  <div class="all-cards-wrap" id="all-view"></div>
  <div class="completion" id="completion">
    <span style="font-size:40px;margin-bottom:12px;display:block">🎉</span>
    <div style="font-family:'Unbounded',sans-serif;font-size:22px;font-weight:900;color:var(--yolk);margin-bottom:8px">All done!</div>
    <p style="font-size:14px;color:rgba(255,255,255,.6);line-height:1.6;margin-bottom:20px">Repetition is how they stick.</p>
    <button onclick="resetAll()" style="font-family:'DM Sans',sans-serif;font-size:13px;font-weight:600;padding:12px 24px;border-radius:100px;border:2px solid rgba(255,255,255,.2);background:transparent;color:rgba(255,255,255,.7);cursor:pointer">Start over</button>
  </div>
  <div class="bottom-links">
    <a class="quiz-link" href="index.html"><div><div class="quiz-link-text">Take today's quiz</div><div class="quiz-link-sub">Put these concepts to the test</div></div><span style="font-size:20px;color:var(--muted)">→</span></a>
    <a class="quiz-link" href="topics.html"><div><div class="quiz-link-text">Request a topic</div><div class="quiz-link-sub">Add to tomorrow's quiz</div></div><span style="font-size:20px;color:var(--muted)">→</span></a>
  </div>"""

    return f"""<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Concept Library · Daily AI Quiz</title>
<style>
{css_base()}
.wrap{{max-width:480px;margin:0 auto;padding:0 20px 80px}}
.top-bar{{padding:28px 0 20px;display:flex;align-items:center;justify-content:space-between}}
a.back-btn{{font-size:13px;font-weight:600;color:var(--muted);text-decoration:none;transition:color .15s}}
a.back-btn:hover{{color:var(--ink)}}
.page-title{{font-family:'Unbounded',sans-serif;font-size:26px;font-weight:900;letter-spacing:-.02em;line-height:1.1;margin-bottom:6px}}
.page-sub{{font-size:14px;color:var(--muted);margin-bottom:8px;line-height:1.6}}
.updated-tag{{font-size:11px;font-weight:600;color:var(--teal);margin-bottom:20px;display:block}}
.controls-row{{display:flex;align-items:center;gap:8px;margin-bottom:20px;flex-wrap:wrap}}
.filters{{display:flex;gap:7px;flex-wrap:wrap;flex:1}}
.filter-btn{{font-family:'DM Sans',sans-serif;font-size:12px;font-weight:600;padding:7px 13px;border-radius:100px;border:2px solid var(--border);background:transparent;color:var(--muted);cursor:pointer;transition:all .15s;white-space:nowrap}}
.filter-btn:hover{{border-color:var(--ink);color:var(--ink)}}.filter-btn.active{{background:var(--ink);color:#fff;border-color:var(--ink)}}
.view-toggle{{display:flex;background:var(--white);border:1.5px solid var(--border);border-radius:100px;overflow:hidden;flex-shrink:0}}
.view-btn{{font-family:'DM Sans',sans-serif;font-size:12px;font-weight:600;padding:6px 12px;border:none;background:transparent;color:var(--muted);cursor:pointer;transition:all .15s}}
.view-btn.active{{background:var(--ink);color:#fff}}
.prog-wrap{{margin-bottom:24px}}
.prog-row{{display:flex;justify-content:space-between;font-size:11px;font-weight:600;color:var(--muted);margin-bottom:6px}}
.prog-track{{height:6px;background:rgba(45,41,38,.10);border-radius:100px;overflow:hidden}}
.prog-fill{{height:100%;background:var(--teal);border-radius:100px;transition:width .4s ease}}
.card-nav{{display:flex;align-items:center;justify-content:space-between;margin-bottom:20px}}
.card-dots{{display:flex;gap:6px;flex-wrap:wrap;flex:1;justify-content:center}}
.cdot{{width:8px;height:8px;border-radius:50%;background:rgba(45,41,38,.15);transition:all .2s;cursor:pointer;flex-shrink:0}}
.cdot.active{{background:var(--ink);transform:scale(1.2)}}.cdot.learned{{background:var(--teal)}}
.nav-btn{{width:40px;height:40px;border-radius:50%;border:2px solid var(--border);background:var(--white);font-size:16px;cursor:pointer;display:flex;align-items:center;justify-content:center;transition:all .15s;flex-shrink:0}}
.nav-btn:hover{{border-color:var(--ink)}}.nav-btn:disabled{{opacity:.3;cursor:default}}
.card-wrap{{perspective:1200px;margin-bottom:16px;cursor:pointer}}
.card-inner{{position:relative;width:100%;min-height:220px;transform-style:preserve-3d;transition:transform .5s cubic-bezier(.4,0,.2,1)}}
.card-wrap.flipped .card-inner{{transform:rotateY(180deg)}}
.card-face{{position:absolute;width:100%;min-height:220px;backface-visibility:hidden;-webkit-backface-visibility:hidden;border-radius:24px;padding:28px}}
.card-front{{background:var(--white);box-shadow:0 2px 16px rgba(45,41,38,.08);display:flex;flex-direction:column;justify-content:space-between}}
.card-back{{background:var(--ink);color:#fff;transform:rotateY(180deg);display:flex;flex-direction:column;justify-content:space-between;min-height:220px;position:absolute;top:0;left:0;right:0}}
.card-cat{{font-size:10px;font-weight:700;letter-spacing:.1em;text-transform:uppercase;margin-bottom:16px;display:flex;align-items:center;gap:8px}}
.cat-dot{{width:8px;height:8px;border-radius:50%;flex-shrink:0}}
.card-concept{{font-family:'Unbounded',sans-serif;font-size:20px;font-weight:900;letter-spacing:-.02em;line-height:1.2;margin-bottom:20px;flex:1;display:flex;align-items:center}}
.flip-hint{{font-size:12px;font-weight:600;color:var(--muted);display:flex;align-items:center;gap:6px}}
.flip-icon{{width:28px;height:28px;background:var(--bg);border-radius:50%;display:flex;align-items:center;justify-content:center;font-size:14px}}
.back-cat{{font-size:10px;font-weight:700;letter-spacing:.1em;text-transform:uppercase;color:rgba(255,255,255,.4);margin-bottom:10px}}
.back-concept{{font-family:'Unbounded',sans-serif;font-size:14px;font-weight:700;color:var(--yolk);margin-bottom:12px;letter-spacing:-.01em}}
.back-exp{{font-size:13.5px;line-height:1.75;color:rgba(255,255,255,.85);flex:1}}
.back-exp strong{{color:var(--teal);font-weight:600}}
.back-exp p{{margin-bottom:10px}}.back-exp p:last-child{{margin-bottom:0}}
.got-it-btn{{margin-top:14px;display:inline-flex;align-items:center;gap:6px;font-family:'DM Sans',sans-serif;font-size:12px;font-weight:600;color:rgba(255,255,255,.5);background:none;border:2px solid rgba(255,255,255,.15);border-radius:100px;padding:7px 14px;cursor:pointer;transition:all .15s}}
.got-it-btn:hover{{border-color:var(--teal);color:var(--teal)}}.got-it-btn.done{{border-color:var(--teal);color:var(--teal);background:rgba(0,201,160,.1)}}
.all-cards-wrap{{display:none;flex-direction:column;gap:14px;margin-bottom:20px}}
.all-cards-wrap.on{{display:flex}}
.stack-card{{background:var(--white);border-radius:20px;overflow:hidden;box-shadow:0 2px 12px rgba(45,41,38,.07)}}
.stack-front{{padding:20px 22px;cursor:pointer;display:flex;align-items:center;justify-content:space-between;gap:12px;transition:background .15s}}
.stack-front:hover{{background:rgba(45,41,38,.02)}}
.stack-concept-name{{font-family:'Unbounded',sans-serif;font-size:14px;font-weight:900;letter-spacing:-.01em;line-height:1.3}}
.stack-cat-chip{{font-size:10px;font-weight:700;padding:3px 9px;border-radius:100px;white-space:nowrap;flex-shrink:0}}
.stack-chevron{{font-size:18px;color:var(--muted);transition:transform .2s;flex-shrink:0}}
.stack-card.open .stack-chevron{{transform:rotate(90deg)}}
.stack-back{{display:none;padding:0 22px 20px;font-size:13.5px;line-height:1.75;border-top:1px solid var(--border)}}
.stack-card.open .stack-back{{display:block;animation:fadeIn .2s ease}}
.stack-back strong{{color:var(--sky);font-weight:700}}
.stack-back p{{margin-bottom:8px}}.stack-back p:last-child{{margin-bottom:0}}
.stack-learned-btn{{margin-top:12px;display:inline-flex;align-items:center;gap:5px;font-family:'DM Sans',sans-serif;font-size:12px;font-weight:600;color:var(--muted);background:none;border:1.5px solid var(--border);border-radius:100px;padding:6px 14px;cursor:pointer;transition:all .15s}}
.stack-learned-btn:hover{{border-color:var(--teal);color:var(--teal)}}.stack-learned-btn.done{{border-color:var(--teal);color:var(--teal);background:#DFFAF1}}
.completion{{display:none;background:var(--ink);border-radius:24px;padding:32px 28px;text-align:center;margin-bottom:16px}}
.completion.show{{display:block}}
.bottom-links{{display:flex;flex-direction:column;gap:10px;margin-top:28px}}
.quiz-link{{display:flex;align-items:center;justify-content:space-between;background:var(--white);border-radius:18px;padding:16px 20px;text-decoration:none;color:var(--ink);box-shadow:0 2px 8px rgba(45,41,38,.05);transition:box-shadow .15s}}
.quiz-link:hover{{box-shadow:0 4px 16px rgba(45,41,38,.10)}}
.quiz-link-text{{font-size:14px;font-weight:600}}
.quiz-link-sub{{font-size:12px;color:var(--muted);margin-top:2px}}
@keyframes fadeIn{{from{{opacity:0}}to{{opacity:1}}}}
</style>
</head>
<body>
<div class="wrap">
  <div class="top-bar">
    <a class="back-btn" href="index.html">← Quiz</a>
    <span id="prog-text" style="font-size:12px;font-weight:600;color:var(--muted)">0 learned</span>
  </div>
  <h1 class="page-title">Concept Library</h1>
  <p class="page-sub">Flip each card or scroll through all at once.</p>
  <span class="updated-tag">Updated {DATE_STR} · {total} concepts · Session {session_num}</span>
  {body}
</div>
<script>
const CONCEPTS={cj};
let current=0,learned=new Set(),activeFilter='All',viewMode='one';
const CATS=['All',...new Set(CONCEPTS.map(c=>c.category||'General'))];
function init(){{
  if(!CONCEPTS.length)return;
  document.getElementById('filters').innerHTML=CATS.map(cat=>
    `<button class="filter-btn ${{cat==='All'?'active':''}}" onclick="setFilter('${{cat}}')">${{cat}}</button>`
  ).join('');
  render();updateProgress();
}}
function setFilter(cat){{
  activeFilter=cat;current=0;
  document.querySelectorAll('.filter-btn').forEach(b=>b.classList.toggle('active',b.textContent===cat));
  document.getElementById('completion')?.classList.remove('show');render();
}}
function setView(v){{
  viewMode=v;
  document.getElementById('one-view').style.display=v==='one'?'block':'none';
  document.getElementById('all-view').classList.toggle('on',v==='all');
  document.querySelectorAll('.view-btn').forEach(b=>b.classList.toggle('active',b.id==='vbtn-'+v));
  render();
}}
function filtered(){{
  return activeFilter==='All'?CONCEPTS.map((_,i)=>i):CONCEPTS.map((c,i)=>(c.category||'General')===activeFilter?i:-1).filter(i=>i>=0);
}}
function render(){{if(viewMode==='one')renderOne();else renderAll();}}
function renderOne(){{
  const ids=filtered(),mount=document.getElementById('card-mount');
  if(!mount)return;
  if(!ids.length){{mount.innerHTML='<p style="text-align:center;color:var(--muted);padding:40px 0">No concepts here yet.</p>';return;}}
  if(current>=ids.length)current=0;
  document.getElementById('btn-prev').disabled=current===0;
  document.getElementById('btn-next').disabled=current===ids.length-1;
  document.getElementById('card-dots').innerHTML=ids.map((ci,pos)=>
    `<div class="cdot ${{pos===current?'active':''}} ${{learned.has(ci)?'learned':''}}" onclick="jumpTo(${{pos}})"></div>`
  ).join('');
  const ci=ids[current],c=CONCEPTS[ci],isL=learned.has(ci);
  mount.innerHTML=`<div class="card-wrap" id="cw-${{ci}}" onclick="flipCard(${{ci}})">
    <div class="card-inner">
      <div class="card-face card-front">
        <div>
          <div class="card-cat"><span class="cat-dot" style="background:${{c.color||'#8FA3D8'}}"></span><span style="color:${{c.color||'#8FA3D8'}}">${{c.category||'General'}}</span>${{c.is_new?'<span style="font-size:9px;font-weight:800;color:#fff;background:var(--coral);padding:2px 7px;border-radius:100px;letter-spacing:.05em;text-transform:uppercase;margin-left:4px">NEW</span>':''}}</div>
          <div class="card-concept">${{c.concept}}</div>
        </div>
        <div class="flip-hint"><div class="flip-icon">↻</div>Tap to reveal</div>
      </div>
      <div class="card-face card-back">
        <div>
          <div class="back-cat">${{c.category||'General'}}</div>
          <div class="back-concept">${{c.concept}}</div>
          <div class="back-exp">${{c.explanation||''}}</div>
        </div>
        <button class="got-it-btn ${{isL?'done':''}}" onclick="event.stopPropagation();markLearned(${{ci}})">${{isL?'✓ Learned':'✓ Got it'}}</button>
      </div>
    </div></div>`;
}}
function flipCard(ci){{document.getElementById('cw-'+ci)?.classList.toggle('flipped');}}
function jumpTo(pos){{current=pos;renderOne();document.getElementById('completion')?.classList.remove('show');}}
function goCard(dir){{
  const ids=filtered();current=Math.max(0,Math.min(ids.length-1,current+dir));
  document.querySelectorAll('.card-wrap').forEach(c=>c.classList.remove('flipped'));
  renderOne();document.getElementById('completion')?.classList.remove('show');
}}
function renderAll(){{
  const ids=filtered(),wrap=document.getElementById('all-view');
  if(!wrap)return;
  if(!ids.length){{wrap.innerHTML='<p style="text-align:center;color:var(--muted);padding:40px 0">No concepts here yet.</p>';return;}}
  wrap.innerHTML=ids.map(ci=>{{
    const c=CONCEPTS[ci],isL=learned.has(ci);
    return `<div class="stack-card" id="sc-${{ci}}">
      <div class="stack-front" onclick="toggleStack(${{ci}})">
        <div>
          <div style="display:flex;align-items:center;gap:6px">
            <div class="stack-concept-name">${{c.concept}}</div>
            ${{c.is_new?'<span style="font-size:9px;font-weight:800;color:#fff;background:var(--coral);padding:2px 7px;border-radius:100px;letter-spacing:.05em;text-transform:uppercase">NEW</span>':''}}
          </div>
          <span class="stack-cat-chip" style="background:${{c.color||'#8FA3D8'}}22;color:${{c.color||'#8FA3D8'}}">${{c.category||'General'}}</span>
        </div>
        <span class="stack-chevron">›</span>
      </div>
      <div class="stack-back" id="sb-${{ci}}">
        <div style="font-size:13.5px;line-height:1.75;margin-bottom:10px">${{c.explanation||''}}</div>
        <button class="stack-learned-btn ${{isL?'done':''}}" onclick="markLearned(${{ci}})">${{isL?'✓ Marked as learned':'✓ Mark as learned'}}</button>
      </div></div>`;
  }}).join('');
}}
function toggleStack(ci){{document.getElementById('sc-'+ci)?.classList.toggle('open');}}
function markLearned(ci){{
  learned.add(ci);updateProgress();
  if(viewMode==='one'){{
    renderOne();
    const ids=filtered();
    if(ids.every(i=>learned.has(i)))setTimeout(()=>document.getElementById('completion')?.classList.add('show'),400);
    else if(current<ids.length-1)setTimeout(()=>goCard(1),500);
  }}else{{
    const btn=document.querySelector(`#sc-${{ci}} .stack-learned-btn`);
    if(btn){{btn.textContent='✓ Marked as learned';btn.classList.add('done');}}
  }}
}}
function updateProgress(){{
  const total=CONCEPTS.length,done=learned.size,pct=Math.round(done/total*100);
  const pt=document.getElementById('prog-text');
  const pf=document.getElementById('prog-fill');
  const pc=document.getElementById('pct-text');
  if(pt)pt.textContent=done+' / '+total+' learned';
  if(pf)pf.style.width=pct+'%';
  if(pc)pc.textContent=pct+'%';
}}
function resetAll(){{learned.clear();current=0;document.getElementById('completion')?.classList.remove('show');render();updateProgress();}}
init();
</script>
</body>
</html>"""

def build_topics(session_num):
    return f"""<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Request a Topic · Daily AI Quiz</title>
<script src="https://unpkg.com/@supabase/supabase-js@2/dist/umd/supabase.js"></script>
<style>
{css_base()}
.wrap{{max-width:480px;margin:0 auto;padding:0 20px 80px}}
.top-bar{{padding:28px 0 24px}}
a.back-btn{{font-size:13px;font-weight:600;color:var(--muted);text-decoration:none;transition:color .15s}}
a.back-btn:hover{{color:var(--ink)}}
.page-title{{font-family:'Unbounded',sans-serif;font-size:26px;font-weight:900;letter-spacing:-.02em;line-height:1.1;margin-bottom:6px}}
.page-sub{{font-size:14px;color:var(--muted);margin-bottom:32px;line-height:1.6}}
.examples{{margin-bottom:32px}}
.examples-title{{font-size:11px;font-weight:700;color:var(--muted);text-transform:uppercase;letter-spacing:.08em;margin-bottom:12px}}
.chip{{display:inline-block;font-size:13px;color:var(--ink);background:var(--white);border:1.5px solid var(--border);border-radius:100px;padding:7px 14px;margin:4px 4px 4px 0;cursor:pointer;transition:all .15s}}
.chip:hover{{border-color:var(--plum);color:var(--plum)}}
.request-box{{background:var(--white);border-radius:20px;padding:24px;margin-bottom:16px;box-shadow:0 2px 12px rgba(45,41,38,.06)}}
.request-title{{font-family:'Unbounded',sans-serif;font-size:16px;font-weight:900;margin-bottom:6px;letter-spacing:-.01em}}
.request-sub{{font-size:13px;color:var(--muted);margin-bottom:18px;line-height:1.6}}
textarea{{width:100%;padding:14px 16px;background:var(--bg);border:2px solid var(--border);border-radius:12px;font-family:'DM Sans',sans-serif;font-size:14px;color:var(--ink);outline:none;transition:border-color .15s;resize:none;height:100px}}
textarea:focus{{border-color:var(--plum)}}
.submit-btn{{font-family:'DM Sans',sans-serif;font-size:13px;font-weight:600;padding:11px 22px;border-radius:100px;border:none;background:var(--ink);color:#fff;cursor:pointer;transition:all .15s;box-shadow:0 4px 12px rgba(45,41,38,.18);margin-top:12px}}
.submit-btn:hover{{transform:translateY(-1px)}}.submit-btn:disabled{{opacity:.5;cursor:default}}
.success-msg{{font-size:13px;color:var(--teal);font-weight:600;display:none;padding:12px 16px;background:#DFFAF1;border-radius:10px;margin-top:10px}}
.success-msg.on{{display:block}}
.err-msg{{font-size:13px;color:var(--muted);padding:12px 16px;background:var(--bg);border-radius:10px;margin-top:8px;display:none}}
.err-msg.on{{display:block}}
.back-link{{display:flex;align-items:center;justify-content:space-between;background:var(--white);border-radius:18px;padding:16px 20px;text-decoration:none;color:var(--ink);box-shadow:0 2px 8px rgba(45,41,38,.05);margin-top:16px}}
.back-link:hover{{box-shadow:0 4px 16px rgba(45,41,38,.10)}}
</style>
</head>
<body>
<div class="wrap">
  <div class="top-bar"><a class="back-btn" href="index.html">← Quiz</a></div>
  <h1 class="page-title">Request a Topic</h1>
  <p class="page-sub">Tell us what you want to learn next. Your request will be included in tomorrow morning's quiz.</p>
  <div class="examples">
    <div class="examples-title">Ideas to get you started</div>
    <span class="chip" onclick="fill(this)">Process reward models</span>
    <span class="chip" onclick="fill(this)">Prompt injection attacks</span>
    <span class="chip" onclick="fill(this)">RLVR and verifiable rewards</span>
    <span class="chip" onclick="fill(this)">Data flywheel strategy</span>
    <span class="chip" onclick="fill(this)">Annotation calibration</span>
    <span class="chip" onclick="fill(this)">Mixture of experts</span>
  </div>
  <div class="request-box">
    <div class="request-title">What do you want to learn?</div>
    <p class="request-sub">Be as specific or broad as you like — a concept, a question, or an area you want to go deeper on.</p>
    <textarea id="req" placeholder="e.g. I want to understand how process reward models differ from outcome reward models..."></textarea>
    <div style="display:flex;align-items:center;gap:12px;flex-wrap:wrap">
      <button class="submit-btn" id="submit-btn" onclick="submit()">Add to tomorrow's quiz →</button>
      <span style="font-size:12px;color:var(--muted)">Takes effect tomorrow morning</span>
    </div>
    <div class="success-msg" id="ok">✓ Request saved! It'll be included in tomorrow's quiz.</div>
    <div class="err-msg" id="err">Please sign in to the quiz app first to save topic requests.</div>
  </div>
  <a class="back-link" href="index.html">
    <div><div style="font-size:14px;font-weight:600">Back to quiz</div><div style="font-size:12px;color:var(--muted);margin-top:2px">Continue learning</div></div>
    <span style="font-size:20px;color:var(--muted)">→</span>
  </a>
</div>
<script>
const SURL='https://cokajjwirhtnvsyubepc.supabase.co';
const SKEY='eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZSIsInJlZiI6ImNva2Fqandpcmh0bnZzeXViZXBjIiwicm9sZSI6ImFub24iLCJpYXQiOjE3Nzk4NDAwNDUsImV4cCI6MjA5NTQxNjA0NX0._zvclbxuixP9dvMcW-uBdA0liOLzqgcVMOyIJ457DFs';
let sb,user=null;
window.addEventListener('load',async()=>{{
  try{{
    sb=supabase.createClient(SURL,SKEY,{{auth:{{persistSession:true,autoRefreshToken:true,storage:window.localStorage}}}});
    const{{data}}=await sb.auth.getSession();
    if(data?.session?.user)user=data.session.user;
  }}catch(e){{console.error(e);}}
}});
function fill(el){{document.getElementById('req').value=el.textContent;document.getElementById('req').focus();}}
async function submit(){{
  const text=document.getElementById('req').value.trim();
  const btn=document.getElementById('submit-btn');
  if(!text)return;
  if(!user){{document.getElementById('err').classList.add('on');return;}}
  btn.textContent='Saving...';btn.disabled=true;
  try{{
    const{{error}}=await sb.from('topic_requests').insert({{user_id:user.id,request:text,fulfilled:false}});
    if(error)throw error;
    document.getElementById('req').value='';
    document.getElementById('ok').classList.add('on');
    setTimeout(()=>document.getElementById('ok').classList.remove('on'),5000);
  }}catch(e){{
    document.getElementById('err').textContent='Could not save — make sure you are signed in.';
    document.getElementById('err').classList.add('on');
  }}finally{{btn.textContent="Add to tomorrow's quiz →";btn.disabled=false;}}
}}
</script>
</body>
</html>"""

def build_archive(session_num):
    return f"""<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Past Quizzes · Daily AI Quiz</title>
<script src="https://unpkg.com/@supabase/supabase-js@2/dist/umd/supabase.js"></script>
<style>
{css_base()}
.wrap{{max-width:640px;margin:0 auto;padding:0 20px 80px}}
.top-bar{{padding:28px 0 20px}}
a.back-btn{{font-size:13px;font-weight:600;color:var(--muted);text-decoration:none;transition:color .15s}}
a.back-btn:hover{{color:var(--ink)}}
.page-title{{font-family:'Unbounded',sans-serif;font-size:26px;font-weight:900;letter-spacing:-.02em;line-height:1.1;margin-bottom:6px}}
.page-sub{{font-size:14px;color:var(--muted);margin-bottom:28px;line-height:1.6}}
#auth-gate{{display:none;text-align:center;padding:80px 20px}}
#auth-gate.on{{display:block}}
.gate-icon{{font-size:40px;margin-bottom:16px}}
.gate-title{{font-family:'Unbounded',sans-serif;font-size:20px;font-weight:900;margin-bottom:8px}}
.gate-sub{{font-size:14px;color:var(--muted);margin-bottom:24px;line-height:1.6}}
.gate-btn{{display:inline-flex;font-family:'DM Sans',sans-serif;font-size:14px;font-weight:600;padding:13px 28px;border-radius:100px;background:var(--ink);color:#fff;text-decoration:none;transition:all .15s}}
.gate-btn:hover{{transform:translateY(-1px)}}
#archive-list{{display:none}}
#archive-list.on{{display:block}}
#quiz-view{{display:none}}
#quiz-view.on{{display:block}}
#results-view{{display:none}}
.loading{{text-align:center;padding:60px 20px;color:var(--muted);font-size:14px}}
.session-card{{background:var(--white);border-radius:18px;padding:18px 22px;margin-bottom:10px;display:flex;align-items:center;gap:16px;box-shadow:0 2px 8px rgba(45,41,38,.05);cursor:pointer;transition:all .15s;border:1.5px solid transparent}}
.session-card:hover{{border-color:var(--plum);box-shadow:0 4px 16px rgba(45,41,38,.10)}}
.session-date-main{{font-family:'Unbounded',sans-serif;font-size:13px;font-weight:900}}
.session-date-sub{{font-size:11px;font-weight:400;color:var(--muted)}}
.session-meta{{flex:1;font-size:13px;color:var(--muted)}}
.session-score{{font-family:'Unbounded',sans-serif;font-size:14px;font-weight:900;min-width:44px;text-align:right}}
.today-badge{{font-size:10px;font-weight:700;color:var(--teal);background:#DFFAF1;padding:3px 8px;border-radius:100px;margin-left:6px;vertical-align:middle}}
.empty-state{{text-align:center;padding:60px 20px;color:var(--muted);font-size:14px}}
.quiz-header{{background:var(--ink);border-radius:20px;padding:20px 24px;margin-bottom:24px;display:flex;align-items:center;justify-content:space-between;flex-wrap:wrap;gap:12px}}
.quiz-header-date{{font-family:'Unbounded',sans-serif;font-size:14px;font-weight:900;color:#fff}}
.quiz-header-sub{{font-size:12px;color:rgba(255,255,255,.5);margin-top:2px}}
.back-to-archive{{font-family:'DM Sans',sans-serif;font-size:12px;font-weight:600;padding:8px 16px;border-radius:100px;border:1.5px solid rgba(255,255,255,.2);background:transparent;color:rgba(255,255,255,.7);cursor:pointer;transition:all .15s;white-space:nowrap}}
.back-to-archive:hover{{border-color:#fff;color:#fff}}
.prog-row{{display:flex;justify-content:space-between;font-size:12px;font-weight:600;color:var(--muted);margin-bottom:8px}}
.prog-track{{height:6px;background:rgba(45,41,38,.10);border-radius:100px;overflow:hidden;margin-bottom:28px}}
.prog-bar{{height:100%;background:var(--coral);border-radius:100px;transition:width .5s cubic-bezier(.4,0,.2,1)}}
.score-pill{{background:var(--ink);color:#fff;font-size:13px;font-weight:600;padding:6px 18px;border-radius:100px}}
.quiz-top{{display:flex;justify-content:space-between;align-items:center;margin-bottom:20px;flex-wrap:wrap;gap:10px}}
.q-card{{background:var(--white);border-radius:24px;padding:clamp(20px,4vw,36px);margin-bottom:14px;border:2px solid transparent;box-shadow:0 2px 20px rgba(45,41,38,.07);animation:pop .3s cubic-bezier(.34,1.3,.64,1) both}}
@keyframes pop{{from{{opacity:0;transform:scale(.97) translateY(8px)}}to{{opacity:1;transform:scale(1) translateY(0)}}}}
.q-card.s-correct{{border-color:var(--teal)}}.q-card.s-wrong{{border-color:var(--coral)}}.q-card.s-flag{{border-color:var(--yolk)}}
.meta-row{{display:flex;align-items:center;gap:7px;margin-bottom:20px;flex-wrap:wrap}}
.chip{{font-size:10px;font-weight:700;letter-spacing:.05em;text-transform:uppercase;padding:4px 11px;border-radius:100px}}
.ch-topic{{background:#E8EDFF;color:#3A52A0}}.ch-f{{background:#E0F5EE;color:#1A7A60}}.ch-p{{background:#FFF0E0;color:#A05A18}}.ch-a{{background:#FFEAEA;color:#A03030}}
.q-count{{font-size:12px;font-weight:700;color:#C8C0B8;margin-left:auto}}
.q-text{{font-family:'Unbounded',sans-serif;font-size:clamp(15px,2.5vw,20px);font-weight:700;line-height:1.45;letter-spacing:-.01em;margin-bottom:clamp(20px,3vw,32px)}}
.opts{{display:flex;flex-direction:column;gap:10px;margin-bottom:20px}}
.opt{{display:flex;align-items:flex-start;gap:14px;padding:clamp(12px,2vw,18px) clamp(14px,2vw,20px);background:var(--bg);border:2px solid transparent;border-radius:16px;cursor:pointer;font-family:'DM Sans',sans-serif;font-size:clamp(13px,1.8vw,15px);font-weight:500;color:var(--ink);text-align:left;line-height:1.55;transition:all .12s;width:100%}}
.opt:hover:not(:disabled){{border-color:var(--plum);background:#F4F0FF}}
.opt:disabled{{cursor:default}}
.opt-key{{width:28px;min-width:28px;height:28px;border-radius:8px;background:rgba(45,41,38,.08);font-size:12px;font-weight:700;display:flex;align-items:center;justify-content:center;flex-shrink:0;color:var(--muted);transition:all .12px}}
.opt.o-selected{{border-color:var(--plum);background:#F4F0FF}}.opt.o-selected .opt-key{{background:var(--plum);color:#fff}}
.opt.o-correct{{border-color:var(--teal);background:#EAFBF5}}.opt.o-correct .opt-key{{background:var(--teal);color:#fff}}
.opt.o-wrong{{border-color:var(--coral);background:#FFF0EE}}.opt.o-wrong .opt-key{{background:var(--coral);color:#fff}}
.opt.o-show{{border-color:var(--teal);background:#EAFBF5}}.opt.o-show .opt-key{{background:var(--teal);color:#fff}}
.actions{{display:flex;align-items:center;gap:8px;flex-wrap:wrap}}
.act{{font-family:'DM Sans',sans-serif;font-size:12px;font-weight:600;padding:8px 16px;border-radius:100px;border:2px solid;cursor:pointer;transition:all .12s;line-height:1;white-space:nowrap}}
.act-flag{{border-color:var(--yolk);color:#8A6200;background:transparent}}.act-flag:hover,.act-flag.on{{background:#FFF5D6}}
.act-exp{{border-color:var(--border);color:var(--muted);background:transparent}}.act-exp:hover{{background:rgba(45,41,38,.04)}}
.fb{{margin-left:auto;font-size:12px;font-weight:700;padding:6px 14px;border-radius:100px;white-space:nowrap}}
.fb.ok{{background:#DFFAF1;color:#00805A}}.fb.no{{background:#FFE9E6;color:#C93B24}}
.exp-panel{{display:none;margin-top:16px;background:#EEF5FF;border-radius:16px;padding:18px 20px;font-size:14px;line-height:1.8}}
.exp-panel.on{{display:block}}
.exp-panel strong{{color:var(--sky);font-weight:700}}
.nav-row{{display:flex;gap:10px;margin-top:12px}}
.nav-row .big-btn{{flex:1;padding:14px;margin:0;font-size:14px}}
.big-btn{{width:100%;padding:15px;border-radius:100px;border:none;font-family:'DM Sans',sans-serif;font-size:14px;font-weight:600;cursor:pointer;transition:transform .15s;display:flex;align-items:center;justify-content:center;gap:8px;margin-bottom:10px;text-decoration:none}}
.big-btn:hover{{transform:translateY(-1px)}}
.btn-dark{{background:var(--ink);color:#fff;box-shadow:0 4px 16px rgba(45,41,38,.2)}}
.btn-light{{background:var(--white);color:var(--ink);box-shadow:0 2px 8px rgba(45,41,38,.10);border:1.5px solid var(--border)}}
.res-hero{{background:var(--ink);border-radius:28px;padding:40px 32px 36px;text-align:center;margin-bottom:16px;position:relative;overflow:hidden}}
.res-c1{{width:120px;height:120px;background:var(--coral);border-radius:50%;position:absolute;top:-30px;right:-20px;opacity:.12}}
.res-c2{{width:80px;height:80px;background:var(--yolk);border-radius:50%;position:absolute;top:40px;right:60px;opacity:.10}}
.res-emoji{{font-size:44px;margin-bottom:12px;display:block;position:relative;z-index:1}}
.res-pct{{font-family:'Unbounded',sans-serif;font-size:72px;font-weight:900;letter-spacing:-.04em;color:var(--yolk);line-height:1;margin-bottom:10px;display:block;position:relative;z-index:1}}
.res-desc{{font-size:14px;color:rgba(255,255,255,.65);line-height:1.6;position:relative;z-index:1}}
.stats-grid{{display:grid;grid-template-columns:repeat(3,1fr);gap:10px;margin-bottom:20px}}
.stat-card{{background:var(--white);border-radius:18px;padding:20px 10px;text-align:center;box-shadow:0 2px 10px rgba(45,41,38,.06)}}
.stat-n{{font-family:'Unbounded',sans-serif;font-size:32px;font-weight:900;line-height:1;margin-bottom:5px}}
.stat-l{{font-size:10px;font-weight:700;color:var(--muted);text-transform:uppercase;letter-spacing:.07em}}
</style>
</head>
<body>
<div class="wrap">
  <div class="top-bar"><a class="back-btn" href="index.html">← Quiz</a></div>
  <div id="auth-gate">
    <div class="gate-icon">🔒</div>
    <div class="gate-title">Sign in to access</div>
    <p class="gate-sub">Past quizzes are available to signed-in members only.</p>
    <a class="gate-btn" href="index.html">Go to sign in →</a>
  </div>
  <div id="archive-list">
    <h1 class="page-title">Past Quizzes</h1>
    <p class="page-sub">Browse and take any previous session. Great for catching up.</p>
    <div id="sessions-list"><div class="loading">Loading sessions...</div></div>
  </div>
  <div id="quiz-view">
    <div class="quiz-header">
      <div>
        <div class="quiz-header-date" id="qv-date">—</div>
        <div class="quiz-header-sub">Archive session</div>
      </div>
      <button class="back-to-archive" onclick="showArchive()">← All sessions</button>
    </div>
    <div class="quiz-top">
      <span style="font-size:13px;font-weight:600;color:var(--muted)" id="qv-lbl"></span>
      <span class="score-pill" id="qv-score">0 / 0</span>
    </div>
    <div class="prog-row"><span id="qv-prog-label">Question 1 of 10</span><span id="qv-prog-pct">10%</span></div>
    <div class="prog-track"><div class="prog-bar" id="qv-prog-bar" style="width:10%"></div></div>
    <div id="qv-mount"></div>
  </div>
  <div id="results-view">
    <div class="res-hero">
      <div class="res-c1"></div><div class="res-c2"></div>
      <span class="res-emoji" id="rv-emoji">🌟</span>
      <span class="res-pct" id="rv-pct">0%</span>
      <div class="res-desc" id="rv-desc"></div>
    </div>
    <div class="stats-grid">
      <div class="stat-card"><div class="stat-n" id="rv-c" style="color:var(--teal)">0</div><div class="stat-l">Correct</div></div>
      <div class="stat-card"><div class="stat-n" id="rv-w" style="color:var(--coral)">0</div><div class="stat-l">Wrong</div></div>
      <div class="stat-card"><div class="stat-n" id="rv-f" style="color:var(--yolk)">0</div><div class="stat-l">Flagged</div></div>
    </div>
    <div class="nav-row">
      <button class="big-btn btn-light" onclick="showArchive()">← Back to archive</button>
      <a class="big-btn btn-dark" href="index.html" style="text-decoration:none">Today's quiz →</a>
    </div>
  </div>
</div>
<script>
const SURL='https://cokajjwirhtnvsyubepc.supabase.co';
const SKEY='eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZSIsInJlZiI6ImNva2Fqandpcmh0bnZzeXViZXBjIiwicm9sZSI6ImFub24iLCJpYXQiOjE3Nzk4NDAwNDUsImV4cCI6MjA5NTQxNjA0NX0._zvclbxuixP9dvMcW-uBdA0liOLzqgcVMOyIJ457DFs';
const BASE='https://aidistilled.online/data/'; // fallback, archive uses Supabase
let sb,user=null,QS=[],ST={{cur:0,answered:{{}},flagged:new Set(),expOpen:new Set(),score:0}};
window.addEventListener('load',async()=>{{
  try{{
    sb=supabase.createClient(SURL,SKEY,{{auth:{{persistSession:true,autoRefreshToken:true,storage:window.localStorage}}}});
    const{{data}}=await sb.auth.getSession();
    if(data?.session?.user){{user=data.session.user;showArchive();}}
    else document.getElementById('auth-gate').classList.add('on');
  }}catch(e){{document.getElementById('auth-gate').classList.add('on');}}
}});
async function showArchive(){{
  document.getElementById('auth-gate').classList.remove('on');
  document.getElementById('quiz-view').classList.remove('on');
  document.getElementById('results-view').style.display='none';
  document.getElementById('archive-list').classList.add('on');
  window.scrollTo(0,0);
  await loadList();
}}
async function loadList(){{
  const c=document.getElementById('sessions-list');
  c.innerHTML='<div class="loading">Loading sessions...</div>';
  try{{
    // Fetch available quizzes from Supabase
    const{{data:quizzes}}=await sb.from('daily_quizzes').select('date,session_number').order('date',{{ascending:false}});
    let scoreMap={{}};
    if(user){{
      const{{data}}=await sb.from('quiz_sessions').select('date,score_pct,is_archive').eq('user_id',user.id);
      if(data)data.forEach(s=>scoreMap[s.date]={{score:s.score_pct,archive:s.is_archive}});
    }}
    if(!quizzes||!quizzes.length){{c.innerHTML='<div class="empty-state">No past quizzes yet — check back tomorrow!</div>';return;}}
    const today=new Date().toISOString().split('T')[0];
    c.innerHTML=quizzes.map(q=>{{
      const d=q.date;
      const dt=new Date(d+'T12:00:00');
      const day=dt.toLocaleDateString('en-US',{{weekday:'short'}});
      const rest=dt.toLocaleDateString('en-US',{{month:'long',day:'numeric'}});
      const entry=scoreMap[d];
      const sc=entry?.score;
      const col=sc>=75?'var(--teal)':sc>=55?'var(--yolk)':'var(--coral)';
      return `<div class="session-card" onclick="loadQuiz('${{d}}')">
        <div><div class="session-date-main">${{day}}${{d===today?'<span class="today-badge">Today</span>':''}}</div><div class="session-date-sub">${{rest}}</div></div>
        <div class="session-meta">${{sc!==undefined?'Completed':'Not taken yet'}}</div>
        <div class="session-score" style="color:${{sc!==undefined?col:'var(--muted)'}}">${{sc!==undefined?sc+'%':'—'}}</div>
        <div style="font-size:18px;color:var(--muted)">›</div>
      </div>`;
    }}).join('');
  }}catch(e){{console.error(e);c.innerHTML='<div class="empty-state">Could not load sessions. Try refreshing.</div>';}}
}}
async function loadQuiz(date){{
  document.getElementById('sessions-list').innerHTML='<div class="loading">Loading quiz...</div>';
  try{{
    // Fetch from Supabase daily_quizzes table
    const{{data,error}}=await sb.from('daily_quizzes').select('questions').eq('date',date).single();
    if(error||!data)throw new Error('Quiz not found');
    QS=data.questions;
    Object.assign(ST,{{cur:0,answered:{{}},selected:{{}},submitted:new Set(),flagged:new Set(),expOpen:new Set(),score:0}});
    const dt=new Date(date+'T12:00:00');
    const label=dt.toLocaleDateString('en-US',{{weekday:'long',month:'long',day:'numeric',year:'numeric'}});
    document.getElementById('qv-date').textContent=label;
    document.getElementById('qv-lbl').textContent=label+' · '+QS.length+' questions';
    document.getElementById('archive-list').classList.remove('on');
    document.getElementById('results-view').style.display='none';
    document.getElementById('quiz-view').classList.add('on');
    window.scrollTo(0,0);renderQ();
  }}catch(e){{
    document.getElementById('sessions-list').innerHTML='<div class="empty-state">Could not load this quiz.</div>';
  }}
}}
function renderQ(){{
  const i=ST.cur,q=QS[i],ans=ST.answered[i]!==undefined,flagged=ST.flagged.has(i),expOpen=ST.expOpen.has(i);
  const pct=Math.round(((i+1)/QS.length)*100);
  document.getElementById('qv-prog-bar').style.width=pct+'%';
  document.getElementById('qv-prog-label').textContent='Question '+(i+1)+' of '+QS.length;
  document.getElementById('qv-prog-pct').textContent=pct+'%';
  document.getElementById('qv-score').textContent=ST.score+' / '+Object.keys(ST.answered).length;
  const cc=ans?(ST.answered[i]===q.correct?'s-correct':'s-wrong'):(flagged?'s-flag':'');
  const dmap={{foundational:'ch-f',practitioner:'ch-p',advanced:'ch-a'}};
  const dlbl={{foundational:'Foundational',practitioner:'Practitioner',advanced:'Advanced'}};
  const optsH=q.opts.map((o,oi)=>{{
    let cls='';if(ans){{if(oi===q.correct)cls='o-show';if(oi===ST.answered[i]&&oi!==q.correct)cls='o-wrong';if(oi===ST.answered[i]&&oi===q.correct)cls='o-correct';}}
    return `<button class="opt ${{cls}}" ${{ans?'disabled':''}} onclick="doAns(${{oi}})"><span class="opt-key">${{String.fromCharCode(65+oi)}}</span><span>${{o}}</span></button>`;
  }}).join('');
  const navH=`<div class="nav-row">${{i>0?`<button class="big-btn btn-light" onclick="doNav(-1)">← Back</button>`:'<span></span>'}}${{ans?(i<QS.length-1?`<button class="big-btn btn-dark" onclick="doNav(1)">Next →</button>`:`<button class="big-btn btn-dark" onclick="showResults()">See results →</button>`):'' }}</div>`;
  document.getElementById('qv-mount').innerHTML=`
    <div class="q-card ${{cc}}">
      <div class="meta-row"><span class="chip ch-topic">${{q.section||''}}</span><span class="chip ${{dmap[q.difficulty]||'ch-f'}}">${{dlbl[q.difficulty]||''}}</span><span class="q-count">${{i+1}} / ${{QS.length}}</span></div>
      <div class="q-text">${{q.q}}</div>
      <div class="opts">${{optsH}}</div>
      <div class="actions">
        <button class="act act-flag ${{flagged?'on':''}}" onclick="doFlag(${{i}})">${{flagged?'⚑ Flagged':'⚐ Flag'}}</button>
        ${{ans?`<button class="act act-exp" onclick="doExp(${{i}})">${{expOpen?'Hide':'Explanation'}}</button>`:''}}
        ${{ans?`<span class="fb ${{ST.answered[i]===q.correct?'ok':'no'}}">${{ST.answered[i]===q.correct?'✓ Correct':'✗ Wrong'}}</span>`:''}}
      </div>
      <div class="exp-panel ${{expOpen?'on':''}}" id="exp-${{i}}">${{q.explanation||''}}</div>
    </div>${{navH}}`;
}}
function doAns(oi){{const i=ST.cur;if(ST.answered[i]!==undefined)return;ST.answered[i]=oi;if(oi===QS[i].correct)ST.score++;renderQ();}}
function doFlag(i){{ST.flagged.has(i)?ST.flagged.delete(i):ST.flagged.add(i);renderQ();}}
function doExp(i){{ST.expOpen.has(i)?ST.expOpen.delete(i):ST.expOpen.add(i);renderQ();}}
function doNav(d){{ST.cur=Math.max(0,Math.min(QS.length-1,ST.cur+d));renderQ();}}
async function showResults(){{
  document.getElementById('quiz-view').classList.remove('on');
  document.getElementById('results-view').style.display='block';
  window.scrollTo(0,0);
  const tot=QS.length,cor=ST.score,wrong=Object.keys(ST.answered).length-cor,pct=Math.round(cor/tot*100);
  const emojis=[[90,'🌟'],[75,'✨'],[55,'💪'],[0,'🌱']];
  const descs=[[90,'Exceptional work.'],[75,'Solid session.'],[55,'Good progress.'],[0,'Keep going!']];
  document.getElementById('rv-emoji').textContent=(emojis.find(([k])=>pct>=k)||[0,'🌱'])[1];
  document.getElementById('rv-pct').textContent=pct+'%';
  document.getElementById('rv-desc').textContent=(descs.find(([k])=>pct>=k)||[0,''])[1];
  document.getElementById('rv-c').textContent=cor;
  document.getElementById('rv-w').textContent=wrong;
  document.getElementById('rv-f').textContent=ST.flagged.size;
  // Save to quiz_sessions with is_archive flag
  if(user&&activeDate){{
    const weak=Object.entries(ST.answered).filter(([i,a])=>parseInt(a)!==QS[parseInt(i)].correct).map(([i])=>QS[parseInt(i)].concept).filter(Boolean);
    await sb.from('quiz_sessions').upsert({{
      user_id:user.id,date:activeDate,score:cor,total:tot,score_pct:pct,
      weak_areas:weak,flagged_concepts:[],topics_covered:[],is_archive:true
    }},{{onConflict:'user_id,date'}});
  }}
}}
</script>
</body>
</html>"""

def build_about():
    return f"""<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>About · Distilled</title>
<style>
{css_base()}
.wrap{{max-width:600px;margin:0 auto;padding:0 20px 80px}}
.top-bar{{padding:28px 0 20px}}
a.back-btn{{font-size:13px;font-weight:600;color:var(--muted);text-decoration:none;transition:color .15s}}
a.back-btn:hover{{color:var(--ink)}}
.hero{{background:var(--ink);border-radius:24px;padding:40px 36px;margin-bottom:20px;position:relative;overflow:hidden}}
.hero-c1{{width:180px;height:180px;background:var(--coral);border-radius:50%;position:absolute;top:-60px;right:-40px;opacity:.12}}
.hero-c2{{width:120px;height:120px;background:var(--yolk);border-radius:50%;position:absolute;top:20px;right:80px;opacity:.10}}
.hero-c3{{width:80px;height:80px;background:var(--plum);border-radius:50%;position:absolute;bottom:-20px;right:30px;opacity:.12}}
.hero-content{{position:relative;z-index:1}}
.hero-eyebrow{{font-size:11px;font-weight:700;letter-spacing:.14em;text-transform:uppercase;color:rgba(255,255,255,.4);margin-bottom:12px}}
.hero-title{{font-family:'Bebas Neue',sans-serif;font-size:clamp(48px,8vw,72px);font-weight:400;line-height:1;letter-spacing:.1em;color:#fff;margin-bottom:14px}}
.hero-title .accent{{color:var(--yolk)}}
.hero-sub{{font-size:15px;color:rgba(255,255,255,.6);line-height:1.75;max-width:440px}}
.section{{background:var(--white);border-radius:20px;padding:28px;margin-bottom:14px;box-shadow:0 2px 10px rgba(45,41,38,.05)}}
.section-label{{font-size:11px;font-weight:700;color:var(--coral);letter-spacing:.12em;text-transform:uppercase;margin-bottom:12px}}
.section-title{{font-family:'Unbounded',sans-serif;font-size:16px;font-weight:900;letter-spacing:-.01em;margin-bottom:10px}}
.section-body{{font-size:14px;color:var(--ink);line-height:1.8}}
.section-body p{{margin-bottom:12px}}
.section-body p:last-child{{margin-bottom:0}}
.section-body strong{{font-weight:700}}
.how-item{{display:flex;gap:14px;margin-bottom:18px}}
.how-item:last-child{{margin-bottom:0}}
.how-num{{width:32px;min-width:32px;height:32px;border-radius:10px;background:var(--bg);font-family:'Unbounded',sans-serif;font-size:13px;font-weight:900;display:flex;align-items:center;justify-content:center;flex-shrink:0;color:var(--ink);margin-top:2px}}
.how-text{{font-size:14px;line-height:1.7;color:var(--ink)}}
.how-text strong{{font-weight:700}}
.topics-grid{{display:flex;flex-wrap:wrap;gap:8px;margin-top:4px}}
.topic-chip{{font-size:12px;font-weight:600;padding:6px 14px;border-radius:100px;background:var(--bg);color:var(--ink);border:1.5px solid var(--border)}}
.install-section{{background:var(--ink);border-radius:20px;padding:28px;margin-bottom:14px;position:relative;overflow:hidden}}
.install-section-c1{{width:100px;height:100px;background:var(--coral);border-radius:50%;position:absolute;top:-30px;right:-20px;opacity:.12}}
.install-section-c2{{width:60px;height:60px;background:var(--plum);border-radius:50%;position:absolute;bottom:-10px;right:40px;opacity:.12}}
.install-label{{font-size:11px;font-weight:700;letter-spacing:.12em;text-transform:uppercase;color:rgba(255,255,255,.4);margin-bottom:10px;position:relative;z-index:1}}
.install-title{{font-family:'Unbounded',sans-serif;font-size:16px;font-weight:900;color:#fff;margin-bottom:20px;position:relative;z-index:1}}
.install-card{{background:rgba(255,255,255,.07);border-radius:14px;padding:18px 20px;margin-bottom:10px;position:relative;z-index:1}}
.install-card-label{{font-family:'Unbounded',sans-serif;font-size:12px;font-weight:900;color:#fff;margin-bottom:4px}}
.install-card-note{{font-size:11px;color:rgba(255,255,255,.4);margin-bottom:14px}}
.install-steps{{display:flex;flex-direction:column;gap:10px}}
.step{{display:flex;align-items:flex-start;gap:10px}}
.step-num{{width:22px;min-width:22px;height:22px;border-radius:6px;background:rgba(255,255,255,.1);font-size:11px;font-weight:700;color:rgba(255,255,255,.6);display:flex;align-items:center;justify-content:center;flex-shrink:0;margin-top:1px}}
.step-text{{font-size:13px;line-height:1.55;color:rgba(255,255,255,.75)}}
.step-text strong{{color:#fff;font-weight:700}}
.install-native-btn{{margin-top:14px;font-family:'DM Sans',sans-serif;font-size:13px;font-weight:700;padding:10px 20px;border-radius:100px;border:none;background:var(--coral);color:#fff;cursor:pointer;transition:all .15s;display:none}}
.install-native-btn:hover{{transform:translateY(-1px)}}
.install-already{{font-size:13px;color:var(--teal);font-weight:600;display:none;padding:10px 14px;background:rgba(0,201,160,.1);border-radius:10px;margin-bottom:14px;position:relative;z-index:1}}
.logo-download{{display:flex;align-items:center;gap:14px;background:var(--white);border-radius:16px;padding:18px 20px;text-decoration:none;color:var(--ink);box-shadow:0 2px 8px rgba(45,41,38,.05);transition:box-shadow .15s;margin-bottom:14px}}
.logo-download:hover{{box-shadow:0 4px 16px rgba(45,41,38,.10)}}
.logo-preview{{width:52px;height:52px;border-radius:14px;background:var(--ink);display:flex;align-items:center;justify-content:center;flex-shrink:0}}
.logo-preview-text{{font-family:'Unbounded',sans-serif;font-size:9px;font-weight:900;color:#fff;letter-spacing:-.02em;text-align:center;line-height:1.2}}
.logo-preview-text span{{color:var(--coral)}}
.logo-dl-text{{font-size:14px;font-weight:600}}
.logo-dl-sub{{font-size:12px;color:var(--muted);margin-top:2px}}
</style>
</head>
<body>
<div class="wrap">
  <div class="top-bar"><a class="back-btn" href="index.html">← Quiz</a></div>

  <div class="hero">
    <div class="hero-c1"></div><div class="hero-c2"></div><div class="hero-c3"></div>
    <div class="hero-content">
      <div class="hero-eyebrow">Welcome to</div>
      <h1 class="hero-title">DIS<span class="accent">TILLED</span></h1>
      <p class="hero-sub">A daily knowledge training tool for people working in and around AI. Built to close the gap between reading about AI and actually understanding it.</p>
    </div>
  </div>

  <div class="section">
    <div class="section-label">The idea</div>
    <div class="section-title">Learning by not being able to guess</div>
    <div class="section-body">
      <p>Most AI content is written to be understood — which means you can follow along without actually learning. Distilled works differently. Each question is designed so you <strong>cannot deduce the answer from surface-level reading</strong>. You have to know the concept.</p>
      <p>The quiz tracks where you go wrong, flags concepts you struggle with, and adjusts future sessions to revisit your gaps. Over time, it builds a personalized curriculum based on your actual knowledge — not what you think you know.</p>
      <p>Every session also adds to your concept library: a growing reference of explained ideas you can flip through, organized by topic, with real-world context for each one.</p>
    </div>
  </div>

  <div class="section">
    <div class="section-label">How to use it</div>
    <div class="section-title">Getting the most out of each session</div>
    <div style="margin-top:4px">
      <div class="how-item">
        <div class="how-num">1</div>
        <div class="how-text"><strong>Take the daily quiz</strong> — 10 questions every morning. Select your answer, then submit before seeing if you're right. Sit with your choice.</div>
      </div>
      <div class="how-item">
        <div class="how-num">2</div>
        <div class="how-text"><strong>Flag what confuses you</strong> — anything that trips you up, mark it. Flagged concepts get revisited in future sessions.</div>
      </div>
      <div class="how-item">
        <div class="how-num">3</div>
        <div class="how-text"><strong>Read the explanations</strong> — after submitting, every question shows a 2-3 sentence explanation written for practitioners, not engineers.</div>
      </div>
      <div class="how-item">
        <div class="how-num">4</div>
        <div class="how-text"><strong>Browse the library</strong> — your concept library grows with every session. Flip through flashcards or scroll through all by category.</div>
      </div>
      <div class="how-item">
        <div class="how-num">5</div>
        <div class="how-text"><strong>Request topics</strong> — if there's something specific you want to cover, add it. It'll appear in tomorrow's quiz.</div>
      </div>
      <div class="how-item">
        <div class="how-num">6</div>
        <div class="how-text"><strong>Catch up with the archive</strong> — missed a session? Browse all past quizzes and take them anytime. Your scores are tracked separately.</div>
      </div>
    </div>
  </div>

  <div class="section">
    <div class="section-label">What we cover</div>
    <div class="section-title">10 content areas, updated daily</div>
    <div class="topics-grid" style="margin-top:14px">
      <span class="topic-chip">Data Quality</span>
      <span class="topic-chip">RLHF & Fine-tuning</span>
      <span class="topic-chip">Evaluation</span>
      <span class="topic-chip">Alignment & Safety</span>
      <span class="topic-chip">Model Behavior</span>
      <span class="topic-chip">Interpretability</span>
      <span class="topic-chip">Infrastructure</span>
      <span class="topic-chip">Prompting</span>
      <span class="topic-chip">RL & Agent Behavior</span>
      <span class="topic-chip">Recent Research</span>
      <span class="topic-chip">Product Launches</span>
    </div>
    <p style="font-size:13px;color:var(--muted);margin-top:14px;line-height:1.7">Each session mixes foundational concepts with practitioner-level questions and frontier research. The balance shifts based on your history and portfolio — think of it as a learning portfolio: 40% durable fundamentals, 40% gaining-importance ideas, 20% frontier speculation.</p>
  </div>

  <div class="install-section">
    <div class="install-section-c1"></div><div class="install-section-c2"></div>
    <div class="install-label">Install the app</div>
    <div class="install-title">Add Distilled to your home screen</div>
    <div class="install-already" id="install-already-about">✓ Already installed — you're good to go!</div>
    <div class="install-card">
      <div class="install-card-label">iPhone &amp; iPad</div>
      <div class="install-card-note">Must use Safari</div>
      <div class="install-steps">
        <div class="step"><div class="step-num">1</div><div class="step-text">Open this site in <strong>Safari</strong></div></div>
        <div class="step"><div class="step-num">2</div><div class="step-text">Tap <strong>Share ⬆</strong> at the bottom</div></div>
        <div class="step"><div class="step-num">3</div><div class="step-text">Tap <strong>"Add to Home Screen"</strong></div></div>
        <div class="step"><div class="step-num">4</div><div class="step-text">Tap <strong>Add</strong></div></div>
      </div>
    </div>
    <div class="install-card">
      <div class="install-card-label">Android</div>
      <div class="install-card-note">Chrome browser</div>
      <div class="install-steps">
        <div class="step"><div class="step-num">1</div><div class="step-text">Tap <strong>⋮ menu</strong> in Chrome</div></div>
        <div class="step"><div class="step-num">2</div><div class="step-text">Tap <strong>"Add to Home Screen"</strong></div></div>
        <div class="step"><div class="step-num">3</div><div class="step-text">Tap <strong>Add</strong></div></div>
      </div>
      <button class="install-native-btn" id="about-android-btn" onclick="doInstall()">Install now →</button>
    </div>
    <div class="install-card">
      <div class="install-card-label">Desktop</div>
      <div class="install-card-note">Chrome or Edge</div>
      <div class="install-steps">
        <div class="step"><div class="step-num">1</div><div class="step-text">Look for <strong>⊕ install icon</strong> in address bar</div></div>
        <div class="step"><div class="step-num">2</div><div class="step-text">Click it and select <strong>Install</strong></div></div>
      </div>
      <button class="install-native-btn" id="about-desktop-btn" onclick="doInstall()">Install now →</button>
    </div>
  </div>

  <a class="logo-download" href="icon.svg" download="distilled-logo.svg">
    <div class="logo-preview">
      <div class="logo-preview-text">DIS<br><span>TILLED</span></div>
    </div>
    <div>
      <div class="logo-dl-text">Download logo</div>
      <div class="logo-dl-sub">SVG · For home screen &amp; sharing</div>
    </div>
    <span style="font-size:20px;color:var(--muted);margin-left:auto">↓</span>
  </a>

</div>
<script src="https://unpkg.com/@supabase/supabase-js@2/dist/umd/supabase.js"></script>
<script>
let deferredPrompt=null;
window.addEventListener('load',()=>{{
  if(window.matchMedia('(display-mode: standalone)').matches||window.navigator.standalone)
    document.getElementById('install-already-about').style.display='block';
  window.addEventListener('beforeinstallprompt',e=>{{
    e.preventDefault();deferredPrompt=e;
    document.getElementById('about-android-btn').style.display='block';
    document.getElementById('about-desktop-btn').style.display='block';
  }});
}});
async function doInstall(){{if(deferredPrompt){{deferredPrompt.prompt();await deferredPrompt.userChoice;deferredPrompt=null;}}}}
</script>
</body>
</html>"""

def main():
    print(f"=== Daily AI Quiz · {DATE_STR} ===")
    session=load_session()
    print(f"Session #{session.get('session_number',0)+1}")
    topic_requests=get_topic_requests()
    request_ids=[r['id'] for r in topic_requests]
    questions=generate_questions(session,topic_requests)
    print(f"Generated {len(questions)} questions")
    if request_ids: fulfill_topic_requests(request_ids)
    concept_library=generate_concept_explanations(session,questions)
    session=update_session(session,questions)
    session["concept_library"]=concept_library
    save_session(session)
    print("Session saved")
    sn=session["session_number"]
    write_html("index.html",build_index(questions,concept_library,sn))
    print("index.html written")
    write_html("learn.html",build_learn(concept_library,sn))
    print("learn.html written")
    write_html("topics.html",build_topics(sn))
    print("topics.html written")
    write_html("archive.html",build_archive(sn))
    print("archive.html written")
    write_html("about.html",build_about())
    print("about.html written")
    save_quiz_file(questions, sn)
    print("=== Done ===")

if __name__=="__main__":
    main()
