for freelancers who’d rather build than write from scratch
Freelance Toolkit
25 tools for the parts of freelancing that aren’t the actual work — profiles, proposals, pricing, and the emails you keep putting off. Fill in a few fields, get a draft, edit to taste.
${toolsInCat.map(buildToolCard).join('')}
`;
});navGroupsEl.innerHTML = navHtml;
mainContentEl.innerHTML = mainHtml;wireGenerateButtons();
wireCopyButtons();
setupActiveNavObserver();
}/* ---------- Filter ---------- */
filterInput.addEventListener('input', () => {
const q = filterInput.value.trim().toLowerCase();
document.querySelectorAll('.nav-group').forEach(group => {
let anyVisible = false;
group.querySelectorAll('li').forEach(li => {
const match = li.textContent.toLowerCase().includes(q);
li.style.display = match ? '' : 'none';
if(match) anyVisible = true;
});
group.style.display = anyVisible ? '' : 'none';
});
});/* ---------- Markdown-lite renderer ---------- */
function mdToHtml(md){
const lines = md.split('\n');
let html = '';
let listType = null;
let paragraphBuffer = [];function inline(text){
return escapeHtml(text)
.replace(/\*\*(.+?)\*\*/g, '$1')
.replace(/(^|[^*])\*([^*]+)\*($|[^*])/g, '$1$2$3');
}
function flushParagraph(){
if(paragraphBuffer.length){
html += '
' + inline(paragraphBuffer.join(' ')) + '
';
paragraphBuffer = [];
}
}
function closeList(){
if(listType){ html += `${listType}>`; listType = null; }
}lines.forEach(rawLine => {
const line = rawLine.trim();
if(line === ''){ flushParagraph(); closeList(); return; }
if(/^###\s+/.test(line)){ flushParagraph(); closeList(); html += `
${inline(line.replace(/^###\s+/,''))}
`; return; }
if(/^##\s+/.test(line)){ flushParagraph(); closeList(); html += `