https://re-wise-psi.vercel.app I want to create a website like this for KUHS I have data

Reading Web Page

https://re-wise-psi.vercel.app

Asking for Preferences
Please share your Excel/CSV file and I'll get started building right away. Once you upload it, I'll:
  1. Parse the data to understand the subject/question structure
  2. Build the full site (index, flashcard mode, browse mode, analysis page) tailored to KUHS 1st Year MBBS subjects (Anatomy, Physiology, Biochemistry)
  3. Package everything as a downloadable ZIP ready to deploy on Vercel or GitHub Pages
Go ahead and attach the file!

Create a complete HTML/CSS/JS template website once you share the data

Running Command

mkdir -p /home/daytona/workspace/kuhs-qbank && ls /home/daytona/workspace/kuhs-q…

Writing File

~/kuhs-qbank/data.js

// ============================================================
//  KUHS 1st Year MBBS Question Bank — Data File
//  Replace the sample questions below with your real data.
//  Structure per question:
//  {
//    id       : unique number,
//    subject  : "Anatomy" | "Physiology" | "Biochemistry",
//    topic    : sub-topic string,
//    type     : "LAQ" | "SAQ" | "MCQ",
//    year     : exam year string e.g. "2022",
//    question : question text,
//    answer   : answer text (optional — used in browse mode),
//    options  : [...] only for MCQ,
//    correct  : index (0-based) only for MCQ
//  }
// ============================================================

const QUESTIONS = [
  // ── ANATOMY ─────────────────────────────────────────────
  {
    id: 1, subject: "Anatomy", topic: "Upper Limb", type: "LAQ", year: "2023",
    question: "Describe the brachial plexus — its formation, branches and clinical importance.",
    answer: "The brachial plexus is formed by the ventral rami of C5–T1 nerve roots. It has roots, trunks, divisions, cords and terminal branches. Terminal branches include musculocutaneous, median, ulnar, radial and axillary nerves. Injury at Erb's point (C5,C6) causes Erb's palsy (waiter's tip position). Klumpke's palsy involves C8,T1 with claw hand and Horner's syndrome."
  },
  {
    id: 2, subject: "Anatomy", topic: "Upper Limb", type: "SAQ", year: "2023",
    question: "Write a short note on the anatomical snuff box.",
    answer: "The anatomical snuff box is a triangular depression on the lateral aspect of the wrist. Boundaries: laterally — tendons of APL and EPB; medially — EPL. Floor: styloid process of radius, scaphoid, trapezium. Contents: radial artery, cephalic vein, superficial branch of radial nerve. Clinical: tenderness here after a fall suggests scaphoid fracture."
  },
  {
    id: 3, subject: "Anatomy", topic: "Upper Limb", type: "MCQ", year: "2022",
    question: "Which nerve is most commonly injured in fracture of the surgical neck of the humerus?",
    options: ["Radial nerve", "Axillary nerve", "Musculocutaneous nerve", "Median nerve"],
    correct: 1,
    answer: "Axillary nerve — it winds around the surgical neck of the humerus and is vulnerable in fractures at that level, causing weakness of deltoid and loss of sensation over the regimental badge area."
  },
  {
    id: 4, subject: "Anatomy", topic: "Lower Limb", type: "LAQ", year: "2022",
    question: "Describe the femoral triangle — its boundaries, contents and clinical significance.",
    answer: "Boundaries: superiorly — inguinal ligament; laterally — medial border of sartorius; medially — medial border of adductor longus; roof — fascia lata; floor — iliopsoas, pectineus, adductor longus. Contents (lateral to medial): femoral nerve, femoral artery, femoral vein, femoral canal (NAVY mnemonic reversed). Clinical: femoral hernia passes through femoral canal medial to the vein; femoral pulse palpable at midinguinal point; femoral artery catheterisation site."
  },
  {
    id: 5, subject: "Anatomy", topic: "Lower Limb", type: "SAQ", year: "2021",
    question: "Write a short note on the popliteal fossa.",
    answer: "The popliteal fossa is a diamond-shaped space behind the knee. Boundaries: superomedially — semimembranosus and semitendinosus; superolaterally — biceps femoris; inferomedially — medial head of gastrocnemius; inferolaterally — lateral head of gastrocnemius and plantaris. Contents: popliteal artery and vein (deepest to most superficial), tibial nerve, common fibular nerve, small saphenous vein, posterior cutaneous nerve of thigh, popliteal lymph nodes."
  },
  {
    id: 6, subject: "Anatomy", topic: "Head & Neck", type: "MCQ", year: "2023",
    question: "The cavernous sinus receives blood from which of the following veins?",
    options: ["Internal jugular vein", "Superior ophthalmic vein", "External jugular vein", "Occipital vein"],
    correct: 1,
    answer: "Superior ophthalmic vein drains into the cavernous sinus, forming an important route for spread of infection from the danger area of the face."
  },
  {
    id: 7, subject: "Anatomy", topic: "Thorax", type: "SAQ", year: "2022",
    question: "Write a short note on the bronchopulmonary segments.",
    answer: "A bronchopulmonary segment is the smallest surgically resectable unit of lung. Each segment has its own segmental bronchus, artery, and intersegmental veins. Right lung: 10 segments (3 in upper, 2 in middle, 5 in lower lobe). Left lung: 8–10 segments. Clinical importance: localisation of tuberculosis, bronchiectasis, and abscess; surgical resection without sacrificing adjacent healthy tissue."
  },
  {
    id: 8, subject: "Anatomy", topic: "Abdomen", type: "LAQ", year: "2021",
    question: "Describe the inguinal canal — its walls, contents and clinical importance.",
    answer: "The inguinal canal is a 4 cm oblique passage through the anterior abdominal wall. It runs from deep inguinal ring to superficial inguinal ring. Walls: anterior — external oblique aponeurosis (+ internal oblique laterally); posterior — transversalis fascia (+ conjoint tendon medially); roof — arched fibres of internal oblique and transversus abdominis; floor — inguinal ligament. Contents in males: spermatic cord (vas deferens, testicular artery, pampiniform plexus, lymphatics, genital branch of genitofemoral nerve, ilioinguinal nerve). Females: round ligament. Clinical: site of indirect inguinal hernia (through deep ring) and direct inguinal hernia (through Hesselbach triangle)."
  },

  // ── PHYSIOLOGY ──────────────────────────────────────────
  {
    id: 9, subject: "Physiology", topic: "Blood", type: "LAQ", year: "2023",
    question: "Describe the mechanism of haemostasis with a note on the coagulation cascade.",
    answer: "Haemostasis has four phases: (1) Vascular spasm — vasoconstriction. (2) Primary haemostasis — platelet plug formation via adhesion (vWF, GpIb), activation, and aggregation (GpIIb/IIIa, fibrinogen). (3) Secondary haemostasis — coagulation cascade: Extrinsic (tissue factor + VII) and Intrinsic (XII→XI→IX→VIII) pathways converge at factor X. Prothrombinase complex converts prothrombin to thrombin; thrombin converts fibrinogen to fibrin. (4) Fibrinolysis — plasmin degrades fibrin clot. Tests: PT (extrinsic), APTT (intrinsic), platelet count."
  },
  {
    id: 10, subject: "Physiology", topic: "Blood", type: "SAQ", year: "2022",
    question: "Write a short note on the ABO blood group system.",
    answer: "ABO system is based on antigens (agglutinogens) on RBC surface and antibodies (agglutinins) in plasma. Blood group A: A antigen, anti-B antibody. Group B: B antigen, anti-A. Group AB: both antigens, no antibody (universal recipient). Group O: no antigens, both antibodies (universal donor). Inheritance: IA, IB, i alleles. Importance: blood transfusion compatibility, organ transplantation, haemolytic disease of newborn."
  },
  {
    id: 11, subject: "Physiology", topic: "CVS", type: "MCQ", year: "2023",
    question: "The normal PR interval on ECG is:",
    options: ["0.04–0.08 sec", "0.12–0.20 sec", "0.25–0.35 sec", "0.40–0.50 sec"],
    correct: 1,
    answer: "Normal PR interval is 0.12–0.20 seconds (3–5 small squares). It represents time for impulse to travel from SA node through atria, AV node, and bundle of His to the ventricles."
  },
  {
    id: 12, subject: "Physiology", topic: "CVS", type: "LAQ", year: "2022",
    question: "Describe the cardiac cycle with reference to pressure and volume changes in the left ventricle.",
    answer: "The cardiac cycle lasts ~0.8 sec at 75 bpm. Phases: (1) Isovolumetric contraction — LV pressure rises, all valves closed, no change in volume. (2) Rapid ejection — aortic valve opens when LV pressure > aortic pressure (~80 mmHg); SV (~70 mL) is ejected. (3) Reduced ejection. (4) Isovolumetric relaxation — all valves closed, LV pressure falls. (5) Rapid filling — mitral valve opens when LV pressure < LA pressure. (6) Slow filling (diastasis). (7) Atrial systole. EDV ~120 mL, ESV ~50 mL, SV ~70 mL, EF ~58%."
  },
  {
    id: 13, subject: "Physiology", topic: "Respiratory", type: "SAQ", year: "2023",
    question: "Write a short note on the oxygen-haemoglobin dissociation curve.",
    answer: "The O₂-Hb dissociation curve is sigmoid-shaped due to cooperative binding of O₂ to haemoglobin. P50 = 26 mmHg (PO₂ at which Hb is 50% saturated). Right shift (decreased affinity — more O₂ released): increased CO₂, H⁺, temperature, 2,3-BPG (Bohr effect). Left shift (increased affinity): decreased CO₂, H⁺, temperature, 2,3-BPG; HbF, CO poisoning. Physiological importance: facilitates O₂ loading in lungs and unloading in tissues."
  },
  {
    id: 14, subject: "Physiology", topic: "Renal", type: "MCQ", year: "2022",
    question: "Normal GFR (Glomerular Filtration Rate) in adults is approximately:",
    options: ["60 mL/min", "90 mL/min", "125 mL/min", "180 mL/min"],
    correct: 2,
    answer: "Normal GFR is approximately 125 mL/min (180 L/day). Of this, ~99% is reabsorbed, with only ~1.5 L excreted as urine per day."
  },
  {
    id: 15, subject: "Physiology", topic: "Nervous System", type: "SAQ", year: "2021",
    question: "Describe the generation and propagation of an action potential.",
    answer: "Resting membrane potential = −70 mV. Depolarisation: stimulus opens voltage-gated Na⁺ channels → Na⁺ influx → membrane potential rises to +30 mV. Repolarisation: Na⁺ channels inactivate; K⁺ channels open → K⁺ efflux → membrane returns to −70 mV. Hyperpolarisation (after-potential). Refractory periods: absolute (no new AP possible) and relative (stronger stimulus needed). Propagation is unidirectional due to refractory period. Saltatory conduction in myelinated fibres (faster)."
  },
  {
    id: 16, subject: "Physiology", topic: "Endocrine", type: "LAQ", year: "2023",
    question: "Describe the synthesis, secretion, actions and regulation of insulin.",
    answer: "Synthesis: β cells of islets of Langerhans; preproinsulin → proinsulin → insulin + C-peptide. Secretion: triggered by hyperglycaemia (glucose sensor via GLUT2 + glucokinase → ATP → K⁺-ATP channel closure → depolarisation → Ca²⁺ entry → exocytosis). Actions: increases glucose uptake (GLUT4 in muscle/fat), glycogen synthesis, protein synthesis; inhibits gluconeogenesis, lipolysis, ketogenesis. Regulation: stimulated by high glucose, GIP, GLP-1, vagus, amino acids; inhibited by somatostatin, sympathetic activity, low glucose. Deficiency → Type 1 DM."
  },

  // ── BIOCHEMISTRY ────────────────────────────────────────
  {
    id: 17, subject: "Biochemistry", topic: "Proteins", type: "LAQ", year: "2023",
    question: "Describe the structure of proteins with suitable examples at each level.",
    answer: "Primary structure: sequence of amino acids linked by peptide bonds (e.g., insulin). Secondary structure: local folding — α-helix (stabilised by H-bonds between NH and C=O 4 residues apart; e.g., keratin) and β-pleated sheet (parallel or antiparallel; e.g., fibroin). Tertiary structure: 3D folding of a single polypeptide — stabilised by H-bonds, ionic bonds, disulphide bridges, hydrophobic interactions (e.g., myoglobin). Quaternary structure: association of two or more polypeptide chains (subunits; e.g., haemoglobin — 2α + 2β chains). Denaturation disrupts secondary/tertiary/quaternary without breaking peptide bonds."
  },
  {
    id: 18, subject: "Biochemistry", topic: "Carbohydrates", type: "SAQ", year: "2022",
    question: "Write a short note on glycolysis.",
    answer: "Glycolysis is the breakdown of glucose to pyruvate in the cytoplasm (10 steps). Investment phase: 2 ATP consumed. Payoff phase: 4 ATP produced. Net gain: 2 ATP, 2 NADH, 2 pyruvate per glucose. Key enzymes: hexokinase (step 1), phosphofructokinase-1 (step 3 — rate-limiting, inhibited by ATP/citrate, stimulated by AMP/fructose-2,6-bisphosphate), pyruvate kinase (step 10). Regulation is at these three irreversible steps. In anaerobic conditions, pyruvate → lactate (regenerates NAD⁺). Total yield (aerobic): ~30–32 ATP per glucose."
  },
  {
    id: 19, subject: "Biochemistry", topic: "Carbohydrates", type: "MCQ", year: "2023",
    question: "The rate-limiting enzyme of glycolysis is:",
    options: ["Hexokinase", "Aldolase", "Phosphofructokinase-1", "Pyruvate kinase"],
    correct: 2,
    answer: "Phosphofructokinase-1 (PFK-1) catalyses the phosphorylation of fructose-6-phosphate to fructose-1,6-bisphosphate and is the key regulatory/rate-limiting enzyme of glycolysis."
  },
  {
    id: 20, subject: "Biochemistry", topic: "Lipids", type: "SAQ", year: "2023",
    question: "Write a short note on cholesterol synthesis.",
    answer: "Cholesterol synthesis occurs mainly in the liver. Key steps: Acetyl-CoA → HMG-CoA → mevalonate (catalysed by HMG-CoA reductase — rate-limiting; inhibited by statins) → squalene → lanosterol → cholesterol. Regulation: SREBP transcription factor upregulates HMG-CoA reductase when cellular cholesterol is low; statins competitively inhibit HMG-CoA reductase. Cholesterol is a precursor for bile acids, steroid hormones, vitamin D, and cell membranes."
  },
  {
    id: 21, subject: "Biochemistry", topic: "Enzymes", type: "LAQ", year: "2022",
    question: "Describe enzyme kinetics with reference to the Michaelis-Menten equation.",
    answer: "The Michaelis-Menten equation: V = Vmax[S] / (Km + [S]). Vmax = maximum velocity (all enzyme sites saturated). Km = substrate concentration at half-Vmax; reflects enzyme-substrate affinity (low Km = high affinity). Lineweaver-Burk plot (double reciprocal) linearises the hyperbola. Inhibition types: Competitive — inhibitor resembles substrate; raises apparent Km, Vmax unchanged; overcome by excess substrate. Non-competitive — inhibitor binds elsewhere; lowers Vmax, Km unchanged. Uncompetitive — binds ES complex; lowers both Km and Vmax. Clinical: enzyme kinetics relevant to drug design, toxicology (e.g., methotrexate as competitive inhibitor of DHFR)."
  },
  {
    id: 22, subject: "Biochemistry", topic: "Vitamins", type: "MCQ", year: "2021",
    question: "Deficiency of which vitamin causes Wernicke's encephalopathy?",
    options: ["Vitamin B2 (Riboflavin)", "Vitamin B1 (Thiamine)", "Vitamin B6 (Pyridoxine)", "Vitamin B12 (Cobalamin)"],
    correct: 1,
    answer: "Thiamine (Vitamin B1) deficiency causes Wernicke's encephalopathy (confusion, ophthalmoplegia, ataxia — classic triad). Thiamine is a cofactor for pyruvate dehydrogenase, α-ketoglutarate dehydrogenase, and transketolase."
  },
  {
    id: 23, subject: "Biochemistry", topic: "Molecular Biology", type: "SAQ", year: "2023",
    question: "Write a short note on the PCR (Polymerase Chain Reaction).",
    answer: "PCR amplifies specific DNA sequences in vitro. Steps (repeated ~30–40 cycles): (1) Denaturation — heat to 94°C, double-stranded DNA separates. (2) Annealing — cool to 50–65°C, primers bind to complementary sequences. (3) Extension — 72°C, Taq polymerase synthesises new DNA strands. Each cycle doubles the target DNA (2ⁿ copies after n cycles). Applications: diagnosis of genetic diseases, infectious diseases (COVID PCR), forensics, cloning, sequencing."
  }
];

// ── Utility helpers ──────────────────────────────────────
const SUBJECTS = [...new Set(QUESTIONS.map(q => q.subject))].sort();
const TYPES    = [...new Set(QUESTIONS.map(q => q.type))].sort();
const YEARS    = [...new Set(QUESTIONS.map(q => q.year))].sort().reverse();

function filterQuestions({ subjects = [], types = [], years = [] } = {}) {
  return QUESTIONS.filter(q =>
    (subjects.length === 0 || subjects.includes(q.subject)) &&
    (types.length    === 0 || types.includes(q.type))       &&
    (years.length    === 0 || years.includes(q.year))
  );
}

function shuffle(arr) {
  const a = [...arr];
  for (let i = a.length - 1; i > 0; i--) {
    const j = Math.floor(Math.random() * (i + 1));
    [a[i], a[j]] = [a[j], a[i]];
  }
  return a;
}
Writing File

~/kuhs-qbank/styles.css

/* =========================================================
   KUHS 1st Year MBBS Question Bank — Global Stylesheet
   ========================================================= */

/* ── Reset & Root ──────────────────────────────────────── */
*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }

:root {
  --primary    : #2563eb;
  --primary-d  : #1d4ed8;
  --primary-l  : #dbeafe;
  --accent     : #7c3aed;
  --accent-l   : #ede9fe;
  --success    : #16a34a;
  --success-l  : #dcfce7;
  --danger     : #dc2626;
  --danger-l   : #fee2e2;
  --warn       : #d97706;
  --warn-l     : #fef3c7;
  --bg         : #f8fafc;
  --surface    : #ffffff;
  --border     : #e2e8f0;
  --text       : #0f172a;
  --text-muted : #64748b;
  --text-light : #94a3b8;
  --radius     : 12px;
  --radius-sm  : 8px;
  --shadow-sm  : 0 1px 3px rgba(0,0,0,.08), 0 1px 2px rgba(0,0,0,.06);
  --shadow     : 0 4px 16px rgba(0,0,0,.10);
  --shadow-lg  : 0 10px 40px rgba(0,0,0,.14);
  --font       : 'Inter', system-ui, -apple-system, sans-serif;
  --transition : .2s cubic-bezier(.4,0,.2,1);
}

html { font-size: 16px; scroll-behavior: smooth; }
body {
  font-family: var(--font);
  background: var(--bg);
  color: var(--text);
  min-height: 100vh;
  line-height: 1.6;
}

/* ── Typography ────────────────────────────────────────── */
h1, h2, h3, h4 { font-weight: 700; line-height: 1.25; }
h1 { font-size: 2rem; }
h2 { font-size: 1.5rem; }
h3 { font-size: 1.125rem; }
p  { color: var(--text-muted); }
a  { color: var(--primary); text-decoration: none; }

/* ── Navbar ────────────────────────────────────────────── */
.navbar {
  background: var(--surface);
  border-bottom: 1px solid var(--border);
  padding: .875rem 1.5rem;
  display: flex;
  align-items: center;
  justify-content: space-between;
  position: sticky;
  top: 0;
  z-index: 100;
  box-shadow: var(--shadow-sm);
}
.navbar-brand {
  display: flex;
  align-items: center;
  gap: .625rem;
  font-weight: 800;
  font-size: 1.25rem;
  color: var(--text);
  text-decoration: none;
}
.navbar-brand .logo-icon {
  width: 36px; height: 36px;
  background: linear-gradient(135deg, var(--primary), var(--accent));
  border-radius: 10px;
  display: flex; align-items: center; justify-content: center;
  color: white; font-size: 1rem; font-weight: 800;
}
.navbar-brand span.sub { font-weight: 400; font-size: .8rem; color: var(--text-muted); margin-left: .25rem; }
.navbar-links { display: flex; gap: .25rem; }
.navbar-links a {
  padding: .45rem .9rem;
  border-radius: var(--radius-sm);
  font-size: .875rem;
  font-weight: 500;
  color: var(--text-muted);
  transition: var(--transition);
}
.navbar-links a:hover,
.navbar-links a.active { background: var(--primary-l); color: var(--primary); }

/* ── Layout Helpers ────────────────────────────────────── */
.container { max-width: 1100px; margin: 0 auto; padding: 0 1.5rem; }
.page-wrap { padding: 2.5rem 0 4rem; }

/* ── Cards ─────────────────────────────────────────────── */
.card {
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  padding: 1.5rem;
  box-shadow: var(--shadow-sm);
}
.card-sm { padding: 1rem 1.25rem; }

/* ── Buttons ────────────────────────────────────────────── */
.btn {
  display: inline-flex; align-items: center; gap: .4rem;
  padding: .625rem 1.25rem;
  border-radius: var(--radius-sm);
  font-size: .875rem; font-weight: 600;
  cursor: pointer; border: none; transition: var(--transition);
  text-decoration: none;
}
.btn-primary { background: var(--primary); color: #fff; }
.btn-primary:hover { background: var(--primary-d); }
.btn-outline {
  background: transparent; color: var(--primary);
  border: 2px solid var(--primary);
}
.btn-outline:hover { background: var(--primary-l); }
.btn-ghost {
  background: transparent; color: var(--text-muted);
  border: 1px solid var(--border);
}
.btn-ghost:hover { background: var(--bg); color: var(--text); }
.btn-success { background: var(--success); color: #fff; }
.btn-success:hover { background: #15803d; }
.btn-danger  { background: var(--danger); color: #fff; }
.btn-danger:hover  { background: #b91c1c; }
.btn-lg { padding: .875rem 1.75rem; font-size: 1rem; border-radius: var(--radius); }
.btn-sm { padding: .375rem .875rem; font-size: .8rem; }
.btn:disabled { opacity: .45; cursor: not-allowed; }

/* ── Subject Badge pills ────────────────────────────────── */
.badge {
  display: inline-flex; align-items: center;
  padding: .25rem .75rem;
  border-radius: 999px;
  font-size: .75rem; font-weight: 600;
  letter-spacing: .02em;
}
.badge-anatomy    { background: #dbeafe; color: #1d4ed8; }
.badge-physiology { background: #dcfce7; color: #15803d; }
.badge-biochemistry { background: #ede9fe; color: #6d28d9; }
.badge-laq  { background: #fef3c7; color: #92400e; }
.badge-saq  { background: #e0f2fe; color: #0369a1; }
.badge-mcq  { background: #fce7f3; color: #9d174d; }

/* ── Tag-style toggles (subject/type selectors) ─────────── */
.tag-group { display: flex; flex-wrap: wrap; gap: .5rem; }
.tag {
  padding: .4rem 1rem;
  border-radius: 999px;
  border: 2px solid var(--border);
  font-size: .8rem; font-weight: 600;
  cursor: pointer; transition: var(--transition);
  background: var(--surface); color: var(--text-muted);
  user-select: none;
}
.tag:hover { border-color: var(--primary); color: var(--primary); }
.tag.selected { background: var(--primary); border-color: var(--primary); color: #fff; }
.tag.anatomy.selected    { background: var(--primary); border-color: var(--primary); }
.tag.physiology.selected { background: var(--success); border-color: var(--success); }
.tag.biochemistry.selected { background: var(--accent); border-color: var(--accent); }

/* ── Mode toggle ────────────────────────────────────────── */
.mode-toggle {
  display: flex;
  background: var(--bg);
  border-radius: var(--radius-sm);
  border: 1px solid var(--border);
  overflow: hidden;
}
.mode-btn {
  padding: .55rem 1.1rem;
  font-size: .875rem; font-weight: 600;
  cursor: pointer; border: none; background: transparent; color: var(--text-muted);
  transition: var(--transition);
}
.mode-btn.active { background: var(--primary); color: #fff; }

/* ── Progress bar ───────────────────────────────────────── */
.progress-wrap { background: var(--border); border-radius: 999px; height: 8px; overflow: hidden; }
.progress-bar  { height: 100%; border-radius: 999px; background: linear-gradient(90deg, var(--primary), var(--accent)); transition: width .4s ease; }

/* ── Stat cards ─────────────────────────────────────────── */
.stat-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(140px, 1fr)); gap: 1rem; }
.stat-card {
  background: var(--surface); border: 1px solid var(--border);
  border-radius: var(--radius); padding: 1.25rem 1rem; text-align: center;
}
.stat-card .stat-value { font-size: 2rem; font-weight: 800; color: var(--primary); }
.stat-card .stat-label { font-size: .8rem; color: var(--text-muted); font-weight: 500; margin-top: .2rem; }

/* ── Bar chart (pure CSS) ───────────────────────────────── */
.bar-row { display: flex; align-items: center; gap: .75rem; margin-bottom: .625rem; }
.bar-label { width: 110px; font-size: .8rem; font-weight: 600; color: var(--text); flex-shrink: 0; }
.bar-track { flex: 1; background: var(--border); border-radius: 999px; height: 14px; }
.bar-fill   { height: 100%; border-radius: 999px; transition: width .6s ease; }
.bar-count  { width: 36px; text-align: right; font-size: .8rem; color: var(--text-muted); flex-shrink: 0; }

/* ── Flashcard ──────────────────────────────────────────── */
.flashcard-scene { perspective: 1200px; width: 100%; }
.flashcard {
  width: 100%; min-height: 300px;
  position: relative; transform-style: preserve-3d;
  transition: transform .55s cubic-bezier(.4,0,.2,1);
  cursor: pointer;
}
.flashcard.flipped { transform: rotateY(180deg); }
.flashcard-face {
  position: absolute; width: 100%; min-height: 300px;
  backface-visibility: hidden;
  border-radius: var(--radius);
  padding: 2rem;
  display: flex; flex-direction: column;
  justify-content: center; align-items: center;
  text-align: center;
  border: 1px solid var(--border);
  box-shadow: var(--shadow);
}
.flashcard-front { background: var(--surface); }
.flashcard-back  {
  background: linear-gradient(135deg, #eff6ff 0%, #f5f3ff 100%);
  transform: rotateY(180deg);
}
.flashcard-hint {
  position: absolute; bottom: 1rem; right: 1.25rem;
  font-size: .72rem; color: var(--text-light); font-style: italic;
}
.flashcard-q { font-size: 1.15rem; font-weight: 600; color: var(--text); line-height: 1.5; }
.flashcard-a { font-size: .9rem; color: var(--text); line-height: 1.7; text-align: left; }

/* MCQ options ─────────────────────────────────────────── */
.options-list { list-style: none; width: 100%; text-align: left; margin-top: 1rem; }
.options-list li {
  padding: .65rem 1rem;
  border-radius: var(--radius-sm);
  border: 2px solid var(--border);
  margin-bottom: .5rem;
  cursor: pointer; transition: var(--transition);
  font-size: .9rem;
}
.options-list li:hover:not(.locked) { border-color: var(--primary); background: var(--primary-l); }
.options-list li.correct { border-color: var(--success); background: var(--success-l); color: var(--success); font-weight: 600; }
.options-list li.wrong   { border-color: var(--danger);  background: var(--danger-l);  color: var(--danger); }

/* ── Browse / Question list ─────────────────────────────── */
.q-item {
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  padding: 1.25rem 1.5rem;
  margin-bottom: .875rem;
  transition: var(--transition);
}
.q-item:hover { border-color: var(--primary); box-shadow: var(--shadow); }
.q-item-header {
  display: flex; align-items: flex-start;
  justify-content: space-between; gap: 1rem;
}
.q-item-meta  { display: flex; align-items: center; gap: .4rem; flex-wrap: wrap; margin-bottom: .5rem; }
.q-item-body  { font-weight: 600; font-size: .95rem; color: var(--text); margin-bottom: .5rem; }
.q-item-answer {
  background: var(--bg); border-left: 4px solid var(--primary);
  border-radius: 0 var(--radius-sm) var(--radius-sm) 0;
  padding: .875rem 1rem;
  font-size: .875rem; color: var(--text);
  margin-top: .75rem; line-height: 1.7;
  display: none;
}
.q-item-answer.open { display: block; }
.toggle-ans {
  font-size: .75rem; color: var(--primary);
  font-weight: 600; cursor: pointer;
  padding: .2rem .5rem; border-radius: 4px;
}
.toggle-ans:hover { background: var(--primary-l); }

/* ── Search & Filter bar ────────────────────────────────── */
.filter-bar {
  display: flex; align-items: center;
  gap: .75rem; flex-wrap: wrap;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  padding: .875rem 1.25rem;
  margin-bottom: 1.5rem;
}
.search-input {
  flex: 1; min-width: 220px;
  padding: .55rem 1rem;
  border-radius: var(--radius-sm);
  border: 1px solid var(--border);
  font-size: .875rem;
  outline: none; transition: var(--transition);
}
.search-input:focus { border-color: var(--primary); box-shadow: 0 0 0 3px rgba(37,99,235,.12); }
.filter-select {
  padding: .55rem .875rem;
  border-radius: var(--radius-sm);
  border: 1px solid var(--border);
  font-size: .875rem;
  background: var(--surface); color: var(--text);
  cursor: pointer; outline: none;
}
.filter-select:focus { border-color: var(--primary); }

/* ── Section headings ───────────────────────────────────── */
.section-title {
  font-size: 1.25rem; font-weight: 700;
  color: var(--text); margin-bottom: 1rem;
  display: flex; align-items: center; gap: .5rem;
}
.section-title .count {
  background: var(--primary-l); color: var(--primary);
  border-radius: 999px; padding: .1rem .6rem;
  font-size: .75rem; font-weight: 700;
}

/* ── Hero banner (index) ────────────────────────────────── */
.hero {
  background: linear-gradient(135deg, #1e3a8a 0%, #4f46e5 50%, #7c3aed 100%);
  color: white; padding: 3rem 0 2.5rem;
}
.hero h1 { font-size: 2.5rem; margin-bottom: .5rem; }
.hero p  { color: rgba(255,255,255,.8); font-size: 1.05rem; }
.hero .badge-wrap { margin-top: .75rem; display: flex; gap: .5rem; flex-wrap: wrap; }
.hero .hero-badge {
  background: rgba(255,255,255,.15);
  color: white; border-radius: 999px;
  padding: .25rem .875rem; font-size: .8rem; font-weight: 600;
}

/* ── Donut chart placeholder ────────────────────────────── */
.donut-wrap { display: flex; justify-content: center; padding: 1rem 0; }
canvas { max-width: 100%; }

/* ── Empty state ────────────────────────────────────────── */
.empty-state {
  text-align: center; padding: 3rem 1rem;
  color: var(--text-muted);
}
.empty-state .icon { font-size: 3rem; margin-bottom: 1rem; }

/* ── Footer ─────────────────────────────────────────────── */
.footer {
  background: var(--surface); border-top: 1px solid var(--border);
  text-align: center; padding: 1.25rem;
  font-size: .8rem; color: var(--text-muted);
  margin-top: auto;
}

/* ── Responsive ─────────────────────────────────────────── */
@media (max-width: 640px) {
  .hero h1 { font-size: 1.75rem; }
  .navbar-links a { padding: .35rem .6rem; font-size: .8rem; }
  .flashcard-face { padding: 1.25rem; min-height: 240px; }
  .flashcard-q { font-size: 1rem; }
  h2 { font-size: 1.25rem; }
}
Writing File

~/kuhs-qbank/index.html

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8" />
  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
  <title>KUHS 1st Year MBBS — Question Bank</title>
  <link rel="preconnect" href="https://fonts.googleapis.com" />
  <link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700;800&display=swap" rel="stylesheet" />
  <link rel="stylesheet" href="styles.css" />
  <style>
    .setup-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 1.5rem; margin-top: 2rem; }
    @media(max-width:700px){ .setup-grid { grid-template-columns:1fr; } }
    .setup-card { background:var(--surface); border:1px solid var(--border); border-radius:var(--radius); padding:1.5rem; }
    .setup-card h3 { margin-bottom:1rem; display:flex; align-items:center; gap:.5rem; }
    .cta-row { display:flex; gap:1rem; margin-top:2rem; flex-wrap:wrap; }
    .count-banner {
      background: var(--primary-l); border:1px solid #bfdbfe;
      border-radius:var(--radius-sm); padding:.65rem 1rem;
      font-size:.875rem; font-weight:600; color:var(--primary);
      display:flex; align-items:center; gap:.5rem; margin-top:1.25rem;
    }
    .subject-hero-cards { display:grid; grid-template-columns:repeat(auto-fit,minmax(160px,1fr)); gap:1rem; margin-top:2rem; }
    .subj-card {
      border-radius:var(--radius); padding:1.25rem 1rem; cursor:pointer;
      border:2px solid transparent; transition:var(--transition); text-align:center;
    }
    .subj-card .icon { font-size:2rem; margin-bottom:.5rem; }
    .subj-card h4 { font-size:.9rem; font-weight:700; }
    .subj-card p  { font-size:.75rem; margin-top:.2rem; }
    .subj-card.anatomy    { background:#dbeafe; }
    .subj-card.physiology { background:#dcfce7; }
    .subj-card.biochemistry { background:#ede9fe; }
    .subj-card.selected, .subj-card:hover { border-color: var(--primary); box-shadow:var(--shadow); transform:translateY(-2px); }
  </style>
</head>
<body>

<!-- Navbar -->
<nav class="navbar">
  <a class="navbar-brand" href="index.html">
    <div class="logo-icon">K</div>
    KUHS QBank <span class="sub">1st Year MBBS</span>
  </a>
  <div class="navbar-links">
    <a href="index.html" class="active">Home</a>
    <a href="browse.html">Browse</a>
    <a href="flashcard.html">Flashcards</a>
    <a href="analysis.html">Analysis</a>
  </div>
</nav>

<!-- Hero -->
<div class="hero">
  <div class="container">
    <h1>KUHS 1st Year MBBS<br>Question Bank</h1>
    <p>Previous year questions from Kerala University of Health Sciences</p>
    <div class="badge-wrap">
      <span class="hero-badge">📚 Anatomy</span>
      <span class="hero-badge">🫀 Physiology</span>
      <span class="hero-badge">🧬 Biochemistry</span>
      <span class="hero-badge" id="hero-total">— Questions</span>
    </div>
  </div>
</div>

<!-- Main -->
<main class="page-wrap">
  <div class="container">

    <div class="setup-grid">
      <!-- Subject selector -->
      <div class="setup-card">
        <h3>📖 Choose Subjects</h3>
        <div class="subject-hero-cards" id="subjectCards">
          <div class="subj-card anatomy selected" data-subj="Anatomy">
            <div class="icon">🦴</div>
            <h4>Anatomy</h4>
            <p class="q-count" id="cnt-Anatomy">0 Qs</p>
          </div>
          <div class="subj-card physiology selected" data-subj="Physiology">
            <div class="icon">🫀</div>
            <h4>Physiology</h4>
            <p class="q-count" id="cnt-Physiology">0 Qs</p>
          </div>
          <div class="subj-card biochemistry selected" data-subj="Biochemistry">
            <div class="icon">🧬</div>
            <h4>Biochemistry</h4>
            <p class="q-count" id="cnt-Biochemistry">0 Qs</p>
          </div>
        </div>
      </div>

      <!-- Filter + mode -->
      <div class="setup-card">
        <h3>⚙️ Study Options</h3>

        <p style="font-size:.8rem;color:var(--text-muted);margin-bottom:.75rem;">Question type</p>
        <div class="tag-group" id="typeTags">
          <span class="tag selected" data-type="LAQ">LAQ</span>
          <span class="tag selected" data-type="SAQ">SAQ</span>
          <span class="tag selected" data-type="MCQ">MCQ</span>
        </div>

        <p style="font-size:.8rem;color:var(--text-muted);margin-bottom:.75rem;margin-top:1.25rem;">Exam year</p>
        <div class="tag-group" id="yearTags"></div>

        <p style="font-size:.8rem;color:var(--text-muted);margin-bottom:.75rem;margin-top:1.25rem;">Study mode</p>
        <div class="mode-toggle">
          <button class="mode-btn active" id="modeFlash">🃏 Flashcards</button>
          <button class="mode-btn" id="modeBrowse">📋 Browse All</button>
        </div>

        <div class="count-banner" id="countBanner">
          <span>✅</span> <span id="selCount">0</span> questions selected
        </div>

        <div class="cta-row">
          <button class="btn btn-primary btn-lg" id="startBtn">Start Studying →</button>
          <a href="browse.html" class="btn btn-ghost btn-lg">Browse First</a>
        </div>
      </div>
    </div>

  </div>
</main>

<footer class="footer">
  KUHS 1st Year MBBS Question Bank &nbsp;·&nbsp; Built with ❤️ for medical students
</footer>

<script src="data.js"></script>
<script>
  // ── Subject card toggling ────────────────────────────────
  const selectedSubjects = new Set(SUBJECTS);
  const selectedTypes    = new Set(TYPES);
  const selectedYears    = new Set(YEARS);
  let   studyMode        = 'flashcard';

  // populate counts
  document.getElementById('hero-total').textContent = QUESTIONS.length + ' Questions';
  SUBJECTS.forEach(s => {
    const el = document.getElementById('cnt-' + s);
    if (el) el.textContent = QUESTIONS.filter(q => q.subject === s).length + ' Qs';
  });

  // Year tags
  const yearTagContainer = document.getElementById('yearTags');
  YEARS.forEach(y => {
    const span = document.createElement('span');
    span.className = 'tag selected'; span.dataset.year = y; span.textContent = y;
    yearTagContainer.appendChild(span);
  });

  function updateCount() {
    const n = filterQuestions({
      subjects: [...selectedSubjects],
      types   : [...selectedTypes],
      years   : [...selectedYears]
    }).length;
    document.getElementById('selCount').textContent = n;
  }
  updateCount();

  // Subject cards
  document.getElementById('subjectCards').addEventListener('click', e => {
    const card = e.target.closest('[data-subj]');
    if (!card) return;
    const s = card.dataset.subj;
    if (selectedSubjects.has(s)) { selectedSubjects.delete(s); card.classList.remove('selected'); }
    else                          { selectedSubjects.add(s);    card.classList.add('selected');    }
    updateCount();
  });

  // Type tags
  document.getElementById('typeTags').addEventListener('click', e => {
    const tag = e.target.closest('[data-type]');
    if (!tag) return;
    const t = tag.dataset.type;
    if (selectedTypes.has(t)) { selectedTypes.delete(t); tag.classList.remove('selected'); }
    else                       { selectedTypes.add(t);    tag.classList.add('selected');    }
    updateCount();
  });

  // Year tags
  yearTagContainer.addEventListener('click', e => {
    const tag = e.target.closest('[data-year]');
    if (!tag) return;
    const y = tag.dataset.year;
    if (selectedYears.has(y)) { selectedYears.delete(y); tag.classList.remove('selected'); }
    else                       { selectedYears.add(y);    tag.classList.add('selected');    }
    updateCount();
  });

  // Mode toggle
  document.getElementById('modeFlash').addEventListener('click', () => {
    studyMode = 'flashcard';
    document.getElementById('modeFlash').classList.add('active');
    document.getElementById('modeBrowse').classList.remove('active');
  });
  document.getElementById('modeBrowse').addEventListener('click', () => {
    studyMode = 'browse';
    document.getElementById('modeBrowse').classList.add('active');
    document.getElementById('modeFlash').classList.remove('active');
  });

  // Start button
  document.getElementById('startBtn').addEventListener('click', () => {
    const params = new URLSearchParams({
      subjects: [...selectedSubjects].join(','),
      types   : [...selectedTypes].join(','),
      years   : [...selectedYears].join(',')
    });
    if (studyMode === 'flashcard') {
      window.location.href = 'flashcard.html?' + params;
    } else {
      window.location.href = 'browse.html?' + params;
    }
  });
</script>
</body>
</html>
Writing File

~/kuhs-qbank/flashcard.html

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8" />
  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
  <title>Flashcards — KUHS QBank</title>
  <link rel="preconnect" href="https://fonts.googleapis.com" />
  <link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700;800&display=swap" rel="stylesheet" />
  <link rel="stylesheet" href="styles.css" />
  <style>
    .fc-layout { display:grid; grid-template-columns:300px 1fr; gap:1.5rem; align-items:start; }
    @media(max-width:860px){ .fc-layout { grid-template-columns:1fr; } }

    .fc-sidebar { position:sticky; top:80px; }
    .sidebar-section { margin-bottom:1.25rem; }
    .sidebar-section h4 { font-size:.8rem; font-weight:700; color:var(--text-muted); text-transform:uppercase; letter-spacing:.08em; margin-bottom:.5rem; }

    .fc-main {}
    .fc-topbar { display:flex; align-items:center; justify-content:space-between; margin-bottom:1.25rem; flex-wrap:wrap; gap:.75rem; }
    .fc-counter { font-size:.875rem; font-weight:600; color:var(--text-muted); }
    .fc-controls { display:flex; gap:.5rem; flex-wrap:wrap; }

    /* keyboard hint */
    .key-hint { font-size:.72rem; color:var(--text-light); margin-top:.5rem; text-align:center; }
    kbd { background:var(--border); border-radius:4px; padding:.1rem .35rem; font-size:.7rem; }

    /* confidence buttons */
    .conf-row { display:flex; gap:.75rem; justify-content:center; margin-top:1.25rem; flex-wrap:wrap; }
    .conf-btn {
      padding:.55rem 1.25rem; border-radius:var(--radius-sm);
      font-size:.875rem; font-weight:700; cursor:pointer;
      border:2px solid transparent; transition:var(--transition);
    }
    .conf-easy  { background:var(--success-l); color:var(--success); border-color:var(--success); }
    .conf-hard  { background:var(--warn-l);    color:var(--warn);    border-color:var(--warn); }
    .conf-again { background:var(--danger-l);  color:var(--danger);  border-color:var(--danger); }
    .conf-easy:hover  { background:var(--success); color:#fff; }
    .conf-hard:hover  { background:var(--warn);    color:#fff; }
    .conf-again:hover { background:var(--danger);  color:#fff; }
    .conf-row { display:none; }
    .conf-row.visible { display:flex; }

    /* Score summary */
    .score-panel {
      text-align:center; padding:2rem;
      background:var(--surface); border:1px solid var(--border);
      border-radius:var(--radius); box-shadow:var(--shadow);
      display:none;
    }
    .score-panel.visible { display:block; }
    .score-big { font-size:4rem; font-weight:800; color:var(--primary); }
    .score-row { display:flex; gap:1rem; justify-content:center; margin:1rem 0; flex-wrap:wrap; }
    .score-pill {
      padding:.4rem 1rem; border-radius:999px; font-size:.875rem; font-weight:600;
    }
    .pill-easy  { background:var(--success-l); color:var(--success); }
    .pill-hard  { background:var(--warn-l);    color:var(--warn); }
    .pill-again { background:var(--danger-l);  color:var(--danger); }

    /* MCQ on flashcard */
    .fc-mcq-opts { width:100%; text-align:left; margin-top:1.25rem; }
    .fc-mcq-opts li {
      padding:.6rem 1rem; border-radius:var(--radius-sm);
      border:2px solid var(--border); margin-bottom:.5rem;
      cursor:pointer; transition:var(--transition); font-size:.875rem;
    }
    .fc-mcq-opts li:hover:not(.locked) { border-color:var(--primary); background:var(--primary-l); }
    .fc-mcq-opts li.correct { border-color:var(--success); background:var(--success-l); color:var(--success); font-weight:600; }
    .fc-mcq-opts li.wrong   { border-color:var(--danger);  background:var(--danger-l);  color:var(--danger); }
  </style>
</head>
<body>

<!-- Navbar -->
<nav class="navbar">
  <a class="navbar-brand" href="index.html">
    <div class="logo-icon">K</div>
    KUHS QBank <span class="sub">1st Year MBBS</span>
  </a>
  <div class="navbar-links">
    <a href="index.html">Home</a>
    <a href="browse.html">Browse</a>
    <a href="flashcard.html" class="active">Flashcards</a>
    <a href="analysis.html">Analysis</a>
  </div>
</nav>

<main class="page-wrap">
  <div class="container">
    <div class="fc-layout">

      <!-- Sidebar -->
      <aside class="fc-sidebar">
        <div class="card" style="padding:1.25rem">
          <div class="sidebar-section">
            <h4>Session Progress</h4>
            <div class="progress-wrap" style="margin-bottom:.5rem">
              <div class="progress-bar" id="progressBar" style="width:0%"></div>
            </div>
            <div style="font-size:.8rem;color:var(--text-muted)" id="progressText">0 / 0 done</div>
          </div>

          <div class="sidebar-section">
            <h4>Results so far</h4>
            <div style="display:flex;flex-direction:column;gap:.5rem" id="sideResults">
              <div style="display:flex;justify-content:space-between;font-size:.85rem">
                <span style="color:var(--success);font-weight:600">✅ Easy</span><span id="cntEasy">0</span>
              </div>
              <div style="display:flex;justify-content:space-between;font-size:.85rem">
                <span style="color:var(--warn);font-weight:600">⚠️ Hard</span><span id="cntHard">0</span>
              </div>
              <div style="display:flex;justify-content:space-between;font-size:.85rem">
                <span style="color:var(--danger);font-weight:600">🔁 Again</span><span id="cntAgain">0</span>
              </div>
            </div>
          </div>

          <div class="sidebar-section">
            <h4>Current Card</h4>
            <div id="sideSubject" style="font-size:.8rem;color:var(--text-muted)">—</div>
            <div id="sideTopic"   style="font-size:.75rem;color:var(--text-light);margin-top:.2rem">—</div>
          </div>

          <div style="display:flex;flex-direction:column;gap:.5rem;margin-top:.5rem">
            <button class="btn btn-ghost btn-sm" id="shuffleBtn">🔀 Shuffle</button>
            <a href="index.html" class="btn btn-ghost btn-sm">← Back to Setup</a>
          </div>
        </div>
      </aside>

      <!-- Main flashcard area -->
      <div class="fc-main">
        <div class="fc-topbar">
          <span class="fc-counter" id="fcCounter">Card 1 of —</span>
          <div class="fc-controls">
            <button class="btn btn-ghost btn-sm" id="prevBtn">← Prev</button>
            <button class="btn btn-ghost btn-sm" id="nextBtn">Next →</button>
          </div>
        </div>

        <!-- The flashcard -->
        <div class="flashcard-scene" id="flashcardScene">
          <div class="flashcard" id="flashcard">
            <div class="flashcard-face flashcard-front" id="cardFront">
              <div id="frontMeta" style="display:flex;gap:.4rem;flex-wrap:wrap;margin-bottom:1rem;justify-content:center"></div>
              <p class="flashcard-q" id="frontQ">Loading…</p>
              <ul class="fc-mcq-opts" id="mcqOpts" style="display:none"></ul>
              <span class="flashcard-hint" id="flipHint">Click to reveal answer</span>
            </div>
            <div class="flashcard-face flashcard-back" id="cardBack">
              <div id="backMeta" style="display:flex;gap:.4rem;flex-wrap:wrap;margin-bottom:1rem;justify-content:center"></div>
              <p class="flashcard-a" id="backA">Answer goes here</p>
              <span class="flashcard-hint">Click to go back</span>
            </div>
          </div>
        </div>
        <p class="key-hint"><kbd>Space</kbd> flip &nbsp; <kbd>←</kbd> prev &nbsp; <kbd>→</kbd> next &nbsp; <kbd>1</kbd> Easy &nbsp; <kbd>2</kbd> Hard &nbsp; <kbd>3</kbd> Again</p>

        <!-- Confidence buttons (shown after flip) -->
        <div class="conf-row" id="confRow">
          <button class="conf-btn conf-easy"  id="btnEasy">✅ Easy</button>
          <button class="conf-btn conf-hard"  id="btnHard">⚠️ Hard</button>
          <button class="conf-btn conf-again" id="btnAgain">🔁 Again</button>
        </div>

        <!-- Score panel (end of deck) -->
        <div class="score-panel" id="scorePanel">
          <div style="font-size:2rem;margin-bottom:.5rem">🎉</div>
          <p style="font-weight:700;font-size:1.25rem;color:var(--text);margin-bottom:.25rem">Session Complete!</p>
          <div class="score-big" id="scorePct">0%</div>
          <p style="margin-bottom:1rem">cards marked easy</p>
          <div class="score-row">
            <span class="score-pill pill-easy"  id="sFinal-easy">Easy: 0</span>
            <span class="score-pill pill-hard"  id="sFinal-hard">Hard: 0</span>
            <span class="score-pill pill-again" id="sFinal-again">Again: 0</span>
          </div>
          <div style="display:flex;gap:1rem;justify-content:center;margin-top:1rem;flex-wrap:wrap">
            <button class="btn btn-primary" id="restartBtn">🔁 Restart Session</button>
            <a href="index.html" class="btn btn-ghost">← Home</a>
          </div>
        </div>

      </div><!-- /fc-main -->
    </div>
  </div>
</main>

<footer class="footer">
  KUHS 1st Year MBBS Question Bank &nbsp;·&nbsp; Built with ❤️ for medical students
</footer>

<script src="data.js"></script>
<script>
(function() {
  // ── Parse URL params ──────────────────────────────────
  const params   = new URLSearchParams(location.search);
  const subjects = params.get('subjects') ? params.get('subjects').split(',') : SUBJECTS;
  const types    = params.get('types')    ? params.get('types').split(',')    : TYPES;
  const years    = params.get('years')    ? params.get('years').split(',')    : YEARS;

  let deck = shuffle(filterQuestions({ subjects, types, years }));
  let idx  = 0;
  let flipped = false;
  const results = { easy: 0, hard: 0, again: 0 };
  const answered = {}; // idx → 'easy'|'hard'|'again'
  let mcqAnswered = false;

  // ── Helpers ───────────────────────────────────────────
  function badgeHTML(q) {
    return `<span class="badge badge-${q.subject.toLowerCase()}">${q.subject}</span>
            <span class="badge badge-${q.type.toLowerCase()}">${q.type}</span>
            <span class="badge" style="background:var(--bg);color:var(--text-muted)">${q.year}</span>`;
  }

  function renderCard() {
    if (deck.length === 0) { showEmpty(); return; }
    const q = deck[idx];
    flipped = false;
    mcqAnswered = false;

    document.getElementById('flashcard').classList.remove('flipped');
    document.getElementById('frontMeta').innerHTML = badgeHTML(q);
    document.getElementById('backMeta').innerHTML  = badgeHTML(q);
    document.getElementById('frontQ').textContent  = q.question;
    document.getElementById('backA').textContent   = q.answer || '—';
    document.getElementById('fcCounter').textContent = `Card ${idx + 1} of ${deck.length}`;
    document.getElementById('sideSubject').textContent = q.subject;
    document.getElementById('sideTopic').textContent   = q.topic || '';
    document.getElementById('confRow').classList.remove('visible');

    // MCQ
    const mcqOpts = document.getElementById('mcqOpts');
    const flipHint = document.getElementById('flipHint');
    if (q.type === 'MCQ' && q.options) {
      mcqOpts.innerHTML = '';
      mcqOpts.style.display = 'block';
      flipHint.textContent = 'Select an answer';
      q.options.forEach((opt, i) => {
        const li = document.createElement('li');
        li.textContent = String.fromCharCode(65 + i) + '. ' + opt;
        li.dataset.idx = i;
        li.addEventListener('click', () => handleMCQ(li, i, q));
        mcqOpts.appendChild(li);
      });
    } else {
      mcqOpts.style.display = 'none';
      flipHint.textContent = 'Click to reveal answer';
    }

    // progress
    const done = Object.keys(answered).length;
    document.getElementById('progressText').textContent = `${done} / ${deck.length} done`;
    document.getElementById('progressBar').style.width  = `${(done/deck.length)*100}%`;
    document.getElementById('prevBtn').disabled = idx === 0;
    document.getElementById('nextBtn').disabled = idx === deck.length - 1;
  }

  function handleMCQ(li, chosen, q) {
    if (mcqAnswered) return;
    mcqAnswered = true;
    const items = li.parentElement.querySelectorAll('li');
    items.forEach(el => el.classList.add('locked'));
    items[q.correct].classList.add('correct');
    if (chosen !== q.correct) li.classList.add('wrong');
    document.getElementById('flipHint').textContent = 'Click to see full answer';
    document.getElementById('confRow').classList.add('visible');
  }

  function flipCard() {
    if (!flipped) {
      document.getElementById('flashcard').classList.add('flipped');
      flipped = true;
      const q = deck[idx];
      if (q.type !== 'MCQ') {
        document.getElementById('confRow').classList.add('visible');
      }
    } else {
      document.getElementById('flashcard').classList.remove('flipped');
      flipped = false;
    }
  }

  function markResult(conf) {
    if (answered[idx]) {
      results[answered[idx]]--;
    }
    results[conf]++;
    answered[idx] = conf;
    document.getElementById('cntEasy').textContent  = results.easy;
    document.getElementById('cntHard').textContent  = results.hard;
    document.getElementById('cntAgain').textContent = results.again;
    // progress
    const done = Object.keys(answered).length;
    document.getElementById('progressText').textContent = `${done} / ${deck.length} done`;
    document.getElementById('progressBar').style.width  = `${(done/deck.length)*100}%`;
    if (idx < deck.length - 1) {
      idx++;
      renderCard();
    } else {
      showScore();
    }
  }

  function showScore() {
    document.getElementById('flashcardScene').style.display = 'none';
    document.getElementById('confRow').style.display = 'none';
    document.querySelector('.key-hint').style.display = 'none';
    document.querySelector('.fc-topbar').style.display = 'none';
    const pct = deck.length ? Math.round((results.easy / deck.length) * 100) : 0;
    document.getElementById('scorePct').textContent = pct + '%';
    document.getElementById('sFinal-easy').textContent  = 'Easy: '  + results.easy;
    document.getElementById('sFinal-hard').textContent  = 'Hard: '  + results.hard;
    document.getElementById('sFinal-again').textContent = 'Again: ' + results.again;
    document.getElementById('scorePanel').classList.add('visible');
  }

  function showEmpty() {
    document.getElementById('fcCounter').textContent = 'No questions match your filters';
    document.getElementById('flashcardScene').innerHTML =
      '<div class="empty-state"><div class="icon">🔍</div><p>No questions match your filters.</p><a href="index.html" class="btn btn-primary" style="margin-top:1rem">← Change Filters</a></div>';
  }

  function restart() {
    deck = shuffle(filterQuestions({ subjects, types, years }));
    idx  = 0;
    Object.keys(results).forEach(k => results[k] = 0);
    Object.keys(answered).forEach(k => delete answered[k]);
    document.getElementById('cntEasy').textContent  = 0;
    document.getElementById('cntHard').textContent  = 0;
    document.getElementById('cntAgain').textContent = 0;
    document.getElementById('scorePanel').classList.remove('visible');
    document.getElementById('flashcardScene').style.display = '';
    document.getElementById('confRow').style.display = '';
    document.querySelector('.key-hint').style.display = '';
    document.querySelector('.fc-topbar').style.display = '';
    renderCard();
  }

  // ── Events ────────────────────────────────────────────
  document.getElementById('flashcard').addEventListener('click', flipCard);
  document.getElementById('nextBtn').addEventListener('click', () => { if (idx < deck.length - 1) { idx++; renderCard(); } });
  document.getElementById('prevBtn').addEventListener('click', () => { if (idx > 0) { idx--; renderCard(); } });
  document.getElementById('btnEasy').addEventListener('click',  () => markResult('easy'));
  document.getElementById('btnHard').addEventListener('click',  () => markResult('hard'));
  document.getElementById('btnAgain').addEventListener('click', () => markResult('again'));
  document.getElementById('shuffleBtn').addEventListener('click', () => { deck = shuffle(deck); idx = 0; renderCard(); });
  document.getElementById('restartBtn').addEventListener('click', restart);

  // Keyboard shortcuts
  document.addEventListener('keydown', e => {
    if (e.key === ' ')          { e.preventDefault(); flipCard(); }
    if (e.key === 'ArrowRight') { if (idx < deck.length - 1) { idx++; renderCard(); } }
    if (e.key === 'ArrowLeft')  { if (idx > 0) { idx--; renderCard(); } }
    if (e.key === '1') document.getElementById('btnEasy').click();
    if (e.key === '2') document.getElementById('btnHard').click();
    if (e.key === '3') document.getElementById('btnAgain').click();
  });

  // Init
  renderCard();
})();
</script>
</body>
</html>
Writing File

~/kuhs-qbank/browse.html

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8" />
  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
  <title>Browse Questions — KUHS QBank</title>
  <link rel="preconnect" href="https://fonts.googleapis.com" />
  <link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700;800&display=swap" rel="stylesheet" />
  <link rel="stylesheet" href="styles.css" />
  <style>
    .q-number {
      width:28px; height:28px; border-radius:50%;
      background:var(--primary-l); color:var(--primary);
      font-size:.75rem; font-weight:800;
      display:flex; align-items:center; justify-content:center;
      flex-shrink:0;
    }
    .subject-group { margin-bottom:2.5rem; }
    .group-header {
      display:flex; align-items:center; justify-content:space-between;
      padding:.75rem 0; border-bottom:2px solid var(--border);
      margin-bottom:1rem;
    }
    .group-header h3 { display:flex; align-items:center; gap:.5rem; }
    .expand-all-btn { font-size:.75rem; color:var(--primary); font-weight:600; cursor:pointer; }
    .expand-all-btn:hover { text-decoration:underline; }
    .mcq-answer-opts { margin-top:.5rem; }
    .mcq-answer-opts li {
      padding:.4rem .75rem; border-radius:var(--radius-sm);
      border:2px solid var(--border); margin-bottom:.35rem;
      font-size:.85rem; list-style:none;
    }
    .mcq-answer-opts li.correct { border-color:var(--success); background:var(--success-l); color:var(--success); font-weight:600; }
    .no-results { text-align:center; padding:3rem 1rem; color:var(--text-muted); }
  </style>
</head>
<body>

<!-- Navbar -->
<nav class="navbar">
  <a class="navbar-brand" href="index.html">
    <div class="logo-icon">K</div>
    KUHS QBank <span class="sub">1st Year MBBS</span>
  </a>
  <div class="navbar-links">
    <a href="index.html">Home</a>
    <a href="browse.html" class="active">Browse</a>
    <a href="flashcard.html">Flashcards</a>
    <a href="analysis.html">Analysis</a>
  </div>
</nav>

<main class="page-wrap">
  <div class="container">

    <div style="display:flex;align-items:flex-start;justify-content:space-between;flex-wrap:wrap;gap:1rem;margin-bottom:1.5rem">
      <div>
        <h2>Browse Questions</h2>
        <p style="margin-top:.25rem">Click any question to reveal the answer</p>
      </div>
      <a href="flashcard.html" class="btn btn-primary">🃏 Switch to Flashcards</a>
    </div>

    <!-- Filter bar -->
    <div class="filter-bar">
      <input type="text" class="search-input" id="searchInput" placeholder="🔍  Search questions…" />
      <select class="filter-select" id="subjectFilter">
        <option value="">All Subjects</option>
        <option value="Anatomy">Anatomy</option>
        <option value="Physiology">Physiology</option>
        <option value="Biochemistry">Biochemistry</option>
      </select>
      <select class="filter-select" id="typeFilter">
        <option value="">All Types</option>
        <option value="LAQ">LAQ</option>
        <option value="SAQ">SAQ</option>
        <option value="MCQ">MCQ</option>
      </select>
      <select class="filter-select" id="yearFilter">
        <option value="">All Years</option>
      </select>
      <button class="btn btn-ghost btn-sm" id="clearFilters">✕ Clear</button>
    </div>

    <!-- Results count -->
    <div class="section-title" id="resultsTitle">
      All Questions <span class="count" id="resultsCount">0</span>
    </div>

    <!-- Question list -->
    <div id="questionList"></div>

  </div>
</main>

<footer class="footer">
  KUHS 1st Year MBBS Question Bank &nbsp;·&nbsp; Built with ❤️ for medical students
</footer>

<script src="data.js"></script>
<script>
(function() {
  // Populate year filter
  const yearFilter = document.getElementById('yearFilter');
  YEARS.forEach(y => {
    const opt = document.createElement('option');
    opt.value = y; opt.textContent = y;
    yearFilter.appendChild(opt);
  });

  // Read URL params for initial filter
  const params = new URLSearchParams(location.search);
  if (params.get('subjects')) document.getElementById('subjectFilter').value = params.get('subjects').split(',')[0] || '';

  let filteredQs = [];

  function getFilters() {
    return {
      search  : document.getElementById('searchInput').value.toLowerCase().trim(),
      subject : document.getElementById('subjectFilter').value,
      type    : document.getElementById('typeFilter').value,
      year    : document.getElementById('yearFilter').value
    };
  }

  function applyFilters() {
    const f = getFilters();
    filteredQs = QUESTIONS.filter(q => {
      if (f.subject && q.subject !== f.subject) return false;
      if (f.type    && q.type    !== f.type)    return false;
      if (f.year    && q.year    !== f.year)     return false;
      if (f.search  && !q.question.toLowerCase().includes(f.search) &&
          !(q.answer || '').toLowerCase().includes(f.search) &&
          !(q.topic  || '').toLowerCase().includes(f.search)) return false;
      return true;
    });
    render();
  }

  function subjectIcon(s) {
    return { Anatomy: '🦴', Physiology: '🫀', Biochemistry: '🧬' }[s] || '📚';
  }
  function subjectColor(s) {
    return { Anatomy: '#2563eb', Physiology: '#16a34a', Biochemistry: '#7c3aed' }[s] || '#64748b';
  }

  function render() {
    const list = document.getElementById('questionList');
    document.getElementById('resultsCount').textContent = filteredQs.length;

    if (filteredQs.length === 0) {
      list.innerHTML = '<div class="no-results"><div style="font-size:3rem">🔍</div><p>No questions match your search.</p></div>';
      return;
    }

    // Group by subject
    const groups = {};
    filteredQs.forEach(q => {
      if (!groups[q.subject]) groups[q.subject] = [];
      groups[q.subject].push(q);
    });

    list.innerHTML = '';
    Object.entries(groups).forEach(([subj, qs]) => {
      const section = document.createElement('div');
      section.className = 'subject-group';

      // Group header
      section.innerHTML = `
        <div class="group-header">
          <h3>${subjectIcon(subj)} ${subj} <span class="badge" style="background:var(--border);color:var(--text-muted)">${qs.length}</span></h3>
          <span class="expand-all-btn" data-group="${subj}">Expand All</span>
        </div>
      `;

      // Questions
      qs.forEach((q, i) => {
        const item = document.createElement('div');
        item.className = 'q-item';
        item.dataset.id = q.id;

        // Build answer HTML
        let answerHTML = '';
        if (q.type === 'MCQ' && q.options) {
          const opts = q.options.map((opt, oi) =>
            `<li class="${oi === q.correct ? 'correct' : ''}">${String.fromCharCode(65+oi)}. ${opt}</li>`
          ).join('');
          answerHTML = `<ul class="mcq-answer-opts">${opts}</ul>`;
          if (q.answer) answerHTML += `<p style="margin-top:.75rem;font-size:.85rem;color:var(--text)">${q.answer}</p>`;
        } else {
          answerHTML = `<p style="font-size:.875rem;line-height:1.7;color:var(--text)">${q.answer || 'Answer not available.'}</p>`;
        }

        item.innerHTML = `
          <div class="q-item-header">
            <div style="flex:1">
              <div class="q-item-meta">
                <span class="q-number">${i + 1}</span>
                <span class="badge badge-${q.subject.toLowerCase()}">${q.subject}</span>
                <span class="badge badge-${q.type.toLowerCase()}">${q.type}</span>
                <span class="badge" style="background:var(--bg);color:var(--text-muted);border:1px solid var(--border)">${q.year}</span>
                ${q.topic ? `<span style="font-size:.75rem;color:var(--text-light)">${q.topic}</span>` : ''}
              </div>
              <div class="q-item-body">${q.question}</div>
            </div>
            <span class="toggle-ans" data-id="${q.id}">Show Answer</span>
          </div>
          <div class="q-item-answer" id="ans-${q.id}">
            ${answerHTML}
          </div>
        `;
        section.appendChild(item);
      });

      list.appendChild(section);
    });

    // Toggle answer
    list.querySelectorAll('.toggle-ans').forEach(btn => {
      btn.addEventListener('click', () => {
        const id  = btn.dataset.id;
        const ans = document.getElementById('ans-' + id);
        if (ans.classList.contains('open')) {
          ans.classList.remove('open'); btn.textContent = 'Show Answer';
        } else {
          ans.classList.add('open'); btn.textContent = 'Hide Answer';
        }
      });
    });

    // Expand all per group
    list.querySelectorAll('.expand-all-btn').forEach(btn => {
      btn.addEventListener('click', () => {
        const grp = btn.dataset.group;
        const allAns = list.querySelectorAll(`[id^="ans-"]`);
        const items  = [...allAns].filter(el => {
          const parentItem = el.closest('.subject-group');
          return parentItem && parentItem.querySelector('.group-header h3')?.textContent.includes(grp);
        });
        const allOpen = items.every(el => el.classList.contains('open'));
        items.forEach(el => {
          const id  = el.id.replace('ans-', '');
          const tog = el.parentElement.querySelector(`.toggle-ans[data-id="${id}"]`);
          if (allOpen) { el.classList.remove('open'); if(tog) tog.textContent='Show Answer'; }
          else         { el.classList.add('open');    if(tog) tog.textContent='Hide Answer'; }
        });
        btn.textContent = allOpen ? 'Expand All' : 'Collapse All';
      });
    });
  }

  // Event listeners
  document.getElementById('searchInput').addEventListener('input', applyFilters);
  document.getElementById('subjectFilter').addEventListener('change', applyFilters);
  document.getElementById('typeFilter').addEventListener('change', applyFilters);
  document.getElementById('yearFilter').addEventListener('change', applyFilters);
  document.getElementById('clearFilters').addEventListener('click', () => {
    document.getElementById('searchInput').value = '';
    document.getElementById('subjectFilter').value = '';
    document.getElementById('typeFilter').value = '';
    document.getElementById('yearFilter').value = '';
    applyFilters();
  });

  applyFilters();
})();
</script>
</body>
</html>
Writing File

~/kuhs-qbank/analysis.html

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8" />
  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
  <title>Analysis — KUHS QBank</title>
  <link rel="preconnect" href="https://fonts.googleapis.com" />
  <link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700;800&display=swap" rel="stylesheet" />
  <link rel="stylesheet" href="styles.css" />
  <style>
    .analysis-grid { display:grid; grid-template-columns:1fr 1fr; gap:1.5rem; }
    @media(max-width:700px){ .analysis-grid { grid-template-columns:1fr; } }
    .card-title { font-size:1rem; font-weight:700; margin-bottom:1.25rem; display:flex; align-items:center; gap:.5rem; }

    /* Donut chart */
    .donut-container { position:relative; width:180px; height:180px; margin:0 auto 1.25rem; }
    .donut-container canvas { width:180px!important; height:180px!important; }
    .donut-center {
      position:absolute; top:50%; left:50%; transform:translate(-50%,-50%);
      text-align:center; pointer-events:none;
    }
    .donut-center .val { font-size:1.75rem; font-weight:800; color:var(--text); }
    .donut-center .lbl { font-size:.7rem; color:var(--text-muted); font-weight:600; }

    /* Legend */
    .legend { display:flex; flex-direction:column; gap:.5rem; }
    .legend-item { display:flex; align-items:center; gap:.6rem; font-size:.85rem; }
    .legend-dot { width:12px; height:12px; border-radius:50%; flex-shrink:0; }

    /* Year heatmap */
    .year-grid { display:grid; grid-template-columns:repeat(auto-fill,minmax(80px,1fr)); gap:.75rem; }
    .year-tile {
      border-radius:var(--radius-sm); padding:.875rem .5rem; text-align:center;
      transition:var(--transition); cursor:default;
    }
    .year-tile .year-num { font-size:.8rem; font-weight:700; color:var(--text-muted); }
    .year-tile .year-cnt { font-size:1.5rem; font-weight:800; color:var(--text); }

    /* Topic table */
    .topic-table { width:100%; border-collapse:collapse; font-size:.875rem; }
    .topic-table th {
      text-align:left; padding:.5rem .75rem;
      border-bottom:2px solid var(--border);
      font-size:.75rem; font-weight:700; color:var(--text-muted); text-transform:uppercase;
    }
    .topic-table td { padding:.55rem .75rem; border-bottom:1px solid var(--border); }
    .topic-table tr:last-child td { border-bottom:none; }
    .topic-table tr:hover td { background:var(--bg); }
    .mini-bar { height:8px; border-radius:999px; display:inline-block; min-width:4px; }
  </style>
</head>
<body>

<!-- Navbar -->
<nav class="navbar">
  <a class="navbar-brand" href="index.html">
    <div class="logo-icon">K</div>
    KUHS QBank <span class="sub">1st Year MBBS</span>
  </a>
  <div class="navbar-links">
    <a href="index.html">Home</a>
    <a href="browse.html">Browse</a>
    <a href="flashcard.html">Flashcards</a>
    <a href="analysis.html" class="active">Analysis</a>
  </div>
</nav>

<main class="page-wrap">
  <div class="container">

    <div style="margin-bottom:2rem">
      <h2>Question Bank Analysis</h2>
      <p style="margin-top:.25rem">Breakdown of KUHS 1st Year previous year questions by subject, type, topic and year.</p>
    </div>

    <!-- Top stat cards -->
    <div class="stat-grid" style="margin-bottom:1.75rem" id="topStats"></div>

    <!-- Row 1 -->
    <div class="analysis-grid" style="margin-bottom:1.5rem">

      <!-- Subject donut -->
      <div class="card">
        <div class="card-title">📚 Questions by Subject</div>
        <div style="display:flex;align-items:center;gap:1.5rem;flex-wrap:wrap;justify-content:center">
          <div class="donut-container">
            <canvas id="subjectDonut"></canvas>
            <div class="donut-center">
              <div class="val" id="totalDot"></div>
              <div class="lbl">Total</div>
            </div>
          </div>
          <div class="legend" id="subjectLegend"></div>
        </div>
      </div>

      <!-- Type donut -->
      <div class="card">
        <div class="card-title">📝 Questions by Type</div>
        <div style="display:flex;align-items:center;gap:1.5rem;flex-wrap:wrap;justify-content:center">
          <div class="donut-container">
            <canvas id="typeDonut"></canvas>
            <div class="donut-center">
              <div class="val" id="typeDot"></div>
              <div class="lbl">Total</div>
            </div>
          </div>
          <div class="legend" id="typeLegend"></div>
        </div>
      </div>
    </div>

    <!-- Row 2 -->
    <div class="analysis-grid" style="margin-bottom:1.5rem">

      <!-- Subject breakdown bars -->
      <div class="card">
        <div class="card-title">🔬 Subject × Type Breakdown</div>
        <div id="subjectTypeBars"></div>
      </div>

      <!-- Year tiles -->
      <div class="card">
        <div class="card-title">📅 Questions by Year</div>
        <div class="year-grid" id="yearGrid"></div>
      </div>
    </div>

    <!-- Full width — top topics -->
    <div class="card">
      <div class="card-title">🗂️ Questions by Topic</div>
      <table class="topic-table" id="topicTable">
        <thead>
          <tr>
            <th>Topic</th>
            <th>Subject</th>
            <th>Count</th>
            <th style="width:40%">Distribution</th>
          </tr>
        </thead>
        <tbody id="topicBody"></tbody>
      </table>
    </div>

  </div>
</main>

<footer class="footer">
  KUHS 1st Year MBBS Question Bank &nbsp;·&nbsp; Built with ❤️ for medical students
</footer>

<script src="data.js"></script>
<script>
// ── Pure-canvas donut chart (no external libraries) ──────
function drawDonut(canvasId, segments) {
  const canvas = document.getElementById(canvasId);
  if (!canvas) return;
  const ctx    = canvas.getContext('2d');
  const W = 180, H = 180;
  canvas.width = W; canvas.height = H;
  const cx = W/2, cy = H/2, R = 78, inner = 52;
  let start = -Math.PI / 2;
  const total = segments.reduce((s, d) => s + d.value, 0);
  segments.forEach(seg => {
    const sweep = (seg.value / total) * 2 * Math.PI;
    ctx.beginPath();
    ctx.moveTo(cx, cy);
    ctx.arc(cx, cy, R, start, start + sweep);
    ctx.closePath();
    ctx.fillStyle = seg.color;
    ctx.fill();
    start += sweep;
  });
  // Cutout
  ctx.beginPath();
  ctx.arc(cx, cy, inner, 0, 2 * Math.PI);
  ctx.fillStyle = '#fff';
  ctx.fill();
}

(function() {
  const total = QUESTIONS.length;
  document.getElementById('totalDot').textContent = total;
  document.getElementById('typeDot').textContent  = total;

  // ── Top stat cards ──────────────────────────────────
  const statData = [
    { label: 'Total Questions', val: total, icon: '📚' },
    { label: 'Subjects',        val: SUBJECTS.length, icon: '🔬' },
    { label: 'Exam Years',      val: YEARS.length, icon: '📅' },
    { label: 'Topics Covered',  val: [...new Set(QUESTIONS.map(q=>q.topic))].filter(Boolean).length, icon: '🗂️' }
  ];
  const statGrid = document.getElementById('topStats');
  statData.forEach(s => {
    statGrid.innerHTML += `
      <div class="stat-card">
        <div style="font-size:1.5rem;margin-bottom:.25rem">${s.icon}</div>
        <div class="stat-value">${s.val}</div>
        <div class="stat-label">${s.label}</div>
      </div>`;
  });

  // ── Subject donut ───────────────────────────────────
  const SUBJ_COLORS = { Anatomy: '#2563eb', Physiology: '#16a34a', Biochemistry: '#7c3aed' };
  const subjCounts  = {};
  SUBJECTS.forEach(s => subjCounts[s] = QUESTIONS.filter(q => q.subject === s).length);

  const subjSegs = SUBJECTS.map(s => ({ value: subjCounts[s], color: SUBJ_COLORS[s] || '#94a3b8' }));
  drawDonut('subjectDonut', subjSegs);

  const subjLegend = document.getElementById('subjectLegend');
  SUBJECTS.forEach(s => {
    subjLegend.innerHTML += `
      <div class="legend-item">
        <div class="legend-dot" style="background:${SUBJ_COLORS[s]}"></div>
        <span><strong>${s}</strong> — ${subjCounts[s]}</span>
      </div>`;
  });

  // ── Type donut ──────────────────────────────────────
  const TYPE_COLORS = { LAQ: '#d97706', SAQ: '#0ea5e9', MCQ: '#ec4899' };
  const typeCounts  = {};
  TYPES.forEach(t => typeCounts[t] = QUESTIONS.filter(q => q.type === t).length);

  const typeSegs = TYPES.map(t => ({ value: typeCounts[t] || 0, color: TYPE_COLORS[t] || '#94a3b8' }));
  drawDonut('typeDonut', typeSegs);

  const typeLegend = document.getElementById('typeLegend');
  TYPES.forEach(t => {
    typeLegend.innerHTML += `
      <div class="legend-item">
        <div class="legend-dot" style="background:${TYPE_COLORS[t]}"></div>
        <span><strong>${t}</strong> — ${typeCounts[t] || 0}</span>
      </div>`;
  });

  // ── Subject × Type bars ─────────────────────────────
  const stBars = document.getElementById('subjectTypeBars');
  SUBJECTS.forEach(s => {
    const sq = QUESTIONS.filter(q => q.subject === s);
    stBars.innerHTML += `<p style="font-size:.8rem;font-weight:700;color:var(--text);margin-bottom:.5rem;margin-top:.75rem">${s}</p>`;
    TYPES.forEach(t => {
      const cnt = sq.filter(q => q.type === t).length;
      const pct = sq.length ? (cnt/sq.length*100).toFixed(0) : 0;
      stBars.innerHTML += `
        <div class="bar-row">
          <div class="bar-label">${t}</div>
          <div class="bar-track"><div class="bar-fill" style="width:${pct}%;background:${TYPE_COLORS[t]}"></div></div>
          <div class="bar-count">${cnt}</div>
        </div>`;
    });
  });

  // ── Year tiles ──────────────────────────────────────
  const yearGrid = document.getElementById('yearGrid');
  const allYears = [...new Set(QUESTIONS.map(q=>q.year))].sort().reverse();
  const maxYearCnt = Math.max(...allYears.map(y => QUESTIONS.filter(q=>q.year===y).length));
  allYears.forEach(y => {
    const cnt = QUESTIONS.filter(q=>q.year===y).length;
    const intensity = cnt / maxYearCnt;
    const bg = `rgba(37,99,235,${0.1 + intensity * 0.7})`;
    yearGrid.innerHTML += `
      <div class="year-tile" style="background:${bg}">
        <div class="year-cnt">${cnt}</div>
        <div class="year-num">${y}</div>
      </div>`;
  });

  // ── Topic table ─────────────────────────────────────
  const topicMap = {};
  QUESTIONS.forEach(q => {
    const key = (q.topic || 'General') + '||' + q.subject;
    topicMap[key] = (topicMap[key] || 0) + 1;
  });
  const topicRows = Object.entries(topicMap)
    .map(([k, v]) => { const [t,s] = k.split('||'); return {topic:t, subject:s, count:v}; })
    .sort((a,b) => b.count - a.count);
  const maxTopicCnt = Math.max(...topicRows.map(r=>r.count));

  const tbody = document.getElementById('topicBody');
  topicRows.forEach(r => {
    const barW = Math.round((r.count / maxTopicCnt) * 100);
    tbody.innerHTML += `
      <tr>
        <td><strong>${r.topic}</strong></td>
        <td><span class="badge badge-${r.subject.toLowerCase()}">${r.subject}</span></td>
        <td>${r.count}</td>
        <td>
          <div class="progress-wrap" style="height:10px">
            <div class="progress-bar" style="width:${barW}%"></div>
          </div>
        </td>
      </tr>`;
  });
})();
</script>
</body>
</html>
Running Command

cd /home/daytona/workspace && ls -la kuhs-qbank/ && zip -r kuhs-qbank.zip kuhs-q…

Running Command

cd /home/daytona/workspace && python3 -c " import zipfile, os files = ['kuhs-qba…

Running Command

cd /home/daytona/workspace && tar -czf kuhs-qbank.tar.gz kuhs-qbank/ && echo "TA…

Your KUHS 1st Year MBBS Question Bank website template is ready! Here's everything that was built:

Download

kuhs qbank.tar

Archive · GZ

Individual files:
  • index

    HTML File · HTML

  • flashcard

    HTML File · HTML

  • browse

    HTML File · HTML

  • analysis

    HTML File · HTML

  • styles

    CSS File · CSS

  • data

    JavaScript File · JS


What's inside

Pages

PageWhat it does
index.htmlHome/setup — choose subjects (Anatomy / Physiology / Biochemistry), question types (LAQ/SAQ/MCQ), exam years, then launch Flashcard or Browse mode
flashcard.html3D flip flashcard mode with confidence buttons (Easy / Hard / Again), MCQ option selection, keyboard shortcuts (Space=flip, ←→=navigate, 1/2/3=rate), session score summary
browse.htmlFull question list with live search, subject/type/year filters, expandable answers per question, "Expand All" per subject group
analysis.htmlStats dashboard — total count cards, subject donut chart, type donut chart, Subject × Type bar breakdown, year heatmap tiles, topic frequency table

Data structure (data.js)

All 23 sample questions cover real KUHS PYQ topics. When you get your actual data, just replace the QUESTIONS array. Each question object looks like:
{
  id: 1,
  subject: "Anatomy",        // Anatomy | Physiology | Biochemistry
  topic: "Upper Limb",
  type: "LAQ",               // LAQ | SAQ | MCQ
  year: "2023",
  question: "Describe...",
  answer: "The brachial plexus...",
  // MCQ only:
  options: ["A","B","C","D"],
  correct: 1                 // 0-based index
}

Deploy to Vercel (free)

  1. Extract the tar.gz
  2. Push the folder to a GitHub repo
  3. Go to vercel.com → New Project → import the repo → Deploy
Done in under 2 minutes, no server needed - it's all static HTML/JS.

What to do next now

Here's your clear next-step roadmap:

Step 1 — Prepare Your Data (Most Important)

Your data is in Excel/CSV. It needs to match the question object format in data.js.
Your CSV should have columns like:
id | subject | topic | type | year | question | answer | option_a | option_b | option_c | option_d | correct
Then share it here — I'll write a converter script that automatically transforms your Excel into the correct QUESTIONS array in data.js. No manual copy-paste needed.

Step 2 — Replace Sample Data

Once converted, you just swap out the QUESTIONS array in data.js with your real questions. Everything else (all 4 pages) updates automatically — no other file changes needed.

Step 3 — Deploy to Vercel (Free, 5 minutes)

  1. Create a free account at vercel.com
  2. Create a free account at github.com
  3. Upload the 6 files to a new GitHub repository
  4. In Vercel → "New Project" → import your GitHub repo → click Deploy
  5. You get a live URL like kuhs-qbank.vercel.app instantly

Optional Improvements I Can Build For You

FeatureWhat it does
Excel → data.js converterUpload your file, get ready-to-use data.js automatically
Mock paper generatorRandomly pick X LAQs + Y SAQs + Z MCQs per subject, printable PDF
Progress trackerSaves your Easy/Hard/Again scores to localStorage between sessions
Topic filter on home pageFilter by sub-topic (e.g. "Upper Limb" only)
Dark modeOne toggle, full dark theme
PWA (offline app)Works without internet, installable on phone

The fastest next action: Share your Excel/CSV file and I'll convert it into data.js immediately. That's the only thing standing between you and a live working site.
Running Command

cd /home/daytona/workspace && python3 -c " import openpyxl wb = openpyxl.load_wo…

Running Command

python3 -c " import openpyxl wb = openpyxl.load_workbook('/home/daytona/workspac…

Running Command

python3 -c " import openpyxl, re wb = openpyxl.load_workbook('/home/daytona/work…

Running Command

python3 -c " import openpyxl, re, json wb = openpyxl.load_workbook('/home/dayto…

Running Command

python3 << 'PYEOF' import openpyxl, re, json wb = openpyxl.load_workbook('/home…

Writing File

~/kuhs-qbank/index.html

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8" />
  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
  <title>KUHS Physiology Question Bank</title>
  <link rel="preconnect" href="https://fonts.googleapis.com" />
  <link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700;800&display=swap" rel="stylesheet" />
  <link rel="stylesheet" href="styles.css" />
  <style>
    .setup-grid { display:grid; grid-template-columns:1fr 1fr; gap:1.5rem; margin-top:2rem; }
    @media(max-width:700px){ .setup-grid { grid-template-columns:1fr; } }
    .setup-card { background:var(--surface); border:1px solid var(--border); border-radius:var(--radius); padding:1.5rem; }
    .setup-card h3 { margin-bottom:1rem; display:flex; align-items:center; gap:.5rem; }
    .cta-row { display:flex; gap:1rem; margin-top:1.5rem; flex-wrap:wrap; }
    .count-banner {
      background:var(--primary-l); border:1px solid #bfdbfe;
      border-radius:var(--radius-sm); padding:.65rem 1rem;
      font-size:.875rem; font-weight:600; color:var(--primary);
      display:flex; align-items:center; gap:.5rem; margin-top:1.25rem;
    }
    .paper-cards { display:grid; grid-template-columns:1fr 1fr; gap:.875rem; margin-bottom:1rem; }
    .paper-card {
      border-radius:var(--radius); padding:1.1rem 1rem; cursor:pointer;
      border:2px solid transparent; transition:var(--transition); text-align:center;
    }
    .paper-card .icon { font-size:1.75rem; margin-bottom:.35rem; }
    .paper-card h4 { font-size:.875rem; font-weight:700; }
    .paper-card p  { font-size:.72rem; margin-top:.2rem; color:var(--text-muted); }
    .paper-card.p1 { background:#dbeafe; }
    .paper-card.p2 { background:#ede9fe; }
    .paper-card.selected { border-color:var(--primary); box-shadow:var(--shadow); transform:translateY(-2px); }

    /* Type pills — all 7 types */
    .type-tag { padding:.35rem .875rem; border-radius:999px; border:2px solid var(--border); font-size:.78rem; font-weight:600; cursor:pointer; transition:var(--transition); background:var(--surface); color:var(--text-muted); user-select:none; }
    .type-tag:hover { border-color:var(--primary); color:var(--primary); }
    .type-tag.selected { background:var(--primary); border-color:var(--primary); color:#fff; }

    /* Year range */
    .year-scroll { display:flex; flex-wrap:wrap; gap:.4rem; max-height:110px; overflow-y:auto; padding-right:4px; }
    .year-scroll::-webkit-scrollbar { width:4px; }
    .year-scroll::-webkit-scrollbar-thumb { background:var(--border); border-radius:4px; }
  </style>
</head>
<body>

<nav class="navbar">
  <a class="navbar-brand" href="index.html">
    <div class="logo-icon">K</div>
    KUHS QBank <span class="sub">Physiology · 1st Year MBBS</span>
  </a>
  <div class="navbar-links">
    <a href="index.html" class="active">Home</a>
    <a href="browse.html">Browse</a>
    <a href="flashcard.html">Flashcards</a>
    <a href="analysis.html">Analysis</a>
  </div>
</nav>

<!-- Hero -->
<div class="hero">
  <div class="container">
    <h1>KUHS Physiology<br>Previous Year Questions</h1>
    <p>Kerala University of Health Sciences · 1st Year MBBS · 2015–2026</p>
    <div class="badge-wrap" id="heroBadges">
      <span class="hero-badge" id="hb-total">— Questions</span>
      <span class="hero-badge">📄 Paper I & II</span>
      <span class="hero-badge">🗓️ 2015–2026</span>
    </div>
  </div>
</div>

<main class="page-wrap">
  <div class="container">
    <div class="setup-grid">

      <!-- Left: Paper + Type -->
      <div class="setup-card">
        <h3>📄 Paper Selection</h3>
        <div class="paper-cards" id="paperCards">
          <div class="paper-card p1 selected" data-paper="Physiology Paper I">
            <div class="icon">📘</div>
            <h4>Paper I</h4>
            <p id="cnt-p1">— Qs</p>
          </div>
          <div class="paper-card p2 selected" data-paper="Physiology Paper II">
            <div class="icon">📗</div>
            <h4>Paper II</h4>
            <p id="cnt-p2">— Qs</p>
          </div>
        </div>

        <h3 style="margin-top:1rem">📝 Question Type</h3>
        <div class="tag-group" id="typeTags" style="gap:.4rem"></div>
      </div>

      <!-- Right: Year + Mode -->
      <div class="setup-card">
        <h3>🗓️ Exam Years</h3>
        <div style="display:flex;gap:.5rem;margin-bottom:.75rem">
          <button class="btn btn-ghost btn-sm" id="selAllYears">Select All</button>
          <button class="btn btn-ghost btn-sm" id="clearYears">Clear</button>
        </div>
        <div class="year-scroll" id="yearTags"></div>

        <h3 style="margin-top:1.25rem">🎯 Study Mode</h3>
        <div class="mode-toggle">
          <button class="mode-btn active" id="modeFlash">🃏 Flashcards</button>
          <button class="mode-btn" id="modeBrowse">📋 Browse All</button>
        </div>

        <div class="count-banner">
          <span>✅</span>
          <span id="selCount">0</span> questions selected
        </div>

        <div class="cta-row">
          <button class="btn btn-primary btn-lg" id="startBtn">Start Studying →</button>
          <a href="browse.html" class="btn btn-ghost btn-lg">Browse First</a>
        </div>
      </div>

    </div>
  </div>
</main>

<footer class="footer">
  KUHS Physiology Question Bank · 1st Year MBBS · Built with ❤️ for medical students
</footer>

<script src="data.js"></script>
<script>
(function(){
  // Counts
  const p1 = QUESTIONS.filter(q=>q.paper==='Physiology Paper I').length;
  const p2 = QUESTIONS.filter(q=>q.paper==='Physiology Paper II').length;
  document.getElementById('cnt-p1').textContent = p1 + ' Qs';
  document.getElementById('cnt-p2').textContent = p2 + ' Qs';
  document.getElementById('hb-total').textContent = QUESTIONS.length + ' Questions';

  const selPapers = new Set(PAPERS);
  const selTypes  = new Set(TYPES);
  const selYears  = new Set(YEARS);
  let studyMode   = 'flashcard';

  // Type tags
  const typeTagsEl = document.getElementById('typeTags');
  TYPES.forEach(t => {
    const span = document.createElement('span');
    span.className = 'type-tag selected';
    span.dataset.type = t;
    span.textContent = TYPE_LABELS[t] || t;
    typeTagsEl.appendChild(span);
  });

  // Year tags
  const yearTagsEl = document.getElementById('yearTags');
  YEARS.forEach(y => {
    const span = document.createElement('span');
    span.className = 'tag selected'; span.dataset.year = y; span.textContent = y;
    yearTagsEl.appendChild(span);
  });

  function updateCount() {
    const n = filterQuestions({ papers:[...selPapers], types:[...selTypes], years:[...selYears] }).length;
    document.getElementById('selCount').textContent = n;
  }
  updateCount();

  // Paper cards
  document.getElementById('paperCards').addEventListener('click', e => {
    const card = e.target.closest('[data-paper]');
    if (!card) return;
    const p = card.dataset.paper;
    if (selPapers.has(p)) { selPapers.delete(p); card.classList.remove('selected'); }
    else                   { selPapers.add(p);    card.classList.add('selected');    }
    updateCount();
  });

  // Type tags
  typeTagsEl.addEventListener('click', e => {
    const tag = e.target.closest('[data-type]');
    if (!tag) return;
    const t = tag.dataset.type;
    if (selTypes.has(t)) { selTypes.delete(t); tag.classList.remove('selected'); }
    else                  { selTypes.add(t);    tag.classList.add('selected');    }
    updateCount();
  });

  // Year tags
  yearTagsEl.addEventListener('click', e => {
    const tag = e.target.closest('[data-year]');
    if (!tag) return;
    const y = tag.dataset.year;
    if (selYears.has(y)) { selYears.delete(y); tag.classList.remove('selected'); }
    else                  { selYears.add(y);    tag.classList.add('selected');    }
    updateCount();
  });

  document.getElementById('selAllYears').addEventListener('click', () => {
    YEARS.forEach(y => selYears.add(y));
    yearTagsEl.querySelectorAll('[data-year]').forEach(t => t.classList.add('selected'));
    updateCount();
  });
  document.getElementById('clearYears').addEventListener('click', () => {
    selYears.clear();
    yearTagsEl.querySelectorAll('[data-year]').forEach(t => t.classList.remove('selected'));
    updateCount();
  });

  // Mode
  document.getElementById('modeFlash').addEventListener('click', () => {
    studyMode = 'flashcard';
    document.getElementById('modeFlash').classList.add('active');
    document.getElementById('modeBrowse').classList.remove('active');
  });
  document.getElementById('modeBrowse').addEventListener('click', () => {
    studyMode = 'browse';
    document.getElementById('modeBrowse').classList.add('active');
    document.getElementById('modeFlash').classList.remove('active');
  });

  // Start
  document.getElementById('startBtn').addEventListener('click', () => {
    const params = new URLSearchParams({
      papers: [...selPapers].join(','),
      types : [...selTypes].join(','),
      years : [...selYears].join(',')
    });
    window.location.href = (studyMode === 'flashcard' ? 'flashcard.html' : 'browse.html') + '?' + params;
  });
})();
</script>
</body>
</html>
Running Command

cat > /home/daytona/workspace/kuhs-qbank/index.html << 'HTMLEOF' <!DOCTYPE html>…

Running Command

cat > /home/daytona/workspace/kuhs-qbank/browse.html << 'HTMLEOF' <!DOCTYPE html…

Running Command

cat > /home/daytona/workspace/kuhs-qbank/flashcard.html << 'HTMLEOF' <!DOCTYPE h…

Running Command

cat > /home/daytona/workspace/kuhs-qbank/analysis.html << 'HTMLEOF' <!DOCTYPE ht…

Running Command

cd /home/daytona/workspace && python3 -c " import zipfile, os files = [ ('k…

Running Command

cd /home/daytona/workspace && python3 -c " import zipfile, os files = [ 'ku…

Running Command

cd /home/daytona/workspace && tar -czf kuhs-physiology.tar.gz kuhs-qbank/ && ls …

All 6 files packed into a 62KB archive. Here's everything ready:

Download Your Site

kuhs physiology.tar

Archive · GZ

(62 KB — all 6 files)
Individual files:
  • data

    JavaScript File · JS

    — all 1070 questions
  • index

    HTML File · HTML

  • browse

    HTML File · HTML

  • flashcard

    HTML File · HTML

  • analysis

    HTML File · HTML


What was converted from your Excel

FieldWhat it maps to
Questionquestion text
Typetype — Long Essay→LAQ, Short Essay→SEQ, Short Answer→SAQ, MCQ→MCQ, One Word Answer→OWA, Draw & Label→DNL, Physiological Basis→PB
Chapterpaper — Paper I / Paper II
Year/File NameyearFull (full string) + year (just the number e.g. 2023)
Times Repeatedrepeated — drives the 🔥 badge on hot questions

New features added for your data

  • 🔥 Hot Questions — any question repeated ≥2 times shows a fire badge with the count
  • "🔥 Hot Qs Only" button in flashcard mode — instantly filter to just repeated questions for fast revision
  • Paper I / Paper II filter on every page
  • Full year string shown (e.g. "August 2023 [QP113001]") on every card
  • Analysis page shows: type donut, Paper I vs II donut, year heatmap (click a year → browse that year), top 30 most-repeated questions table
  • Sort by Most Repeated in Browse mode

Next step: Extract the tar.gz, push to GitHub, deploy to Vercel. When you're ready to add Anatomy and Biochemistry files, just share those and I'll merge them in.

Show me how to deploy this to Vercel (step-by-step)

Here's the complete step-by-step guide. No coding knowledge needed.

Step 1 — Extract the files

Download kuhs-physiology.tar.gz and extract it.
  • Windows: Right-click → "Extract All" (or use 7-Zip if Windows can't open .tar.gz)
  • Mac: Double-click the file — it extracts automatically
  • Result: A folder called kuhs-qbank with 6 files inside

Step 2 — Create a GitHub account (if you don't have one)

Go to github.com → Sign up (it's free)

Step 3 — Create a new GitHub repository

  1. Click the + button (top right) → New repository
  2. Name it: kuhs-qbank (or anything you like)
  3. Set it to Public
  4. Leave everything else as default
  5. Click Create repository

Step 4 — Upload your files to GitHub

On the new repo page:
  1. Click "uploading an existing file" (shown in the empty repo)
  2. Drag and drop all 6 files from your kuhs-qbank folder:
    • index.html
    • browse.html
    • flashcard.html
    • analysis.html
    • styles.css
    • data.js
  3. Scroll down → Click "Commit changes"
Your files are now on GitHub.

Step 5 — Create a Vercel account

Go to vercel.com → Click Sign Up → Choose "Continue with GitHub" (this links both accounts automatically)

Step 6 — Deploy on Vercel

  1. On the Vercel dashboard, click "Add New Project"
  2. You'll see your GitHub repos listed — find kuhs-qbank and click "Import"
  3. On the next screen, don't change anything — just click "Deploy"
  4. Wait ~30 seconds ⏳
  5. 🎉 Done! Vercel gives you a live URL like:
https://kuhs-qbank.vercel.app

Step 7 — Share your live site

Copy that URL and share it with your classmates. It works on mobile and desktop, no app install needed.

Updating the site later

Whenever you want to add more questions or fix something:
  1. Edit the files locally
  2. Go back to your GitHub repo
  3. Click on the file → pencil icon (Edit) → paste new content → Commit
  4. Vercel automatically redeploys within 30 seconds — no manual steps needed

Optional — Custom domain (e.g. kuhsphysiology.com)

If you want a custom domain name instead of .vercel.app:
  • Buy a domain from Namecheap (~$10/year)
  • In Vercel → your project → Settings → Domains → add your domain
  • Follow the DNS instructions Vercel provides

That's it. The whole process takes about 10 minutes and everything (GitHub + Vercel) is completely free.
attachment
attachment

Got this

I can see exactly what happened. Your site is deployed at kuhsqbank.vercel.app (Status: Ready ✅) but it's showing a 404 NOT_FOUND error.
The problem: Vercel can't find your index.html because the files were uploaded inside a subfolder (kuhs-qbank/) instead of at the root level of the repository.

Fix — 2 options:


Option A: Re-upload files directly (Easiest)

  1. Go to your GitHub repo (github.com → your kuhsqbank repo)
  2. You'll see a folder called kuhs-qbankclick into it
  3. You should see the 6 files inside (index.html, data.js, etc.)
  4. Move them to root: Click each file → Edit (pencil icon) → change the filename at the top from kuhs-qbank/index.html to just index.html → Commit
OR the faster way:
  1. Delete the repo entirely (Settings → scroll down → Delete repository)
  2. Create a fresh repo
  3. This time upload the 6 individual files directly — NOT the whole kuhs-qbank folder
  4. Vercel auto-redeploys and the 404 is gone

Option B: Tell Vercel where your files are (No re-upload needed)

  1. In Vercel → your kuhsqbank project → Settings
  2. Click "General"
  3. Find "Root Directory"
  4. Type: kuhs-qbank
  5. Click Save
  6. Go to Deployments → click the three dots on the latest deployment → Redeploy
✅ Done — your site will load correctly.

Option B is faster since your files are already uploaded. Try that first!
attachment
attachment

Where is root directorty

You're on the right page! The Root Directory is under "Build and Deployment" — not General.
Here's exactly what to do:

Steps:

1. Click "Build and Deployment" in the left sidebar (you can see it right below General)
2. Scroll down — you'll see a section called "Root Directory"
3. Click the text box next to it and type:
kuhs-qbank
4. Click Save
5. Now go back to the left sidebar → click Deployments
6. Click the three dots (...) next to the latest deployment → click Redeploy
7. Wait ~20 seconds → click Visit → your site should be live! ✅

That's it. The Root Directory setting tells Vercel "look inside the kuhs-qbank folder for index.html" instead of looking at the top level of the repo.

There is no deployments but built and depoyments is there

No problem! Here's what to do:

1. Click "Build and Deployment" in the left sidebar
2. Scroll down until you see "Root Directory" — type kuhs-qbank and click Save
3. Now go back to the Overview page — click the ← back arrow at the top of the sidebar (next to "Settings")
4. On the Overview page you'll see your deployment with a "Visit" button and next to it a small dropdown arrow ▼
5. Click that dropdown arrow ▼ next to the Visit button → you'll see "Redeploy"
6. Click Redeploy → confirm → wait 20 seconds → click Visit

Like this:
Overview page
    ↓
Production Deployment card
    ↓
[ Visit ▼ ]  ← click the ▼ arrow
    ↓
Redeploy  ← click this
Your site will be live at kuhsqbank.vercel.app ✅The error says "kuhsqbank" does not exist — meaning the folder name in your GitHub repo is different from what you typed.
The folder is actually called kuhs-qbank (with a hyphen) but you may have typed kuhsqbank (no hyphen).

Fix in 3 steps:

1. Go to Settings → Build and Deployment → Root Directory
2. Clear what's there and type exactly:
kuhs-qbank
⚠️ Make sure there's a hyphen ( - ) between kuhs and qbank
3. Click Save → then Redeploy

But first — let's confirm the exact folder name. Go to your GitHub repo and tell me what the folder is called, or send a screenshot. It will be one of these:
  • kuhs-qbank ← with hyphen
  • kuhs_qbank ← with underscore
  • kuhsqbank ← no separator
Once we match the Root Directory exactly to the folder name, it will work. ✅

I have tried with hypen also it shows same

The Root Directory approach isn't working. The easiest fix is to clear the Root Directory and instead move the files to the root of your GitHub repo directly.

Fix — Do this now:

Step 1 — Clear the Root Directory setting

  1. Settings → Build and Deployment → Root Directory
  2. Delete everything — leave it completely blank
  3. Click Save

Step 2 — Check your GitHub repo structure

Go to your GitHub repo. Tell me what you see:
  • Option A: You see a folder called kuhs-qbank (click it to see files inside) — this is the problem
  • Option B: You see the 6 files directly (index.html, data.js, etc.) — files are already at root

If Option A (folder exists):

You need to move files to root. Here's the quickest way:
  1. Go to your GitHub repo → click the kuhs-qbank folder → click index.html
  2. Click the pencil icon (Edit)
  3. At the top you'll see the filename path — change kuhs-qbank/index.html to just index.html
  4. Scroll down → Commit changes
  5. Repeat for all 6 files: browse.html, flashcard.html, analysis.html, styles.css, data.js
  6. Then delete the empty kuhs-qbank folder
After that Vercel will auto-redeploy and it will work. ✅

Quickest alternative: Send me a screenshot of your GitHub repo page and I'll tell you exactly what to do in one step.
attachment
attachment

I see this

I can see the problem clearly now! You uploaded the tar.gz file itself to GitHub instead of the individual HTML/CSS/JS files inside it. GitHub can't serve a compressed archive.
You need to upload the 6 actual files. Here's exactly what to do:

Fix — Step by step:

Step 1 — Delete the tar.gz from GitHub

  1. Click on kuhs-physiology.tar.gz in your repo
  2. Click the three dots (...) top right → Delete file
  3. Click Commit changes

Step 2 — Upload the 6 real files

First, you need the files extracted on your iPad/device. Since you're on iPad, the easiest way:
On iPad:
  1. Go to the Files app
  2. Find kuhs-physiology.tar.gz in Downloads
  3. Tap and hold it → tap "Uncompress" (iOS can open tar.gz natively)
  4. You'll get a folder with the 6 files inside
Then back on GitHub:
  1. Click the "+" button"Upload files"
  2. Upload all 6 files:
    • index.html
    • browse.html
    • flashcard.html
    • analysis.html
    • styles.css
    • data.js
  3. Click Commit changes

Step 3 — Fix Root Directory in Vercel

Go to Vercel → Settings → Build and Deployment → Root Directory → clear it completely (leave blank) → Save → Redeploy
Vercel will find index.html at the root and your site goes live ✅

Can't open tar.gz on iPad? Let me know and I'll create a direct download of each file separately instead.
attachment
attachment

Got this again

The files still aren't at the root of your repo. Let me fix this a completely different way — I'll create a single downloadable HTML file for each page so you can upload them one by one directly from your iPad browser.
Go to your GitHub repo right now and send me a screenshot of what files you see there. I need to see exactly what's in the repo before telling you the next step.
Also — the fastest fix right now:

Easiest solution — Upload files directly from this chat

I'll give you direct download links for each individual file. On your iPad:
  1. Click each link below → it opens in browser → tap Share → Save to Files
  2. Then go to GitHub → Upload files → pick all 6 from Files app
Here are your 6 files as individual downloads:
  • index

    HTML File · HTML

  • browse

    HTML File · HTML

  • flashcard

    HTML File · HTML

  • analysis

    HTML File · HTML

  • styles

    CSS File · CSS

  • data

    JavaScript File · JS


Then on GitHub:
  1. Delete all existing files in the repo (including the tar.gz)
  2. Click "Add file" → "Upload files"
  3. Upload all 6 files above
  4. Commit
On Vercel — make sure Root Directory is blank/empty → Redeploy → ✅ done.Your credits are exhausted. You can purchase a top-up or subscribe to a plan to continue using Orris. If you're already subscribed, your daily credits will reset tomorrow.
attachment
attachment

This is what I see at github

Your credits are exhausted. You can purchase a top-up or subscribe to a plan to continue using Orris. If you're already subscribed, your daily credits will reset tomorrow.
This is a shared conversation. Sign in to Orris to start your own chat.