Загрузка...

Интерактивный конструктор резюме с предпросмотром: создавайте и редактируйте разделы резюме (опыт, образование, навыки) с AI-подсказками и мгновенным предпросмотром. Идеально для карьерных порталов.
<main class="sm:px-6 lg:px-8 max-w-7xl mr-auto ml-auto pt-8 pr-4 pb-8 pl-4">
<div class="grid grid-cols-1 lg:grid-cols-12 gap-6">
<!-- Editor Panel -->
<section class="lg:col-span-8 space-y-6">
<!-- Contact Information -->
<div class="rounded-2xl border border-neutral-800 bg-neutral-900/50">
<div class="flex items-center justify-between px-4 sm:px-6 py-4 border-b border-neutral-800">
<div class="flex items-center gap-3">
<button type="button" data-toggle="contact" aria-controls="section-contact" aria-expanded="true" class="group inline-flex items-center gap-2">
<span class="h-7 w-7 rounded-md bg-neutral-800 flex items-center justify-center">
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" data-lucide="chevron-down" class="lucide lucide-chevron-down h-4 w-4 transition-transform" data-chevron="contact"><path d="m6 9 6 6 6-6" class=""></path></svg>
</span>
<h2 class="text-lg sm:text-xl tracking-tight font-semibold">Contact Information</h2>
</button>
</div>
<div class="flex items-center gap-2">
<button class="inline-flex items-center gap-2 rounded-md bg-neutral-800 hover:bg-neutral-700 px-3 py-1.5 text-sm font-medium" data-ai-target="contact">
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" data-lucide="wand-2" class="lucide lucide-wand-2 h-4 w-4"><path d="m21.64 3.64-1.28-1.28a1.21 1.21 0 0 0-1.72 0L2.36 18.64a1.21 1.21 0 0 0 0 1.72l1.28 1.28a1.2 1.2 0 0 0 1.72 0L21.64 5.36a1.2 1.2 0 0 0 0-1.72" class=""></path><path d="m14 7 3 3" class=""></path><path d="M5 6v4" class=""></path><path d="M19 14v4" class=""></path><path d="M10 2v2" class=""></path><path d="M7 8H3" class=""></path><path d="M21 16h-4" class=""></path><path d="M11 3H9" class=""></path></svg>
Enhance
</button>
</div>
</div>
<div id="section-contact" class="px-4 sm:px-6 py-5 grid grid-cols-1 sm:grid-cols-2 gap-4">
<!-- Name -->
<div class="sm:col-span-1">
<label class="block text-sm text-neutral-300 mb-2">First name</label>
<div class="flex items-center gap-2">
<label class="relative inline-flex items-center">
<input type="checkbox" class="peer sr-only include-checkbox" checked="" data-include-for="firstName">
<span class="h-5 w-5 rounded-md border border-neutral-700 bg-neutral-900 ring-1 ring-inset ring-neutral-800 flex items-center justify-center peer-checked:bg-emerald-600 peer-checked:border-emerald-600 transition">
<svg viewBox="0 0 24 24" class="h-3.5 w-3.5 text-white opacity-0 peer-checked:opacity-100 transition"><path fill="currentColor" d="M20.285 6.709a1 1 0 0 1 0 1.414l-9.19 9.19a1 1 0 0 1-1.414 0l-5.19-5.19a1 1 0 0 1 1.414-1.414l4.483 4.483 8.483-8.483a1 1 0 0 1 1.414 0Z" class=""></path></svg>
</span>
</label>
<input id="firstName" type="text" value="John" class="w-full rounded-lg bg-neutral-900 border border-neutral-800 focus:outline-none focus:ring-2 focus:ring-emerald-600/60 px-3 py-2 text-sm">
</div>
</div>
<div class="sm:col-span-1">
<label class="block text-sm text-neutral-300 mb-2">Last name</label>
<div class="flex items-center gap-2">
<label class="relative inline-flex items-center">
<input type="checkbox" class="peer sr-only include-checkbox" checked="" data-include-for="lastName">
<span class="h-5 w-5 rounded-md border border-neutral-700 bg-neutral-900 ring-1 ring-inset ring-neutral-800 flex items-center justify-center peer-checked:bg-emerald-600 peer-checked:border-emerald-600 transition">
<svg viewBox="0 0 24 24" class="h-3.5 w-3.5 text-white opacity-0 peer-checked:opacity-100 transition"><path fill="currentColor" d="M20.285 6.709a1 1 0 0 1 0 1.414l-9.19 9.19a1 1 0 0 1-1.414 0l-5.19-5.19a1 1 0 0 1 1.414-1.414l4.483 4.483 8.483-8.483a1 1 0 0 1 1.414 0Z" class=""></path></svg>
</span>
</label>
<input id="lastName" type="text" value="Iseghohi" class="w-full rounded-lg bg-neutral-900 border border-neutral-800 focus:outline-none focus:ring-2 focus:ring-emerald-600/60 px-3 py-2 text-sm">
</div>
</div>
<!-- Contact Row -->
<div class="sm:col-span-2 grid grid-cols-1 md:grid-cols-2 xl:grid-cols-4 gap-4">
<div class="">
<label class="block text-sm text-neutral-300 mb-2">Email</label>
<div class="flex items-center gap-2">
<label class="relative inline-flex items-center">
<input type="checkbox" class="peer sr-only include-checkbox" checked="" data-include-for="email">
<span class="h-5 w-5 rounded-md border border-neutral-700 bg-neutral-900 ring-1 ring-inset ring-neutral-800 flex items-center justify-center peer-checked:bg-emerald-600 peer-checked:border-emerald-600 transition">
<svg viewBox="0 0 24 24" class="h-3.5 w-3.5 text-white opacity-0 peer-checked:opacity-100 transition"><path fill="currentColor" d="M20.285 6.709a1 1 0 0 1 0 1.414l-9.19 9.19a1 1 0 0 1-1.414 0l-5.19-5.19a1 1 0 0 1 1.414-1.414l4.483 4.483 8.483-8.483a1 1 0 0 1 1.414 0Z" class=""></path></svg>
</span>
</label>
<input id="email" type="email" value="iseghohi.john@gmail.com" class="w-full rounded-lg bg-neutral-900 border border-neutral-800 focus:outline-none focus:ring-2 focus:ring-emerald-600/60 px-3 py-2 text-sm">
</div>
</div>
<div class="">
<label class="block text-sm text-neutral-300 mb-2">Phone</label>
<div class="flex items-center gap-2">
<label class="relative inline-flex items-center">
<input type="checkbox" class="peer sr-only include-checkbox" checked="" data-include-for="phone">
<span class="h-5 w-5 rounded-md border border-neutral-700 bg-neutral-900 ring-1 ring-inset ring-neutral-800 flex items-center justify-center peer-checked:bg-emerald-600 peer-checked:border-emerald-600 transition">
<svg viewBox="0 0 24 24" class="h-3.5 w-3.5 text-white opacity-0 peer-checked:opacity-100 transition"><path fill="currentColor" d="M20.285 6.709a1 1 0 0 1 0 1.414l-9.19 9.19a1 1 0 0 1-1.414 0l-5.19-5.19a1 1 0 0 1 1.414-1.414l4.483 4.483 8.483-8.483a1 1 0 0 1 1.414 0Z" class=""></path></svg>
</span>
</label>
<input id="phone" type="text" value="+44 7862 060572" class="w-full rounded-lg bg-neutral-900 border border-neutral-800 focus:outline-none focus:ring-2 focus:ring-emerald-600/60 px-3 py-2 text-sm">
</div>
</div>
<div class="">
<label class="block text-sm text-neutral-300 mb-2">LinkedIn</label>
<div class="flex items-center gap-2">
<label class="relative inline-flex items-center">
<input type="checkbox" class="peer sr-only include-checkbox" checked="" data-include-for="linkedin">
<span class="h-5 w-5 rounded-md border border-neutral-700 bg-neutral-900 ring-1 ring-inset ring-neutral-800 flex items-center justify-center peer-checked:bg-emerald-600 peer-checked:border-emerald-600 transition">
<svg viewBox="0 0 24 24" class="h-3.5 w-3.5 text-white opacity-0 peer-checked:opacity-100 transition"><path fill="currentColor" d="M20.285 6.709a1 1 0 0 1 0 1.414l-9.19 9.19a1 1 0 0 1-1.414 0l-5.19-5.19a1 1 0 0 1 1.414-1.414l4.483 4.483 8.483-8.483a1 1 0 0 1 1.414 0Z" class=""></path></svg>
</span>
</label>
<input id="linkedin" type="text" value="linkedin.com/in/johniseghohi" class="w-full rounded-lg bg-neutral-900 border border-neutral-800 focus:outline-none focus:ring-2 focus:ring-emerald-600/60 px-3 py-2 text-sm">
</div>
</div>
<div class="">
<label class="block text-sm text-neutral-300 mb-2">Location</label>
<div class="flex items-center gap-2">
<label class="relative inline-flex items-center">
<input type="checkbox" class="peer sr-only include-checkbox" checked="" data-include-for="location">
<span class="h-5 w-5 rounded-md border border-neutral-700 bg-neutral-900 ring-1 ring-inset ring-neutral-800 flex items-center justify-center peer-checked:bg-emerald-600 peer-checked:border-emerald-600 transition">
<svg viewBox="0 0 24 24" class="h-3.5 w-3.5 text-white opacity-0 peer-checked:opacity-100 transition"><path fill="currentColor" d="M20.285 6.709a1 1 0 0 1 0 1.414l-9.19 9.19a1 1 0 0 1-1.414 0l-5.19-5.19a1 1 0 0 1 1.414-1.414l4.483 4.483 8.483-8.483a1 1 0 0 1 1.414 0Z" class=""></path></svg>
</span>
</label>
<input id="location" type="text" value="Manchester, UK" class="w-full rounded-lg bg-neutral-900 border border-neutral-800 focus:outline-none focus:ring-2 focus:ring-emerald-600/60 px-3 py-2 text-sm">
</div>
</div>
</div>
</div>
</div>
<!-- Target Title -->
<div class="rounded-2xl border border-neutral-800 bg-neutral-900/50">
<div class="flex sm:px-6 border-neutral-800 border-b pt-4 pr-4 pb-4 pl-4 items-center justify-between">
<button type="button" data-toggle="title" aria-controls="section-title" aria-expanded="true" class="group inline-flex items-center gap-2">
<span class="h-7 w-7 rounded-md bg-neutral-800 flex items-center justify-center">
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" data-lucide="chevron-down" class="lucide lucide-chevron-down h-4 w-4 transition-transform" data-chevron="title"><path d="m6 9 6 6 6-6" class=""></path></svg>
</span>
<h2 class="text-lg sm:text-xl tracking-tight font-semibold">Target Title</h2>
</button>
<div class="flex items-center gap-2">
<button class="inline-flex items-center gap-2 rounded-md bg-neutral-800 hover:bg-neutral-700 px-3 py-1.5 text-sm font-medium" data-ai-target="title">
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" data-lucide="wand-2" class="lucide lucide-wand-2 h-4 w-4"><path d="m21.64 3.64-1.28-1.28a1.21 1.21 0 0 0-1.72 0L2.36 18.64a1.21 1.21 0 0 0 0 1.72l1.28 1.28a1.2 1.2 0 0 0 1.72 0L21.64 5.36a1.2 1.2 0 0 0 0-1.72" class=""></path><path d="m14 7 3 3" class=""></path><path d="M5 6v4" class=""></path><path d="M19 14v4" class=""></path><path d="M10 2v2" class=""></path><path d="M7 8H3" class=""></path><path d="M21 16h-4" class=""></path><path d="M11 3H9" class=""></path></svg>
Suggest
</button>
</div>
</div>
<div id="section-title" class="px-4 sm:px-6 py-5">
<div class="flex items-center gap-2">
<label class="relative inline-flex items-center">
<input type="checkbox" class="peer sr-only include-checkbox" checked="" data-include-for="targetTitle">
<span class="h-5 w-5 rounded-md border border-neutral-700 bg-neutral-900 ring-1 ring-inset ring-neutral-800 flex items-center justify-center peer-checked:bg-emerald-600 peer-checked:border-emerald-600 transition">
<svg viewBox="0 0 24 24" class="h-3.5 w-3.5 text-white opacity-0 peer-checked:opacity-100 transition"><path fill="currentColor" d="M20.285 6.709a1 1 0 0 1 0 1.414l-9.19 9.19a1 1 0 0 1-1.414 0l-5.19-5.19a1 1 0 0 1 1.414-1.414l4.483 4.483 8.483-8.483a1 1 0 0 1 1.414 0Z" class=""></path></svg>
</span>
</label>
<input id="targetTitle" type="text" value="Senior UX Designer" class="w-full rounded-lg bg-neutral-900 border border-neutral-800 focus:outline-none focus:ring-2 focus:ring-emerald-600/60 px-3 py-2 text-sm">
</div>
</div>
</div>
<!-- Professional Summary -->
<div class="rounded-2xl border border-neutral-800 bg-neutral-900/50">
<div class="flex items-center justify-between px-4 sm:px-6 py-4 border-b border-neutral-800">
<button type="button" data-toggle="summary" aria-controls="section-summary" aria-expanded="true" class="group inline-flex items-center gap-2">
<span class="h-7 w-7 rounded-md bg-neutral-800 flex items-center justify-center">
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" data-lucide="chevron-down" class="lucide lucide-chevron-down h-4 w-4 transition-transform" data-chevron="summary"><path d="m6 9 6 6 6-6" class=""></path></svg>
</span>
<h2 class="text-lg sm:text-xl tracking-tight font-semibold">Professional Summary</h2>
</button>
<div class="flex items-center gap-2">
<button class="inline-flex items-center gap-2 rounded-md bg-neutral-800 hover:bg-neutral-700 px-3 py-1.5 text-sm font-medium" data-ai-target="summary">
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" data-lucide="wand-2" class="lucide lucide-wand-2 h-4 w-4"><path d="m21.64 3.64-1.28-1.28a1.21 1.21 0 0 0-1.72 0L2.36 18.64a1.21 1.21 0 0 0 0 1.72l1.28 1.28a1.2 1.2 0 0 0 1.72 0L21.64 5.36a1.2 1.2 0 0 0 0-1.72" class=""></path><path d="m14 7 3 3" class=""></path><path d="M5 6v4" class=""></path><path d="M19 14v4" class=""></path><path d="M10 2v2" class=""></path><path d="M7 8H3" class=""></path><path d="M21 16h-4" class=""></path><path d="M11 3H9" class=""></path></svg>
Generate
</button>
</div>
</div>
<div id="section-summary" class="px-4 sm:px-6 py-5 space-y-4">
<!-- Summary option 1 -->
<div class="space-y-2">
<div class="flex items-start gap-2">
<label class="relative inline-flex items-center mt-1">
<input type="checkbox" class="peer sr-only include-checkbox" checked="" data-include-for="summary1">
<span class="h-5 w-5 rounded-md border border-neutral-700 bg-neutral-900 ring-1 ring-inset ring-neutral-800 flex items-center justify-center peer-checked:bg-emerald-600 peer-checked:border-emerald-600 transition">
<svg viewBox="0 0 24 24" class="h-3.5 w-3.5 text-white opacity-0 peer-checked:opacity-100 transition"><path fill="currentColor" d="M20.285 6.709a1 1 0 0 1 0 1.414l-9.19 9.19a1 1 0 0 1-1.414 0l-5.19-5.19a1 1 0 0 1 1.414-1.414l4.483 4.483 8.483-8.483a1 1 0 0 1 1.414 0Z" class=""></path></svg>
</span>
</label>
<textarea id="summary1" rows="3" class="w-full rounded-lg bg-neutral-900 border border-neutral-800 focus:outline-none focus:ring-2 focus:ring-emerald-600/60 px-3 py-2 text-sm">With 13+ years of experience, I lead UX design, strategy, and research for financial services products. Increased onboarding success by 25% and drove 100,000+ app downloads in 12 months. Previously shipped a self-service mobile app with measurable impact.</textarea>
</div>
</div>
<!-- Summary option 2 -->
<div class="space-y-2">
<div class="flex items-start gap-2">
<label class="relative inline-flex items-center mt-1">
<input type="checkbox" class="peer sr-only include-checkbox" data-include-for="summary2">
<span class="h-5 w-5 rounded-md border border-neutral-700 bg-neutral-900 ring-1 ring-inset ring-neutral-800 flex items-center justify-center peer-checked:bg-emerald-600 peer-checked:border-emerald-600 transition">
<svg viewBox="0 0 24 24" class="h-3.5 w-3.5 text-white opacity-0 peer-checked:opacity-100 transition"><path fill="currentColor" d="M20.285 6.709a1 1 0 0 1 0 1.414l-9.19 9.19a1 1 0 0 1-1.414 0l-5.19-5.19a1 1 0 0 1 1.414-1.414l4.483 4.483 8.483-8.483a1 1 0 0 1 1.414 0Z" class=""></path></svg>
</span>
</label>
<textarea id="summary2" rows="3" class="w-full rounded-lg bg-neutral-900 border border-neutral-800 focus:outline-none focus:ring-2 focus:ring-emerald-600/60 px-3 py-2 text-sm" placeholder="Alternative summary (optional)"></textarea>
</div>
</div>
</div>
</div>
<!-- Work Experience -->
<div class="rounded-2xl border border-neutral-800 bg-neutral-900/50">
<div class="flex sm:px-6 border-neutral-800 border-b pt-4 pr-4 pb-4 pl-4 items-center justify-between">
<button type="button" data-toggle="work" aria-controls="section-work" aria-expanded="true" class="group inline-flex items-center gap-2">
<span class="h-7 w-7 rounded-md bg-neutral-800 flex items-center justify-center">
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" data-lucide="chevron-down" class="lucide lucide-chevron-down h-4 w-4 transition-transform" data-chevron="work"><path d="m6 9 6 6 6-6" class=""></path></svg>
</span>
<h2 class="text-lg sm:text-xl tracking-tight font-semibold">Work Experience</h2>
</button>
<div class="flex items-center gap-2">
<button class="inline-flex items-center gap-2 rounded-md bg-neutral-800 hover:bg-neutral-700 px-3 py-1.5 text-sm font-medium" data-ai-target="work">
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" data-lucide="wand-2" class="lucide lucide-wand-2 h-4 w-4"><path d="m21.64 3.64-1.28-1.28a1.21 1.21 0 0 0-1.72 0L2.36 18.64a1.21 1.21 0 0 0 0 1.72l1.28 1.28a1.2 1.2 0 0 0 1.72 0L21.64 5.36a1.2 1.2 0 0 0 0-1.72" class=""></path><path d="m14 7 3 3" class=""></path><path d="M5 6v4" class=""></path><path d="M19 14v4" class=""></path><path d="M10 2v2" class=""></path><path d="M7 8H3" class=""></path><path d="M21 16h-4" class=""></path><path d="M11 3H9" class=""></path></svg>
Generate bullets
</button>
</div>
</div>
<div class="sm:px-6 pt-5 pr-4 pb-5 pl-4 space-y-5" id="section-work">
<!-- Experience Item -->
<div class="rounded-xl border border-neutral-800 bg-neutral-950/40">
<div
class="flex flex-col sm:flex-row sm:items-center justify-between gap-3 border-b border-neutral-800 px-4 py-3">
<div class="flex items-center gap-3">
<label class="relative inline-flex items-center">
<input type="checkbox" class="peer sr-only include-checkbox" checked="" data-include-for="company1">
<span class="h-5 w-5 rounded-md border border-neutral-700 bg-neutral-900 ring-1 ring-inset ring-neutral-800 flex items-center justify-center peer-checked:bg-emerald-600 peer-checked:border-emerald-600 transition">
<svg viewBox="0 0 24 24" class="h-3.5 w-3.5 text-white opacity-0 peer-checked:opacity-100 transition"><path fill="currentColor" d="M20.285 6.709a1 1 0 0 1 0 1.414l-9.19 9.19a1 1 0 0 1-1.414 0l-5.19-5.19a1 1 0 0 1 1.414-1.414l4.483 4.483 8.483-8.483a1 1 0 0 1 1.414 0Z" class=""></path></svg>
</span>
</label>
<input id="company1" type="text" value="Amazon" class="bg-transparent border border-neutral-800 rounded-md px-2.5 py-1.5 text-sm">
</div>
<div class="grid grid-cols-1 sm:grid-cols-3 gap-2 sm:items-center w-full sm:w-auto">
<input id="role1" type="text" value="Senior UX Designer · Global Instalment Lending · Payments Product" class="bg-transparent border border-neutral-800 rounded-md px-2.5 py-1.5 text-sm sm:col-span-2">
<input id="dates1" type="text" value="Jan 2021 – Present · UK · Full-time" class="bg-transparent border border-neutral-800 rounded-md px-2.5 py-1.5 text-sm">
</div>
</div>
<div class="p-4 space-y-3">
<!-- Bullet 1 -->
<div class="flex items-start gap-2">
<label class="relative inline-flex items-center mt-1">
<input type="checkbox" class="peer sr-only include-checkbox" checked="" data-include-for="bullet1-1">
<span class="h-5 w-5 rounded-md border border-neutral-700 bg-neutral-900 ring-1 ring-inset ring-neutral-800 flex items-center justify-center peer-checked:bg-emerald-600 peer-checked:border-emerald-600 transition">
<svg viewBox="0 0 24 24" class="h-3.5 w-3.5 text-white opacity-0 peer-checked:opacity-100 transition"><path fill="currentColor" d="M20.285 6.709a1 1 0 0 1 0 1.414l-9.19 9.19a1 1 0 0 1-1.414 0l-5.19-5.19a1 1 0 0 1 1.414-1.414l4.483 4.483 8.483-8.483a1 1 0 0 1 1.414 0Z" class=""></path></svg>
</span>
</label>
<textarea id="bullet1-1" rows="2" class="w-full rounded-lg bg-neutral-900 border border-neutral-800 focus:outline-none focus:ring-2 focus:ring-emerald-600/60 px-3 py-2 text-sm">Led experience design, strategy, and research for Amazon Insurance Store; increased conversions by 35% and reduced customer complaints by 25% within first year.</textarea>
</div>
<!-- Bullet 2 -->
<div class="flex items-start gap-2">
<label class="relative inline-flex items-center mt-1">
<input type="checkbox" class="peer sr-only include-checkbox" data-include-for="bullet1-2">
<span class="h-5 w-5 rounded-md border border-neutral-700 bg-neutral-900 ring-1 ring-inset ring-neutral-800 flex items-center justify-center peer-checked:bg-emerald-600 peer-checked:border-emerald-600 transition">
<svg viewBox="0 0 24 24" class="h-3.5 w-3.5 text-white opacity-0 peer-checked:opacity-100 transition"><path fill="currentColor" d="M20.285 6.709a1 1 0 0 1 0 1.414l-9.19 9.19a1 1 0 0 1-1.414 0l-5.19-5.19a1 1 0 0 1 1.414-1.414l4.483 4.483 8.483-8.483a1 1 0 0 1 1.414 0Z" class=""></path></svg>
</span>
</label>
<textarea id="bullet1-2" rows="2" class="w-full rounded-lg bg-neutral-900 border border-neutral-800 focus:outline-none focus:ring-2 focus:ring-emerald-600/60 px-3 py-2 text-sm" placeholder="Add an impact-driven bullet (optional)"></textarea>
</div>
<div class="flex pt-2 items-center justify-between">
<button class="inline-flex items-center gap-2 text-sm text-neutral-300 hover:text-white" data-add-bullet="1">
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" data-lucide="plus" class="lucide lucide-plus h-4 w-4"><path d="M5 12h14" class=""></path><path d="M12 5v14" class=""></path></svg> Add bullet
</button>
</div>
</div>
</div>
<!-- Add more roles if needed -->
</div>
</div>
<div class="bg-neutral-900/50 border-neutral-800 border rounded-2xl">
<div class="flex sm:px-6 border-neutral-800 border-b pt-4 pr-4 pb-4 pl-4 items-center justify-between">
<button type="button" data-toggle="education" aria-controls="section-education" aria-expanded="true" class="group inline-flex items-center gap-2">
<span class="h-7 w-7 rounded-md bg-neutral-800 flex items-center justify-center">
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" data-lucide="chevron-down" class="lucide lucide-chevron-down h-4 w-4 transition-transform" data-chevron="education"><path d="m6 9 6 6 6-6" class=""></path></svg>
</span>
<h2 class="text-lg sm:text-xl tracking-tight font-semibold">Education</h2>
</button>
<div class="flex items-center gap-2">
</div>
</div>
<div class="sm:px-6 pt-5 pr-4 pb-5 pl-4 space-y-5" id="section-education">
<div class="rounded-xl border border-neutral-800 bg-neutral-950/40">
<div class="pt-4 pr-4 pb-4 pl-4 space-y-4">
<div id="education-list" class="space-y-6"></div>
<div class="pt-2 flex items-center justify-between">
<button type="button" class="inline-flex gap-2 hover:text-white text-sm text-neutral-300 gap-x-2 gap-y-2 items-center" id="btn-add-education">
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" data-lucide="plus" class="lucide lucide-plus h-4 w-4"><path d="M5 12h14" class=""></path><path d="M12 5v14" class=""></path></svg>
Add education
<script>
(function() {
const btn = document.getElementById('btn-add-education');
function renderIconsSafe() { try { if (window.lucide) window.lucide.createIcons(); } catch(e) {} }
function monthValue(val) {
// Accepts "", "YYYY-MM" or any date-like string and returns "YYYY-MM" or ""
if (!val) return '';
if (/^\d{4}-\d{2}$/.test(val)) return val;
const d = new Date(val);
if (isNaN(d.getTime())) return '';
const m = String(d.getMonth() + 1).padStart(2, '0');
const y = d.getFullYear();
return y + '-' + m;
}
function closeModal(modal) {
if (modal && modal.parentNode) modal.parentNode.removeChild(modal);
}
function buildModal() {
const overlay = document.createElement('div');
overlay.id = 'modal-add-education';
overlay.className = 'fixed inset-0 z-40 flex items-center justify-center p-4 bg-neutral-950/40 backdrop-blur';
overlay.innerHTML = `
<div class="w-full sm:w-auto sm:max-w-3xl rounded-2xl border border-neutral-800 bg-neutral-900 shadow-lg ring-1 ring-inset ring-neutral-800">
<div class="flex items-center justify-between px-4 sm:px-5 py-3 border-b border-neutral-800">
<h3 class="text-lg sm:text-xl tracking-tight font-semibold text-neutral-100">Add Education</h3>
<button type="button" data-close class="inline-flex items-center justify-center h-8 w-8 rounded-md text-neutral-300 hover:text-white hover:bg-neutral-800/60">
<i data-lucide="x" class="h-4 w-4"></i>
</button>
</div>
<div class="px-4 sm:px-5 py-4">
<div class="grid grid-cols-1 gap-4">
<div class="grid sm:grid-cols-12 gap-3 items-center">
<label class="sm:col-span-3 text-sm text-neutral-300">School or Institution</label>
<div class="sm:col-span-9">
<input type="text" data-field="institution" placeholder="e.g. Columbia University" class="w-full rounded-lg bg-neutral-900 border border-neutral-800 focus:outline-none focus:ring-2 focus:ring-emerald-600/60 px-3 py-2 text-sm">
</div>
</div>
<div class="grid sm:grid-cols-12 gap-3 items-center">
<label class="sm:col-span-3 text-sm text-neutral-300">Location</label>
<div class="sm:col-span-9 grid sm:grid-cols-2 gap-3">
<input type="text" data-field="location" placeholder="e.g. New York, NY" class="w-full rounded-lg bg-neutral-900 border border-neutral-800 focus:outline-none focus:ring-2 focus:ring-emerald-600/60 px-3 py-2 text-sm">
<input type="text" data-field="gpa" placeholder="e.g. 3.8/4.0" class="w-full rounded-lg bg-neutral-900 border border-neutral-800 focus:outline-none focus:ring-2 focus:ring-emerald-600/60 px-3 py-2 text-sm" aria-label="GPA">
</div>
</div>
<div class="grid sm:grid-cols-12 gap-3 items-center">
<label class="sm:col-span-3 text-sm text-neutral-300">Degree</label>
<div class="sm:col-span-9 grid sm:grid-cols-2 gap-3">
<input type="text" data-field="degree" placeholder="e.g. Bachelor's" class="w-full rounded-lg bg-neutral-900 border border-neutral-800 focus:outline-none focus:ring-2 focus:ring-emerald-600/60 px-3 py-2 text-sm">
<input type="text" data-field="field" placeholder="e.g. Marketing" class="w-full rounded-lg bg-neutral-900 border border-neutral-800 focus:outline-none focus:ring-2 focus:ring-emerald-600/60 px-3 py-2 text-sm" aria-label="Field of Study">
</div>
</div>
<div class="grid sm:grid-cols-12 gap-3 items-center">
<label class="sm:col-span-3 text-sm text-neutral-300">Dates</label>
<div class="sm:col-span-9 grid sm:grid-cols-2 gap-3">
<input type="month" data-field="start" placeholder="Start" class="w-full rounded-lg bg-neutral-900 border border-neutral-800 focus:outline-none focus:ring-2 focus:ring-emerald-600/60 px-3 py-2 text-sm">
<input type="month" data-field="end" placeholder="End" class="w-full rounded-lg bg-neutral-900 border border-neutral-800 focus:outline-none focus:ring-2 focus:ring-emerald-600/60 px-3 py-2 text-sm">
</div>
</div>
<div class="grid sm:grid-cols-12 gap-3">
<label class="sm:col-span-3 text-sm text-neutral-300">Additional Information</label>
<div class="sm:col-span-9">
<textarea rows="4" data-field="notes" placeholder="e.g. Dean's List, relevant coursework, or academic awards" class="w-full rounded-lg bg-neutral-900 border border-neutral-800 focus:outline-none focus:ring-2 focus:ring-emerald-600/60 px-3 py-2 text-sm"></textarea>
</div>
</div>
</div>
</div>
<div class="px-4 sm:px-5 py-3 border-t border-neutral-800 flex items-center justify-end gap-2">
<button type="button" data-close class="inline-flex items-center gap-2 text-sm text-neutral-300 hover:text-white px-3 py-2 rounded-md">Cancel</button>
<button type="button" data-save class="inline-flex items-center gap-2 text-sm font-medium text-white bg-emerald-600 hover:bg-emerald-500 px-3 py-2 rounded-md">
<i data-lucide="save" class="h-4 w-4"></i>
Save
</button>
</div>
</div>
`;
return overlay;
}
function insertEducationItem(data) {
const list = document.getElementById('education-list');
if (!list) return;
const id = 'edu-' + Date.now();
const item = document.createElement('div');
item.className = 'education-item rounded-xl border border-neutral-800 bg-neutral-950/40';
item.setAttribute('data-edu-id', id);
item.innerHTML = `
<div class="px-4 py-4 space-y-4">
<div class="flex items-center gap-2">
<label class="relative inline-flex items-center">
<input type="checkbox" class="peer sr-only edu-include" checked data-include-for="education-${id}">
<span class="h-5 w-5 rounded-md border border-neutral-700 bg-neutral-900 ring-1 ring-inset ring-neutral-800 flex items-center justify-center peer-checked:bg-emerald-600 peer-checked:border-emerald-600 transition">
<svg viewBox="0 0 24 24" class="h-3.5 w-3.5 text-white opacity-0 peer-checked:opacity-100 transition"><path fill="currentColor" d="M20.285 6.709a1 1 0 0 1 0 1.414l-9.19 9.19a1 1 0 0 1-1.414 0l-5.19-5.19a1 1 0 0 1 1.414-1.414l4.483 4.483 8.483-8.483a1 1 0 0 1 1.414 0Z"></path></svg>
</span>
</label>
<input type="text" class="w-full bg-transparent border border-neutral-800 rounded-md px-2.5 py-1.5 text-sm edu-institution" value="">
</div>
<div class="grid sm:grid-cols-2 gap-2">
<input type="text" class="bg-transparent border border-neutral-800 rounded-md px-2.5 py-1.5 text-sm edu-degree" placeholder="Degree">
<input type="text" class="bg-transparent border border-neutral-800 rounded-md px-2.5 py-1.5 text-sm edu-field" placeholder="Field of study">
</div>
<div class="grid sm:grid-cols-3 gap-2">
<input type="month" class="bg-transparent border border-neutral-800 rounded-md px-2.5 py-1.5 text-sm edu-start">
<input type="month" class="bg-transparent border border-neutral-800 rounded-md px-2.5 py-1.5 text-sm edu-end">
<input type="text" class="bg-transparent border border-neutral-800 rounded-md px-2.5 py-1.5 text-sm edu-location" placeholder="Location">
</div>
<textarea rows="3" class="w-full rounded-md bg-neutral-900 border border-neutral-800 px-3 py-2 text-sm edu-notes" placeholder="Notes (optional)"></textarea>
<div class="flex items-center justify-end">
<button type="button" data-remove-education class="inline-flex items-center gap-1 text-sm text-neutral-300 hover:text-white px-2 py-1">
<i data-lucide="trash-2" class="h-4 w-4"></i>
Remove
</button>
</div>
</div>
`;
list.appendChild(item);
// Populate values
item.querySelector('.edu-institution').value = data.institution || '';
item.querySelector('.edu-location').value = data.location || '';
item.querySelector('.edu-degree').value = data.degree || '';
item.querySelector('.edu-field').value = data.field || '';
item.querySelector('.edu-start').value = monthValue(data.start);
item.querySelector('.edu-end').value = monthValue(data.end);
// Merge GPA into notes if provided
const notesEl = item.querySelector('.edu-notes');
const baseNotes = data.notes || '';
const notesWithGPA = data.gpa ? (baseNotes ? (baseNotes + ' • GPA: ' + data.gpa) : ('GPA: ' + data.gpa)) :
baseNotes;
notesEl.value = notesWithGPA;
// Remove handler
const removeBtn = item.querySelector('[data-remove-education]');
if (removeBtn) {
removeBtn.addEventListener('click', () => {
if (item && item.parentNode) item.parentNode.removeChild(item);
try { if (window.buildPreview) window.buildPreview(); } catch(e) {}
});
}
renderIconsSafe();
try { if (window.buildPreview) window.buildPreview(); } catch(e) {}
item.scrollIntoView({ behavior: 'smooth', block: 'center' });
}
function openModal() {
const modal = buildModal();
document.body.appendChild(modal);
renderIconsSafe();
const closeBtns = modal.querySelectorAll('[data-close]');
closeBtns.forEach(b => b.addEventListener('click', () => closeModal(modal)));
modal.addEventListener('click', (e) => {
if (e.target === modal) closeModal(modal);
});
document.addEventListener('keydown', function escHandler(e) {
if (e.key === 'Escape') {
closeModal(modal);
document.removeEventListener('keydown', escHandler);
}
});
const first = modal.querySelector('input[data-field="institution"]');
if (first) first.focus();
const saveBtn = modal.querySelector('[data-save]');
saveBtn.addEventListener('click', () => {
const vals = {};
modal.querySelectorAll('[data-field]').forEach(input => {
vals[input.getAttribute('data-field')] = input.value.trim();
});
// Simple validation for institution
const instEl = modal.querySelector('input[data-field="institution"]');
instEl.classList.remove('ring-2','ring-red-500/60');
if (!vals.institution) {
instEl.classList.add('ring-2','ring-red-500/60');
instEl.focus();
return;
}
insertEducationItem(vals);
closeModal(modal);
});
}
// Intercept the existing click handler (if any) and open modal instead
if (!btn.__eduModalBound) {
btn.addEventListener('click', (e) => {
// Prevent other bound listeners (from the page script) so we can use our modal flow
e.preventDefault();
e.stopImmediatePropagation();
openModal();
});
btn.__eduModalBound = true;
}
})();
</script>
</button>
</div>
<div class="pt-4">
<div class="flex items-center justify-between">
<label class="block text-sm text-neutral-300">Certifications</label>
<button type="button" class="inline-flex gap-2 hover:text-white text-sm text-neutral-300 gap-x-2 gap-y-2 items-center" id="btn-add-certification">
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" data-lucide="plus" class="lucide lucide-plus h-4 w-4"><path d="M5 12h14" class=""></path><path d="M12 5v14" class=""></path></svg>
Add certification
<script>
(function() {
const btn = document.querySelector('button[data-element-id="aura-emgbgsv1o"]');
if (!btn || btn.__certModalBound) return;
btn.__certModalBound = true;
function renderIconsSafe() { try { if (window.lucide) window.lucide.createIcons(); } catch(e) {} }
function uid(prefix) { uid._i = (uid._i || Date.now()); uid._i += 1; return prefix + '-' + uid._i; }
function closeModal(modal) {
if (modal && modal.parentNode) modal.parentNode.removeChild(modal);
}
function buildModal() {
const overlay = document.createElement('div');
overlay.id = 'modal-add-certification';
overlay.className = 'fixed inset-0 z-50 flex items-center justify-center bg-neutral-950/80 backdrop-blur-sm p-4';
overlay.innerHTML = `
<div class="w-full max-w-lg rounded-2xl border border-neutral-800 bg-neutral-900 shadow-2xl ring-1 ring-inset ring-neutral-800">
<div class="flex items-center justify-between px-5 py-4 border-b border-neutral-800">
<h3 class="text-lg tracking-tight font-semibold text-neutral-100">Add Certification</h3>
<button type="button" data-close class="inline-flex items-center justify-center h-8 w-8 rounded-md text-neutral-300 hover:text-white hover:bg-neutral-800">
<i data-lucide="x" class="h-4 w-4"></i>
</button>
</div>
<div class="px-5 py-5 space-y-4">
<div>
<label class="block text-sm text-neutral-300 mb-2">Certification name</label>
<input type="text" data-field="name" placeholder="e.g. AWS Certified Solutions Architect" class="w-full rounded-lg bg-neutral-900 border border-neutral-800 focus:outline-none focus:ring-2 focus:ring-emerald-600/60 px-3 py-2 text-sm">
</div>
<div>
<label class="block text-sm text-neutral-300 mb-2">Provider</label>
<input type="text" data-field="provider" placeholder="e.g. Amazon Web Services" class="w-full rounded-lg bg-neutral-900 border border-neutral-800 focus:outline-none focus:ring-2 focus:ring-emerald-600/60 px-3 py-2 text-sm">
</div>
<div class="grid grid-cols-1 sm:grid-cols-2 gap-3">
<div>
<label class="block text-sm text-neutral-300 mb-2">Start date</label>
<input type="month" data-field="start" class="w-full rounded-lg bg-neutral-900 border border-neutral-800 focus:outline-none focus:ring-2 focus:ring-emerald-600/60 px-3 py-2 text-sm">
</div>
</div>
</div>
<div class="flex items-center justify-end gap-2 px-5 py-4 border-t border-neutral-800">
<button type="button" data-close class="inline-flex items-center gap-2 rounded-md text-sm text-neutral-300 hover:text-white px-3 py-2">Cancel</button>
<button type="button" data-save class="inline-flex items-center gap-2 rounded-md bg-emerald-600 hover:bg-emerald-500 text-white px-3 py-2 text-sm font-medium">
<i data-lucide="save" class="h-4 w-4"></i>
Save
</button>
</div>
</div>
`;
return overlay;
}
function formatMonth(val) {
if (!val) return '';
const [y, m] = val.split('-');
if (!y || !m) return '';
try {
return new Date(Number(y), Number(m) - 1, 1).toLocaleString(undefined, { month: 'short', year: 'numeric' });
} catch(e) { return y + '-' + m; }
}
function createCertItem({ name, provider, start }) {
const list = document.getElementById('certifications-list');
if (!list) return;
const id = uid('cert');
const wrap = document.createElement('div');
wrap.className = 'cert-item rounded-xl border border-neutral-800 bg-neutral-950/40';
wrap.setAttribute('data-cert-id', id);
wrap.innerHTML = `
<div class="px-4 py-4 space-y-3">
<div class="flex items-start gap-2">
<label class="relative inline-flex items-center mt-1">
<input type="checkbox" class="peer sr-only cert-include include-checkbox" checked data-include-for="cert-${id}">
<span class="h-5 w-5 rounded-md border border-neutral-700 bg-neutral-900 ring-1 ring-inset ring-neutral-800 flex items-center justify-center peer-checked:bg-emerald-600 peer-checked:border-emerald-600 transition">
<svg viewBox="0 0 24 24" class="h-3.5 w-3.5 text-white opacity-0 peer-checked:opacity-100 transition"><path fill="currentColor" d="M20.285 6.709a1 1 0 0 1 0 1.414l-9.19 9.19a1 1 0 0 1-1.414 0l-5.19-5.19a1 1 0 0 1 1.414-1.414l4.483 4.483 8.483-8.483a1 1 0 0 1 1.414 0Z"></path></svg>
</span>
</label>
<div class="flex-1 grid sm:grid-cols-3 gap-2">
<input type="text" class="cert-name bg-transparent border border-neutral-800 rounded-md px-2.5 py-1.5 text-sm" placeholder="Certification name" value="${name.replace(/"/g,'"')}">
<input type="text" class="cert-issuer bg-transparent border border-neutral-800 rounded-md px-2.5 py-1.5 text-sm" placeholder="Provider" value="${(provider || '').replace(/"/g,'"')}">
<input type="month" class="cert-date bg-transparent border border-neutral-800 rounded-md px-2.5 py-1.5 text-sm" value="${start || ''}">
</div>
<button type="button" data-remove-cert class="inline-flex items-center gap-1 text-sm text-neutral-300 hover:text-white px-2 py-1">
<i data-lucide="trash-2" class="h-4 w-4"></i>
Remove
</button>
</div>
<div class="text-xs text-neutral-500 pl-8">
<span class="js-cert-preview">${[provider, formatMonth(start)].filter(Boolean).join(' • ')}</span>
</div>
</div>
`;
list.appendChild(wrap);
// Bind interactions
const nameEl = wrap.querySelector('.cert-name');
const provEl = wrap.querySelector('.cert-issuer');
const dateEl = wrap.querySelector('.cert-date');
const includeEl = wrap.querySelector('.cert-include');
const metaEl = wrap.querySelector('.js-cert-preview');
function updateMeta() {
metaEl.textContent = [provEl.value.trim(), formatMonth(dateEl.value)].filter(Boolean).join(' • ');
}
[nameEl, provEl, dateEl].forEach(el => {
el.addEventListener('input', () => {
updateMeta();
try { if (window.buildPreview) window.buildPreview(); } catch(e) {}
});
});
if (includeEl) {
includeEl.addEventListener('change', () => {
try { if (window.buildPreview) window.buildPreview(); } catch(e) {}
});
}
const rm = wrap.querySelector('[data-remove-cert]');
if (rm) {
rm.addEventListener('click', () => {
if (wrap && wrap.parentNode) wrap.parentNode.removeChild(wrap);
try { if (window.buildPreview) window.buildPreview(); } catch(e) {}
});
}
renderIconsSafe();
try { if (window.buildPreview) window.buildPreview(); } catch(e) {}
wrap.scrollIntoView({ behavior: 'smooth', block: 'center' });
}
function openModal() {
const modal = buildModal();
document.body.appendChild(modal);
renderIconsSafe();
const nameEl = modal.querySelector('[data-field="name"]');
const provEl = modal.querySelector('[data-field="provider"]');
const startEl = modal.querySelector('[data-field="start"]');
const saveBtn = modal.querySelector('[data-save]');
const closeBtns = modal.querySelectorAll('[data-close]');
closeBtns.forEach(b => b.addEventListener('click', () => closeModal(modal)));
modal.addEventListener('click', (e) => { if (e.target === modal) closeModal(modal); });
document.addEventListener('keydown', function escHandler(e) {
if (e.key === 'Escape') { closeModal(modal); document.removeEventListener('keydown', escHandler); }
});
if (nameEl) nameEl.focus();
saveBtn.addEventListener('click', () => {
nameEl.classList.remove('ring-2','ring-red-500/60');
const name = (nameEl.value || '').trim();
const provider = (provEl.value || '').trim();
const start = (startEl.value || '').trim();
if (!name) {
nameEl.classList.add('ring-2','ring-red-500/60');
nameEl.focus();
return;
}
createCertItem({ name, provider, start });
closeModal(modal);
});
}
btn.addEventListener('click', (e) => {
e.preventDefault();
e.stopImmediatePropagation();
openModal();
});
})();
</script>
</button>
</div>
<div id="certifications-list" class="mt-2 space-y-3"></div>
</div>
<script>
(function() {
const root = document.querySelector('[data-element-id="aura-emgbfan1g"]');
if (!root) return;
function renderIconsSafe() { try { if (window.lucide) window.lucide.createIcons(); } catch(e) {} }
function uid(prefix) {
uid._i = (uid._i || Date.now());
uid._i += 1;
return prefix + '-' + uid._i;
}
function formatMonth(val) {
if (!val) return '';
const [y, m] = val.split('-');
if (!y || !m) return '';
const date = new Date(Number(y), Number(m) - 1, 1);
try {
return date.toLocaleString(undefined, { month: 'short', year: 'numeric' });
} catch(e) {
return y + '-' + m;
}
}
function q(sel, ctx = root) { return ctx.querySelector(sel); }
function qa(sel, ctx = root) { return Array.from(ctx.querySelectorAll(sel)); }
function getEducationData() {
const items = [];
qa('#education-list .education-item').forEach(item => {
const include = !!q('.edu-include', item)?.checked;
const institution = (q('.edu-institution', item)?.value || '').trim();
const location = (q('.edu-location', item)?.value || '').trim();
const degree = (q('.edu-degree', item)?.value || '').trim();
const field = (q('.edu-field', item)?.value || '').trim();
const start = (q('.edu-start', item)?.value || '').trim();
const end = (q('.edu-end', item)?.value || '').trim();
const notes = (q('.edu-notes', item)?.value || '').trim();
const id = item.getAttribute('data-edu-id') || uid('edu');
items.push({ id, include, institution, location, degree, field, start, end, notes });
});
return items;
}
function getCertData() {
const items = [];
qa('#certifications-list .cert-item').forEach(item => {
const include = !!q('.cert-include', item)?.checked;
const name = (q('.cert-name', item)?.value || '').trim();
const issuer = (q('.cert-issuer', item)?.value || '').trim();
const date = (q('.cert-date', item)?.value || '').trim();
const id = item.getAttribute('data-cert-id') || uid('cert');
items.push({ id, include, name, issuer, date });
});
return items;
}
function saveAll() {
try {
const data = {
education: getEducationData(),
certifications: getCertData()
};
localStorage.setItem('resume-education-data', JSON.stringify(data));
} catch(e) {}
}
function loadAll() {
try {
const raw = localStorage.getItem('resume-education-data');
if (!raw) return null;
return JSON.parse(raw);
} catch(e) { return null; }
}
function renderEducationPreview() {
const preview = document.getElementById('preview');
if (!preview) return;
const oldEdu = document.getElementById('education-preview');
const oldCert = document.getElementById('certifications-preview');
if (oldEdu && oldEdu.parentNode) oldEdu.parentNode.removeChild(oldEdu);
if (oldCert && oldCert.parentNode) oldCert.parentNode.removeChild(oldCert);
const edu = getEducationData().filter(e => e.include && (e.institution || e.degree || e.field || e.location));
const certs = getCertData().filter(c => c.include && (c.name || c.issuer));
if (edu.length) {
const block = document.createElement('div');
block.id = 'education-preview';
const head = document.createElement('div');
head.className = 'pt-4 text-sm font-medium text-neutral-300';
head.textContent = 'Education';
block.appendChild(head);
edu.forEach(e => {
const wrap = document.createElement('div');
wrap.className = 'mt-2 space-y-1';
if (e.institution) {
const inst = document.createElement('div');
inst.className = 'font-medium';
inst.textContent = e.institution;
wrap.appendChild(inst);
}
const line2Parts = [];
if (e.degree) line2Parts.push(e.degree);
if (e.field) line2Parts.push(e.field);
if (line2Parts.length) {
const d = document.createElement('div');
d.className = 'text-neutral-300';
d.textContent = line2Parts.join(' · ');
wrap.appendChild(d);
}
const line3Parts = [];
if (e.start) line3Parts.push(formatMonth(e.start));
if (e.end) line3Parts.push(formatMonth(e.end));
const dateStr = line3Parts.filter(Boolean).join(' – ');
const locStr = e.location ? e.location : '';
const meta = [dateStr, locStr].filter(Boolean).join(' · ');
if (meta) {
const m = document.createElement('div');
m.className = 'text-xs text-neutral-500';
m.textContent = meta;
wrap.appendChild(m);
}
if (e.notes) {
const p = document.createElement('div');
p.className = 'text-sm text-neutral-200';
p.textContent = e.notes;
wrap.appendChild(p);
}
block.appendChild(wrap);
});
preview.appendChild(block);
}
if (certs.length) {
const block = document.createElement('div');
block.id = 'certifications-preview';
const head = document.createElement('div');
head.className = 'pt-4 text-sm font-medium text-neutral-300';
head.textContent = 'Certifications';
block.appendChild(head);
const list = document.createElement('ul');
list.className = 'mt-2 space-y-1';
certs.forEach(c => {
const li = document.createElement('li');
li.className = 'text-sm text-neutral-200';
const parts = [];
if (c.name) parts.push(c.name);
if (c.issuer) parts.push('— ' + c.issuer);
const dateFmt = formatMonth(c.date);
if (dateFmt) parts.push('(' + dateFmt + ')');
li.textContent = parts.join(' ');
list.appendChild(li);
});
block.appendChild(list);
preview.appendChild(block);
}
}
function wrapBuildPreview() {
if (window._eduWrapped) return;
const tryWrap = () => {
if (typeof window.buildPreview === 'function') {
const orig = window.buildPreview;
window.buildPreview = function() {
orig();
renderEducationPreview();
};
window._eduWrapped = true;
renderEducationPreview();
} else {
setTimeout(tryWrap, 150);
}
};
tryWrap();
}
function attachEducationHandlers(item) {
qa('input, textarea', item).forEach(el => {
el.addEventListener('input', () => {
saveAll();
if (window.buildPreview) window.buildPreview();
});
if (el.type === 'checkbox') {
el.addEventListener('change', () => {
saveAll();
if (window.buildPreview) window.buildPreview();
});
}
});
const removeBtn = q('[data-remove-education]', item);
const dupBtn = q('[data-duplicate-education]', item);
if (removeBtn) {
removeBtn.addEventListener('click', () => {
const list = q('#education-list');
if (!list) return;
if (list.children.length > 1) {
item.parentNode.removeChild(item);
toggleRemoveButtons();
saveAll();
if (window.buildPreview) window.buildPreview();
}
});
}
if (dupBtn) {
dupBtn.addEventListener('click', () => {
const data = extractEducationFromItem(item);
addEducation(data);
});
}
}
function extractEducationFromItem(item) {
return {
include: !!q('.edu-include', item)?.checked,
institution: (q('.edu-institution', item)?.value || '').trim(),
location: (q('.edu-location', item)?.value || '').trim(),
degree: (q('.edu-degree', item)?.value || '').trim(),
field: (q('.edu-field', item)?.value || '').trim(),
start: (q('.edu-start', item)?.value || '').trim(),
end: (q('.edu-end', item)?.value || '').trim(),
notes: (q('.edu-notes', item)?.value || '').trim()
};
}
function addEducation(prefill) {
const tpl = q('#tpl-education-item');
const list = q('#education-list');
if (!tpl || !list) return;
const frag = document.importNode(tpl.content, true);
const item = frag.querySelector('.education-item');
const id = uid('edu');
item.setAttribute('data-edu-id', id);
const cb = item.querySelector('.edu-include');
if (cb) cb.setAttribute('data-include-for', 'education-' + id);
if (prefill) {
if (cb) cb.checked = !!prefill.include;
const set = (sel, val) => { const el = item.querySelector(sel); if (el) el.value = val || ''; };
set('.edu-institution', prefill.institution);
set('.edu-location', prefill.location);
set('.edu-degree', prefill.degree);
set('.edu-field', prefill.field);
set('.edu-start', prefill.start);
set('.edu-end', prefill.end);
set('.edu-notes', prefill.notes);
}
list.appendChild(frag);
const appended = list.querySelector('.education-item:last-child');
attachEducationHandlers(appended);
toggleRemoveButtons();
renderIconsSafe();
saveAll();
if (window.buildPreview) window.buildPreview();
}
function toggleRemoveButtons() {
const items = qa('#education-list .education-item');
items.forEach((it, idx) => {
const btn = q('[data-remove-education]', it);
if (btn) btn.classList.toggle('hidden', items.length <= 1);
});
}
function attachCertHandlers(item) {
qa('input', item).forEach(el => {
el.addEventListener('input', () => {
saveAll();
if (window.buildPreview) window.buildPreview();
});
if (el.type === 'checkbox') {
el.addEventListener('change', () => {
saveAll();
if (window.buildPreview) window.buildPreview();
});
}
});
const removeBtn = q('[data-remove-cert]', item);
if (removeBtn) {
removeBtn.addEventListener('click', () => {
item.parentNode.removeChild(item);
saveAll();
if (window.buildPreview) window.buildPreview();
});
}
}
function addCertification(prefill) {
const tpl = q('#tpl-certification-item');
const list = q('#certifications-list');
if (!tpl || !list) return;
const frag = document.importNode(tpl.content, true);
const item = frag.querySelector('.cert-item');
const id = uid('cert');
item.setAttribute('data-cert-id', id);
const cb = item.querySelector('.cert-include');
if (cb) cb.setAttribute('data-include-for', 'cert-' + id);
if (prefill) {
if (cb) cb.checked = !!prefill.include;
const set = (sel, val) => { const el = item.querySelector(sel); if (el) el.value = val || ''; };
set('.cert-name', prefill.name);
set('.cert-issuer', prefill.issuer);
set('.cert-date', prefill.date);
}
list.appendChild(frag);
const appended = list.querySelector('.cert-item:last-child');
attachCertHandlers(appended);
renderIconsSafe();
saveAll();
if (window.buildPreview) window.buildPreview();
}
function hydrateFromStorage() {
const data = loadAll();
if (!data) {
// Initialize existing first item IDs and handlers
const first = q('#education-list .education-item');
if (first) {
const id = first.getAttribute('data-edu-id') || 'edu-1';
first.setAttribute('data-edu-id', id);
const include = q('.edu-include', first);
if (include) include.setAttribute('data-include-for', 'education-' + id);
attachEducationHandlers(first);
}
toggleRemoveButtons();
renderIconsSafe();
if (window.buildPreview) window.buildPreview();
return;
}
// Rebuild education
const list = q('#education-list');
if (list) list.innerHTML = '';
(data.education || []).forEach(ed => addEducation(ed));
if (!data.education || !data.education.length) addEducation(null);
// Rebuild certifications
const certList = q('#certifications-list');
if (certList) certList.innerHTML = '';
(data.certifications || []).forEach(c => addCertification(c));
}
function setup() {
const addEdu = q('#btn-add-education');
if (addEdu) addEdu.addEventListener('click', () => addEducation(null));
const addCert = q('#btn-add-certification');
if (addCert) addCert.addEventListener('click', () => addCertification(null));
hydrateFromStorage();
wrapBuildPreview();
renderIconsSafe();
// Also trigger preview on input for first static item
qa('#education-list .education-item input, #education-list .education-item textarea').forEach(el => {
el.addEventListener('input', () => {
saveAll();
if (window.buildPreview) window.buildPreview();
});
if (el.type === 'checkbox') {
el.addEventListener('change', () => {
saveAll();
if (window.buildPreview) window.buildPreview();
});
}
});
}
setTimeout(setup, 100);
})();
</script>
</div>
</div>
</div>
</div>
<div class="bg-neutral-900/50 border-neutral-800 border rounded-2xl">
<div class="flex sm:px-6 border-neutral-800 border-b pt-4 pr-4 pb-4 pl-4 items-center justify-between">
<button type="button" data-toggle="skills" aria-controls="section-skills" aria-expanded="true" class="group inline-flex items-center gap-2">
<span class="h-7 w-7 rounded-md bg-neutral-800 flex items-center justify-center">
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" data-lucide="chevron-down" class="lucide lucide-chevron-down h-4 w-4 transition-transform" data-chevron="skills"><path d="m6 9 6 6 6-6" class=""></path></svg>
</span>
<h2 class="text-lg sm:text-xl tracking-tight font-semibold">Skills</h2>
</button>
<div class="flex items-center gap-2">
<button class="inline-flex items-center gap-2 rounded-md bg-neutral-800 hover:bg-neutral-700 px-3 py-1.5 text-sm font-medium" data-ai-target="skills">
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" data-lucide="wand-2" class="lucide lucide-wand-2 h-4 w-4"><path d="m21.64 3.64-1.28-1.28a1.21 1.21 0 0 0-1.72 0L2.36 18.64a1.21 1.21 0 0 0 0 1.72l1.28 1.28a1.2 1.2 0 0 0 1.72 0L21.64 5.36a1.2 1.2 0 0 0 0-1.72" class=""></path><path d="m14 7 3 3" class=""></path><path d="M5 6v4" class=""></path><path d="M19 14v4" class=""></path><path d="M10 2v2" class=""></path><path d="M7 8H3" class=""></path><path d="M21 16h-4" class=""></path><path d="M11 3H9" class=""></path></svg>
Generate skills
</button>
</div>
</div>
<div class="sm:px-6 pt-5 pr-4 pb-5 pl-4 space-y-4" id="section-skills">
<div id="skills-list" class="flex flex-wrap gap-2">
<!-- Chip: User Research -->
<div class="skill-row group relative">
<label class="inline-block cursor-pointer">
<input type="checkbox" class="peer sr-only include-checkbox" checked="" data-include-for="skill-ur">
<span class="inline-flex items-center gap-2 px-3 py-1.5 rounded-lg border border-neutral-700 ring-1 ring-inset ring-neutral-800 bg-neutral-900 hover:bg-neutral-800 text-sm text-neutral-100 transition peer-checked:border-emerald-600 peer-checked:ring-emerald-600/40">
<span class="h-5 w-5 rounded-md border border-neutral-700 bg-neutral-900 ring-1 ring-inset ring-neutral-800 flex items-center justify-center peer-checked:bg-emerald-600 peer-checked:border-emerald-600 transition">
<svg viewBox="0 0 24 24" class="h-3.5 w-3.5 text-white opacity-0 peer-checked:opacity-100 transition"><path fill="currentColor" d="M20.285 6.709a1 1 0 0 1 0 1.414l-9.19 9.19a1 1 0 0 1-1.414 0l-5.19-5.19a1 1 0 0 1 1.414-1.414l4.483 4.483 8.483-8.483a1 1 0 0 1 1.414 0Z" class=""></path></svg>
</span>
<span class="skill-label">User Research</span>
</span>
</label>
<input id="skill-ur" type="text" value="User Research" class="skill-input sr-only" aria-hidden="true" tabindex="-1">
<button type="button" class="absolute -top-2 -right-2 hidden group-hover:inline-flex items-center justify-center h-6 w-6 rounded-md text-neutral-300 hover:text-white hover:bg-neutral-800" data-remove-skill="" title="Remove">
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" data-lucide="x" class="lucide lucide-x h-4 w-4"><path d="M18 6 6 18" class=""></path><path d="m6 6 12 12" class=""></path></svg>
</button>
</div>
<!-- Chip: Interaction Design -->
<div class="skill-row group relative">
<label class="inline-block cursor-pointer">
<input type="checkbox" class="peer sr-only include-checkbox" checked="" data-include-for="skill-ixd">
<span class="inline-flex items-center gap-2 px-3 py-1.5 rounded-lg border border-neutral-700 ring-1 ring-inset ring-neutral-800 bg-neutral-900 hover:bg-neutral-800 text-sm text-neutral-100 transition peer-checked:border-emerald-600 peer-checked:ring-emerald-600/40">
<span class="h-5 w-5 rounded-md border border-neutral-700 bg-neutral-900 ring-1 ring-inset ring-neutral-800 flex items-center justify-center peer-checked:bg-emerald-600 peer-checked:border-emerald-600 transition">
<svg viewBox="0 0 24 24" class="h-3.5 w-3.5 text-white opacity-0 peer-checked:opacity-100 transition"><path fill="currentColor" d="M20.285 6.709a1 1 0 0 1 0 1.414l-9.19 9.19a1 1 0 0 1-1.414 0l-5.19-5.19a1 1 0 0 1 1.414-1.414l4.483 4.483 8.483-8.483a1 1 0 0 1 1.414 0Z" class=""></path></svg>
</span>
<span class="skill-label">Interaction Design</span>
</span>
</label>
<input id="skill-ixd" type="text" value="Interaction Design" class="skill-input sr-only" aria-hidden="true" tabindex="-1">
<button type="button" class="absolute -top-2 -right-2 hidden group-hover:inline-flex items-center justify-center h-6 w-6 rounded-md text-neutral-300 hover:text-white hover:bg-neutral-800" data-remove-skill="" title="Remove">
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" data-lucide="x" class="lucide lucide-x h-4 w-4"><path d="M18 6 6 18" class=""></path><path d="m6 6 12 12" class=""></path></svg>
</button>
</div>
<!-- Chip: Prototyping -->
<div class="skill-row group relative">
<label class="inline-block cursor-pointer">
<input type="checkbox" class="peer sr-only include-checkbox" checked="" data-include-for="skill-proto">
<span class="inline-flex items-center gap-2 px-3 py-1.5 rounded-lg border border-neutral-700 ring-1 ring-inset ring-neutral-800 bg-neutral-900 hover:bg-neutral-800 text-sm text-neutral-100 transition peer-checked:border-emerald-600 peer-checked:ring-emerald-600/40">
<span class="h-5 w-5 rounded-md border border-neutral-700 bg-neutral-900 ring-1 ring-inset ring-neutral-800 flex items-center justify-center peer-checked:bg-emerald-600 peer-checked:border-emerald-600 transition">
<svg viewBox="0 0 24 24" class="h-3.5 w-3.5 text-white opacity-0 peer-checked:opacity-100 transition"><path fill="currentColor" d="M20.285 6.709a1 1 0 0 1 0 1.414l-9.19 9.19a1 1 0 0 1-1.414 0l-5.19-5.19a1 1 0 0 1 1.414-1.414l4.483 4.483 8.483-8.483a1 1 0 0 1 1.414 0Z" class=""></path></svg>
</span>
<span class="skill-label">Prototyping</span>
</span>
</label>
<input id="skill-proto" type="text" value="Prototyping" class="skill-input sr-only" aria-hidden="true" tabindex="-1">
<button type="button" class="absolute -top-2 -right-2 hidden group-hover:inline-flex items-center justify-center h-6 w-6 rounded-md text-neutral-300 hover:text-white hover:bg-neutral-800" data-remove-skill="" title="Remove">
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" data-lucide="x" class="lucide lucide-x h-4 w-4"><path d="M18 6 6 18" class=""></path><path d="m6 6 12 12" class=""></path></svg>
</button>
</div>
<!-- Chip: Figma -->
<div class="skill-row group relative">
<label class="inline-block cursor-pointer">
<input type="checkbox" class="peer sr-only include-checkbox" checked="" data-include-for="skill-figma">
<span class="inline-flex items-center gap-2 px-3 py-1.5 rounded-lg border border-neutral-700 ring-1 ring-inset ring-neutral-800 bg-neutral-900 hover:bg-neutral-800 text-sm text-neutral-100 transition peer-checked:border-emerald-600 peer-checked:ring-emerald-600/40">
<span class="h-5 w-5 rounded-md border border-neutral-700 bg-neutral-900 ring-1 ring-inset ring-neutral-800 flex items-center justify-center peer-checked:bg-emerald-600 peer-checked:border-emerald-600 transition">
<svg viewBox="0 0 24 24" class="h-3.5 w-3.5 text-white opacity-0 peer-checked:opacity-100 transition"><path fill="currentColor" d="M20.285 6.709a1 1 0 0 1 0 1.414l-9.19 9.19a1 1 0 0 1-1.414 0l-5.19-5.19a1 1 0 0 1 1.414-1.414l4.483 4.483 8.483-8.483a1 1 0 0 1 1.414 0Z" class=""></path></svg>
</span>
<span class="skill-label">Figma</span>
</span>
</label>
<input id="skill-figma" type="text" value="Figma" class="skill-input sr-only" aria-hidden="true" tabindex="-1">
<button type="button" class="absolute -top-2 -right-2 hidden group-hover:inline-flex items-center justify-center h-6 w-6 rounded-md text-neutral-300 hover:text-white hover:bg-neutral-800" data-remove-skill="" title="Remove">
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" data-lucide="x" class="lucide lucide-x h-4 w-4"><path d="M18 6 6 18" class=""></path><path d="m6 6 12 12" class=""></path></svg>
</button>
</div>
</div>
<div class="flex pt-2 items-center justify-between">
<button class="inline-flex items-center gap-2 text-sm text-neutral-300 hover:text-white" id="btn-add-skill">
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" data-lucide="plus" class="lucide lucide-plus h-4 w-4"><path d="M5 12h14" class=""></path><path d="M12 5v14" class=""></path></svg>
Add skill
</button>
</div>
<script>
(function() {
function renderIconsSafe() { try { if (window.lucide) window.lucide.createIcons(); } catch(e) {} }
function uniqueId() { uniqueId.seed = (uniqueId.seed || Date.now()); uniqueId.seed += 1; return 'skill-' + uniqueId.seed; }
function getSkills() {
const rows = Array.from(document.querySelectorAll('#section-skills #skills-list .skill-row'));
const skills = [];
rows.forEach(row => {
const cb = row.querySelector('.include-checkbox');
const input = row.querySelector('.skill-input');
if (cb && input && cb.checked) {
const val = (input.value || '').trim();
if (val) skills.push(val);
}
});
return skills;
}
function renderSkillsPreview() {
const preview = document.getElementById('preview');
if (!preview) return;
const existing = document.getElementById('skills-preview');
if (existing && existing.parentNode) existing.parentNode.removeChild(existing);
const skills = getSkills();
if (!skills.length) return;
const block = document.createElement('div');
block.id = 'skills-preview';
const head = document.createElement('div');
head.className = 'pt-4 text-sm font-medium text-neutral-300';
head.textContent = 'Skills';
const list = document.createElement('div');
list.className = 'mt-2 flex flex-wrap gap-2';
skills.forEach(s => {
const chip = document.createElement('span');
chip.className = 'text-xs px-2.5 py-1 rounded-md bg-neutral-800 text-neutral-100';
chip.textContent = s;
list.appendChild(chip);
});
block.appendChild(head);
block.appendChild(list);
preview.appendChild(block);
}
function attachRowHandlers(row) {
const cb = row.querySelector('.include-checkbox');
const hiddenInput = row.querySelector('.skill-input');
const labelSpan = row.querySelector('.skill-label');
const removeBtn = row.querySelector('[data-remove-skill]');
const chip = row.querySelector('label > span');
if (cb) {
cb.addEventListener('change', () => {
if (window.buildPreview) window.buildPreview();
});
}
if (removeBtn) {
removeBtn.addEventListener('click', () => {
row.parentNode.removeChild(row);
if (window.buildPreview) window.buildPreview();
});
}
if (chip && hiddenInput && labelSpan) {
chip.addEventListener('dblclick', (e) => {
e.preventDefault();
const val = (hiddenInput.value || '').trim();
const newVal = prompt('Edit skill', val);
if (newVal !== null) {
const v = newVal.trim();
if (v) {
hiddenInput.value = v;
labelSpan.textContent = v;
if (window.buildPreview) window.buildPreview();
}
}
});
}
}
function addSkill(value = '') {
if (!value) {
const v = prompt('Add skill');
if (!v || !v.trim()) return;
value = v.trim();
}
const id = uniqueId();
const list = document.getElementById('skills-list');
if (!list) return;
const row = document.createElement('div');
row.className = 'skill-row group relative';
row.innerHTML = `
<label class="inline-block cursor-pointer">
<input type="checkbox" class="peer sr-only include-checkbox" checked data-include-for="${id}">
<span class="inline-flex items-center gap-2 px-3 py-1.5 rounded-lg border border-neutral-700 ring-1 ring-inset ring-neutral-800 bg-neutral-900 hover:bg-neutral-800 text-sm text-neutral-100 transition peer-checked:border-emerald-600 peer-checked:ring-emerald-600/40">
<span class="h-5 w-5 rounded-md border border-neutral-700 bg-neutral-900 ring-1 ring-inset ring-neutral-800 flex items-center justify-center peer-checked:bg-emerald-600 peer-checked:border-emerald-600 transition">
<svg viewBox="0 0 24 24" class="h-3.5 w-3.5 text-white opacity-0 peer-checked:opacity-100 transition"><path fill="currentColor" d="M20.285 6.709a1 1 0 0 1 0 1.414l-9.19 9.19a1 1 0 0 1-1.414 0l-5.19-5.19a1 1 0 0 1 1.414-1.414l4.483 4.483 8.483-8.483a1 1 0 0 1 1.414 0Z"></path></svg>
</span>
<span class="skill-label"></span>
</span>
</label>
<input id="${id}" type="text" value="" class="skill-input sr-only" aria-hidden="true" tabindex="-1">
<button type="button" class="absolute -top-2 -right-2 hidden group-hover:inline-flex items-center justify-center h-6 w-6 rounded-md text-neutral-300 hover:text-white hover:bg-neutral-800" data-remove-skill title="Remove">
<i data-lucide="x" class="h-4 w-4"></i>
</button>
`;
list.appendChild(row);
const input = row.querySelector('.skill-input');
const labelSpan = row.querySelector('.skill-label');
input.value = value.replace(/\s+/g, ' ').trim();
labelSpan.textContent = input.value;
attachRowHandlers(row);
renderIconsSafe();
if (window.buildPreview) window.buildPreview();
}
function bindExistingRows() {
document.querySelectorAll('#skills-list .skill-row').forEach(attachRowHandlers);
}
function generateSkills() {
const suggestions = [
'Usability Testing',
'A/B Testing',
'Information Architecture',
'Accessibility (WCAG)',
'Journey Mapping',
'Stakeholder Management',
'Rapid Prototyping',
'Heuristic Evaluation',
'Design Systems',
'Design Ops'
];
const current = new Set(getSkills().map(s => s.toLowerCase()));
let added = 0;
for (let s of suggestions) {
if (!current.has(s.toLowerCase())) {
addSkill(s);
added++;
}
if (added >= 5) break;
}
if (added === 0 && window.buildPreview) window.buildPreview();
}
function wrapBuildPreview() {
if (window._origBuildPreviewForSkills) return;
if (typeof window.buildPreview === 'function') {
window._origBuildPreviewForSkills = window.buildPreview;
window.buildPreview = function() {
window._origBuildPreviewForSkills();
renderSkillsPreview();
};
renderSkillsPreview();
} else {
setTimeout(wrapBuildPreview, 150);
}
}
function setup() {
const addBtn = document.getElementById('btn-add-skill');
if (addBtn) addBtn.addEventListener('click', () => addSkill(''));
document.querySelectorAll('[data-ai-target="skills"]').forEach(btn => {
btn.addEventListener('click', () => setTimeout(generateSkills, 850));
});
bindExistingRows();
wrapBuildPreview();
renderIconsSafe();
}
setTimeout(setup, 150);
})();
</script>
</div>
<script>
(function() {
function renderIconsSafe() { try { if (window.lucide) window.lucide.createIcons(); } catch(e) {} }
function uniqueId() {
uniqueId.seed = (uniqueId.seed || Date.now());
uniqueId.seed += 1;
return 'skill-' + uniqueId.seed;
}
function getSkills() {
const rows = Array.from(document.querySelectorAll('#section-skills #skills-list .skill-row'));
const skills = [];
rows.forEach(row => {
const cb = row.querySelector('.include-checkbox');
const input = row.querySelector('.skill-input');
if (cb && input && cb.checked) {
const val = (input.value || '').trim();
if (val) skills.push(val);
}
});
return skills;
}
function renderSkillsPreview() {
const preview = document.getElementById('preview');
if (!preview) return;
const existing = document.getElementById('skills-preview');
if (existing && existing.parentNode) existing.parentNode.removeChild(existing);
const skills = getSkills();
if (!skills.length) return;
const block = document.createElement('div');
block.id = 'skills-preview';
const head = document.createElement('div');
head.className = 'pt-4 text-sm font-medium text-neutral-300';
head.textContent = 'Skills';
const list = document.createElement('div');
list.className = 'mt-2 flex flex-wrap gap-2';
skills.forEach(s => {
const chip = document.createElement('span');
chip.className = 'text-xs px-2.5 py-1 rounded-md bg-neutral-800 text-neutral-100';
chip.textContent = s;
list.appendChild(chip);
});
block.appendChild(head);
block.appendChild(list);
preview.appendChild(block);
}
function attachRowHandlers(row) {
const input = row.querySelector('.skill-input');
const cb = row.querySelector('.include-checkbox');
const removeBtn = row.querySelector('[data-remove-skill]');
if (input) input.addEventListener('input', () => window.buildPreview && window.buildPreview());
if (cb) cb.addEventListener('change', () => window.buildPreview && window.buildPreview());
if (removeBtn) {
removeBtn.addEventListener('click', () => {
row.parentNode.removeChild(row);
if (window.buildPreview) window.buildPreview();
});
}
}
function addSkill(value = '') {
const id = uniqueId();
const list = document.getElementById('skills-list');
if (!list) return;
const row = document.createElement('div');
row.className = 'skill-row flex items-center gap-3';
row.innerHTML = `
<label class="relative inline-flex items-center">
<input type="checkbox" class="peer sr-only include-checkbox" checked data-include-for="${id}">
<span class="h-5 w-5 rounded-md border border-neutral-700 bg-neutral-900 ring-1 ring-inset ring-neutral-800 flex items-center justify-center peer-checked:bg-emerald-600 peer-checked:border-emerald-600 transition">
<svg viewBox="0 0 24 24" class="h-3.5 w-3.5 text-white opacity-0 peer-checked:opacity-100 transition"><path fill="currentColor" d="M20.285 6.709a1 1 0 0 1 0 1.414l-9.19 9.19a1 1 0 0 1-1.414 0l-5.19-5.19a1 1 0 0 1 1.414-1.414l4.483 4.483 8.483-8.483a1 1 0 0 1 1.414 0Z"></path></svg>
</span>
</label>
<input id="${id}" type="text" value="${value.replace(/"/g, '"')}" class="skill-input w-full rounded-lg bg-neutral-900 border border-neutral-800 focus:outline-none focus:ring-2 focus:ring-emerald-600/60 px-3 py-2 text-sm" placeholder="Add a skill">
<button type="button" class="inline-flex items-center gap-1 text-sm text-neutral-300 hover:text-white px-2 py-1" data-remove-skill title="Remove">
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" data-lucide="trash-2" class="lucide h-4 w-4"></svg>
</button>
`;
list.appendChild(row);
attachRowHandlers(row);
renderIconsSafe();
if (window.buildPreview) window.buildPreview();
const input = row.querySelector('.skill-input');
if (input && !value) input.focus();
}
function bindExistingRows() {
document.querySelectorAll('#skills-list .skill-row').forEach(attachRowHandlers);
}
function generateSkills() {
const suggestions = [
'Usability Testing',
'A/B Testing',
'Information Architecture',
'Accessibility (WCAG)',
'Journey Mapping',
'Design Critique',
'Stakeholder Management',
'Rapid Prototyping',
'Heuristic Evaluation',
'Design Ops'
];
const current = new Set(getSkills().map(s => s.toLowerCase()));
let added = 0;
for (let s of suggestions) {
if (!current.has(s.toLowerCase())) {
addSkill(s);
added++;
}
if (added >= 5) break;
}
if (added === 0 && window.buildPreview) window.buildPreview();
}
function wrapBuildPreview() {
if (window._origBuildPreviewForSkills) return;
if (typeof window.buildPreview === 'function') {
window._origBuildPreviewForSkills = window.buildPreview;
window.buildPreview = function() {
window._origBuildPreviewForSkills();
renderSkillsPreview();
};
renderSkillsPreview();
} else {
setTimeout(wrapBuildPreview, 150);
}
}
function setup() {
const addBtn = document.getElementById('btn-add-skill');
if (addBtn) addBtn.addEventListener('click', () => addSkill(''));
// Tie into existing AI button spinner; run after it finishes
document.querySelectorAll('[data-ai-target="skills"]').forEach(btn => {
btn.addEventListener('click', () => {
setTimeout(generateSkills, 850);
});
});
bindExistingRows();
wrapBuildPreview();
renderIconsSafe();
}
setTimeout(setup, 150);
})();
</script>
</div>
<div class="bg-neutral-900/50 border-neutral-800 border rounded-2xl">
<div class="flex sm:px-6 border-neutral-800 border-b pt-4 pr-4 pb-4 pl-4 items-center justify-between">
<button type="button" data-toggle="interests" aria-controls="section-interests" aria-expanded="true" class="group inline-flex items-center gap-2">
<span class="h-7 w-7 rounded-md bg-neutral-800 flex items-center justify-center">
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" data-lucide="chevron-down" class="lucide lucide-chevron-down h-4 w-4 transition-transform" data-chevron="interests"><path d="m6 9 6 6 6-6" class=""></path></svg>
</span>
<h2 class="text-lg sm:text-xl tracking-tight font-semibold">Interests</h2>
</button>
<div class="flex items-center gap-2">
<button class="inline-flex items-center gap-2 rounded-md bg-neutral-800 hover:bg-neutral-700 px-3 py-1.5 text-sm font-medium" data-ai-target="interests">
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" data-lucide="wand-2" class="lucide lucide-wand-2 h-4 w-4"><path d="m21.64 3.64-1.28-1.28a1.21 1.21 0 0 0-1.72 0L2.36 18.64a1.21 1.21 0 0 0 0 1.72l1.28 1.28a1.2 1.2 0 0 0 1.72 0L21.64 5.36a1.2 1.2 0 0 0 0-1.72" class=""></path><path d="m14 7 3 3" class=""></path><path d="M5 6v4" class=""></path><path d="M19 14v4" class=""></path><path d="M10 2v2" class=""></path><path d="M7 8H3" class=""></path><path d="M21 16h-4" class=""></path><path d="M11 3H9" class=""></path></svg>
Generate interests
</button>
</div>
</div>
<div class="sm:px-6 pt-5 pr-4 pb-5 pl-4 space-y-4" id="section-interests">
<div id="interests-list" class="flex flex-wrap gap-2">
<!-- Chip: Cycling -->
<div class="interest-row group relative">
<label class="inline-block cursor-pointer">
<input type="checkbox" class="peer sr-only include-checkbox" checked="" data-include-for="interest-cycling">
<span class="inline-flex items-center gap-2 px-3 py-1.5 rounded-lg border border-neutral-700 ring-1 ring-inset ring-neutral-800 bg-neutral-900 hover:bg-neutral-800 text-sm text-neutral-100 transition peer-checked:border-emerald-600 peer-checked:ring-emerald-600/40">
<span class="h-5 w-5 rounded-md border border-neutral-700 bg-neutral-900 ring-1 ring-inset ring-neutral-800 flex items-center justify-center peer-checked:bg-emerald-600 peer-checked:border-emerald-600 transition">
<svg viewBox="0 0 24 24" class="h-3.5 w-3.5 text-white opacity-0 peer-checked:opacity-100 transition"><path fill="currentColor" d="M20.285 6.709a1 1 0 0 1 0 1.414l-9.19 9.19a1 1 0 0 1-1.414 0l-5.19-5.19a1 1 0 0 1 1.414-1.414l4.483 4.483 8.483-8.483a1 1 0 0 1 1.414 0Z" class=""></path></svg>
</span>
<span class="interest-label">Cycling</span>
</span>
</label>
<input id="interest-cycling" type="text" value="Cycling" class="interest-input sr-only" aria-hidden="true" tabindex="-1">
<button type="button" class="absolute -top-2 -right-2 hidden group-hover:inline-flex items-center justify-center h-6 w-6 rounded-md text-neutral-300 hover:text-white hover:bg-neutral-800" data-remove-interest="" title="Remove">
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" data-lucide="x" class="lucide lucide-x h-4 w-4"><path d="M18 6 6 18" class=""></path><path d="m6 6 12 12" class=""></path></svg>
</button>
</div>
<!-- Chip: Football -->
<div class="interest-row group relative">
<label class="inline-block cursor-pointer">
<input type="checkbox" class="peer sr-only include-checkbox" checked="" data-include-for="interest-football">
<span class="inline-flex items-center gap-2 px-3 py-1.5 rounded-lg border border-neutral-700 ring-1 ring-inset ring-neutral-800 bg-neutral-900 hover:bg-neutral-800 text-sm text-neutral-100 transition peer-checked:border-emerald-600 peer-checked:ring-emerald-600/40">
<span class="h-5 w-5 rounded-md border border-neutral-700 bg-neutral-900 ring-1 ring-inset ring-neutral-800 flex items-center justify-center peer-checked:bg-emerald-600 peer-checked:border-emerald-600 transition">
<svg viewBox="0 0 24 24" class="h-3.5 w-3.5 text-white opacity-0 peer-checked:opacity-100 transition"><path fill="currentColor" d="M20.285 6.709a1 1 0 0 1 0 1.414l-9.19 9.19a1 1 0 0 1-1.414 0l-5.19-5.19a1 1 0 0 1 1.414-1.414l4.483 4.483 8.483-8.483a1 1 0 0 1 1.414 0Z" class=""></path></svg>
</span>
<span class="interest-label">Football</span>
</span>
</label>
<input id="interest-football" type="text" value="Football" class="interest-input sr-only" aria-hidden="true" tabindex="-1">
<button type="button" class="absolute -top-2 -right-2 hidden group-hover:inline-flex items-center justify-center h-6 w-6 rounded-md text-neutral-300 hover:text-white hover:bg-neutral-800" data-remove-interest="" title="Remove">
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" data-lucide="x" class="lucide lucide-x h-4 w-4"><path d="M18 6 6 18" class=""></path><path d="m6 6 12 12" class=""></path></svg>
</button>
</div>
</div>
<div class="flex pt-2 items-center justify-between">
<button class="inline-flex items-center gap-2 text-sm text-neutral-300 hover:text-white" id="btn-add-interest">
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" data-lucide="plus" class="lucide lucide-plus h-4 w-4"><path d="M5 12h14" class=""></path><path d="M12 5v14" class=""></path></svg>
Add interest
</button>
</div>
<script>
(function() {
function renderIconsSafe() { try { if (window.lucide) window.lucide.createIcons(); } catch(e) {} }
function uniqueId() { uniqueId.seed = (uniqueId.seed || Date.now()); uniqueId.seed += 1; return 'interest-' + uniqueId.seed; }
function getInterests() {
const rows = Array.from(document.querySelectorAll('#section-interests #interests-list .interest-row'));
const interests = [];
rows.forEach(row => {
const cb = row.querySelector('.include-checkbox');
const input = row.querySelector('.interest-input');
if (cb && input && cb.checked) {
const val = (input.value || '').trim();
if (val) interests.push(val);
}
});
return interests;
}
function renderInterestsPreview() {
const preview = document.getElementById('preview');
if (!preview) return;
const existing = document.getElementById('interests-preview');
if (existing && existing.parentNode) existing.parentNode.removeChild(existing);
const interests = getInterests();
if (!interests.length) return;
const block = document.createElement('div');
block.id = 'interests-preview';
const head = document.createElement('div');
head.className = 'pt-4 text-sm font-medium text-neutral-300';
head.textContent = 'Interests';
const list = document.createElement('div');
list.className = 'mt-2 flex flex-wrap gap-2';
interests.forEach(i => {
const chip = document.createElement('span');
chip.className = 'text-xs px-2.5 py-1 rounded-md bg-neutral-800 text-neutral-100';
chip.textContent = i;
list.appendChild(chip);
});
block.appendChild(head);
block.appendChild(list);
preview.appendChild(block);
}
function attachRowHandlers(row) {
const cb = row.querySelector('.include-checkbox');
const hiddenInput = row.querySelector('.interest-input');
const labelSpan = row.querySelector('.interest-label');
const removeBtn = row.querySelector('[data-remove-interest]');
const chip = row.querySelector('label > span');
if (cb) {
cb.addEventListener('change', () => {
if (window.buildPreview) window.buildPreview();
});
}
if (removeBtn) {
removeBtn.addEventListener('click', () => {
row.parentNode.removeChild(row);
if (window.buildPreview) window.buildPreview();
});
}
if (chip && hiddenInput && labelSpan) {
chip.addEventListener('dblclick', (e) => {
e.preventDefault();
const val = (hiddenInput.value || '').trim();
const newVal = prompt('Edit interest', val);
if (newVal !== null) {
const v = newVal.trim();
if (v) {
hiddenInput.value = v;
labelSpan.textContent = v;
if (window.buildPreview) window.buildPreview();
}
}
});
}
}
function addInterest(value = '') {
if (!value) {
const v = prompt('Add interest');
if (!v || !v.trim()) return;
value = v.trim();
}
const id = uniqueId();
const list = document.getElementById('interests-list');
if (!list) return;
const row = document.createElement('div');
row.className = 'interest-row group relative';
row.innerHTML = `
<label class="inline-block cursor-pointer">
<input type="checkbox" class="peer sr-only include-checkbox" checked data-include-for="${id}">
<span class="inline-flex items-center gap-2 px-3 py-1.5 rounded-lg border border-neutral-700 ring-1 ring-inset ring-neutral-800 bg-neutral-900 hover:bg-neutral-800 text-sm text-neutral-100 transition peer-checked:border-emerald-600 peer-checked:ring-emerald-600/40">
<span class="h-5 w-5 rounded-md border border-neutral-700 bg-neutral-900 ring-1 ring-inset ring-neutral-800 flex items-center justify-center peer-checked:bg-emerald-600 peer-checked:border-emerald-600 transition">
<svg viewBox="0 0 24 24" class="h-3.5 w-3.5 text-white opacity-0 peer-checked:opacity-100 transition"><path fill="currentColor" d="M20.285 6.709a1 1 0 0 1 0 1.414l-9.19 9.19a1 1 0 0 1-1.414 0l-5.19-5.19a1 1 0 0 1 1.414-1.414l4.483 4.483 8.483-8.483a1 1 0 0 1 1.414 0Z"></path></svg>
</span>
<span class="interest-label"></span>
</span>
</label>
<input id="${id}" type="text" value="" class="interest-input sr-only" aria-hidden="true" tabindex="-1">
<button type="button" class="absolute -top-2 -right-2 hidden group-hover:inline-flex items-center justify-center h-6 w-6 rounded-md text-neutral-300 hover:text-white hover:bg-neutral-800" data-remove-interest title="Remove">
<i data-lucide="x" class="h-4 w-4"></i>
</button>
`;
list.appendChild(row);
const input = row.querySelector('.interest-input');
const labelSpan = row.querySelector('.interest-label');
input.value = value.replace(/\s+/g, ' ').trim();
labelSpan.textContent = input.value;
attachRowHandlers(row);
renderIconsSafe();
if (window.buildPreview) window.buildPreview();
}
function bindExistingRows() {
document.querySelectorAll('#interests-list .interest-row').forEach(attachRowHandlers);
}
function generateInterests() {
const suggestions = [
'Photography',
'Hiking',
'Reading',
'Chess',
'Cooking',
'Volunteering',
'Music',
'Travel'
];
const current = new Set(getInterests().map(i => i.toLowerCase()));
let added = 0;
for (let i of suggestions) {
if (!current.has(i.toLowerCase())) {
addInterest(i);
added++;
}
if (added >= 3) break;
}
if (added === 0 && window.buildPreview) window.buildPreview();
}
function wrapBuildPreview() {
if (window._origBuildPreviewForInterests) return;
if (typeof window.buildPreview === 'function') {
window._origBuildPreviewForInterests = window.buildPreview;
window.buildPreview = function() {
window._origBuildPreviewForInterests();
renderInterestsPreview();
};
renderInterestsPreview();
} else {
setTimeout(wrapBuildPreview, 150);
}
}
function setup() {
const addBtn = document.getElementById('btn-add-interest');
if (addBtn) addBtn.addEventListener('click', () => addInterest(''));
document.querySelectorAll('[data-ai-target="interests"]').forEach(btn => {
btn.addEventListener('click', () => setTimeout(generateInterests, 850));
});
bindExistingRows();
wrapBuildPreview();
renderIconsSafe();
}
setTimeout(setup, 150);
})();
</script>
</div>
</div>
</section>
<!-- Preview / Variant Panel -->
<aside class="lg:col-span-4 space-y-4">
<div class="rounded-2xl overflow-hidden border border-neutral-800 bg-neutral-900/40">
<div class="h-28 w-full bg-gradient-to-br from-neutral-800 to-neutral-900 relative">
<img src="https://images.unsplash.com/photo-1500530855697-b586d89ba3ee?q=80&w=800&auto=format&fit=crop" alt="" class="absolute inset-0 h-full w-full object-cover opacity-30">
<div class="absolute inset-0 bg-gradient-to-b from-transparent to-neutral-900/60"></div>
</div>
<div class="p-5">
<div class="flex items-center justify-between">
<h3 class="text-xl tracking-tight font-semibold">Variant Preview</h3>
<button class="inline-flex items-center gap-2 rounded-md bg-neutral-800 hover:bg-neutral-700 px-3 py-1.5 text-xs font-medium" id="btn-copy">
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" data-lucide="copy" class="lucide lucide-copy h-4 w-4"><rect width="14" height="14" x="8" y="8" rx="2" ry="2" class=""></rect><path d="M4 16c-1.1 0-2-.9-2-2V4c0-1.1.9-2 2-2h10c1.1 0 2 .9 2 2" class=""></path></svg>
Copy
</button>
</div>
<div id="preview" class="mt-4 space-y-4 text-sm leading-6 text-neutral-200">
<div class="text-lg font-semibold tracking-tight">John Iseghohi</div>
<div class="text-neutral-300">Senior UX Designer</div>
<div class="text-xs text-neutral-400">iseghohi.john@gmail.com • +44 7862 060572 • linkedin.com/in/johniseghohi
• Manchester, UK</div>
<div class="pt-4 text-sm font-medium text-neutral-300">Professional Summary</div>
<p class="text-sm text-neutral-200">With 13+ years of experience, I lead UX design, strategy, and research for
financial services products. Increased onboarding success by 25% and drove 100,000+ app downloads in 12
months. Previously shipped a self-service mobile app with measurable impact.</p>
<div class="pt-4 text-sm font-medium text-neutral-300">Work Experience</div>
<div class="space-y-1">
<div class="font-medium">Amazon</div>
<div class="text-neutral-300">Senior UX Designer · Global Instalment Lending · Payments Product</div>
<div class="text-xs text-neutral-500">Jan 2021 – Present · UK · Full-time</div>
<ul class="list-disc pl-5 space-y-1">
<li>Led experience design, strategy, and research for Amazon Insurance Store; increased conversions by 35%
and reduced customer complaints by 25% within first year.</li>
</ul>
</div>
<div id="skills-preview">
<div class="pt-4 text-sm font-medium text-neutral-300">Skills</div>
<div class="mt-2 flex flex-wrap gap-2">
<span class="text-xs px-2.5 py-1 rounded-md bg-neutral-800 text-neutral-100">User Research</span><span class="text-xs px-2.5 py-1 rounded-md bg-neutral-800 text-neutral-100">Interaction Design</span><span class="text-xs px-2.5 py-1 rounded-md bg-neutral-800 text-neutral-100">Prototyping</span><span class="text-xs px-2.5 py-1 rounded-md bg-neutral-800 text-neutral-100">Figma</span>
</div>
</div>
<div id="interests-preview">
<div class="pt-4 text-sm font-medium text-neutral-300">Interests</div>
<div class="mt-2 flex flex-wrap gap-2">
<span class="text-xs px-2.5 py-1 rounded-md bg-neutral-800 text-neutral-100">Cycling</span><span class="text-xs px-2.5 py-1 rounded-md bg-neutral-800 text-neutral-100">Football</span>
</div>
</div>
</div>
</div>
</div>
<div class="rounded-2xl border border-neutral-800 bg-neutral-900/40 p-4">
<div class="flex items-center justify-between mb-2">
<h4 class="font-medium">Included parts</h4>
<span id="included-count" class="text-xs text-neutral-400">16 selected</span>
</div>
<p class="text-xs text-neutral-400">Use the checkboxes in the editor to include or exclude any field, summary, or
bullet from this variant.</p>
</div>
</aside>
</div>
</main>