const { useState, useEffect, useMemo, useRef, useCallback, useContext, createContext } = React; // ─── i18n ──────────────────────────────────────────────────────────────────── const LangContext = createContext((k) => k); const useT = () => useContext(LangContext); // Hardcoded English fallback — if translations.js fails to load on the deployed // server the UI shows real text instead of raw keys like "nav_grade". const EN_FALLBACK = { time:"Time", more_count:"more", release_to_remove:"Release to remove", nav_grade:"Grade", nav_dashboard:"Dashboard", nav_exam:"Exam", nav_compare:"Compare", settings:"Settings", upgrade:"Upgrade", login:"Log in", logout:"Log out", free_plan:"Free plan", plans_billing:"Plans & billing", billing_invoices:"Billing & invoices", subject:"Subject", strictness:"Strictness", lenient:"Lenient", fair:"Fair", harsh:"Harsh", grade_my_work:"Grade my work", grading:"Grading\u2026", sign_up_continue:"Sign up to keep grading \u2192", paste_work:"Paste your work here\u2026", add_rubric:"+ Add rubric (optional)", free_grade_hint:"1 free grade \xb7 no account needed", no_grade_yet:"No grade yet", paste_and_tap:"Paste your work and tap Grade.", score:"Score", summary:"Summary", strengths:"Strengths", to_improve:"To improve", coaches_note:"Coach\u2019s note", save:"Save", copy:"Copy", re_grade:"Re-grade", vs_previous:"vs previous grade", saved:"Saved", saving:"Saving\u2026", save_grade:"Save grade", save_name_placeholder:"Name this grade\u2026", account:"Account", my_account:"My account", profile:"Profile", danger_zone:"Danger zone", change_password:"Change password", delete_account:"Delete account", // ── EN_FALLBACK new keys (hero, auth, upsell, dashboard, etc.) ────────── sign_in:"Sign in", create_account:"Create account", feedback_lang:"Feedback language", grade_system:"Grade system", weekly_grades:"Grades this week", grades_used:"used", grades_left:"left", // Auth modal auth_check_email:"Check your email", auth_reset_pw:"Reset password", auth_get_free:"Get 2 free grades per week", auth_welcome_back:"Welcome back", auth_google:"Continue with Google", auth_redirecting:"Redirecting\u2026", auth_sending_code:"Sending code\u2026", auth_create_btn:"Create account \u2014 it\u2019s free", auth_forgot:"Forgot password?", auth_verif_code:"Verification code", auth_verifying:"Verifying\u2026", auth_verify_continue:"Verify & continue", auth_diff_email:"\u2190 Use a different email", auth_send_reset:"Send reset code", auth_sending:"Sending\u2026", auth_back_login:"\u2190 Back to log in", auth_resetting:"Resetting\u2026", auth_set_new_pw:"Set new password", auth_new_pw:"New password", modal_foot:"No card required \xb7 2 free grades every week", auth_signup_detail:"Create a free account to get 2 free grades per week, forever.", auth_login_detail:"Log in to get 2 free grades per week, forever.", auth_forgot_detail:"Enter your email \u2014 we\u2019ll send a 6-digit code to reset your password.", auth_or:"or", // Landing hero_eyebrow:"AI grading \xb7 Instant \xb7 Full country support", hero_h1a:"Know your grade", hero_h1b:"before you hand it in", hero_desc:"Paste your essay, answer, or assignment. Get a grade in seconds.", sign_up_free:"Sign up free", hero_perks:"2 grades/week \xb7 saves to dashboard \xb7 free forever", try_once:"Try once without signing in", try_note:"1 grade only \xb7 results not saved", auth_create_title:"Create your free account", auth_create_detail:"2 grades per week, saved to your dashboard, free forever. No card required.", // Upsell upsell_title:"Want more? Sign up free", upsell_sub:"No card needed \xb7 takes 30 seconds", upsell_li1:"2 grades per week, free forever", upsell_li2:"Dashboard to track all your grades", create_free_acct:"Create free account \u2192", // Grade view paste_drop:"Paste your work \u2014 or drop in a file", free_badge_txt:"1 free grade \xb7 no account needed", type_to_grade:"Type or paste your work above to grade.", free_grade_used:"Free grade used \u2014 sign up for 2 per week, free forever.", // Dashboard no_saved_yet:"No saved grades yet", no_saved_yet_d:"Head to Grade, paste your work, and save the result here.", demo_signup_cta:"Sign up to track your real grades \u2192", demo_save_title:"Save your dashboard", demo_save_detail:"Sign up free \u2014 2 grades per week, forever.", usage_free_txt:"2 grades per week on Free. Upgrade for more.", // Smart Insight unlock_title:"Unlock Smart Insight", unlock_detail:"Sign up free, then upgrade to Student or Pro for AI-powered study insights.", // Auth gate reasons auth_used_title:"You\u2019ve used your free grade", auth_used_detail:"Sign up free to get 2 grades per week, forever. No card required.", auth_save_title:"Save your grade", auth_save_detail:"Sign up free to get 2 free grades per week, forever.", auth_welcome_back_detail:"Log in to get 2 free grades per week, forever.", // Subject chips my_subjects:"My subjects", less:"Less", // Compare draft1:"Draft 1", original_lbl:"original", draft2:"Draft 2", revised_lbl:"revised", paste_draft1:"Paste your first draft\u2026", paste_draft2:"Paste your revised draft\u2026", // Exam exam_generate:"\u2726 Generate with AI", exam_paste_q:"Paste questions", paste_questions:"Paste your questions (one per line)\u2026", // Exam view labels exam_title:"Generate an exam", exam_desc:"AI writes timed questions, you sit them under a real timer, then get graded \u2014 with model answers revealed.", configure:"Configure", format_difficulty:"Format & difficulty", question:"Question", mcq_label:"Multiple choice", mcq_desc:"Pick from 4 options", short_label:"Short answer", short_desc:"Min. words each", essay_label:"Essay", essay_desc:"Long-form analysis", easy:"Easy", medium:"Medium", hard:"Hard", paste_your_own:"Paste your own", exam_topic_ph:'e.g. "French Revolution", "Pythagoras", "photosynthesis"', exams_left_week:"exams left this week", generate_arrow:"Generate \u2192", try_demo:"Try demo", q_questions:"questions", q_short_answer:"short-answer questions", q_essay_one:"essay question", q_essay_many:"essay questions", min_each:"min each", // Usage card usage:"Usage", usage_desc:"Your grading quota for this week.", grades_left_week:"grades left this week", checking:"Checking\u2026", resets:"Resets", tomorrow:"tomorrow", in_n_days:"in {n} days", usage_paid_txt:"{n} grades per week on {plan}.", // Grade view upload_doc:"Upload document", reading:"Reading\u2026", scored_english:"Scored on length, vocabulary, sentence rhythm and structure.", scored_correctness:"Scored on correctness, method and explanation.", // Footer automated_grading:"automated grading", // Dashboard summary dash_first_grade:"Save your first grade to start building your dashboard.", dash_entries_summary:"{n} {entry} across {s} {subj} \xb7 {sys}", entry_one:"entry", entry_many:"entries", subject_one:"subject", subject_many:"subjects", // ── Completed fallback: every remaining t() key (English) ────────────── about_you:"About you", about_you_desc:"The AI uses this to calibrate every grade and tip to your level. Optional \u2014 leave blank to keep it general.", ai_assessment:"AI assessment", ai_feedback_tab:"AI feedback", all_entries:"All entries", appearance:"Appearance & behaviour", appearance_desc:"Small quality-of-life things.", at_least_6:"At least 6 characters", auto_save:"Auto-save graded work", auto_save_desc:"Drop every grade into your dashboard automatically.", avg_score:"Avg score", back_to_grading:"\u2190 Back to grading", best:"Best", cancel:"Cancel", change_arrow:"Change →", change_pw_desc:"Confirms with your current password and a code sent to {email}.", choose_plan:"Choose {plan}", choose_plan_desc:"Cancel anytime. Cheap as a coffee.", clear:"Clear", clear_all_desc:"Wipes your saved grades from this account. Cannot be undone.", clear_all_entries:"Clear all entries", coaches_note_locked:"Full coach’s note is on Student & Pro", coaches_note_locked_d:"A 2-sentence next-step plan tailored to this piece of work.", code_sent_note:"We sent a 6-digit code to {email}. It expires in 10 minutes.", compact_mode:"Compact mode", compact_mode_desc:"Tighter spacing \u2014 more on screen at once.", confirm_change:"Confirm change", confirm_new_password:"Confirm new password", confirmation_code_label:"Confirmation code", country:"Country", country_feedback_desc:"So feedback matches your education system.", country_system_desc:"Sets your grading scale and subject suggestions.", current_password:"Current password", current_plan:"Current plan", dash_lock_d_text:"Save every grade, drag entries between subjects, override with the grade you actually got, and let an AI coach point out where to focus next.", dash_lock_li1:"Persistent grade history, synced across devices", dash_lock_li2:"Subject columns with per-subject averages", dash_lock_li3:"Drag & drop to re-organize", dash_lock_li4:"Edit any grade with your real score", dash_lock_li5:"AI-personalized study insights", dash_lock_t:"Your dashboard is part of Student & Pro", default_strictness:"Default strictness", default_strictness_desc:"How harshly the AI grades, until you change it.", default_subject:"Default subject", default_subject_desc:"Pre-selected on the grade screen.", display_name:"Display name", display_name_desc:"Shown in greetings and the header.", download_json:"Download .json", drag_entries_hint:"Drag entries between subjects · click to edit", drop_file_hint:"Drop file to extract text", drop_here:"Drop here", email_desc:"Used to sign in. Contact support to change.", email_label:"Email", end_of_exam:"End of examination", exam_complete_badge:"Exam complete", export_everything:"Export everything", export_everything_desc:"Download a JSON file with your account, preferences, and saved grades.", extracting_text:"extracting text…", fb_bug_ph:"What happened? What did you expect to happen?", fb_feature_ph:"Describe what would help, and what problem it solves.", fb_general_ph:"What’s on your mind?", fb_praise_ph:"Tell us what you like!", forgot_current_pw:"Forgot current password?", get_more:"Get more →", grade_range:"Grade range", grade_year:"Grade / year", grade_year_desc:"What stage you\u2019re studying at.", grades_used_week:"You’ve used your 2 free grades this week. Resets Monday.", grading_defaults:"Grading defaults", grading_defaults_desc:"What loads when you start a new grade.", grading_template:"Grading template", highlight_many:"highlights", highlight_one:"highlight", hover_to_see:"hover to see each note", in_list:"in list", inline_notes:"Inline notes", insight_empty:"Save a few grades and tap Generate for a quick read on where you stand.", language:"Language", language_desc:"Language for the UI and AI feedback.", legal:"Legal", legal_desc:"How we handle your data and what you agree to.", lowest_stat:"Lowest", manage_billing:"Manage billing", manage_in_billing:"Manage in billing →", manage_sub_desc:"Manage your subscription in the billing portal — change card, see invoices, or cancel.", model_answer:"Model Answer", model_answers:"Model Answers", more_inline_notes_d:"Hover-to-see comments on grammar, wording, punctuation and praise — across your whole piece.", more_inline_notes_title:"{n} more inline notes on Student & Pro", more_subjects_hint:"More subjects appear here as you grade new work", most_popular:"Most popular", multiple_choice_exam:"Multiple Choice Examination", n_of_n_grades:"{used} of {total} grades used this week", new_password_label:"New password", no_countries_match:"No countries match \"{q}\"", no_langs_match:"No languages match \"{q}\"", password_section:"Password", password_section_d:"Keep your account secure. We’ll email a code to confirm.", pick_a_plan:"Pick a plan", plan:"Plan", plan_desc_free:"2 grades per week. Upgrade for the dashboard, history, and AI coach.", plan_desc_pro:"30 grades per week, all features, priority queue. Manage card, invoices, or cancel in the billing portal.", plan_desc_student:"10 grades per week, dashboard, and AI coach. Manage card, invoices, or cancel in the billing portal.", privacy:"Privacy", profile_desc:"Who you are in the app.", projected_final:"Projected final", quiz_complete:"Quiz complete", reduce_motion:"Reduce motion", reduce_motion_desc:"Damp transitions and hover lifts.", reset_code_label:"Reset code", reset_code_note:"If an account exists for {email}, a reset code is on its way. Enter it below with your new password.", reset_password_btn:"Reset password", see_plans:"See plans →", select_country:"Select country…", send_confirm_code:"Send confirmation code", send_fb_btn:"Send feedback", send_feedback_d:"Found a bug, got an idea, or just want to say hi? The team reads everything.", send_feedback_title:"Send feedback", sending_busy:"Sending…", sent_anonymously:"Sent anonymously — we can’t reply", sent_ok:"✓ Sent", settings_desc:"Defaults, data, and the boring bits.", settings_foot:"Preferences are stored on this device.", show_ai_tips:"Show AI tips inline", show_ai_tips_desc:"A short coach note appears below each result.", sign_up:"Sign up", signed_in_as:"Signed in as", smart_insight_locked:"Smart Insight is part of Student & Pro", smart_insight_locked_d:"AI reads your grade history and tells you where to focus next.", stages_in:"Stages in the curriculum of", subjects_heading:"Subjects", submitted_work:"Submitted work", sure:"Sure?", switch_to_plan:"Switch to {plan} →", tap_chip_hint:"tap a chip or type a score", terms:"Terms", theme:"Theme", theme_desc:"Light, dark, or follow your device.", total_weight:"Total weight", trimmed_to_fit:"trimmed to fit", type_to_search:"Type to search subjects…", unlock_full_grade:"Unlock the full grade & dashboard", unlock_full_grade_d:"10 grades / week, full feedback, AI coach", updating:"Updating…", upgrade_arrow:"Upgrade →", upgrade_to:"Upgrade to {tier} →", upgrade_to_student:"Upgrade to Student", upgrade_unlock_subject:"Upgrade to unlock subject analysis", upgrade_unlock_subject_d:"See trends, grade ranges, best/worst entries and AI coaching for each subject.", what_would_look_like:"What would a {grade} look like?", words:"words", words_pulled_in:"{n} words pulled in", yes_clear:"Yes, clear", your_data:"Your data", your_data_desc:"You own it \u2014 take it or wipe it any time.", your_grade:"Your grade", your_name_ph:"Your name", your_plan:"Your plan", }; const makeTFn = (lang) => { const map = (window.TRANSLATIONS || {})[lang] || {}; const en = (window.TRANSLATIONS || {}).en || {}; return (k) => map[k] ?? en[k] ?? EN_FALLBACK[k] ?? k; }; const { createPortal } = ReactDOM; // ─── Supabase client (Google OAuth) ───────────────────────────────────── // Keys come from supabase-config.js (loaded before this script). // If keys are not set, Google auth is hidden and the rest of the app is unaffected. const _sbUrl = window.__SUPABASE_URL || ""; const _sbKey = window.__SUPABASE_ANON_KEY || ""; const supabaseClient = (_sbUrl && _sbKey && window.supabase) ? window.supabase.createClient(_sbUrl, _sbKey, { auth: { // Keep users signed in across reloads/restarts and silently refresh // the token before it expires, so a Google session never forces a re-login. persistSession: true, autoRefreshToken: true, detectSessionInUrl: true, // PKCE flow: uses ?code= query param instead of #access_token= hash. // This prevents Supabase from calling window.location.replace() to strip // the hash, which was causing a full-page reload (white screen) after // every Google OAuth redirect-back. flowType: "pkce", storageKey: "gradecheck-auth", }, }) : null; // ─── Tweak defaults (editable via Tweaks panel) ────────────────────────── const TWEAK_DEFAULTS = /*EDITMODE-BEGIN*/{ "brandName": "GradeCheck", "brandAccent": "Check", "logoColor": "#63D378", "accentColor": "#16a34a", "freeLimit": 2, "featuredPlan": "student", "resultStyle": "ring" } /*EDITMODE-END*/; // ─── Grading systems ────────────────────────────────────────────────────── const SYSTEMS = { // ── Europe ──────────────────────────────────────────────────────────── danish: { id: "danish", group: "Europe", name: "Danish 7-trins", flag: "DK", scale: "12 → −3", bands: [ { grade: "12", min: 95 }, { grade: "10", min: 85 }, { grade: "7", min: 70 }, { grade: "4", min: 55 }, { grade: "02", min: 40 }, { grade: "00", min: 20 }, { grade: "−3", min: 0 }], instruction: "Return one of: 12, 10, 7, 4, 02, 00, −3." }, german: { id: "german", group: "Europe", name: "German Noten", flag: "DE", scale: "1 → 6", bands: [ { grade: "1", min: 90 }, { grade: "2", min: 75 }, { grade: "3", min: 60 }, { grade: "4", min: 45 }, { grade: "5", min: 20 }, { grade: "6", min: 0 }], instruction: "Return a single digit from 1 to 6." }, french: { id: "french", group: "Europe", name: "French (sur 20)", flag: "FR", scale: "0–20", bands: [ { grade: "20", min: 99 }, { grade: "18", min: 90 }, { grade: "16", min: 80 }, { grade: "14", min: 70 }, { grade: "12", min: 60 }, { grade: "10", min: 50 }, { grade: "8", min: 38 }, { grade: "6", min: 25 }, { grade: "4", min: 13 }, { grade: "2", min: 5 }, { grade: "0", min: 0 }], instruction: "Return a number from 0 to 20 (one decimal allowed), e.g. '14.5'." }, swedish: { id: "swedish", group: "Europe", name: "Swedish Betyg", flag: "SE", scale: "A–F", bands: [ { grade: "A", min: 90 }, { grade: "B", min: 80 }, { grade: "C", min: 67 }, { grade: "D", min: 55 }, { grade: "E", min: 40 }, { grade: "F", min: 0 }], instruction: "Return one of: A, B, C, D, E, F." }, norwegian: { id: "norwegian", group: "Europe", name: "Norwegian Karakter", flag: "NO", scale: "6 → 1", bands: [ { grade: "6", min: 90 }, { grade: "5", min: 77 }, { grade: "4", min: 62 }, { grade: "3", min: 47 }, { grade: "2", min: 30 }, { grade: "1", min: 0 }], instruction: "Return a single digit from 1 to 6." }, dutch: { id: "dutch", group: "Europe", name: "Dutch Cijfer", flag: "NL", scale: "1–10", bands: [ { grade: "10", min: 95 }, { grade: "9", min: 87 }, { grade: "8", min: 77 }, { grade: "7", min: 67 }, { grade: "6", min: 55 }, { grade: "5", min: 45 }, { grade: "4", min: 35 }, { grade: "≤3", min: 0 }], instruction: "Return a number from 1 to 10 (one decimal allowed), e.g. '7.5'." }, spanish: { id: "spanish", group: "Europe", name: "Spanish Nota", flag: "ES", scale: "0–10", bands: [ { grade: "10", min: 95 }, { grade: "9", min: 87 }, { grade: "8", min: 77 }, { grade: "7", min: 67 }, { grade: "6", min: 57 }, { grade: "5", min: 50 }, { grade: "4", min: 40 }, { grade: "≤3", min: 0 }], instruction: "Return a number from 0 to 10 (one decimal allowed), e.g. '7.5'." }, finnish: { id: "finnish", group: "Europe", name: "Finnish Arvosana", flag: "FI", scale: "4–10", bands: [ { grade: "10", min: 95 }, { grade: "9", min: 85 }, { grade: "8", min: 75 }, { grade: "7", min: 65 }, { grade: "6", min: 55 }, { grade: "5", min: 40 }, { grade: "4", min: 0 }], instruction: "Return a single digit from 4 to 10." }, uk: { id: "uk", group: "Europe", name: "UK Honours", flag: "UK", scale: "1st → Fail", bands: [ { grade: "1st", min: 70 }, { grade: "2:1", min: 60 }, { grade: "2:2", min: 50 }, { grade: "3rd", min: 40 }, { grade: "Fail", min: 0 }], instruction: "Return one of: 1st, 2:1, 2:2, 3rd, Fail." }, // ── International ────────────────────────────────────────────────────── ib: { id: "ib", group: "International", name: "IB Diploma", flag: "IB", scale: "7 → 1", bands: [ { grade: "7", min: 85 }, { grade: "6", min: 75 }, { grade: "5", min: 65 }, { grade: "4", min: 50 }, { grade: "3", min: 35 }, { grade: "2", min: 20 }, { grade: "1", min: 0 }], instruction: "Return a single digit from 1 to 7." }, ap: { id: "ap", group: "International", name: "AP Exam", flag: "AP", scale: "5 → 1", bands: [ { grade: "5", min: 80 }, { grade: "4", min: 67 }, { grade: "3", min: 50 }, { grade: "2", min: 30 }, { grade: "1", min: 0 }], instruction: "Return a single digit from 1 to 5." }, cambridge: { id: "cambridge", group: "International", name: "Cambridge A-Level", flag: "CAM", scale: "A*–E", bands: [ { grade: "A*", min: 90 }, { grade: "A", min: 80 }, { grade: "B", min: 70 }, { grade: "C", min: 60 }, { grade: "D", min: 50 }, { grade: "E", min: 40 }, { grade: "U", min: 0 }], instruction: "Return one of: A*, A, B, C, D, E, U." }, // ── Other ────────────────────────────────────────────────────────────── usa: { id: "usa", group: "Other", name: "USA Letter", flag: "US", scale: "A–F", bands: [ { grade: "A+", min: 97 }, { grade: "A", min: 93 }, { grade: "A-", min: 90 }, { grade: "B+", min: 87 }, { grade: "B", min: 83 }, { grade: "B-", min: 80 }, { grade: "C+", min: 77 }, { grade: "C", min: 73 }, { grade: "C-", min: 70 }, { grade: "D+", min: 67 }, { grade: "D", min: 63 }, { grade: "D-", min: 60 }, { grade: "F", min: 0 }], instruction: "Return one of A+, A, A-, B+, B, B-, C+, C, C-, D+, D, D-, F." }, australia: { id: "australia", group: "Other", name: "Australian", flag: "AU", scale: "HD → F", bands: [ { grade: "HD", min: 85 }, { grade: "D", min: 75 }, { grade: "C", min: 65 }, { grade: "P", min: 50 }, { grade: "F", min: 0 }], instruction: "Return one of: HD, D, C, P, F." }, percent: { id: "percent", group: "Other", name: "Percent", flag: "%", scale: "0–100", bands: [ { grade: "90+", min: 90 }, { grade: "75+", min: 75 }, { grade: "60+", min: 60 }, { grade: "50+", min: 50 }, { grade: "<50", min: 0 }], instruction: "Return a percentage from 0 to 100, e.g. '78'." }, }; // Grouped for the system picker menu: { Europe: [...], International: [...], Other: [...] } const SYSTEMS_BY_GROUP = (() => { const g = {}; for (const s of Object.values(SYSTEMS)) (g[s.group] = g[s.group] || []).push(s); return g; })(); // ─── Country → system / flag / group mappings ──────────────────────────── const COUNTRY_FLAGS = { // Europe "Denmark":"DK","Germany":"DE","France":"FR","Sweden":"SE","Norway":"NO", "Netherlands":"NL","Spain":"ES","Finland":"FI","United Kingdom":"UK", "Italy":"IT","Poland":"PL","Switzerland":"CH","Ireland":"IE", "Austria":"AT","Belgium":"BE","Portugal":"PT","Greece":"GR", "Czech Republic":"CZ","Slovakia":"SK","Hungary":"HU","Romania":"RO", "Bulgaria":"BG","Croatia":"HR","Serbia":"RS","Slovenia":"SI", "Bosnia and Herzegovina":"BA","Montenegro":"ME","North Macedonia":"MK", "Albania":"AL","Kosovo":"XK","Lithuania":"LT","Latvia":"LV","Estonia":"EE", "Belarus":"BY","Ukraine":"UA","Moldova":"MD","Luxembourg":"LU", "Iceland":"IS","Malta":"MT","Cyprus":"CY","Turkey":"TR","Russia":"RU", "Liechtenstein":"LI","Monaco":"MC","Andorra":"AD","San Marino":"SM", // Americas "United States":"US","Canada":"CA","Mexico":"MX","Brazil":"BR", "Argentina":"AR","Chile":"CL","Colombia":"CO","Peru":"PE","Venezuela":"VE", "Ecuador":"EC","Bolivia":"BO","Paraguay":"PY","Uruguay":"UY", "Cuba":"CU","Jamaica":"JM","Haiti":"HT","Dominican Republic":"DO", "Guatemala":"GT","Honduras":"HN","El Salvador":"SV","Nicaragua":"NI", "Costa Rica":"CR","Panama":"PA","Trinidad and Tobago":"TT", "Barbados":"BB","Guyana":"GY","Belize":"BZ","Suriname":"SR", "Bahamas":"BS","Grenada":"GD","Saint Lucia":"LC","Saint Vincent":"VC", "Antigua and Barbuda":"AG","Dominica":"DM","Saint Kitts and Nevis":"KN", // Asia & Pacific "Japan":"JP","China":"CN","South Korea":"KR","India":"IN", "Pakistan":"PK","Bangladesh":"BD","Sri Lanka":"LK","Nepal":"NP", "Bhutan":"BT","Myanmar":"MM","Thailand":"TH","Vietnam":"VN", "Cambodia":"KH","Laos":"LA","Malaysia":"MY","Singapore":"SG", "Indonesia":"ID","Philippines":"PH","Taiwan":"TW","Hong Kong":"HK", "Mongolia":"MN","Kazakhstan":"KZ","Uzbekistan":"UZ","Tajikistan":"TJ", "Kyrgyzstan":"KG","Turkmenistan":"TM","Afghanistan":"AF", "Australia":"AU","New Zealand":"NZ","Papua New Guinea":"PG", "Fiji":"FJ","Samoa":"WS","Tonga":"TO","Vanuatu":"VU", "Solomon Islands":"SB","Timor-Leste":"TL","Brunei":"BN","Maldives":"MV", // Middle East "Iran":"IR","Iraq":"IQ","Syria":"SY","Lebanon":"LB","Jordan":"JO", "Israel":"IL","Palestine":"PS","Saudi Arabia":"SA","UAE":"AE", "Kuwait":"KW","Qatar":"QA","Bahrain":"BH","Oman":"OM","Yemen":"YE", "Armenia":"AM","Azerbaijan":"AZ","Georgia":"GE", // Africa "South Africa":"ZA","Nigeria":"NG","Kenya":"KE","Ghana":"GH", "Ethiopia":"ET","Tanzania":"TZ","Uganda":"UG","Rwanda":"RW", "Zimbabwe":"ZW","Zambia":"ZM","Mozambique":"MZ","Angola":"AO", "Cameroon":"CM","Ivory Coast":"CI","Senegal":"SN","Mali":"ML", "Burkina Faso":"BF","Guinea":"GN","Egypt":"EG","Morocco":"MA", "Algeria":"DZ","Tunisia":"TN","Libya":"LY","Sudan":"SD", "South Sudan":"SS","Somalia":"SO","Madagascar":"MG", "DR Congo":"CD","Republic of Congo":"CG","Gabon":"GA", "Equatorial Guinea":"GQ","Namibia":"NA","Botswana":"BW", "Lesotho":"LS","Eswatini":"SZ","Malawi":"MW","Sierra Leone":"SL", "Liberia":"LR","Togo":"TG","Benin":"BJ","Niger":"NE","Chad":"TD", "Central African Republic":"CF","Eritrea":"ER","Djibouti":"DJ", "Comoros":"KM","Mauritius":"MU","Seychelles":"SC", "Cape Verde":"CV","Gambia":"GM","Guinea-Bissau":"GW","Mauritania":"MR", // International "IB Diploma":"IB","AP Exam":"AP","Cambridge A-Level":"CAM" }; // Codes that have a real CSS flag in flags.css. Everything else falls back // to a polished 2-letter code chip so nothing ever renders blank. const FLAG_CODES = new Set("AD AE AF AG AL AM AO AR AT AU AZ BA BB BD BE BF BG BH BJ BN BO BR BS BT BW BY BZ CA CD CF CG CH CI CL CM CN CO CR CU CV CY CZ DE DJ DK DM DO DZ EC EE EG ER ES ET FI FJ FO FR GA GD GE GH GM GN GQ GR GT GW GY HK HN HR HT HU ID IE IL IN IQ IR IS IT JM JO JP KE KG KH KM KN KR KW KZ LA LB LC LI LK LR LS LT LU LV LY MA MC MD ME MG MK ML MM MN MR MT MU MV MW MX MY MZ NA NE NG NI NL NO NP NZ OM PA PE PG PH PK PL PS PT PW PY QA RO RS RU RW SA SB SC SD SE SG SI SK SL SM SN SO SR SS SV SY SZ TD TG TH TJ TL TM TN TO TR TT TW TZ UA UG UK US UY UZ VC VE VN VU WS XK YE ZA ZM ZW".split(" ")); function Flag({ country, code }) { const cc = code || (country ? COUNTRY_FLAGS[country] : "") || ""; if (!cc) return