🇪🇸 Spain Work Visa Assessment 2026
⏳
Please wait,
Analyzing your responses…
Checking your responses…
✅
Thank you,
Based on your responses, you appear to have a foundational profile worth exploring further for a Spain Work Visa. Spain offers various work authorisation pathways including the Highly Qualified Professionals Visa, the EU Blue Card, and the standard Work & Residence Permit — each with differing requirements around qualifications, job offers, and salary thresholds.
This assessment is informational only. Actual eligibility depends on many additional factors including your nationality, specific job offer, employer sponsorship, and current Spanish immigration policy.
We strongly recommend consulting a certified immigration professional.
Frequently Asked Questions
What documents do I need for a Spain Work Visa?
Generally you will need a valid passport (with at least 6 months' validity), a signed job contract from a Spanish employer, proof of qualifications/education, a criminal background check from your home country, proof of medical insurance, and a completed national visa application form. Requirements vary by visa category, so always verify with the Spanish consulate in your country.
How long does the Spain Work Visa process take?
Processing times can range from 4 to 12 weeks depending on the consulate workload, visa type, and completeness of your application. The employer must first obtain a work permit approval from Spanish Labour Authorities before you apply for the visa — this initial step can add several additional weeks to the timeline.
Can I bring my family with me on a Spain Work Visa?
Yes. Spain allows work visa holders to apply for family reunification (reagrupación familiar) once you have been legally residing in Spain for at least one year. Eligible family members typically include a spouse or civil partner, dependent children under 18, and dependent parents. Each family member must apply separately and meet health and financial requirements.
Disclaimer: This assessment tool is provided for informational purposes only and does not constitute legal, immigration, or professional advice. Results are not a guarantee of visa approval or employment. This platform is not affiliated with, endorsed by, or associated with the Government of Spain, any Spanish consulate, or any official immigration authority. No visa or job placement is guaranteed. Always consult a qualified immigration lawyer or the official Spanish immigration authorities before making any decisions. Laws and requirements change — verify all information through official channels.
/* =============================================
RESET & BASE
============================================= */
*, *::before, *::after {
box-sizing: border-box;
margin: 0;
padding: 0;
}
html {
scroll-behavior: smooth;
}
body {
font-family: 'Segoe UI', system-ui, -apple-system, BlinkMacSystemFont, sans-serif;
background: linear-gradient(160deg, #eef2f7 0%, #dce8f5 100%);
min-height: 100vh;
display: flex;
align-items: flex-start;
justify-content: center;
padding: 32px 16px 64px;
color: #111827;
line-height: 1.6;
}
/* =============================================
PAGE WRAPPER
============================================= */
.page-wrapper {
width: 100%;
display: flex;
justify-content: center;
}
/* =============================================
CARD
============================================= */
.card {
width: 100%;
max-width: 760px;
background: #ffffff;
border-radius: 18px;
box-shadow:
0 4px 6px rgba(0, 0, 0, 0.04),
0 10px 40px rgba(0, 0, 0, 0.10),
0 30px 80px rgba(15, 76, 129, 0.08);
overflow: hidden;
transition: transform 0.3s ease, box-shadow 0.3s ease;
}
.card:hover {
transform: translateY(-3px);
box-shadow:
0 8px 16px rgba(0, 0, 0, 0.06),
0 20px 60px rgba(0, 0, 0, 0.13),
0 40px 100px rgba(15, 76, 129, 0.10);
}
/* =============================================
CARD HEADER
============================================= */
.card-header {
background: linear-gradient(135deg, #0F4C81, #C60B1E);
padding: 48px 40px 44px;
text-align: center;
}
.header-title {
font-size: clamp(1.6rem, 4vw, 2.25rem);
font-weight: 800;
color: #ffffff;
letter-spacing: -0.3px;
margin-bottom: 12px;
line-height: 1.2;
}
.header-desc {
font-size: clamp(0.9rem, 2vw, 1rem);
color: rgba(255, 255, 255, 0.88);
max-width: 520px;
margin: 0 auto;
}
/* =============================================
PROGRESS BAR
============================================= */
.progress-wrap {
background: #E5E7EB;
height: 6px;
width: 100%;
}
.progress-wrap[aria-hidden="true"] {
display: none;
}
.progress-track {
background: #E5E7EB;
height: 100%;
width: 100%;
}
.progress-fill {
background: #16A34A;
height: 100%;
width: 0%;
transition: width 0.45s cubic-bezier(0.4, 0, 0.2, 1);
border-radius: 0 3px 3px 0;
}
/* =============================================
CARD BODY
============================================= */
.card-body {
padding: 40px;
background: #ffffff;
}
/* =============================================
SCREENS — fade transition
============================================= */
.screen {
display: none;
opacity: 0;
transform: translateY(12px);
transition: opacity 0.4s ease, transform 0.4s ease;
}
.screen.active {
display: block;
opacity: 1;
transform: translateY(0);
}
/* =============================================
WELCOME SCREEN
============================================= */
.welcome-icon {
font-size: 3rem;
text-align: center;
margin-bottom: 12px;
}
.welcome-title {
font-size: 1.9rem;
font-weight: 800;
color: #111827;
text-align: center;
margin-bottom: 8px;
}
.welcome-sub {
color: #6B7280;
text-align: center;
margin-bottom: 32px;
font-size: 0.97rem;
}
/* =============================================
FORM FIELDS
============================================= */
.field-group {
margin-bottom: 20px;
}
.field-label {
display: block;
font-size: 0.875rem;
font-weight: 600;
color: #374151;
margin-bottom: 6px;
letter-spacing: 0.2px;
}
.field-input {
width: 100%;
padding: 13px 16px;
border: 2px solid #D1D5DB;
border-radius: 10px;
font-size: 1rem;
color: #111827;
background: #F9FAFB;
transition: border-color 0.2s, box-shadow 0.2s, background 0.2s;
outline: none;
}
.field-input:focus {
border-color: #16A34A;
background: #ffffff;
box-shadow: 0 0 0 3px rgba(22, 163, 74, 0.15);
}
.field-input::placeholder {
color: #9CA3AF;
}
.field-hint {
font-size: 0.83rem;
color: #DC2626;
min-height: 20px;
margin-top: 4px;
margin-bottom: 8px;
}
/* =============================================
BUTTONS
============================================= */
.btn {
display: inline-flex;
align-items: center;
justify-content: center;
gap: 6px;
padding: 13px 28px;
border-radius: 10px;
font-size: 0.97rem;
font-weight: 700;
cursor: pointer;
border: 2px solid transparent;
transition:
background 0.2s ease,
color 0.2s ease,
transform 0.15s ease,
box-shadow 0.2s ease,
border-color 0.2s ease;
text-decoration: none;
letter-spacing: 0.15px;
white-space: nowrap;
}
.btn:active {
transform: scale(0.97);
}
.btn-full {
width: 100%;
display: flex;
}
/* Green */
.btn-green {
background: #16A34A;
color: #ffffff;
border-color: #16A34A;
}
.btn-green:hover:not(:disabled) {
background: #15803D;
border-color: #15803D;
box-shadow: 0 4px 14px rgba(22, 163, 74, 0.35);
transform: translateY(-1px);
}
.btn-green:disabled {
background: #86EFAC;
border-color: #86EFAC;
cursor: not-allowed;
color: #ffffff;
}
/* Outline / ghost */
.btn-outline {
background: transparent;
color: #374151;
border-color: #D1D5DB;
}
.btn-outline:hover {
background: #F3F4F6;
border-color: #9CA3AF;
transform: translateY(-1px);
}
/* Spain Red */
.btn-spain-red {
background: #C60B1E;
color: #ffffff;
border-color: #C60B1E;
}
.btn-spain-red:hover {
background: #A5091A;
border-color: #A5091A;
box-shadow: 0 4px 14px rgba(198, 11, 30, 0.35);
transform: translateY(-1px);
}
/* WhatsApp */
.btn-whatsapp {
background: #25D366;
color: #ffffff;
border-color: #25D366;
}
.btn-whatsapp:hover {
background: #1DAA54;
border-color: #1DAA54;
box-shadow: 0 4px 14px rgba(37, 211, 102, 0.35);
transform: translateY(-1px);
}
/* =============================================
QUIZ SCREEN
============================================= */
.question-area {
margin-bottom: 32px;
}
.question-counter {
font-size: 0.8rem;
font-weight: 700;
text-transform: uppercase;
letter-spacing: 1px;
color: #16A34A;
margin-bottom: 10px;
}
.question-text {
font-size: clamp(1.1rem, 3vw, 1.4rem);
font-weight: 800;
color: #111827;
margin-bottom: 22px;
line-height: 1.35;
}
/* Options list */
.options-list {
display: flex;
flex-direction: column;
gap: 10px;
}
.option-item {
display: flex;
align-items: center;
gap: 14px;
padding: 14px 18px;
border: 2px solid #E5E7EB;
border-radius: 12px;
cursor: pointer;
transition: background 0.18s, border-color 0.18s, transform 0.15s, box-shadow 0.18s;
background: #FAFAFA;
user-select: none;
list-style: none;
}
.option-item:hover {
border-color: #16A34A;
background: #F0FDF4;
transform: translateX(3px);
box-shadow: 0 2px 8px rgba(22, 163, 74, 0.10);
}
.option-item.selected {
background: #ECFDF5;
border-color: #16A34A;
color: #065F46;
}
.option-item.selected .option-icon {
border-color: #16A34A;
background: #16A34A;
color: #fff;
}
.option-icon {
flex-shrink: 0;
width: 22px;
height: 22px;
border-radius: 50%;
border: 2px solid #D1D5DB;
display: flex;
align-items: center;
justify-content: center;
font-size: 0.6rem;
transition: background 0.18s, border-color 0.18s;
}
.option-icon::after {
content: '';
width: 8px;
height: 8px;
border-radius: 50%;
background: transparent;
transition: background 0.18s;
}
.option-item.selected .option-icon::after {
background: #fff;
}
.option-text {
font-size: 0.97rem;
color: inherit;
font-weight: 500;
line-height: 1.4;
}
/* Navigation row */
.nav-row {
display: flex;
justify-content: space-between;
align-items: center;
gap: 12px;
}
.nav-row .btn {
min-width: 130px;
}
#btnPrev {
visibility: visible;
}
#btnPrev.hidden {
visibility: hidden;
pointer-events: none;
}
/* =============================================
LOADING SCREEN
============================================= */
.loading-icon {
font-size: 2.8rem;
text-align: center;
margin-bottom: 16px;
animation: pulse 1.4s ease-in-out infinite;
}
@keyframes pulse {
0%, 100% { transform: scale(1); }
50% { transform: scale(1.1); }
}
.loading-greeting {
font-size: 1.1rem;
color: #6B7280;
text-align: center;
}
.loading-name {
font-size: 1.6rem;
font-weight: 800;
color: #111827;
text-align: center;
margin-bottom: 6px;
}
.loading-sub {
color: #6B7280;
text-align: center;
margin-bottom: 32px;
font-size: 0.97rem;
}
/* Animated loading bar */
.loading-track {
background: #E5E7EB;
border-radius: 999px;
height: 8px;
overflow: hidden;
margin-bottom: 18px;
}
.loading-fill {
background: linear-gradient(90deg, #16A34A, #22C55E, #16A34A);
background-size: 200% 100%;
height: 100%;
width: 0%;
border-radius: 999px;
animation: loadingAnim 4.5s ease-in-out forwards, shimmer 1.4s linear infinite;
}
@keyframes loadingAnim {
0% { width: 0%; }
20% { width: 30%; }
50% { width: 60%; }
80% { width: 85%; }
100% { width: 100%; }
}
@keyframes shimmer {
0% { background-position: 200% 0; }
100% { background-position: -200% 0; }
}
.loading-message {
text-align: center;
font-size: 0.9rem;
color: #6B7280;
font-style: italic;
min-height: 22px;
transition: opacity 0.4s;
}
/* =============================================
RESULT SCREEN
============================================= */
.result-badge {
font-size: 3rem;
text-align: center;
margin-bottom: 12px;
}
.result-thanks {
font-size: 1.4rem;
font-weight: 600;
color: #6B7280;
text-align: center;
}
.result-name {
font-size: 2rem;
font-weight: 900;
color: #111827;
text-align: center;
margin-bottom: 28px;
}
.result-message {
background: #F9FAFB;
border-left: 4px solid #16A34A;
border-radius: 0 10px 10px 0;
padding: 20px 22px;
margin-bottom: 28px;
}
.result-message p {
color: #374151;
font-size: 0.97rem;
line-height: 1.7;
}
.result-message p + p {
margin-top: 12px;
}
/* CTA group */
.cta-group {
display: flex;
flex-direction: column;
gap: 12px;
margin-bottom: 36px;
}
/* =============================================
FAQ SECTION
============================================= */
.faq-section {
margin-bottom: 32px;
}
.faq-heading {
font-size: 1.15rem;
font-weight: 800;
color: #111827;
margin-bottom: 14px;
}
.faq-item {
border: 2px solid #E5E7EB;
border-radius: 10px;
margin-bottom: 10px;
overflow: hidden;
transition: border-color 0.2s;
}
.faq-item[open] {
border-color: #16A34A;
}
.faq-question {
padding: 15px 18px;
font-size: 0.97rem;
font-weight: 700;
color: #111827;
cursor: pointer;
list-style: none;
display: flex;
align-items: center;
justify-content: space-between;
gap: 12px;
transition: background 0.18s;
}
.faq-question::-webkit-details-marker {
display: none;
}
.faq-question::after {
content: '+';
font-size: 1.3rem;
font-weight: 400;
color: #16A34A;
flex-shrink: 0;
transition: transform 0.25s;
}
.faq-item[open] .faq-question::after {
transform: rotate(45deg);
}
.faq-question:hover {
background: #F9FAFB;
}
.faq-answer {
padding: 0 18px 16px;
border-top: 1px solid #E5E7EB;
}
.faq-answer p {
color: #4B5563;
font-size: 0.93rem;
line-height: 1.7;
padding-top: 14px;
}
/* =============================================
DISCLAIMER
============================================= */
.disclaimer {
background: #FFF7ED;
border: 1px solid #FED7AA;
border-radius: 10px;
padding: 16px 18px;
margin-bottom: 24px;
}
.disclaimer p {
font-size: 0.82rem;
color: #92400E;
line-height: 1.6;
}
/* =============================================
RESPONSIVE — Tablet
============================================= */
@media (max-width: 820px) {
.card-header {
padding: 36px 28px 32px;
}
.card-body {
padding: 28px;
}
}
/* =============================================
RESPONSIVE — Mobile
============================================= */
@media (max-width: 540px) {
body {
padding: 0;
align-items: flex-start;
background: #ffffff;
}
.card {
border-radius: 0;
box-shadow: none;
}
.card:hover {
transform: none;
box-shadow: none;
}
.card-header {
padding: 28px 20px 24px;
}
.card-body {
padding: 22px 18px;
}
.nav-row .btn {
min-width: 100px;
padding: 11px 18px;
font-size: 0.9rem;
}
.result-name {
font-size: 1.55rem;
}
}
/* =============================================
ACCESSIBILITY — reduced motion
============================================= */
@media (prefers-reduced-motion: reduce) {
*, *::before, *::after {
animation-duration: 0.01ms !important;
transition-duration: 0.01ms !important;
}
}
/**
* =============================================
* Spain Work Visa Assessment — script.js
* =============================================
* Vanilla JS only. No external libraries.
* All DOM manipulation, screen transitions,
* quiz logic, loading animation, and reset
* functionality live here.
* =============================================
*/
/* =============================================
QUESTION DATA — 15 questions
============================================= */
const questions = [
{
id: 1,
text: "What is your current age?",
options: [
"Under 18",
"18 – 24",
"25 – 34",
"35 – 44",
"45 – 54",
"55 or older"
]
},
{
id: 2,
text: "Which country do you currently live in?",
options: [
"Pakistan",
"India",
"Bangladesh",
"Philippines",
"Morocco",
"Other (non-EU country)"
]
},
{
id: 3,
text: "What is your highest level of education?",
options: [
"No formal qualification",
"Secondary school / O-Levels",
"Diploma or vocational certificate",
"Bachelor's degree",
"Master's degree or higher"
]
},
{
id: 4,
text: "Do you currently hold a valid passport?",
options: [
"Yes, valid for more than 12 months",
"Yes, valid but expiring within 12 months",
"No, but I can apply for one",
"No, I do not have a passport"
]
},
{
id: 5,
text: "How many years of professional work experience do you have?",
options: [
"No experience",
"Less than 1 year",
"1 – 2 years",
"3 – 5 years",
"6 – 10 years",
"More than 10 years"
]
},
{
id: 6,
text: "How would you describe your English language level?",
options: [
"No English",
"Basic (can understand simple sentences)",
"Intermediate (can hold conversations)",
"Upper-intermediate (confident in most situations)",
"Advanced / Native speaker"
]
},
{
id: 7,
text: "Have you ever worked outside your home country before?",
options: [
"Yes, in Europe (including Spain)",
"Yes, in the Middle East or Gulf region",
"Yes, in another region",
"No, I have only worked in my home country",
"I have not worked yet"
]
},
{
id: 8,
text: "Are you willing to relocate to Spain for work?",
options: [
"Yes, I am ready to move immediately",
"Yes, within the next 3 – 6 months",
"Yes, but I need 6 – 12 months to prepare",
"I am not sure yet",
"No, I prefer remote or home-country work"
]
},
{
id: 9,
text: "What type of job are you looking for in Spain?",
options: [
"Agriculture / Farming / Fruit picking",
"Construction / Labour",
"Hospitality / Hotel & Restaurant",
"Healthcare / Nursing",
"Technology / IT",
"Other skilled profession"
]
},
{
id: 10,
text: "When are you hoping to start the visa application process?",
options: [
"As soon as possible",
"Within 1 – 3 months",
"In 3 – 6 months",
"In 6 – 12 months",
"I am just exploring for now"
]
},
{
id: 11,
text: "Is your CV / résumé currently up to date?",
options: [
"Yes, fully updated and ready",
"Mostly done, needs minor updates",
"Partially — I need help completing it",
"No, I have not started one yet"
]
},
{
id: 12,
text: "Are you comfortable working a standard full-time schedule (approx. 40 hrs/week)?",
options: [
"Yes, fully comfortable",
"Yes, with occasional overtime",
"I prefer part-time hours",
"I need flexible hours due to personal commitments"
]
},
{
id: 13,
text: "Are you comfortable with physically demanding or outdoor work roles?",
options: [
"Yes, I have experience and enjoy outdoor work",
"Yes, I can do it even without prior experience",
"Somewhat — lighter physical tasks only",
"No, I prefer office or desk-based work"
]
},
{
id: 14,
text: "What is your preferred working shift?",
options: [
"Day shift (morning to afternoon)",
"Afternoon / Evening shift",
"Night shift",
"Rotating shifts (any schedule)",
"No preference"
]
},
{
id: 15,
text: "Would you like to receive future updates about Spain work visa opportunities?",
options: [
"Yes, via WhatsApp channel",
"Yes, via the website guide",
"Yes, both WhatsApp and website",
"No, I only needed this assessment"
]
}
];
/* =============================================
STATE
============================================= */
const state = {
firstName: "",
lastName: "",
currentQuestion: 0, // 0-based index
answers: new Array(questions.length).fill(null)
};
/* =============================================
LOADING MESSAGES — rotated during loading
============================================= */
const loadingMessages = [
"Checking your responses…",
"Reviewing your answers…",
"Preparing your assessment…",
"Matching common work visa requirements…",
"Almost finished…"
];
/* =============================================
DOM REFERENCES
============================================= */
const $ = (id) => document.getElementById(id);
const screenWelcome = $("screenWelcome");
const screenQuiz = $("screenQuiz");
const screenLoading = $("screenLoading");
const screenResult = $("screenResult");
const progressWrap = $("progressWrap");
const progressFill = $("progressFill");
const firstNameInput = $("firstName");
const lastNameInput = $("lastName");
const nameError = $("nameError");
const btnContinue = $("btnContinue");
const questionCounter = $("questionCounter");
const questionText = $("questionText");
const optionsList = $("optionsList");
const btnPrev = $("btnPrev");
const btnNext = $("btnNext");
const loadingName = $("loadingName");
const loadingFill = $("loadingFill");
const loadingMessage = $("loadingMessage");
const resultName = $("resultName");
const btnRestart = $("btnRestart");
/* =============================================
SCREEN TRANSITION HELPER
============================================= */
/**
* showScreen — hides all screens then fades in the target.
* @param {HTMLElement} target
*/
function showScreen(target) {
const screens = [screenWelcome, screenQuiz, screenLoading, screenResult];
screens.forEach((s) => {
if (s !== target) {
s.classList.remove("active");
}
});
// Small tick to allow CSS transition to play
requestAnimationFrame(() => {
target.classList.add("active");
});
}
/* =============================================
PROGRESS BAR
============================================= */
/**
* updateProgress — updates the green progress fill.
* Called after each question advance.
*/
function updateProgress() {
const total = questions.length;
const current = state.currentQuestion + 1; // 1-based for display
const pct = Math.round((current / total) * 100);
progressFill.style.width = pct + "%";
progressFill.setAttribute("aria-valuenow", pct);
}
/* =============================================
RENDER QUESTION
============================================= */
/**
* renderQuestion — builds option buttons for the current question
* and updates nav button visibility/state.
*/
function renderQuestion() {
const q = questions[state.currentQuestion];
const saved = state.answers[state.currentQuestion];
const total = questions.length;
const index = state.currentQuestion;
const isLast = index === total - 1;
// Counter & text
questionCounter.textContent = `Question ${index + 1} of ${total}`;
questionText.textContent = q.text;
// Build options
optionsList.innerHTML = "";
q.options.forEach((opt, i) => {
const li = document.createElement("li");
li.className = "option-item" + (saved === i ? " selected" : "");
li.setAttribute("role", "radio");
li.setAttribute("aria-checked", saved === i ? "true" : "false");
li.setAttribute("tabindex", "0");
li.dataset.index = i;
const icon = document.createElement("span");
icon.className = "option-icon";
icon.setAttribute("aria-hidden", "true");
const text = document.createElement("span");
text.className = "option-text";
text.textContent = opt;
li.appendChild(icon);
li.appendChild(text);
// Click handler
li.addEventListener("click", () => selectOption(i));
// Keyboard handler (Enter / Space)
li.addEventListener("keydown", (e) => {
if (e.key === "Enter" || e.key === " ") {
e.preventDefault();
selectOption(i);
}
});
optionsList.appendChild(li);
});
// Previous button
if (index === 0) {
btnPrev.classList.add("hidden");
} else {
btnPrev.classList.remove("hidden");
}
// Next / Finish button label
if (isLast) {
btnNext.textContent = "Finish Assessment ✓";
} else {
btnNext.textContent = "Next →";
}
// Disable Next if nothing selected
btnNext.disabled = (saved === null);
// Update progress bar
updateProgress();
}
/* =============================================
SELECT OPTION
============================================= */
/**
* selectOption — records user's answer and highlights the chosen option.
* @param {number} optionIndex
*/
function selectOption(optionIndex) {
state.answers[state.currentQuestion] = optionIndex;
// Update UI
const items = optionsList.querySelectorAll(".option-item");
items.forEach((item, i) => {
if (i === optionIndex) {
item.classList.add("selected");
item.setAttribute("aria-checked", "true");
} else {
item.classList.remove("selected");
item.setAttribute("aria-checked", "false");
}
});
btnNext.disabled = false;
}
/* =============================================
LOADING SCREEN LOGIC
============================================= */
/**
* startLoading — shows loading screen, animates messages, then shows result.
*/
function startLoading() {
// Set display name
loadingName.textContent = state.firstName;
// Show loading screen
showScreen(screenLoading);
// Reset bar animation by cloning node
const oldFill = loadingFill;
const newFill = oldFill.cloneNode(true);
oldFill.parentNode.replaceChild(newFill, oldFill);
// Rotate loading messages
let msgIndex = 0;
loadingMessage.textContent = loadingMessages[msgIndex];
const msgInterval = setInterval(() => {
msgIndex = (msgIndex + 1) % loadingMessages.length;
// Fade transition
loadingMessage.style.opacity = "0";
setTimeout(() => {
loadingMessage.textContent = loadingMessages[msgIndex];
loadingMessage.style.opacity = "1";
}, 200);
}, 900);
// After 4.5 s, show result
setTimeout(() => {
clearInterval(msgInterval);
showResultScreen();
}, 4500);
}
/* =============================================
RESULT SCREEN
============================================= */
/**
* showResultScreen — populates result with user name and reveals screen.
*/
function showResultScreen() {
resultName.textContent = state.firstName + " " + state.lastName;
showScreen(screenResult);
// Scroll to top of card
document.getElementById("assessmentCard").scrollIntoView({ behavior: "smooth", block: "start" });
}
/* =============================================
WELCOME SCREEN — CONTINUE BUTTON
============================================= */
btnContinue.addEventListener("click", () => {
const fname = firstNameInput.value.trim();
const lname = lastNameInput.value.trim();
// Validation
if (!fname || !lname) {
nameError.textContent = "⚠️ Both First Name and Last Name are required.";
if (!fname) firstNameInput.focus();
else lastNameInput.focus();
return;
}
if (fname.length < 2 || lname.length < 2) {
nameError.textContent = "⚠️ Names must be at least 2 characters.";
return;
}
nameError.textContent = "";
state.firstName = capitalise(fname);
state.lastName = capitalise(lname);
// Show progress bar
progressWrap.removeAttribute("aria-hidden");
progressWrap.style.display = "";
// Go to quiz
state.currentQuestion = 0;
renderQuestion();
showScreen(screenQuiz);
});
/* =============================================
QUIZ NAVIGATION
============================================= */
// NEXT
btnNext.addEventListener("click", () => {
const isLast = state.currentQuestion === questions.length - 1;
if (isLast) {
// Go to loading
startLoading();
} else {
state.currentQuestion++;
renderQuestion();
}
});
// PREVIOUS
btnPrev.addEventListener("click", () => {
if (state.currentQuestion > 0) {
state.currentQuestion--;
renderQuestion();
}
});
/* =============================================
RESTART
============================================= */
btnRestart.addEventListener("click", () => {
// Reset state
state.firstName = "";
state.lastName = "";
state.currentQuestion = 0;
state.answers = new Array(questions.length).fill(null);
// Reset form
firstNameInput.value = "";
lastNameInput.value = "";
nameError.textContent = "";
// Reset progress bar
progressFill.style.width = "0%";
progressWrap.setAttribute("aria-hidden", "true");
// Show welcome
showScreen(screenWelcome);
// Scroll to top
document.getElementById("assessmentCard").scrollIntoView({ behavior: "smooth", block: "start" });
firstNameInput.focus();
});
/* =============================================
INPUT — clear error on type
============================================= */
firstNameInput.addEventListener("input", () => { nameError.textContent = ""; });
lastNameInput.addEventListener("input", () => { nameError.textContent = ""; });
// Allow pressing Enter on name fields to trigger Continue
[firstNameInput, lastNameInput].forEach((input) => {
input.addEventListener("keydown", (e) => {
if (e.key === "Enter") {
e.preventDefault();
btnContinue.click();
}
});
});
/* =============================================
UTILITY — capitalise first letter
============================================= */
/**
* capitalise — uppercases the first letter of a string.
* @param {string} str
* @returns {string}
*/
function capitalise(str) {
return str.charAt(0).toUpperCase() + str.slice(1);
}
/* =============================================
INIT — ensure welcome screen is visible on load
============================================= */
(function init() {
showScreen(screenWelcome);
firstNameInput.focus();
})();