{"id":768,"date":"2026-06-19T13:37:00","date_gmt":"2026-06-19T13:37:00","guid":{"rendered":"https:\/\/silcom.io\/?page_id=768"},"modified":"2026-06-22T11:45:40","modified_gmt":"2026-06-22T11:45:40","slug":"invoice-builder","status":"publish","type":"page","link":"https:\/\/silcom.io\/invoice-builder\/","title":{"rendered":"Invoice Builder"},"content":{"rendered":"\t<section class=\"silcom-invoice-builder\" data-silcom-invoice-builder>\n\t\t<div class=\"silcom-invoice-builder__inner\">\n\t\t\t<nav class=\"silcom-invoice-builder__breadcrumb\" aria-label=\"Breadcrumb\">\n\t\t\t\t<a href=\"https:\/\/silcom.io\/\">Home<\/a><span aria-hidden=\"true\">\/<\/span><span>Invoice Builder<\/span>\n\t\t\t<\/nav>\n\t\t\t<div class=\"silcom-invoice-builder__header\">\n\t\t\t\t<p>Silcom Tools<\/p>\n\t\t\t\t<h1>Invoice Builder<\/h1>\n\t\t\t\t<span>Build a clean invoice while you type. Preview, print, save as PDF, or open an email draft with the invoice details.<\/span>\n\t\t\t<\/div>\n\t\t\t<div class=\"silcom-invoice-builder__grid\">\n\t\t\t\t<form class=\"silcom-invoice-form\" data-invoice-form>\n\t\t\t\t\t<section class=\"silcom-invoice-form__section\">\n\t\t\t\t\t\t<h2>Brand and invoice setup<\/h2>\n\t\t\t\t\t\t<div class=\"silcom-invoice-field\">\n\t\t\t\t\t\t\t<label>Upload logo<\/label>\n\t\t\t\t\t\t\t<input type=\"file\" accept=\"image\/*\" data-invoice-logo>\n\t\t\t\t\t\t\t<small>Used locally in the preview only.<\/small>\n\t\t\t\t\t\t<\/div>\n\t\t\t\t\t\t<div class=\"silcom-invoice-form__split\">\n\t\t\t\t\t\t\t<label class=\"silcom-invoice-field\">Business name<input type=\"text\" value=\"Silcom\" data-invoice-field=\"businessName\"><\/label>\n\t\t\t\t\t\t\t<label class=\"silcom-invoice-field\">Your email<input type=\"email\" placeholder=\"accounts@example.com\" data-invoice-field=\"senderEmail\"><\/label>\n\t\t\t\t\t\t<\/div>\n\t\t\t\t\t\t<div class=\"silcom-invoice-form__split\">\n\t\t\t\t\t\t\t<label class=\"silcom-invoice-field\">Invoice background<select data-invoice-field=\"theme\"><option value=\"auto\">Auto match uploaded logo<\/option><option value=\"light\">Light invoice<\/option><option value=\"dark\">Dark invoice<\/option><\/select><\/label>\n\t\t\t\t\t\t\t<label class=\"silcom-invoice-field\">Currency<select data-invoice-field=\"currency\"><option value=\"NZD\">NZD<\/option><option value=\"AUD\">AUD<\/option><option value=\"GBP\">GBP<\/option><option value=\"CAD\">CAD<\/option><option value=\"USD\">USD<\/option><\/select><\/label>\n\t\t\t\t\t\t<\/div>\n\t\t\t\t\t\t<button class=\"silcom-invoice-preview-hold\" type=\"button\" data-invoice-preview-hold data-preview-step=\"brand\" data-preview-label=\"brand and invoice setup\">Hold for Preview<\/button>\n\t\t\t\t\t<\/section>\n\t\t\t\t\t<section class=\"silcom-invoice-form__section\">\n\t\t\t\t\t\t<h2>Recipient and numbering<\/h2>\n\t\t\t\t\t\t<div class=\"silcom-invoice-form__split\">\n\t\t\t\t\t\t\t<label class=\"silcom-invoice-field\">Bill to<input type=\"text\" placeholder=\"Client or company name\" data-invoice-field=\"recipientName\"><\/label>\n\t\t\t\t\t\t\t<label class=\"silcom-invoice-field\">Recipient email<input type=\"email\" placeholder=\"client@example.com\" data-invoice-field=\"recipientEmail\"><\/label>\n\t\t\t\t\t\t<\/div>\n\t\t\t\t\t\t<div class=\"silcom-invoice-form__split\">\n\t\t\t\t\t\t\t<label class=\"silcom-invoice-field\">Invoice number<select data-invoice-field=\"numberMode\"><option value=\"date\">Date based<\/option><option value=\"auto\">Random generated<\/option><option value=\"series\">Prefix and series<\/option><option value=\"manual\">Manual<\/option><\/select><\/label>\n\t\t\t\t\t\t\t<label class=\"silcom-invoice-field\">Prefix<input type=\"text\" value=\"INV\" data-invoice-field=\"prefix\"><\/label>\n\t\t\t\t\t\t<\/div>\n\t\t\t\t\t\t<div class=\"silcom-invoice-form__split\">\n\t\t\t\t\t\t\t<label class=\"silcom-invoice-field\">Manual number \/ series start<input type=\"text\" value=\"1001\" data-invoice-field=\"manualNumber\"><\/label>\n\t\t\t\t\t\t\t<label class=\"silcom-invoice-field\">Due date<input type=\"date\" data-invoice-field=\"dueDate\"><\/label>\n\t\t\t\t\t\t<\/div>\n\t\t\t\t\t\t<button class=\"silcom-invoice-preview-hold\" type=\"button\" data-invoice-preview-hold data-preview-step=\"recipient\" data-preview-label=\"recipient and numbering\">Hold for Preview<\/button>\n\t\t\t\t\t<\/section>\n\t\t\t\t\t<section class=\"silcom-invoice-form__section\">\n\t\t\t\t\t\t<h2>Products and tax<\/h2>\n\t\t\t\t\t\t<div class=\"silcom-invoice-form__split\">\n\t\t\t\t\t\t\t<label class=\"silcom-invoice-field\">Tax mode<select data-invoice-field=\"taxMode\"><option value=\"exclusive\">GST\/tax exclusive<\/option><option value=\"inclusive\">GST\/tax inclusive<\/option><option value=\"none\">No tax<\/option><\/select><\/label>\n\t\t\t\t\t\t\t<label class=\"silcom-invoice-field\">Tax rate<select data-invoice-field=\"taxRate\"><option value=\"0.15\">GST 15%<\/option><option value=\"0.10\">GST 10%<\/option><option value=\"0.20\">VAT 20%<\/option><option value=\"0.05\">5%<\/option><option value=\"0\">0%<\/option><\/select><\/label>\n\t\t\t\t\t\t<\/div>\n\t\t\t\t\t\t<div class=\"silcom-invoice-items\" data-invoice-items><\/div>\n\t\t\t\t\t\t<button class=\"silcom-invoice-secondary\" type=\"button\" data-invoice-add>Add product or service<\/button>\n\t\t\t\t\t\t<button class=\"silcom-invoice-preview-hold\" type=\"button\" data-invoice-preview-hold data-preview-step=\"items\" data-preview-label=\"products and tax\">Hold for Preview<\/button>\n\t\t\t\t\t<\/section>\n\t\t\t\t\t\t<section class=\"silcom-invoice-form__section\">\n\t\t\t\t\t\t\t<h2>Payment notes<\/h2>\n\t\t\t\t\t\t\t<label class=\"silcom-invoice-field\">Bank account \/ payment details<textarea rows=\"4\" data-invoice-field=\"bankDetails\">Bank account:\n\tAccount name:\n\tReference: invoice number<\/textarea><\/label>\n\t\t\t\t\t\t\t<label class=\"silcom-invoice-field\">Customer message<textarea rows=\"3\" data-invoice-field=\"message\">Thank you for your business. Please contact us if you have any questions about this invoice.<\/textarea><\/label>\n\t\t\t\t\t\t\t<button class=\"silcom-invoice-preview-hold\" type=\"button\" data-invoice-preview-hold data-preview-step=\"payment\" data-preview-label=\"payment notes\">Hold for Preview<\/button>\n\t\t\t\t\t\t<\/section>\n\t\t\t\t\t\t<section class=\"silcom-invoice-form__section\">\n\t\t\t\t\t\t\t<h2>Signature<\/h2>\n\t\t\t\t\t\t\t<div class=\"silcom-invoice-form__split\">\n\t\t\t\t\t\t\t\t<label class=\"silcom-invoice-field\">Add a signature?<select data-invoice-field=\"signatureEnabled\"><option value=\"no\">No<\/option><option value=\"yes\">Yes<\/option><\/select><\/label>\n\t\t\t\t\t\t\t\t<label class=\"silcom-invoice-field\" data-signature-control>Signature type<select data-invoice-field=\"signatureType\"><option value=\"typed\">Typed signature<\/option><option value=\"upload\">Upload signature image<\/option><\/select><\/label>\n\t\t\t\t\t\t\t<\/div>\n\t\t\t\t\t\t\t<div class=\"silcom-invoice-form__split\" data-signature-control>\n\t\t\t\t\t\t\t\t<label class=\"silcom-invoice-field\" data-signature-typed>Typed name<input type=\"text\" placeholder=\"Your name\" data-invoice-field=\"signatureText\"><\/label>\n\t\t\t\t\t\t\t\t<label class=\"silcom-invoice-field\" data-signature-upload>Upload signature<input type=\"file\" accept=\"image\/*\" data-invoice-signature><small>Best with a dark signature on white paper. The white background is cleaned locally in your browser.<\/small><\/label>\n\t\t\t\t\t\t\t<\/div>\n\t\t\t\t\t\t\t<button class=\"silcom-invoice-preview-hold\" type=\"button\" data-invoice-preview-hold data-preview-step=\"signature\" data-preview-label=\"signature\">Hold for Preview<\/button>\n\t\t\t\t\t\t<\/section>\n\t\t\t\t\t<\/form>\n\t\t\t\t\t<div class=\"silcom-invoice-preview-shell\" id=\"silcom-invoice-preview-shell\">\n\t\t\t\t\t\t<div class=\"silcom-invoice-actions\">\n\t\t\t\t\t\t\t<button type=\"button\" data-invoice-print>Print \/ save PDF<\/button>\n\t\t\t\t\t\t\t<button type=\"button\" data-invoice-email>Email draft<\/button>\n\t\t\t\t\t\t<\/div>\n\t\t\t\t\t\t<div class=\"silcom-invoice-preview-status\" data-preview-status>Preview is synced with your invoice setup.<\/div>\n\t\t\t\t\t\t<article class=\"silcom-invoice-preview is-light\" data-invoice-preview>\n\t\t\t\t\t\t\t<header class=\"silcom-invoice-preview__top\" data-preview-zone=\"brand\">\n\t\t\t\t\t\t\t\t<div><img alt=\"\" data-preview-logo hidden><h2 data-preview-business>Silcom<\/h2><p data-preview-sender>accounts@example.com<\/p><\/div>\n\t\t\t\t\t\t\t\t<div class=\"silcom-invoice-preview__meta\" data-preview-zone=\"recipient\"><strong>Invoice<\/strong><span data-preview-number>INV-0001<\/span><small data-preview-dates><\/small><\/div>\n\t\t\t\t\t\t\t<\/header>\n\t\t\t\t\t\t\t<section class=\"silcom-invoice-preview__recipient\" data-preview-zone=\"recipient\"><span>Bill to<\/span><strong data-preview-recipient>Client name<\/strong><p data-preview-recipient-email>client@example.com<\/p><\/section>\n\t\t\t\t\t\t\t<div data-preview-zone=\"items\"><table class=\"silcom-invoice-preview__table\"><thead><tr><th>Product \/ service<\/th><th>Qty<\/th><th>Price<\/th><th>Total<\/th><\/tr><\/thead><tbody data-preview-items><\/tbody><\/table><\/div>\n\t\t\t\t\t\t\t<section class=\"silcom-invoice-preview__totals\" data-preview-zone=\"items\"><div><span>Subtotal<\/span><strong data-preview-subtotal><\/strong><\/div><div><span data-preview-tax-label>GST<\/span><strong data-preview-tax><\/strong><\/div><div class=\"silcom-invoice-preview__grand\"><span>Total<\/span><strong data-preview-total><\/strong><\/div><\/section>\n\t\t\t\t\t\t\t\t<footer class=\"silcom-invoice-preview__footer\" data-preview-zone=\"payment\"><div><span>Payment details<\/span><p data-preview-bank><\/p><\/div><div><span>Message<\/span><p data-preview-message><\/p><\/div><\/footer>\n\t\t\t\t\t\t\t\t<section class=\"silcom-invoice-preview__signature\" data-preview-zone=\"signature\" data-preview-signature hidden><span>Authorised signature<\/span><strong data-preview-signature-text><\/strong><img alt=\"Signature\" data-preview-signature-image hidden><\/section>\n\t\t\t\t\t\t\t<\/article>\n\t\t\t\t\t\t<p class=\"silcom-invoice-email-note\">Email draft pre-fills recipient, subject and invoice summary. Use Print \/ save PDF when you need a downloadable invoice file.<\/p>\n\t\t\t\t<\/div>\n\t\t\t<\/div>\n\t\t<\/div>\n\t<\/section>\n\t<script>\n\t(function(){\n\t\tvar root=document.querySelector('[data-silcom-invoice-builder]'); if(!root) return;\n\t\t\tvar itemsWrap=root.querySelector('[data-invoice-items]'), preview=root.querySelector('[data-invoice-preview]'), previewShell=root.querySelector('.silcom-invoice-preview-shell'), logoPreview=root.querySelector('[data-preview-logo]'), signatureImage=root.querySelector('[data-preview-signature-image]');\n\t\t\tvar invoiceSeed=Math.floor(1000+Math.random()*9000), autoTheme='light';\n\t\t\tvar uploadedSignature='';\n\t\tfunction val(name){var el=root.querySelector('[data-invoice-field=\"'+name+'\"]'); return el?el.value:'';}\n\t\tfunction set(sel,text){var el=root.querySelector(sel); if(el) el.textContent=text;}\n\t\tfunction html(text){return String(text).replace(\/[&<>\"']\/g,function(c){return({'&':'&amp;','<':'&lt;','>':'&gt;','\"':'&quot;',\"'\":'&#039;'})[c];});}\n\t\tfunction money(n){return new Intl.NumberFormat('en-NZ',{style:'currency',currency:val('currency')||'NZD'}).format(Number.isFinite(n)?n:0);}\n\t\tfunction today(){return new Date().toISOString().slice(0,10);}\n\t\tfunction invNo(){var p=(val('prefix')||'INV').toUpperCase().replace(\/[^A-Z0-9-]\/g,'')||'INV', m=val('numberMode'), manual=val('manualNumber')||'1001', d=new Date(), stamp=''+d.getFullYear()+String(d.getMonth()+1).padStart(2,'0')+String(d.getDate()).padStart(2,'0'); if(m==='manual')return manual; if(m==='series')return p+'-'+String(parseInt(manual,10)||1).padStart(4,'0'); if(m==='auto')return p+'-'+invoiceSeed; return p+'-'+stamp+'-001';}\n\t\tfunction addItem(desc,qty,price){var row=document.createElement('div'); row.className='silcom-invoice-item'; row.innerHTML='<label class=\"silcom-invoice-field silcom-invoice-field--wide\">Product \/ service<input type=\"text\" data-item-desc placeholder=\"Managed IT support\" value=\"'+html(desc||'')+'\"><\/label><label class=\"silcom-invoice-field\">Qty<input type=\"number\" data-item-qty min=\"0\" step=\"0.01\" value=\"'+(qty||'1')+'\"><\/label><label class=\"silcom-invoice-field\">Price<input type=\"number\" data-item-price min=\"0\" step=\"0.01\" value=\"'+(price||'0')+'\"><\/label><button type=\"button\" aria-label=\"Remove item\" data-item-remove>&times;<\/button>'; itemsWrap.appendChild(row); update();}\n\t\tfunction items(){return Array.from(itemsWrap.querySelectorAll('.silcom-invoice-item')).map(function(r){var q=Math.max(0,parseFloat(r.querySelector('[data-item-qty]').value||'0')), price=Math.max(0,parseFloat(r.querySelector('[data-item-price]').value||'0')); return{desc:r.querySelector('[data-item-desc]').value||'Product or service',qty:q,price:price,total:q*price};});}\n\t\tfunction totals(){var rate=parseFloat(val('taxRate')||'0'), mode=val('taxMode'), gross=items().reduce(function(s,i){return s+i.total;},0); if(mode==='none'||rate===0)return{subtotal:gross,tax:0,total:gross}; if(mode==='inclusive'){var tax=gross-(gross\/(1+rate)); return{subtotal:gross-tax,tax:tax,total:gross};} return{subtotal:gross,tax:gross*rate,total:gross+gross*rate};}\n\t\t\tfunction updateTheme(){var theme=val('theme')==='auto'?autoTheme:val('theme'); preview.classList.toggle('is-dark',theme==='dark'); preview.classList.toggle('is-light',theme!=='dark');}\n\t\t\t\tfunction syncPreviewFocus(target){var map={businessName:'brand',senderEmail:'brand',theme:'brand',currency:'items',recipientName:'recipient',recipientEmail:'recipient',numberMode:'recipient',prefix:'recipient',manualNumber:'recipient',dueDate:'recipient',taxMode:'items',taxRate:'items',bankDetails:'payment',message:'payment',signatureEnabled:'signature',signatureType:'signature',signatureText:'signature'}, zone=''; if(target){if(target.matches('[data-item-desc],[data-item-qty],[data-item-price]'))zone='items'; if(target.matches('[data-invoice-signature]'))zone='signature'; if(!zone && target.getAttribute('data-invoice-field'))zone=map[target.getAttribute('data-invoice-field')]||'';} root.querySelectorAll('[data-preview-zone]').forEach(function(el){el.classList.toggle('is-syncing',zone && el.getAttribute('data-preview-zone')===zone);}); var status=root.querySelector('[data-preview-status]'); if(status){status.textContent=zone?('Preview updated: '+({brand:'business details',recipient:'recipient and invoice number',items:'products, tax and totals',payment:'payment notes',signature:'authorised signature'}[zone]||'invoice details')):'Preview is synced with your invoice setup.';}}\n\t\t\t\tfunction updateSignatureControls(){var enabled=val('signatureEnabled')==='yes', type=val('signatureType')||'typed'; root.querySelectorAll('[data-signature-control]').forEach(function(el){el.hidden=!enabled;}); var typed=root.querySelector('[data-signature-typed]'), upload=root.querySelector('[data-signature-upload]'); if(typed)typed.hidden=!enabled||type!=='typed'; if(upload)upload.hidden=!enabled||type!=='upload';}\n\t\t\t\tfunction updateSignaturePreview(){var enabled=val('signatureEnabled')==='yes', type=val('signatureType')||'typed', wrap=root.querySelector('[data-preview-signature]'), typed=root.querySelector('[data-preview-signature-text]'); if(!wrap)return; wrap.hidden=!enabled; typed.hidden=true; signatureImage.hidden=true; if(!enabled)return; if(type==='upload' && uploadedSignature){signatureImage.src=uploadedSignature; signatureImage.hidden=false;}else{typed.textContent=val('signatureText')||val('businessName')||'Authorised signature'; typed.hidden=false;}}\n\t\t\t\tfunction update(){var t=totals(), body=root.querySelector('[data-preview-items]'); updateTheme(); updateSignatureControls(); set('[data-preview-business]',val('businessName')||'Your business'); set('[data-preview-sender]',val('senderEmail')||'your@email.com'); set('[data-preview-number]',invNo()); set('[data-preview-dates]','Issued '+today()+(val('dueDate')?' | Due '+val('dueDate'):'')); set('[data-preview-recipient]',val('recipientName')||'Client name'); set('[data-preview-recipient-email]',val('recipientEmail')||'client@example.com'); body.innerHTML=items().map(function(i){return'<tr><td>'+html(i.desc)+'<\/td><td>'+i.qty+'<\/td><td>'+money(i.price)+'<\/td><td>'+money(i.total)+'<\/td><\/tr>';}).join(''); set('[data-preview-subtotal]',money(t.subtotal)); set('[data-preview-tax-label]',val('taxMode')==='inclusive'?'Included tax':(val('taxMode')==='none'?'Tax':'Tax')); set('[data-preview-tax]',money(t.tax)); set('[data-preview-total]',money(t.total)); set('[data-preview-bank]',val('bankDetails')||'Add payment instructions here.'); set('[data-preview-message]',val('message')||'Thank you for your business.'); updateSignaturePreview();}\n\t\t\t\tfunction analyseLogo(src){var img=new Image(); img.onload=function(){var c=document.createElement('canvas'), size=24; c.width=size; c.height=size; var x=c.getContext('2d'); x.drawImage(img,0,0,size,size); var d=x.getImageData(0,0,size,size).data,total=0,count=0,average=255; for(var i=0;i<d.length;i+=4){if(d[i+3]<20)continue; total+=(d[i]+d[i+1]+d[i+2])\/3; count++;} if(count>0){average=total\/count;} autoTheme=average<120?'dark':'light'; update();}; img.src=src;}\n\t\t\t\tfunction cleanSignature(src){var img=new Image(); img.onload=function(){var c=document.createElement('canvas'), max=700, scale=Math.min(1,max\/img.width); c.width=Math.max(1,Math.round(img.width*scale)); c.height=Math.max(1,Math.round(img.height*scale)); var x=c.getContext('2d'); x.drawImage(img,0,0,c.width,c.height); var d=x.getImageData(0,0,c.width,c.height), p=d.data; for(var i=0;i<p.length;i+=4){var r=p[i],g=p[i+1],b=p[i+2], bright=(r+g+b)\/3; if(bright>218 && Math.max(r,g,b)-Math.min(r,g,b)<38){p[i+3]=0;}else if(bright>188){p[i+3]=Math.max(0,255-(bright-188)*5);}} x.putImageData(d,0,0); uploadedSignature=c.toDataURL('image\/png'); update(); syncPreviewFocus(root.querySelector('[data-invoice-signature]'));}; img.src=src;}\n\t\t\t\t\tfunction emailDraft(){var t=totals(), amp=String.fromCharCode(38), subject=encodeURIComponent('Invoice '+invNo()+' from '+(val('businessName')||'Your business')), body=encodeURIComponent('Hello '+(val('recipientName')||'')+',\\n\\nPlease find invoice '+invNo()+' summary below.\\n\\n'+items().map(function(i){return i.desc+' | Qty '+i.qty+' | '+money(i.total);}).join('\\n')+'\\n\\nSubtotal: '+money(t.subtotal)+'\\nTax: '+money(t.tax)+'\\nTotal: '+money(t.total)+'\\n\\n'+(val('bankDetails')||'')+'\\n\\n'+(val('message')||'Thank you for your business.')), to=encodeURIComponent(val('recipientEmail')||''), cc=val('senderEmail')?amp+'cc='+encodeURIComponent(val('senderEmail')):''; window.location.href='mailto:'+to+'?subject='+subject+cc+amp+'body='+body;}\n\t\t\t\tfunction printInvoice(){update(); var invoice=root.querySelector('[data-invoice-preview]'), oldFrame=document.querySelector('[data-silcom-print-frame]'), title='Invoice '+invNo(), css='@page{size:A4;margin:14mm;}body{background:#f3f4f6;margin:0;padding:24px;font-family:Arial,sans-serif;color:#0f172a;}.silcom-invoice-preview{background:#fff;color:#0f172a;max-width:820px;margin:0 auto;padding:38px;border-radius:10px;box-sizing:border-box;}.silcom-invoice-preview.is-dark{background:#050505;color:#f8fafc;}.silcom-invoice-preview__top{display:flex;justify-content:space-between;gap:18px;margin:0 0 28px;}.silcom-invoice-preview__top img{display:block;max-height:64px;max-width:190px;object-fit:contain;}.silcom-invoice-preview h2{font-size:28px;margin:0 0 4px;}.silcom-invoice-preview p{margin:0;white-space:pre-line;}.silcom-invoice-preview__meta{text-align:right;}.silcom-invoice-preview__meta strong{display:block;font-size:28px;text-transform:uppercase;}.silcom-invoice-preview__meta span,.silcom-invoice-preview__meta small{display:block;}.silcom-invoice-preview__recipient{border:1px solid rgba(15,23,42,.12);border-radius:8px;margin:0 0 22px;padding:14px;}.silcom-invoice-preview__recipient span,.silcom-invoice-preview__footer span,.silcom-invoice-preview__signature span{color:#2563eb;display:block;font-size:12px;font-weight:900;margin:0 0 6px;text-transform:uppercase;}.silcom-invoice-preview__table{border-collapse:collapse;font-size:14px;width:100%;}.silcom-invoice-preview__table th,.silcom-invoice-preview__table td{border-bottom:1px solid rgba(15,23,42,.12);padding:11px 8px;text-align:left;}.silcom-invoice-preview__table th:nth-child(n+2),.silcom-invoice-preview__table td:nth-child(n+2){text-align:right;}.silcom-invoice-preview__totals{display:grid;gap:8px;margin:20px 0 26px auto;max-width:300px;}.silcom-invoice-preview__totals div{display:flex;justify-content:space-between;}.silcom-invoice-preview__grand{border-top:2px solid currentColor;font-size:20px;font-weight:900;padding-top:10px;}.silcom-invoice-preview__footer{display:grid;gap:18px;grid-template-columns:1fr 1fr;}.silcom-invoice-preview__signature{border-top:1px solid rgba(15,23,42,.12);margin:24px 0 0 auto;max-width:280px;padding-top:16px;text-align:right;}.silcom-invoice-preview__signature strong{display:block;font-family:cursive;font-size:30px;font-weight:400;line-height:1.1;}.silcom-invoice-preview__signature img{display:block;margin:0 0 0 auto;max-height:86px;max-width:240px;object-fit:contain;}@media print{body{background:#fff;padding:0;}.silcom-invoice-preview{box-shadow:none;margin:0;max-width:none;}.silcom-invoice-preview [data-preview-zone]{background:transparent!important;outline:0!important;}}'; if(oldFrame)oldFrame.remove(); var frame=document.createElement('iframe'); frame.setAttribute('data-silcom-print-frame','true'); frame.title=title; frame.style.cssText='position:fixed;right:0;bottom:0;width:0;height:0;border:0;visibility:hidden;'; document.body.appendChild(frame); var doc=frame.contentWindow.document; doc.open(); doc.write('<!doctype html><html><head><meta charset=\"utf-8\"><title>'+html(title)+'<\/title><style>'+css+'<\/style><\/head><body>'+invoice.outerHTML+'<\/body><\/html>'); doc.close(); window.setTimeout(function(){try{frame.contentWindow.focus(); frame.contentWindow.print();}catch(e){window.print();}}, 300);}\n\t\t\tfunction revealProgressivePreview(button){\n\t\t\t\tupdate();\n\t\t\t\tif(previewShell && window.matchMedia('(max-width: 620px)').matches){\n\t\t\t\t\tpreviewShell.classList.remove('is-progressive-hidden');\n\t\t\t\t\tpreviewShell.setAttribute('data-preview-revealed-step',button.getAttribute('data-preview-step')||'');\n\t\t\t\t}\n\t\t\t\tbutton.classList.add('is-held');\n\t\t\t\tbutton.setAttribute('aria-label','Preview updated through '+(button.getAttribute('data-preview-label')||'this section'));\n\t\t\t}\n\t\t\tfunction bindPreviewHold(button){\n\t\t\t\tvar timer=null;\n\t\t\t\tbutton.addEventListener('pointerdown',function(){\n\t\t\t\t\tbutton.removeAttribute('data-preview-skip-click');\n\t\t\t\t\ttimer=window.setTimeout(function(){\n\t\t\t\t\t\tbutton.setAttribute('data-preview-skip-click','true');\n\t\t\t\t\t\trevealProgressivePreview(button);\n\t\t\t\t\t},550);\n\t\t\t\t});\n\t\t\t\tbutton.addEventListener('pointerup',function(){if(timer){window.clearTimeout(timer);timer=null;}});\n\t\t\t\tbutton.addEventListener('pointercancel',function(){if(timer){window.clearTimeout(timer);timer=null;}});\n\t\t\t\tbutton.addEventListener('click',function(){\n\t\t\t\t\tif(button.hasAttribute('data-preview-skip-click')){button.removeAttribute('data-preview-skip-click');return;}\n\t\t\t\t\trevealProgressivePreview(button);\n\t\t\t\t});\n\t\t\t}\n\t\t\troot.querySelectorAll('[data-invoice-preview-hold]').forEach(bindPreviewHold);\n\t\t\tif(previewShell && window.matchMedia('(max-width: 620px)').matches){previewShell.classList.add('is-progressive-hidden');}\n\t\t\troot.addEventListener('input',function(e){update(); syncPreviewFocus(e.target);}); root.addEventListener('change',function(e){update(); syncPreviewFocus(e.target);}); root.addEventListener('focusin',function(e){syncPreviewFocus(e.target);}); root.querySelector('[data-invoice-add]').addEventListener('click',function(){addItem('', '1','0'); syncPreviewFocus(itemsWrap);}); itemsWrap.addEventListener('click',function(e){if(!e.target.matches('[data-item-remove]'))return; e.target.closest('.silcom-invoice-item').remove(); if(!itemsWrap.children.length)addItem('Product or service','1','0'); update(); syncPreviewFocus(itemsWrap);}); root.querySelector('[data-invoice-print]').addEventListener('click',printInvoice); root.querySelector('[data-invoice-email]').addEventListener('click',emailDraft);\n\t\t\t\troot.querySelector('[data-invoice-logo]').addEventListener('change',function(e){var file=e.target.files?e.target.files[0]:null; if(!file)return; var r=new FileReader(); r.onload=function(){logoPreview.src=r.result; logoPreview.hidden=false; analyseLogo(r.result); update();}; r.readAsDataURL(file);});\n\t\t\t\troot.querySelector('[data-invoice-signature]').addEventListener('change',function(e){var file=e.target.files?e.target.files[0]:null; if(!file)return; var r=new FileReader(); r.onload=function(){cleanSignature(r.result);}; r.readAsDataURL(file);});\n\t\tvar due=root.querySelector('[data-invoice-field=\"dueDate\"]'), dd=new Date(); dd.setDate(dd.getDate()+14); due.value=dd.toISOString().slice(0,10); addItem('Product or service','1','0'); addItem('Consulting \/ setup','1','0'); update();\n\t}());\n\t<\/script>\n\t<script>\n\t(function(){\n\t\tvar root=document.querySelector('[data-silcom-bi-tool]');\n\t\tif(!root)return;\n\t\tvar tool=root.querySelector('[data-investment-tool]');\n\t\tif(!tool)return;\n\t\tvar searchUrl=tool.getAttribute('data-search-url'), chartUrl=tool.getAttribute('data-chart-url'), query=tool.querySelector('[data-investment-query]'), searchButton=tool.querySelector('[data-investment-search]'), results=tool.querySelector('[data-investment-results]');\n\t\tvar nameEl=tool.querySelector('[data-investment-name]'), marketEl=tool.querySelector('[data-investment-market]'), priceEl=tool.querySelector('[data-investment-price]'), currencyEl=tool.querySelector('[data-investment-currency]'), moveEl=tool.querySelector('[data-investment-move]'), rangeEl=tool.querySelector('[data-investment-range]');\n\t\tvar totalEl=tool.querySelector('[data-investment-total]'), qtyEl=tool.querySelector('[data-investment-qty]'), dcaEl=tool.querySelector('[data-investment-dca]'), resultEl=tool.querySelector('[data-investment-result]'), trendLabel=tool.querySelector('[data-investment-trend-label]'), chart=tool.querySelector('[data-investment-chart]'), insights=tool.querySelector('[data-investment-insights]');\n\t\tvar selected=null, chartData=null, currentPrice=0;\n\t\tfunction apiUrl(base,params){var url=new URL(base,window.location.href); Object.keys(params).forEach(function(key){url.searchParams.set(key,params[key]);}); return url.toString();}\n\t\tfunction esc(text){return String(text==null?'':text).replace(\/[&<>\"']\/g,function(c){return({'&':'&amp;','<':'&lt;','>':'&gt;','\"':'&quot;',\"'\":'&#039;'})[c];});}\n\t\tfunction money(value,currency){var n=Number(value||0); return (currency||'')+' '+n.toLocaleString(undefined,{maximumFractionDigits:2});}\n\t\tfunction numberValue(value){var n=parseFloat(String(value||'').replace(\/[$,%\\s,]\/g,'')); return Number.isFinite(n)?n:0;}\n\t\tfunction pct(value){return (value>=0?'+':'')+Number(value||0).toFixed(2)+'%';}\n\t\tfunction searchTickers(){var text=query.value.trim(); if(text.length<2){results.hidden=false; results.innerHTML='<p>Type at least two characters.<\/p>'; return;} results.hidden=false; results.innerHTML='<p>Searching live market listings...<\/p>'; fetch(apiUrl(searchUrl,{query:text})).then(function(res){if(!res.ok)throw new Error('Ticker search failed.'); return res.json();}).then(function(data){var list=data.results||[]; if(!list.length){results.innerHTML='<p>No equity listings found. Try a ticker symbol instead.<\/p>'; return;} results.innerHTML=list.map(function(item){return '<button type=\"button\" data-symbol=\"'+esc(item.symbol)+'\" data-name=\"'+esc(item.name)+'\" data-exchange=\"'+esc(item.exchange)+'\" data-sector=\"'+esc(item.sector)+'\" data-industry=\"'+esc(item.industry)+'\"><strong>'+esc(item.symbol)+'<\/strong><span>'+esc(item.name)+'<\/span><small>'+esc(item.exchange)+' \u00b7 '+esc(item.sector||item.type||'Equity')+'<\/small><\/button>';}).join('');}).catch(function(error){results.innerHTML='<p>'+esc(error.message||'Search failed.')+'<\/p>';});}\n\t\tfunction selectTicker(button){selected={symbol:button.getAttribute('data-symbol')||'',name:button.getAttribute('data-name')||'',exchange:button.getAttribute('data-exchange')||'',sector:button.getAttribute('data-sector')||'',industry:button.getAttribute('data-industry')||''}; nameEl.textContent=selected.symbol+' \u00b7 '+selected.name; marketEl.textContent=(selected.exchange||'Market')+(selected.sector?' \u00b7 '+selected.sector:''); results.hidden=true; loadChart(selected.symbol);}\n\t\tfunction loadChart(symbol){trendLabel.textContent='Loading '+symbol+'...'; fetch(apiUrl(chartUrl,{symbol:symbol,range:'1y'})).then(function(res){if(!res.ok)throw new Error('Price history failed.'); return res.json();}).then(function(data){chartData=data; var meta=data.meta||{}, closes=(data.close||[]).filter(function(v){return Number.isFinite(Number(v));}); currentPrice=Number(meta.regularMarketPrice||closes[closes.length-1]||0); priceEl.textContent=money(currentPrice,meta.currency||''); currencyEl.textContent=(meta.fullExchangeName||meta.exchangeName||selected.exchange||'Market')+' \u00b7 '+(meta.currency||''); var first=closes[0]||0, last=closes[closes.length-1]||currentPrice, move=first?((last-first)\/Math.abs(first))*100:0, high=Math.max.apply(null,closes), low=Math.min.apply(null,closes); moveEl.textContent=pct(move); rangeEl.textContent='1Y range '+money(low,meta.currency||'')+' to '+money(high,meta.currency||''); trendLabel.textContent=selected.symbol+' 1-year price trend'; drawInvestmentChart(closes,meta.currency||''); renderPositions(); renderInvestmentInsights(closes,meta);}).catch(function(error){trendLabel.textContent='Could not load live chart'; renderInvestmentInsights([],{}); rangeEl.textContent=error.message||'Market data unavailable.';});}\n\t\tfunction drawInvestmentChart(values,currency){var ctx=chart.getContext('2d'), w=chart.width, h=chart.height, pad=42; ctx.clearRect(0,0,w,h); ctx.fillStyle='#020617'; ctx.fillRect(0,0,w,h); ctx.strokeStyle='rgba(148,163,184,.25)'; ctx.lineWidth=1; for(var g=0;g<5;g++){var y=pad+(h-pad*2)*g\/4; ctx.beginPath(); ctx.moveTo(pad,y); ctx.lineTo(w-pad,y); ctx.stroke();} if(!values.length){ctx.fillStyle='#94a3b8'; ctx.font='16px system-ui'; ctx.fillText('Search and select a ticker to draw the trend.',pad,68); return;} var min=Math.min.apply(null,values), max=Math.max.apply(null,values); if(max===min)max=min+1; ctx.strokeStyle='#38bdf8'; ctx.lineWidth=3; ctx.beginPath(); values.forEach(function(value,index){var x=pad+(w-pad*2)*index\/Math.max(values.length-1,1), y=h-pad-(value-min)*(h-pad*2)\/(max-min); if(index===0)ctx.moveTo(x,y); else ctx.lineTo(x,y);}); ctx.stroke(); ctx.fillStyle='#cbd5e1'; ctx.font='12px system-ui'; ctx.fillText(money(max,currency),pad,24); ctx.fillText(money(min,currency),pad,h-12);}\n\t\tfunction renderPositions(){var total=0, qty=0; Array.prototype.forEach.call(tool.querySelectorAll('.silcom-investment-position'),function(row){var price=numberValue(row.querySelector('[data-position-price]').value), amount=numberValue(row.querySelector('[data-position-qty]').value), cost=price*amount; total+=cost; qty+=amount; row.querySelector('[data-position-cost]').textContent=money(cost,chartData?(chartData.meta||{}).currency:'');}); var avg=qty?total\/qty:0, value=currentPrice*qty, gain=value-total, gainPct=total?gain\/total*100:0; totalEl.textContent=money(total,chartData?(chartData.meta||{}).currency:''); qtyEl.textContent=qty.toLocaleString(undefined,{maximumFractionDigits:4}); dcaEl.textContent=qty?money(avg,chartData?(chartData.meta||{}).currency:''):'-'; resultEl.textContent=qty?money(gain,chartData?(chartData.meta||{}).currency:'')+' ('+pct(gainPct)+')':'-';}\n\t\tfunction renderInvestmentInsights(values,meta){var currency=meta.currency||'', first=values[0]||0, last=values[values.length-1]||currentPrice, oneYear=first?((last-first)\/Math.abs(first))*100:0, high=values.length?Math.max.apply(null,values):0, low=values.length?Math.min.apply(null,values):0, avg50=values.slice(-50).reduce(function(a,b){return a+b;},0)\/Math.max(values.slice(-50).length,1), avg200=values.slice(-200).reduce(function(a,b){return a+b;},0)\/Math.max(values.slice(-200).length,1), momentum=last>avg50?'Positive short-term momentum':'Weak or neutral short-term momentum', longTerm=last>avg200?'above':'below'; insights.innerHTML=''+\n\t\t\t'<article><span>Stock snapshot<\/span><p>'+esc(selected?selected.name:'No company selected')+' trades as '+esc(selected?selected.symbol:'-')+' on '+esc(meta.fullExchangeName||(selected?selected.exchange:'')||'the selected market')+'. Current price is '+esc(money(currentPrice,currency))+'.<\/p><\/article>'+\n\t\t\t'<article><span>Technical trend<\/span><p>One-year movement is '+esc(pct(oneYear))+'. The latest price is '+esc(longTerm)+' the rough 200-day average. '+esc(momentum)+'.<\/p><\/article>'+\n\t\t\t'<article><span>Range context<\/span><p>Observed one-year range from this source is '+esc(money(low,currency))+' to '+esc(money(high,currency))+'. This helps show whether the current price is near the top, middle, or lower part of the recent range.<\/p><\/article>'+\n\t\t\t'<article><span>Position \/ DCA mirror<\/span><p>Add entry prices and quantities above. Insight Studio compares your average entry against the current price and estimates unrealised movement.<\/p><\/article>'+\n\t\t\t'<article><span>Valuation \/ fair value<\/span><p>No-key chart data cannot safely calculate intrinsic value. A local fair-value calculator or API-backed fundamentals module can be added next.<\/p><\/article>'+\n\t\t\t'<article><span>Factor grades<\/span><p>Momentum can be estimated from chart data. Value, growth, profitability and EPS revision grades need a fundamentals provider such as FMP, Alpha Vantage, or another API.<\/p><\/article>'+\n\t\t\t'<article><span>Insider and alternative signals<\/span><p>This module is reserved for sources like OpenInsider or Quiver-style alternative data if we decide to connect them later.<\/p><\/article>'+\n\t\t\t'<article><span>Plain-English summary<\/span><p>This first version gives a live ticker lookup, market listing, current price, technical trend, range context and position\/DCA mirror without storing a local ticker database.<\/p><\/article>';}\n\t\tsearchButton.addEventListener('click',searchTickers);\n\t\tquery.addEventListener('keydown',function(event){if(event.key==='Enter'){event.preventDefault(); searchTickers();}});\n\t\tresults.addEventListener('click',function(event){var button=event.target.closest('[data-symbol]'); if(!button)return; selectTicker(button);});\n\t\ttool.addEventListener('input',function(event){if(event.target.matches('[data-position-price], [data-position-qty]'))renderPositions();});\n\t\tdrawInvestmentChart([],'');\n\t}());\n\t<\/script>\n\t\n","protected":false},"excerpt":{"rendered":"","protected":false},"author":0,"featured_media":0,"parent":0,"menu_order":0,"comment_status":"closed","ping_status":"closed","template":"","meta":{"_uag_custom_page_level_css":"","footnotes":""},"class_list":["post-768","page","type-page","status-publish","hentry"],"uagb_featured_image_src":{"full":false,"thumbnail":false,"medium":false,"medium_large":false,"large":false,"1536x1536":false,"2048x2048":false,"woocommerce_thumbnail":false,"woocommerce_single":false,"woocommerce_gallery_thumbnail":false,"dgwt-wcas-product-suggestion":false},"uagb_author_info":{"display_name":"","author_link":"https:\/\/silcom.io\/author\/"},"uagb_comment_info":0,"uagb_excerpt":null,"_links":{"self":[{"href":"https:\/\/silcom.io\/wp-json\/wp\/v2\/pages\/768","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/silcom.io\/wp-json\/wp\/v2\/pages"}],"about":[{"href":"https:\/\/silcom.io\/wp-json\/wp\/v2\/types\/page"}],"replies":[{"embeddable":true,"href":"https:\/\/silcom.io\/wp-json\/wp\/v2\/comments?post=768"}],"version-history":[{"count":1,"href":"https:\/\/silcom.io\/wp-json\/wp\/v2\/pages\/768\/revisions"}],"predecessor-version":[{"id":776,"href":"https:\/\/silcom.io\/wp-json\/wp\/v2\/pages\/768\/revisions\/776"}],"wp:attachment":[{"href":"https:\/\/silcom.io\/wp-json\/wp\/v2\/media?parent=768"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}