Fix loading race condition, improve quiz UX, editor toolbar, settings page

- Fix: component loader now fires tabChanged AFTER HTML is in DOM
  (fixes CMS/settings stuck at loading)
- CMS: separate "New Article", "New Quiz", "New Pearl" buttons
- CMS: editor toolbar with formatting buttons (bold, italic, headings,
  lists, links, tables, quotes, code, hr)
- CMS: quiz questions have numbered headers (Q1, Q2...), larger inputs,
  clear "check correct answer" labels
- CMS: validation prevents saving questions without correct answer marked
- Quiz UX: large card-style question boxes with gradient number badges,
  bigger option buttons with hover effects and selection feedback,
  checkmark/cross icons on results
- Settings: improved card spacing, input styling, section headers with
  bottom borders for visual hierarchy
- Settings: updated compliance section (removed HIPAA references, added
  BAA/institution guidelines language)
This commit is contained in:
Daniel Onyejesi 2026-03-23 20:37:35 -04:00
parent 82a0911d24
commit ffacdcbedb
5 changed files with 155 additions and 60 deletions

View file

@ -45,7 +45,11 @@
<!-- Content list view -->
<div id="cms-list-view">
<div class="cms-toolbar">
<button id="btn-lh-new-content" class="btn-sm btn-primary"><i class="fas fa-plus"></i> New Content</button>
<div style="display:flex;gap:8px;">
<button id="btn-lh-new-content" class="btn-sm btn-primary" data-type="article"><i class="fas fa-file-alt"></i> New Article</button>
<button id="btn-lh-new-quiz" class="btn-sm" style="background:var(--amber-light);color:#92400e;border:1px solid var(--amber);border-radius:6px;padding:6px 12px;font-size:13px;cursor:pointer;"><i class="fas fa-clipboard-question"></i> New Quiz</button>
<button id="btn-lh-new-pearl" class="btn-sm" style="background:var(--purple-light);color:var(--purple);border:1px solid var(--purple);border-radius:6px;padding:6px 12px;font-size:13px;cursor:pointer;"><i class="fas fa-gem"></i> New Pearl</button>
</div>
<div class="cms-search">
<i class="fas fa-search"></i>
<input type="text" id="cms-search" placeholder="Search content..." class="cms-input-sm">
@ -108,10 +112,27 @@
</div>
</div>
<!-- Body editor -->
<!-- Body editor with toolbar -->
<div class="cms-body-section">
<label class="cms-label">Body <span style="color:var(--g400);font-weight:400;">(supports basic HTML: bold, lists, tables, links)</span></label>
<textarea id="lh-cms-edit-body" class="cms-body-editor" rows="16" placeholder="Write your content here...&#10;&#10;You can use HTML tags like <b>, <ul>, <ol>, <table>, <blockquote>, <a>, <h2>, <h3>, <code>, <p>..."></textarea>
<label class="cms-label">Body</label>
<div class="cms-editor-toolbar">
<button type="button" class="cms-tb-btn" data-insert="b" title="Bold"><i class="fas fa-bold"></i></button>
<button type="button" class="cms-tb-btn" data-insert="i" title="Italic"><i class="fas fa-italic"></i></button>
<button type="button" class="cms-tb-btn" data-insert="u" title="Underline"><i class="fas fa-underline"></i></button>
<span class="cms-tb-sep"></span>
<button type="button" class="cms-tb-btn" data-insert="h2" title="Heading 2">H2</button>
<button type="button" class="cms-tb-btn" data-insert="h3" title="Heading 3">H3</button>
<span class="cms-tb-sep"></span>
<button type="button" class="cms-tb-btn" data-insert="ul" title="Bullet List"><i class="fas fa-list-ul"></i></button>
<button type="button" class="cms-tb-btn" data-insert="ol" title="Numbered List"><i class="fas fa-list-ol"></i></button>
<button type="button" class="cms-tb-btn" data-insert="blockquote" title="Quote"><i class="fas fa-quote-left"></i></button>
<span class="cms-tb-sep"></span>
<button type="button" class="cms-tb-btn" data-insert="a" title="Link"><i class="fas fa-link"></i></button>
<button type="button" class="cms-tb-btn" data-insert="code" title="Code"><i class="fas fa-code"></i></button>
<button type="button" class="cms-tb-btn" data-insert="table" title="Table"><i class="fas fa-table"></i></button>
<button type="button" class="cms-tb-btn" data-insert="hr" title="Horizontal Rule"><i class="fas fa-minus"></i></button>
</div>
<textarea id="lh-cms-edit-body" class="cms-body-editor" rows="16" placeholder="Write your content here..."></textarea>
</div>
<!-- Quiz builder -->

View file

@ -78,20 +78,19 @@
</div>
</div>
<!-- HIPAA -->
<!-- Compliance -->
<div class="settings-section card">
<h3><i class="fas fa-lock"></i> HIPAA & Privacy</h3>
<h3><i class="fas fa-shield-halved"></i> Compliance & Usage</h3>
<div class="hipaa-info">
<p><strong>Current Status:</strong> This tool processes data through third-party AI APIs.</p>
<p><strong>AWS Bedrock</strong> is available with a Business Associate Agreement (BAA) for HIPAA-eligible workloads.</p>
<ul>
<li>✅ All connections use HTTPS/TLS encryption</li>
<li>✅ Authentication required</li>
<li>✅ No patient data stored on server</li>
<li>✅ 2FA available</li>
<li>⚠️ OpenRouter does not currently offer BAA</li>
<li>⚠️ For full HIPAA: use Azure OpenAI or AWS Bedrock with BAA</li>
<li>✅ Authentication with optional 2FA</li>
<li>✅ No patient data stored on server beyond session</li>
<li>✅ AWS Bedrock supports BAA for HIPAA compliance</li>
<li>✅ Azure OpenAI supports BAA for HIPAA compliance</li>
</ul>
<p><strong>Recommendation:</strong> Do not enter real PHI until your organization has executed BAAs with all AI providers.</p>
<p><strong>Important:</strong> Check with your institution's guidelines and policies before use. This tool is not intended for production clinical use without proper organizational authorization and provider BAAs in place. Use with caution.</p>
</div>
</div>

View file

@ -439,12 +439,16 @@ body{font-family:'Inter',system-ui,sans-serif;background:var(--g50);color:var(--
.shadess-flag-btn{font-size:11px;padding:2px 6px;}
/* Settings page */
.settings-page{display:flex;flex-direction:column;gap:16px;max-width:800px;}
.settings-page .settings-section{padding:20px;}
.settings-page .settings-section h3{font-size:16px;font-weight:700;color:var(--g800);margin-bottom:12px;display:flex;align-items:center;gap:8px;}
.settings-page .settings-section p{font-size:13px;color:var(--g600);margin-bottom:12px;}
.settings-page{display:flex;flex-direction:column;gap:0;max-width:800px;}
.settings-page .settings-section{padding:20px;margin-bottom:12px;border-radius:var(--radius);}
.settings-page .settings-section h3{font-size:16px;font-weight:700;color:var(--g800);margin-bottom:14px;padding-bottom:10px;border-bottom:1px solid var(--g100);display:flex;align-items:center;gap:8px;}
.settings-page .settings-section p{font-size:13px;color:var(--g600);margin-bottom:12px;line-height:1.6;}
.settings-page .hipaa-info ul{margin:10px 0 10px 20px;font-size:13px;}
.settings-page .hipaa-info li{margin-bottom:4px;}
.settings-page .hipaa-info li{margin-bottom:6px;line-height:1.5;}
.settings-page .form-group{margin-bottom:14px;}
.settings-page .form-group label{font-size:13px;font-weight:600;color:var(--g600);margin-bottom:4px;display:block;}
.settings-page .form-group input{width:100%;padding:8px 12px;border:1.5px solid var(--g300);border-radius:6px;font-size:13px;box-sizing:border-box;}
.settings-page .form-group input:focus{border-color:var(--blue);outline:none;box-shadow:0 0 0 2px var(--blue-light);}
/* Inline load popover — compact dropdown, right-aligned under Load button */
.enc-load-popover{position:absolute;right:0;top:calc(100% + 4px);width:360px;max-width:calc(100vw - 24px);background:white;border:1px solid var(--g300);border-radius:10px;box-shadow:0 8px 24px rgba(0,0,0,0.15);z-index:600;display:flex;flex-direction:column;}
@ -531,20 +535,23 @@ body{font-family:'Inter',system-ui,sans-serif;background:var(--g50);color:var(--
.lh-content-body th,.lh-content-body td{padding:8px 12px;border:1px solid var(--g200);font-size:13px;}
.lh-content-body th{background:var(--g50);font-weight:600;}
/* Quiz */
.lh-quiz-q{margin-bottom:20px;padding-bottom:16px;border-bottom:1px solid var(--g100);}
.lh-quiz-q:last-child{border-bottom:none;margin-bottom:0;}
.lh-quiz-q-header{display:flex;gap:8px;align-items:center;margin-bottom:6px;}
.lh-quiz-q-num{font-size:12px;font-weight:700;color:var(--blue);background:var(--blue-light);padding:2px 8px;border-radius:6px;}
.lh-quiz-q-type{font-size:11px;color:var(--g400);}
.lh-quiz-q-text{font-size:14px;font-weight:500;margin-bottom:10px;color:var(--g800);}
.lh-quiz-options{display:flex;flex-direction:column;gap:6px;}
.lh-quiz-option{display:flex;align-items:center;gap:10px;padding:10px 14px;border:1.5px solid var(--g200);border-radius:8px;cursor:pointer;transition:all 0.15s;font-size:13px;}
.lh-quiz-option:hover{border-color:var(--blue-light);background:var(--g50);}
.lh-quiz-option input[type="radio"]{accent-color:var(--blue);width:16px;height:16px;}
/* Quiz — large, visual question cards */
.lh-quiz-q{margin-bottom:24px;padding:20px;background:white;border:1.5px solid var(--g200);border-radius:12px;box-shadow:0 1px 4px rgba(0,0,0,0.04);}
.lh-quiz-q:last-child{margin-bottom:0;}
.lh-quiz-q-header{display:flex;gap:10px;align-items:center;margin-bottom:10px;}
.lh-quiz-q-num{font-size:13px;font-weight:700;color:white;background:linear-gradient(135deg,var(--blue),var(--purple));padding:4px 12px;border-radius:8px;min-width:32px;text-align:center;}
.lh-quiz-q-type{font-size:11px;color:var(--g400);background:var(--g100);padding:2px 8px;border-radius:4px;}
.lh-quiz-q-text{font-size:16px;font-weight:600;margin-bottom:14px;color:var(--g800);line-height:1.5;}
.lh-quiz-options{display:flex;flex-direction:column;gap:8px;}
.lh-quiz-option{display:flex;align-items:center;gap:12px;padding:14px 18px;border:2px solid var(--g200);border-radius:10px;cursor:pointer;transition:all 0.2s;font-size:14px;line-height:1.4;background:white;}
.lh-quiz-option:hover{border-color:var(--blue);background:var(--blue-light);transform:translateY(-1px);box-shadow:0 2px 8px rgba(37,99,235,0.1);}
.lh-quiz-option input[type="radio"]{accent-color:var(--blue);width:18px;height:18px;flex-shrink:0;}
.lh-quiz-option span{flex:1;}
.lh-opt-correct{border-color:var(--green) !important;background:var(--green-light) !important;}
.lh-opt-wrong{border-color:var(--red) !important;background:var(--red-light) !important;}
.lh-opt-correct{border-color:var(--green) !important;background:var(--green-light) !important;box-shadow:0 0 0 2px rgba(16,185,129,0.2) !important;}
.lh-opt-correct::after{content:'\\2713';position:absolute;right:14px;color:var(--green);font-weight:700;font-size:18px;}
.lh-quiz-option.lh-opt-correct,.lh-quiz-option.lh-opt-wrong{position:relative;}
.lh-opt-wrong{border-color:var(--red) !important;background:var(--red-light) !important;box-shadow:0 0 0 2px rgba(239,68,68,0.2) !important;}
.lh-opt-wrong::after{content:'\\2717';position:absolute;right:14px;color:var(--red);font-weight:700;font-size:18px;}
/* Quiz results */
.lh-result-item{margin-bottom:14px;padding-bottom:14px;border-bottom:1px solid var(--g100);}
@ -556,7 +563,7 @@ body{font-family:'Inter',system-ui,sans-serif;background:var(--g50);color:var(--
.lh-expl-general{background:var(--blue-light);color:#1e40af;}
/* CMS question builder */
.lh-question-block{padding:12px;border:1px solid var(--g200);border-radius:8px;margin-bottom:8px;background:var(--g50);}
.lh-question-block{padding:16px;border:1.5px solid var(--g200);border-radius:10px;margin-bottom:12px;background:white;box-shadow:0 1px 3px rgba(0,0,0,0.04);transition:outline 0.2s;}
/* ============================================================
CONTENT MANAGER (WordPress-like CMS)
@ -628,6 +635,13 @@ body{font-family:'Inter',system-ui,sans-serif;background:var(--g50);color:var(--
.cms-quiz-header{display:flex;justify-content:space-between;align-items:center;margin-bottom:12px;}
.cms-quiz-header h3{margin:0;font-size:15px;color:var(--g700);}
/* Editor toolbar */
.cms-editor-toolbar{display:flex;gap:2px;padding:6px 8px;background:var(--g50);border:1px solid var(--g300);border-bottom:none;border-radius:8px 8px 0 0;flex-wrap:wrap;}
.cms-tb-btn{width:32px;height:28px;border:none;background:transparent;cursor:pointer;border-radius:4px;font-size:12px;color:var(--g600);display:flex;align-items:center;justify-content:center;font-family:inherit;font-weight:600;}
.cms-tb-btn:hover{background:var(--g200);color:var(--g800);}
.cms-tb-sep{width:1px;background:var(--g300);margin:2px 4px;align-self:stretch;}
.cms-body-section .cms-body-editor{border-radius:0 0 8px 8px;}
/* Mobile responsive */
@media(max-width:768px){
.cms-layout{grid-template-columns:1fr;}
@ -640,7 +654,7 @@ body{font-family:'Inter',system-ui,sans-serif;background:var(--g50);color:var(--
.cms-stat{padding:8px 10px;}
.cms-stat-value{font-size:18px;}
}
.lh-option-row{display:flex;align-items:center;gap:6px;margin-bottom:4px;}
.lh-option-row{display:flex;align-items:center;gap:8px;margin-bottom:6px;padding:6px 8px;border-radius:6px;background:var(--g50);}
@media(max-width:640px){
.lh-feed-item-header{flex-direction:column;gap:6px;}

View file

@ -74,12 +74,15 @@ document.addEventListener('DOMContentLoaded', function() {
var tabEl = document.getElementById(tabName + '-tab');
if (tabEl) {
tabEl.classList.add('active');
// Lazy-load component HTML if not yet loaded
loadComponent(tabEl);
// Lazy-load component HTML, then fire tabChanged after DOM is ready
loadComponent(tabEl).then(function() {
document.dispatchEvent(new CustomEvent('tabChanged', { detail: { tab: tabName } }));
});
} else {
document.dispatchEvent(new CustomEvent('tabChanged', { detail: { tab: tabName } }));
}
// Persist last tab in localStorage (restored after login in auth.js)
try { localStorage.setItem('ped_last_tab', tabName); } catch(e) {}
document.dispatchEvent(new CustomEvent('tabChanged', { detail: { tab: tabName } }));
// Close sidebar on mobile after tab click
var sidebar = document.getElementById('sidebar');
if (sidebar && window.innerWidth <= 768) {

View file

@ -56,12 +56,18 @@
// ── CMS events ─────────────────────────────
if (e.target.closest('#btn-lh-add-cat')) { addCategory(); return; }
if (e.target.closest('#btn-lh-new-content')) { openEditor(null); return; }
if (e.target.closest('#btn-lh-new-content')) { openEditor(null, 'article'); return; }
if (e.target.closest('#btn-lh-new-quiz')) { openEditor(null, 'quiz'); return; }
if (e.target.closest('#btn-lh-new-pearl')) { openEditor(null, 'pearl'); return; }
if (e.target.closest('#btn-lh-close-editor')) { closeEditor(); return; }
if (e.target.closest('#btn-lh-save-content')) { saveContent(); return; }
if (e.target.closest('#btn-lh-delete-content')) { deleteContent(); return; }
if (e.target.closest('#btn-lh-add-question')) { addQuestionBlock(); return; }
// Editor toolbar buttons
var tbBtn = e.target.closest('.cms-tb-btn');
if (tbBtn && tbBtn.dataset.insert) { insertTag(tbBtn.dataset.insert); return; }
// CMS content item edit
var cmsItem = e.target.closest('.lh-cms-content-item');
if (cmsItem && cmsItem.dataset.id) { openEditor(cmsItem.dataset.id); return; }
@ -554,7 +560,33 @@
renderCmsContentList(filtered);
}
function openEditor(contentId) {
// Editor toolbar — insert HTML tags at cursor in textarea
function insertTag(tag) {
var ta = document.getElementById('lh-cms-edit-body');
if (!ta) return;
var start = ta.selectionStart, end = ta.selectionEnd;
var sel = ta.value.substring(start, end);
var insert = '';
switch(tag) {
case 'b': case 'i': case 'u': case 'code': case 'h2': case 'h3':
insert = '<' + tag + '>' + (sel || 'text') + '</' + tag + '>'; break;
case 'a':
var url = prompt('Enter URL:');
if (!url) return;
insert = '<a href="' + url + '">' + (sel || 'link text') + '</a>'; break;
case 'ul': insert = '<ul>\n <li>' + (sel || 'item') + '</li>\n <li></li>\n</ul>'; break;
case 'ol': insert = '<ol>\n <li>' + (sel || 'item') + '</li>\n <li></li>\n</ol>'; break;
case 'blockquote': insert = '<blockquote>' + (sel || 'quote') + '</blockquote>'; break;
case 'table': insert = '<table>\n<tr><th>Header 1</th><th>Header 2</th></tr>\n<tr><td>' + (sel || 'data') + '</td><td></td></tr>\n</table>'; break;
case 'hr': insert = '<hr>'; break;
default: return;
}
ta.value = ta.value.substring(0, start) + insert + ta.value.substring(end);
ta.focus();
ta.selectionStart = ta.selectionEnd = start + insert.length;
}
function openEditor(contentId, contentType) {
var editor = document.getElementById('lh-cms-editor');
var listView = document.getElementById('cms-list-view');
if (!editor) return;
@ -567,11 +599,16 @@
document.getElementById('lh-cms-edit-subject').value = '';
document.getElementById('lh-cms-edit-body').value = '';
document.getElementById('lh-cms-edit-category').value = '';
document.getElementById('lh-cms-edit-type').value = 'article';
document.getElementById('lh-cms-edit-type').value = contentType || 'article';
document.getElementById('lh-cms-edit-published').value = 'false';
document.getElementById('lh-cms-questions').innerHTML = '';
document.getElementById('btn-lh-delete-content').classList.add('hidden');
// For quiz type, add a starter question
if (contentType === 'quiz' && !contentId) {
addQuestionBlock();
}
if (!contentId) return;
// Load existing content
@ -618,11 +655,14 @@
if (!title) { showToast('Title required', 'error'); return; }
// Gather questions
// Gather questions with validation
var questions = [];
document.querySelectorAll('#lh-cms-questions .lh-question-block').forEach(function(qBlock) {
var validationError = false;
var qBlocks = document.querySelectorAll('#lh-cms-questions .lh-question-block');
for (var qi = 0; qi < qBlocks.length; qi++) {
var qBlock = qBlocks[qi];
var qText = qBlock.querySelector('.lh-q-text').value.trim();
if (!qText) return;
if (!qText) continue;
var qType = qBlock.querySelector('.lh-q-type').value;
var qExpl = qBlock.querySelector('.lh-q-explanation').value.trim();
var qId = qBlock.dataset.questionId || null;
@ -638,6 +678,17 @@
});
});
// Validate: must have a correct answer marked
var hasCorrect = options.some(function(o) { return o.is_correct; });
if (options.length > 0 && !hasCorrect) {
showToast('Question "' + qText.substring(0, 50) + '..." needs a correct answer marked', 'error');
qBlock.style.outline = '2px solid var(--red)';
qBlock.scrollIntoView({ behavior: 'smooth', block: 'center' });
validationError = true;
break;
}
qBlock.style.outline = '';
questions.push({
id: qId,
question_text: qText,
@ -645,7 +696,8 @@
explanation: qExpl,
options: options
});
});
}
if (validationError) return;
var payload = { title: title, body: body, category_id: category_id, subject: subject, content_type: content_type, published: published };
@ -733,21 +785,28 @@
block.className = 'lh-question-block';
if (existingQ && existingQ.id) block.dataset.questionId = existingQ.id;
var qNum = container.querySelectorAll('.lh-question-block').length + 1;
block.innerHTML =
'<div style="display:flex;justify-content:space-between;align-items:center;margin-bottom:6px;">' +
'<span style="font-size:12px;font-weight:600;color:var(--g600);">Question</span>' +
'<button class="btn-sm btn-ghost lh-rm-question" style="padding:2px 6px;font-size:11px;color:var(--red);"><i class="fas fa-trash"></i> Remove</button>' +
'<div style="display:flex;justify-content:space-between;align-items:center;margin-bottom:8px;padding-bottom:8px;border-bottom:1px solid var(--g200);">' +
'<div style="display:flex;align-items:center;gap:8px;">' +
'<span style="background:linear-gradient(135deg,var(--blue),var(--purple));color:white;font-size:12px;font-weight:700;padding:3px 10px;border-radius:6px;">Q' + qNum + '</span>' +
'<select class="lh-q-type" style="font-size:12px;padding:4px 8px;border:1px solid var(--g300);border-radius:6px;">' +
'<option value="mcq"' + (existingQ && existingQ.question_type === 'mcq' ? ' selected' : '') + '>Single Choice</option>' +
'<option value="true_false"' + (existingQ && existingQ.question_type === 'true_false' ? ' selected' : '') + '>True / False</option>' +
'</select>' +
'</div>' +
'<button class="btn-sm btn-ghost lh-rm-question" style="padding:2px 8px;font-size:12px;color:var(--red);"><i class="fas fa-trash"></i></button>' +
'</div>' +
'<input type="text" class="lh-q-text" placeholder="Question text..." value="' + esc(existingQ ? existingQ.question_text : '') + '" style="width:100%;font-size:13px;padding:7px;border:1px solid var(--g300);border-radius:6px;box-sizing:border-box;margin-bottom:6px;">' +
'<div style="display:flex;gap:8px;margin-bottom:6px;">' +
'<select class="lh-q-type" style="font-size:12px;padding:4px 8px;border:1px solid var(--g300);border-radius:6px;">' +
'<option value="mcq"' + (existingQ && existingQ.question_type === 'mcq' ? ' selected' : '') + '>Multiple Choice</option>' +
'<option value="true_false"' + (existingQ && existingQ.question_type === 'true_false' ? ' selected' : '') + '>True / False</option>' +
'</select>' +
'<input type="text" class="lh-q-explanation" placeholder="General explanation (shown after answering)" value="' + esc(existingQ ? existingQ.explanation || '' : '') + '" style="flex:1;font-size:12px;padding:4px 8px;border:1px solid var(--g300);border-radius:6px;">' +
'<input type="text" class="lh-q-text" placeholder="Enter question text..." value="' + esc(existingQ ? existingQ.question_text : '') + '" style="width:100%;font-size:14px;font-weight:500;padding:10px;border:1.5px solid var(--g300);border-radius:8px;box-sizing:border-box;margin-bottom:8px;">' +
'<div style="margin-bottom:8px;">' +
'<label style="font-size:11px;font-weight:600;color:var(--g500);text-transform:uppercase;letter-spacing:0.3px;display:block;margin-bottom:4px;">Answer Options <span style="color:var(--green);">(check the correct answer)</span></label>' +
'</div>' +
'<div class="lh-options-list"></div>' +
'<button class="btn-sm btn-ghost lh-add-option" style="margin-top:4px;font-size:12px;"><i class="fas fa-plus"></i> Add Option</button>';
'<button class="btn-sm btn-ghost lh-add-option" style="margin-top:6px;font-size:12px;"><i class="fas fa-plus"></i> Add Option</button>' +
'<div style="margin-top:8px;">' +
'<label style="font-size:11px;font-weight:600;color:var(--g500);display:block;margin-bottom:3px;">Explanation (shown after answering)</label>' +
'<input type="text" class="lh-q-explanation" placeholder="Explain the correct answer..." value="' + esc(existingQ ? existingQ.explanation || '' : '') + '" style="width:100%;font-size:12px;padding:6px 10px;border:1px solid var(--g300);border-radius:6px;box-sizing:border-box;">' +
'</div>';
container.appendChild(block);
@ -768,13 +827,12 @@
var row = document.createElement('div');
row.className = 'lh-option-row';
row.innerHTML =
'<label style="display:flex;align-items:center;gap:4px;cursor:pointer;" title="Mark as correct">' +
'<input type="checkbox" class="lh-opt-correct"' + (opt && opt.is_correct ? ' checked' : '') + '>' +
'<i class="fas fa-check" style="font-size:11px;color:var(--green);"></i>' +
'<label style="display:flex;align-items:center;gap:4px;cursor:pointer;min-width:28px;" title="Mark as correct answer">' +
'<input type="checkbox" class="lh-opt-correct"' + (opt && opt.is_correct ? ' checked' : '') + ' style="accent-color:var(--green);width:16px;height:16px;">' +
'</label>' +
'<input type="text" class="lh-opt-text" placeholder="Option text" value="' + esc(opt ? opt.option_text : '') + '" style="flex:1;font-size:12px;padding:4px 8px;border:1px solid var(--g300);border-radius:6px;">' +
'<input type="text" class="lh-opt-expl" placeholder="Why wrong (optional)" value="' + esc(opt ? opt.explanation || '' : '') + '" style="flex:1;font-size:12px;padding:4px 8px;border:1px solid var(--g300);border-radius:6px;">' +
'<button class="btn-sm btn-ghost lh-rm-option" style="padding:2px 6px;font-size:11px;color:var(--red);"><i class="fas fa-times"></i></button>';
'<input type="text" class="lh-opt-text" placeholder="Option text" value="' + esc(opt ? opt.option_text : '') + '" style="flex:2;font-size:13px;padding:6px 10px;border:1.5px solid var(--g300);border-radius:6px;">' +
'<input type="text" class="lh-opt-expl" placeholder="Explanation if wrong" value="' + esc(opt ? opt.explanation || '' : '') + '" style="flex:1;font-size:12px;padding:6px 10px;border:1px solid var(--g300);border-radius:6px;color:var(--g600);">' +
'<button class="btn-sm btn-ghost lh-rm-option" style="padding:2px 6px;font-size:12px;color:var(--g400);"><i class="fas fa-times"></i></button>';
container.appendChild(row);
}