diff --git a/README.md b/README.md
index 1748f0a..d22892d 100644
--- a/README.md
+++ b/README.md
@@ -1,2 +1,29 @@
-# PathFinder
-help to find path for new students
+# PathFinder AI
+
+PathFinder AI helps students choose the stream/career path that best fits them: Frontend, Backend, DevOps, Data Science, UI/UX, and Mobile Development.
+
+## What you get
+- 18 meaningful questions (10 core + 8 adaptive) instead of random quiz items
+- Adaptive logic: after core answers, follow-up questions are selected from your top 2 fields
+- ML-style percentage prediction for each stream
+- Personalized roadmap for top matches
+- Resource manager where you can add/remove your own links per stream (saved in localStorage)
+- Daily study-time aware roadmap pacing estimate
+
+## Train model on internet data
+You can train/update model bias weights from a public internet dataset (Stack Overflow survey) and feed them into the website.
+
+```bash
+python3 scripts/train_model.py
+```
+
+This creates/updates:
+- `data/model_weights.json`
+
+The app auto-loads this file at startup.
+
+## Run locally
+```bash
+python3 -m http.server 8000
+```
+Then open `http://localhost:8000`.
diff --git a/app.js b/app.js
new file mode 100644
index 0000000..dd97e7c
--- /dev/null
+++ b/app.js
@@ -0,0 +1,326 @@
+const fields = ["Frontend", "Backend", "DevOps", "Data Science", "UI/UX", "Mobile Development"];
+
+const likertOptions = [
+ { label: "Strongly Disagree", value: 1 },
+ { label: "Disagree", value: 2 },
+ { label: "Neutral", value: 3 },
+ { label: "Agree", value: 4 },
+ { label: "Strongly Agree", value: 5 }
+];
+
+const coreQuestions = [
+ { text: "I enjoy building visual interfaces users can directly interact with.", weights: { "Frontend": 1.2, "UI/UX": 1.1 } },
+ { text: "I like designing APIs and backend logic.", weights: { "Backend": 1.3, "DevOps": 0.4 } },
+ { text: "I am curious about data analysis and prediction models.", weights: { "Data Science": 1.4, "Backend": 0.4 } },
+ { text: "I enjoy automation, deployment and infrastructure work.", weights: { "DevOps": 1.4, "Backend": 0.5 } },
+ { text: "I care deeply about user behavior and product usability.", weights: { "UI/UX": 1.4, "Frontend": 0.5 } },
+ { text: "I want to build apps that run smoothly on mobile devices.", weights: { "Mobile Development": 1.5, "Frontend": 0.5 } },
+ { text: "I like solving complex bugs in layered systems.", weights: { "Backend": 0.8, "DevOps": 0.8, "Data Science": 0.4 } },
+ { text: "I prefer work that gives quick visible output.", weights: { "Frontend": 1.0, "Mobile Development": 0.8, "UI/UX": 0.7 } },
+ { text: "I am comfortable with statistics and mathematical reasoning.", weights: { "Data Science": 1.4, "Backend": 0.3 } },
+ { text: "I often think about scalability and reliability.", weights: { "DevOps": 1.1, "Backend": 1.0 } }
+];
+
+const adaptiveQuestions = {
+ "Frontend": [
+ { text: "I enjoy optimizing accessibility and browser compatibility.", weights: { "Frontend": 1.3, "UI/UX": 0.5 } },
+ { text: "I like component-based frameworks like React/Vue.", weights: { "Frontend": 1.2, "Mobile Development": 0.3 } },
+ { text: "I care about web performance metrics and Lighthouse scores.", weights: { "Frontend": 1.2, "DevOps": 0.3 } },
+ { text: "I enjoy translating design mockups into responsive pages.", weights: { "Frontend": 1.3, "UI/UX": 0.4 } }
+ ],
+ "Backend": [
+ { text: "I enjoy database schema design and data modeling.", weights: { "Backend": 1.3, "Data Science": 0.4 } },
+ { text: "I am interested in authentication and API security.", weights: { "Backend": 1.3, "DevOps": 0.4 } },
+ { text: "I like optimizing queries and reducing response latency.", weights: { "Backend": 1.2, "DevOps": 0.4 } },
+ { text: "I enjoy writing services consumed by web and mobile clients.", weights: { "Backend": 1.2, "Mobile Development": 0.4 } }
+ ],
+ "DevOps": [
+ { text: "I am excited by CI/CD pipelines and release automation.", weights: { "DevOps": 1.4, "Backend": 0.4 } },
+ { text: "I enjoy cloud infrastructure setup and monitoring.", weights: { "DevOps": 1.4 } },
+ { text: "I like containers and orchestration (Docker/Kubernetes).", weights: { "DevOps": 1.5 } },
+ { text: "I prefer improving developer productivity and deployment speed.", weights: { "DevOps": 1.3, "Frontend": 0.2 } }
+ ],
+ "Data Science": [
+ { text: "I like cleaning noisy datasets before modeling.", weights: { "Data Science": 1.2, "Backend": 0.3 } },
+ { text: "I enjoy feature engineering and model evaluation.", weights: { "Data Science": 1.4 } },
+ { text: "I am interested in data visualization and storytelling.", weights: { "Data Science": 1.2, "UI/UX": 0.4 } },
+ { text: "I want to build prediction systems from real-world behavior data.", weights: { "Data Science": 1.5 } }
+ ],
+ "UI/UX": [
+ { text: "I enjoy mapping user journeys and pain points.", weights: { "UI/UX": 1.4, "Frontend": 0.4 } },
+ { text: "I like creating design systems and reusable UI patterns.", weights: { "UI/UX": 1.3, "Frontend": 0.5 } },
+ { text: "I value usability testing and iterative feedback loops.", weights: { "UI/UX": 1.4 } },
+ { text: "I can explain design decisions with user evidence.", weights: { "UI/UX": 1.3, "Data Science": 0.3 } }
+ ],
+ "Mobile Development": [
+ { text: "I like designing interactions for smaller touch screens.", weights: { "Mobile Development": 1.4, "UI/UX": 0.4 } },
+ { text: "I enjoy working with app lifecycle and performance constraints.", weights: { "Mobile Development": 1.3, "Backend": 0.3 } },
+ { text: "I am interested in integrating device features like camera/GPS.", weights: { "Mobile Development": 1.4 } },
+ { text: "I want to publish apps and improve them using user reviews.", weights: { "Mobile Development": 1.3, "Frontend": 0.2 } }
+ ]
+};
+
+const roadmaps = {
+ "Frontend": ["HTML/CSS basics", "JavaScript + TypeScript", "React/Vue", "State/API integration", "Testing + accessibility"],
+ "Backend": ["Language fundamentals", "DB + SQL", "API architecture", "Security", "Scaling"],
+ "DevOps": ["Linux + networking", "Git + CI/CD", "Containers", "Cloud", "Observability"],
+ "Data Science": ["Python + SQL", "Statistics", "Data preprocessing", "ML modeling", "Deployment/MLOps"],
+ "UI/UX": ["UX principles", "Wireframing", "Research methods", "Design systems", "Developer handoff"],
+ "Mobile Development": ["Flutter/React Native or native stack", "Architecture", "State/API", "Performance", "Release + analytics"]
+};
+
+const defaultResources = {
+ "Frontend": ["https://developer.mozilla.org/en-US/docs/Web", "https://roadmap.sh/frontend"],
+ "Backend": ["https://roadmap.sh/backend", "https://www.postman.com/api-platform/api-testing/"],
+ "DevOps": ["https://roadmap.sh/devops", "https://docs.docker.com/get-started/"],
+ "Data Science": ["https://www.kaggle.com/learn", "https://scikit-learn.org/stable/"],
+ "UI/UX": ["https://www.nngroup.com/articles/", "https://www.figma.com/resource-library/"],
+ "Mobile Development": ["https://docs.flutter.dev/", "https://developer.android.com/courses"]
+};
+
+const defaultBias = {
+ "Frontend": 1,
+ "Backend": 1,
+ "DevOps": 1,
+ "Data Science": 1,
+ "UI/UX": 1,
+ "Mobile Development": 1
+};
+
+let modelBias = { ...defaultBias };
+let studentName = "";
+let dailyTime = 1;
+let questions = [];
+let answers = [];
+let idx = 0;
+
+const setupForm = document.getElementById("setupForm");
+const setupCard = document.getElementById("setupCard");
+const quizCard = document.getElementById("quizCard");
+const resultCard = document.getElementById("resultCard");
+const modelStatus = document.getElementById("modelStatus");
+const questionType = document.getElementById("questionType");
+const questionCounter = document.getElementById("questionCounter");
+const questionText = document.getElementById("questionText");
+const optionsWrap = document.getElementById("options");
+const prevBtn = document.getElementById("prevBtn");
+const nextBtn = document.getElementById("nextBtn");
+const resultTitle = document.getElementById("resultTitle");
+const resultMeta = document.getElementById("resultMeta");
+const scoreGrid = document.getElementById("scoreGrid");
+const roadmapWrap = document.getElementById("roadmapWrap");
+const restartBtn = document.getElementById("restartBtn");
+const resourceManager = document.getElementById("resourceManager");
+
+bootstrap();
+
+async function bootstrap() {
+ await loadModelWeights();
+ renderResourceManager();
+}
+
+async function loadModelWeights() {
+ try {
+ const response = await fetch("./data/model_weights.json", { cache: "no-store" });
+ if (!response.ok) throw new Error("weights file missing");
+ const data = await response.json();
+ if (data.field_bias && typeof data.field_bias === "object") {
+ modelBias = { ...defaultBias, ...data.field_bias };
+ modelStatus.textContent = `ML weights loaded (${data.source || "trained model"})`;
+ modelStatus.classList.add("ready");
+ return;
+ }
+ throw new Error("invalid weights structure");
+ } catch (_error) {
+ modelStatus.textContent = "Using default weights. Run scripts/train_model.py to train on internet dataset.";
+ }
+}
+
+setupForm.addEventListener("submit", (event) => {
+ event.preventDefault();
+ studentName = document.getElementById("studentName").value.trim();
+ dailyTime = Number(document.getElementById("dailyTime").value);
+
+ questions = [...coreQuestions];
+ answers = Array(18).fill(null);
+ idx = 0;
+
+ setupCard.classList.add("hidden");
+ quizCard.classList.remove("hidden");
+ renderQuestion();
+});
+
+prevBtn.addEventListener("click", () => {
+ if (idx === 0) return;
+ idx -= 1;
+ renderQuestion();
+});
+
+nextBtn.addEventListener("click", () => {
+ if (answers[idx] == null) return;
+
+ if (idx === coreQuestions.length - 1 && questions.length === coreQuestions.length) {
+ const top2 = getTopFields(calculateRawScores(answers.slice(0, 10), coreQuestions), 2);
+ questions = [...coreQuestions, ...top2.flatMap((f) => adaptiveQuestions[f].slice(0, 4))];
+ }
+
+ if (idx < questions.length - 1) {
+ idx += 1;
+ renderQuestion();
+ } else {
+ renderResults();
+ }
+});
+
+restartBtn.addEventListener("click", () => window.location.reload());
+
+function renderQuestion() {
+ const q = questions[idx];
+ questionType.textContent = idx < 10 ? "Core Question" : "Adaptive Question";
+ questionCounter.textContent = `${idx + 1}/${questions.length}`;
+ questionText.textContent = q.text;
+ prevBtn.disabled = idx === 0;
+
+ optionsWrap.innerHTML = "";
+ likertOptions.forEach((opt) => {
+ const button = document.createElement("button");
+ button.type = "button";
+ button.textContent = opt.label;
+ if (answers[idx] === opt.value) button.classList.add("active");
+ button.addEventListener("click", () => {
+ answers[idx] = opt.value;
+ renderQuestion();
+ });
+ optionsWrap.appendChild(button);
+ });
+}
+
+function calculateRawScores(answerList, questionList) {
+ const scores = Object.fromEntries(fields.map((f) => [f, 0]));
+ questionList.forEach((q, i) => {
+ const val = Number(answerList[i] ?? 3);
+ Object.entries(q.weights).forEach(([field, w]) => {
+ scores[field] += val * w;
+ });
+ });
+ return scores;
+}
+
+function getTopFields(scoreMap, take = 2) {
+ return Object.entries(scoreMap)
+ .sort((a, b) => b[1] - a[1])
+ .slice(0, take)
+ .map(([field]) => field);
+}
+
+function normalizeWithModel(rawScores) {
+ const logits = fields.map((f) => (rawScores[f] || 0) + (modelBias[f] || 0));
+ const expVals = logits.map((v) => Math.exp(v / 16));
+ const sum = expVals.reduce((a, b) => a + b, 0);
+ return fields
+ .map((field, i) => ({ field, percent: Math.round((expVals[i] / sum) * 100) }))
+ .sort((a, b) => b.percent - a.percent);
+}
+
+function estimateWeeksPerStep() {
+ const baseHoursPerStep = 70;
+ return Math.max(1, Math.round(baseHoursPerStep / (dailyTime * 7)));
+}
+
+function renderResults() {
+ quizCard.classList.add("hidden");
+ resultCard.classList.remove("hidden");
+
+ const raw = calculateRawScores(answers.slice(0, questions.length), questions);
+ const ranked = normalizeWithModel(raw);
+ const top3 = ranked.slice(0, 3);
+
+ resultTitle.textContent = `${studentName}, your stream match report`;
+ resultMeta.textContent = `Predicted with weighted assessment + trained model bias. Suggested pace: ~${estimateWeeksPerStep()} week(s) per roadmap step.`;
+
+ scoreGrid.innerHTML = ranked
+ .map(({ field, percent }) => `
+
+ ${field}
+ ${percent}% interest
+
+
+ `)
+ .join("");
+
+ roadmapWrap.innerHTML = top3
+ .map(({ field }) => {
+ const steps = roadmaps[field].map((s) => `
${s}`).join("");
+ const links = [...defaultResources[field], ...getCustomResources(field)]
+ .map((url) => `${url}`)
+ .join("");
+ return `
+
+ ${field}
+ ${steps}
+ Resources
+
+
+ `;
+ })
+ .join("");
+}
+
+function keyFor(field) {
+ return `pathfinder_resources_${field}`;
+}
+
+function getCustomResources(field) {
+ return JSON.parse(localStorage.getItem(keyFor(field)) || "[]");
+}
+
+function addCustomResource(field, url) {
+ const items = getCustomResources(field);
+ items.push(url);
+ localStorage.setItem(keyFor(field), JSON.stringify(items));
+}
+
+function removeCustomResource(field, index) {
+ const items = getCustomResources(field);
+ items.splice(index, 1);
+ localStorage.setItem(keyFor(field), JSON.stringify(items));
+}
+
+function renderResourceManager() {
+ resourceManager.innerHTML = "";
+ fields.forEach((field) => {
+ const node = document.getElementById("resourceTemplate").content.cloneNode(true);
+ const box = node.querySelector(".resource-box");
+ const heading = box.querySelector("h4");
+ const list = box.querySelector(".resource-list");
+ const form = box.querySelector(".resource-form");
+
+ heading.textContent = field;
+
+ const paint = () => {
+ const custom = getCustomResources(field);
+ const defaults = defaultResources[field].map((url) => `${url} (default)`);
+ const userLinks = custom.map((url, i) => `${url} `);
+ list.innerHTML = [...defaults, ...userLinks].join("");
+
+ list.querySelectorAll("button[data-remove]").forEach((btn) => {
+ btn.addEventListener("click", () => {
+ removeCustomResource(field, Number(btn.getAttribute("data-remove")));
+ paint();
+ });
+ });
+ };
+
+ form.addEventListener("submit", (event) => {
+ event.preventDefault();
+ const url = new FormData(form).get("url").toString().trim();
+ if (!url) return;
+ addCustomResource(field, url);
+ form.reset();
+ paint();
+ });
+
+ paint();
+ resourceManager.appendChild(node);
+ });
+}
diff --git a/data/model_weights.json b/data/model_weights.json
new file mode 100644
index 0000000..25b9fc3
--- /dev/null
+++ b/data/model_weights.json
@@ -0,0 +1,13 @@
+{
+ "source": "fallback-sample (network blocked)",
+ "trained_at": "2026-02-23T16:05:25.705445+00:00",
+ "rows": 14,
+ "field_bias": {
+ "Frontend": 2.316,
+ "Backend": 2.316,
+ "DevOps": 2.316,
+ "Data Science": 2.316,
+ "UI/UX": 2.036,
+ "Mobile Development": 2.316
+ }
+}
\ No newline at end of file
diff --git a/index.html b/index.html
new file mode 100644
index 0000000..cf7369a
--- /dev/null
+++ b/index.html
@@ -0,0 +1,90 @@
+
+
+
+
+
+ PathFinder AI | Student Career Stream Predictor
+
+
+
+
+
+
+
+ How this works
+
+ - 18 connected questions (10 core + 8 adaptive questions)
+ - Weighted ML scoring across Frontend, Backend, DevOps, Data Science, UI/UX, and Mobile
+ - Roadmap + resources based on your top matches
+ - You can add your own links/resources any time
+
+ Loading ML weights…
+
+
+
+
+
+
+
Core Question
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Your top roadmaps
+
+
+
+
+
+ Manage your own resources
+ Add links for each stream. These are saved in your browser.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/scripts/train_model.py b/scripts/train_model.py
new file mode 100644
index 0000000..b12c5f4
--- /dev/null
+++ b/scripts/train_model.py
@@ -0,0 +1,107 @@
+#!/usr/bin/env python3
+"""Train simple PathFinder field-bias weights from internet data.
+
+Data source:
+- Stack Overflow Developer Survey 2023 (public CSV hosted on GitHub)
+
+The script learns field prevalence from role labels and writes `data/model_weights.json`.
+This is intentionally lightweight so the frontend can load the artifact statically.
+"""
+
+from __future__ import annotations
+
+import csv
+import json
+import math
+import pathlib
+import urllib.request
+from collections import Counter
+from datetime import datetime, timezone
+
+DATA_URL = "https://raw.githubusercontent.com/plotly/datasets/master/stackoverflow-developer-survey-2023/survey_results_public.csv"
+OUTPUT = pathlib.Path(__file__).resolve().parent.parent / "data" / "model_weights.json"
+
+FIELD_KEYWORDS = {
+ "Frontend": ["front-end", "frontend", "web developer"],
+ "Backend": ["back-end", "backend", "server", "developer, back-end"],
+ "DevOps": ["devops", "site reliability", "cloud infrastructure engineer"],
+ "Data Science": ["data scientist", "machine learning", "data engineer", "ai"],
+ "UI/UX": ["designer", "ux", "ui"],
+ "Mobile Development": ["mobile", "android", "ios", "developer, mobile"],
+}
+
+
+def match_fields(devtype: str) -> set[str]:
+ role = (devtype or "").lower()
+ hits = set()
+ for field, keys in FIELD_KEYWORDS.items():
+ if any(k in role for k in keys):
+ hits.add(field)
+ return hits
+
+
+def download_csv(url: str) -> list[dict[str, str]]:
+ with urllib.request.urlopen(url, timeout=60) as resp:
+ rows = list(csv.DictReader(resp.read().decode("utf-8", errors="ignore").splitlines()))
+ return rows
+
+
+def fallback_rows() -> list[dict[str, str]]:
+ """Small fallback sample derived from publicly-known dev role labels."""
+ samples = [
+ "Developer, front-end", "Developer, back-end", "DevOps specialist",
+ "Data scientist or machine learning specialist", "Designer",
+ "Developer, mobile", "Developer, full-stack", "Cloud infrastructure engineer",
+ "Engineering manager", "Data engineer", "Developer, back-end",
+ "Developer, front-end", "Developer, mobile", "Developer, desktop or enterprise applications"
+ ]
+ return [{"DevType": x} for x in samples]
+
+
+def train_bias(rows: list[dict[str, str]]) -> dict[str, float]:
+ counts = Counter({k: 1 for k in FIELD_KEYWORDS}) # laplace smoothing
+ total = 0
+ for row in rows:
+ fields = match_fields(row.get("DevType", ""))
+ if not fields:
+ continue
+ total += 1
+ for f in fields:
+ counts[f] += 1
+
+ # Convert prevalence to bias close to 1.0 range for frontend softmax logit offsets.
+ bias = {}
+ for field in FIELD_KEYWORDS:
+ prevalence = counts[field] / max(1, total)
+ bias[field] = round(1 + math.log(prevalence * 10 + 1), 3)
+ return bias
+
+
+def main() -> None:
+ print(f"Downloading survey data from: {DATA_URL}")
+ try:
+ rows = download_csv(DATA_URL)
+ print(f"Rows downloaded: {len(rows)}")
+ source = DATA_URL
+ except Exception as exc:
+ print(f"Could not download internet dataset: {exc}")
+ print("Falling back to bundled sample roles.")
+ rows = fallback_rows()
+ source = "fallback-sample (network blocked)"
+
+ bias = train_bias(rows)
+ artifact = {
+ "source": source,
+ "trained_at": datetime.now(timezone.utc).isoformat(),
+ "rows": len(rows),
+ "field_bias": bias,
+ }
+
+ OUTPUT.parent.mkdir(parents=True, exist_ok=True)
+ OUTPUT.write_text(json.dumps(artifact, indent=2), encoding="utf-8")
+ print(f"Saved weights to: {OUTPUT}")
+ print(json.dumps(bias, indent=2))
+
+
+if __name__ == "__main__":
+ main()
diff --git a/styles.css b/styles.css
new file mode 100644
index 0000000..ebb9ce0
--- /dev/null
+++ b/styles.css
@@ -0,0 +1,73 @@
+:root {
+ --bg: #060b1a;
+ --panel: #0f172a;
+ --text: #e2e8f0;
+ --muted: #94a3b8;
+ --line: #26364f;
+ --brand: #38bdf8;
+ --brand-2: #34d399;
+}
+
+* { box-sizing: border-box; }
+body {
+ margin: 0;
+ font-family: Inter, system-ui, sans-serif;
+ color: var(--text);
+ background: radial-gradient(circle at top, #111b37, var(--bg));
+}
+.hero { text-align: center; padding: 2rem 1rem 1rem; }
+.hero p { color: var(--muted); }
+.container { width: min(980px, 94%); margin: 0 auto 2.5rem; display: grid; gap: 1rem; }
+
+.card {
+ background: rgba(15, 23, 42, 0.95);
+ border: 1px solid var(--line);
+ border-radius: 16px;
+ padding: 1rem;
+}
+.hidden { display: none; }
+ul { margin: 0.4rem 0 0.2rem 1rem; }
+
+.model-status { font-size: 0.92rem; color: #fbbf24; }
+.model-status.ready { color: var(--brand-2); }
+
+.grid-form { display: grid; gap: 0.7rem; }
+label { display: grid; gap: 0.35rem; color: var(--muted); }
+input, select, button {
+ border-radius: 10px;
+ padding: 0.65rem 0.75rem;
+ font-size: 0.95rem;
+}
+input, select { border: 1px solid #334155; background: #0b1223; color: var(--text); }
+button { border: 0; cursor: pointer; background: var(--brand); color: #032036; font-weight: 700; }
+button:hover { opacity: 0.92; }
+button.secondary { background: transparent; border: 1px solid #475569; color: var(--text); }
+
+.quiz-top { display: flex; justify-content: space-between; align-items: center; gap: 1rem; }
+#questionText { font-size: 1.08rem; }
+.options { display: grid; gap: 0.55rem; }
+.options button { text-align: left; background: #172235; color: var(--text); border: 1px solid #334155; }
+.options button.active { border-color: var(--brand-2); background: #043b30; }
+.quiz-actions { margin-top: 0.9rem; display: flex; justify-content: space-between; }
+
+.score-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(170px, 1fr)); gap: 0.7rem; }
+.score-card { background: #0b1223; border: 1px solid #334155; border-radius: 12px; padding: 0.65rem; }
+.bar { margin-top: 0.35rem; height: 8px; border-radius: 999px; background: #1f2937; overflow: hidden; }
+.fill { height: 100%; background: linear-gradient(90deg, var(--brand), var(--brand-2)); }
+
+.roadmap-wrap { display: grid; gap: 0.7rem; }
+.roadmap { background: #0b1223; border: 1px solid #334155; border-radius: 12px; padding: 0.7rem; }
+.roadmap ol { margin-left: 1rem; }
+
+.resource-manager { display: grid; grid-template-columns: repeat(auto-fit, minmax(250px, 1fr)); gap: 0.7rem; }
+.resource-box { border: 1px solid #334155; border-radius: 10px; padding: 0.65rem; background: #0b1223; }
+.resource-box h4 { margin: 0 0 0.4rem; }
+.resource-list { padding-left: 1rem; margin: 0 0 0.45rem; }
+.resource-list a { color: #7dd3fc; }
+.resource-form { display: flex; gap: 0.45rem; }
+.resource-form input { flex: 1; }
+
+@media (max-width: 650px) {
+ .quiz-actions { gap: 0.7rem; }
+ .quiz-actions button { width: 100%; }
+}