feat: icons on the buttons, delivery, payment, status and Kanban
All checks were successful
Build and deploy / Validate source (push) Successful in 5s
Build and deploy / Integration suite on a real stack (push) Successful in 2m40s
Build and deploy / Secret scan and release gate (push) Successful in 5s
Build and deploy / Publish images (push) Successful in 53s

- Buttons: add to cart (bag with plus), go to payment (lock), remove from
  the cart (bin).
- Delivery: a pin for pickup in Franca, a truck for home delivery.
- Payment: card and QR code on the methods, copy on the PIX code, a clock on
  its countdown, a check on the confirmation.
- Status: a check when the files are sent, check, alert, redo and cross in
  the quality list instead of Unicode symbols, and a check on finished steps.
- Kanban: a truck or pin for where an order goes, a speech bubble on the
  WhatsApp link, a download arrow on the file buttons.

All Lucide shapes drawn inline, as in the header.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Cauê Faleiros
2026-09-30 13:13:45 -03:00
parent 20982a945f
commit 70a76feb23
10 changed files with 73 additions and 20 deletions

View File

@@ -150,7 +150,7 @@
const tempo=s=>s<60 ? 'menos de 1 min' : 'cerca de '+Math.ceil(s/60)+' min';
function textoEnvio(e) {
if (!e) return '';
if (e.fase==='pronto') return 'Arquivos enviados ✓';
if (e.fase==='pronto') return 'Arquivos enviados';
if (e.fase==='verificando') return 'Arquivos enviados · verificando a segurança…';
// Until the speed is measured, the range from the file size.
return 'Enviando seus arquivos: '+e.pct+'% · '+mb(e.sent)+' de '+mb(e.total)+' · '+
@@ -181,6 +181,7 @@
const i=document.createElement('i'); i.style.width=(e.fase==='verificando'?100:e.pct)+'%'; barra.append(i);
el.append(barra);
}
if (e?.fase==='pronto') el.insertAdjacentHTML('beforeend', icone('okCirculo'));
if (texto) el.append(document.createTextNode(texto));
el.classList.toggle('ok', e?.fase==='pronto');
}
@@ -188,7 +189,7 @@
if (topo) topo.textContent = !e || e.fase==='pronto' ? '' : e.fase==='verificando' ? '· verificando' : '· enviando '+e.pct+'%';
if (busy && texto) {
message(texto);
$('bPagar').textContent = e && e.fase!=='pronto' ? (e.fase==='verificando' ? 'Verificando os arquivos…' : 'Enviando arquivos… '+e.pct+'%') : $('bPagar').textContent;
$('bPagarTx').textContent = e && e.fase!=='pronto' ? (e.fase==='verificando' ? 'Verificando os arquivos…' : 'Enviando arquivos… '+e.pct+'%') : $('bPagarTx').textContent;
}
}
// Leaving the page pauses an upload; it resumes, but the customer is warned.
@@ -388,7 +389,9 @@
function confirmado(quote) {
clearInterval(waitTimer);
const box=node('div',null,'confirmado');
box.append(node('h4','Pagamento confirmado'),
const titulo=node('h4','Pagamento confirmado');
titulo.insertAdjacentHTML('afterbegin', icone('okCirculo'));
box.append(titulo,
node('p','Pedido #'+quote.order.number+' recebido e enviado para a produção. Você acompanha cada etapa em Meus pedidos.'));
button('Ver meus pedidos',()=>location.assign('/portal.html?order='+quote.order.id),box);
actions.append(box);
@@ -406,6 +409,7 @@
const radios=opcoes.map(([key,label])=>{
const el=button(label,()=>{ if(escolhido!==key){ escolhido=key; pinta(); } },grupo,'metodo');
el.setAttribute('role','radio'); el.dataset.metodo=key;
el.insertAdjacentHTML('afterbegin', icone(key==='pix'?'qrcode':'cartao'));
return el;
});
actions.append(node('h4','Forma de pagamento'),grupo,area);
@@ -453,10 +457,14 @@
code.readOnly=true;code.value=intent.pix_qr_code;code.setAttribute('aria-label','Código PIX copia e cola');
linha.append(code);
const copiar=button('Copiar código',async()=>{
try{ await navigator.clipboard.writeText(intent.pix_qr_code); copiar.textContent='Código copiado'; }
try{ await navigator.clipboard.writeText(intent.pix_qr_code);
copiar.replaceChildren(); copiar.insertAdjacentHTML('beforeend', icone('ok')+'<span>Código copiado</span>'); }
catch(_){ code.select(); }
},linha);
copiar.insertAdjacentHTML('afterbegin', icone('copiar'));
const prazo=node('p',null,'prazo');
prazo.insertAdjacentHTML('afterbegin', icone('relogio'));
const prazoTx=node('span'); prazo.append(prazoTx);
box.append(linha,prazo,node('p','Aguardando a confirmação do pagamento…','aguarda'));
actions.append(box);
waitForOrder();
@@ -465,7 +473,7 @@
if (!Number.isFinite(fim)) { prazo.remove(); return; }
const tick=()=>{
const s=Math.max(0,Math.ceil((fim-Date.now())/1000));
prazo.textContent='Pague em '+String(Math.floor(s/60)).padStart(2,'0')+':'+String(s%60).padStart(2,'0');
prazoTx.textContent='Pague em '+String(Math.floor(s/60)).padStart(2,'0')+':'+String(s%60).padStart(2,'0');
if (s>0) return;
clearInterval(pixClock);
const fimBox=node('div',null,'pixBox');

View File

@@ -1024,7 +1024,7 @@ footer a:hover{color:var(--laranja2)}
<span>Li e entendi. Quero imprimir assim mesmo.</span></label>
</div>
</div>
<button type="button" class="cAdd" id="bAdd" disabled>Adicionar ao carrinho</button>
<button type="button" class="cAdd" id="bAdd" disabled><svg class="ic" viewBox="0 0 24 24" aria-hidden="true" focusable="false"><path d="M6 2 3 6v14a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2V6l-3-4Z"/><path d="M3 6h18"/><path d="M12 10v6"/><path d="M9 13h6"/></svg><span>Adicionar ao carrinho</span></button>
<p class="cPe">Revisamos e reencaixamos antes de imprimir, sem custo.<span id="cPartes"></span></p>
</div>
@@ -1070,7 +1070,7 @@ footer a:hover{color:var(--laranja2)}
<div class="pinfo" id="pInfo"></div>
</div>
<div class="pcta">
<button class="pb p" id="pOk">Adicionar ao carrinho</button>
<button class="pb p" id="pOk"><svg class="ic" viewBox="0 0 24 24" aria-hidden="true" focusable="false"><path d="M6 2 3 6v14a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2V6l-3-4Z"/><path d="M3 6h18"/><path d="M12 10v6"/><path d="M9 13h6"/></svg><span>Adicionar ao carrinho</span></button>
<button class="pb" id="pMais">Adicionar mais artes</button>
<button class="pb" id="pVolta">Trocar o arquivo</button>
</div>
@@ -1115,7 +1115,7 @@ footer a:hover{color:var(--laranja2)}
<button class="opE on" data-ent="retira">
<span class="mk"></span>
<div>
<b>Retirar em Franca</b>
<b><svg class="ic" viewBox="0 0 24 24" aria-hidden="true" focusable="false"><path d="M20 10c0 4.99-5.54 10.19-7.4 11.8a1 1 0 0 1-1.2 0C9.54 20.19 4 14.99 4 10a8 8 0 0 1 16 0"/><circle cx="12" cy="10" r="3"/></svg>Retirar em Franca</b>
<span>Av. Major Elias Motta, 1281 · Jardim São Luís<br>
Segunda a sexta, das 7h às 17h</span>
</div>
@@ -1124,7 +1124,7 @@ footer a:hover{color:var(--laranja2)}
<button class="opE" data-ent="frete">
<span class="mk"></span>
<div>
<b>Receber em casa</b>
<b><svg class="ic" viewBox="0 0 24 24" aria-hidden="true" focusable="false"><path d="M14 18V6a2 2 0 0 0-2-2H4a2 2 0 0 0-2 2v11a1 1 0 0 0 1 1h2"/><path d="M15 18H9"/><path d="M19 18h2a1 1 0 0 0 1-1v-3.65a1 1 0 0 0-.22-.62l-3.48-4.35A1 1 0 0 0 17.52 8H14"/><circle cx="17" cy="18" r="2"/><circle cx="7" cy="18" r="2"/></svg>Receber em casa</b>
<span>Entrega pela Jadlog em todo o Brasil. Informe o CEP para ver o valor e o prazo.</span>
</div>
<em id="vFrete">—</em>
@@ -1162,7 +1162,7 @@ footer a:hover{color:var(--laranja2)}
<div id="resLin"></div>
<div class="tot"><span>Total</span><b id="carrTot">R$ 0,00</b></div>
<div class="cbts">
<button id="bPagar">Ir para o pagamento</button>
<button id="bPagar"><svg class="ic" viewBox="0 0 24 24" aria-hidden="true" focusable="false"><path d="M5 11h14a2 2 0 0 1 2 2v7a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-7a2 2 0 0 1 2-2Z"/><path d="M7 11V7a5 5 0 0 1 10 0v4"/></svg><span id="bPagarTx">Ir para o pagamento</span></button>
<button id="bMais" class="sec">Continuar comprando</button>
</div>
<p class="envioArq" id="envioArq" role="status" aria-live="polite"></p>

View File

@@ -202,3 +202,8 @@ table.log td{padding:10px 12px;border-bottom:1px solid #1E2226;vertical-align:to
.split .list{width:auto;border-right:0;border-bottom:1px solid var(--line)}
.facts,.pane .fields{grid-template-columns:1fr}
}
/* Icons beside a label: the download buttons, where a card ships, the WhatsApp link. */
.btn.with-icon{display:inline-flex;align-items:center;gap:6px}
.card .foot .where{display:inline-flex;align-items:center;gap:4px}
a.zap{display:inline-flex;align-items:center;gap:4px;vertical-align:bottom}

View File

@@ -54,7 +54,7 @@ function contato(customer){
const d=String(customer.zap).replace(/\D/g,'').replace(/^55(?=\d{10,11}$)/,'');
const shown=d.length===11?'('+d.slice(0,2)+') '+d.slice(2,7)+'-'+d.slice(7):d.length===10?'('+d.slice(0,2)+') '+d.slice(2,6)+'-'+d.slice(6):customer.zap;
const a=node('a',shown,'zap');a.href=href;a.target='_blank';a.rel='noopener noreferrer';
a.title='Abrir conversa no WhatsApp';
a.title='Abrir conversa no WhatsApp';a.prepend(icon(ICON_CHAT));
line.append(a);return line;
}
function node(tag,text,className){const e=document.createElement(tag);if(text!==undefined&&text!==null)e.textContent=text;if(className)e.className=className;return e;}
@@ -80,6 +80,14 @@ function icon(path){
return svg;
}
const ICON_OK=['M5 12l4 4 10-10'], ICON_WARN=['M12 8v5M12 17h.01','circle 12 12 9'];
// Lucide shapes (ISC licence), in the same stroke as the two above.
const ICON_TRUCK=['M14 18V6a2 2 0 0 0-2-2H4a2 2 0 0 0-2 2v11a1 1 0 0 0 1 1h2','M15 18H9',
'M19 18h2a1 1 0 0 0 1-1v-3.65a1 1 0 0 0-.22-.62l-3.48-4.35A1 1 0 0 0 17.52 8H14','circle 17 18 2','circle 7 18 2'];
const ICON_PIN=['M20 10c0 4.99-5.54 10.19-7.4 11.8a1 1 0 0 1-1.2 0C9.54 20.19 4 14.99 4 10a8 8 0 0 1 16 0','circle 12 10 3'];
const ICON_DOWNLOAD=['M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4','m7 10 5 5 5-5','M12 15V3'];
const ICON_CHAT=['M7.9 20A9 9 0 1 0 4 16.1L2 22Z'];
// A download button: the arrow, then its label.
function downloadButton(text,fn,className){const b=button(text,fn,className);b.prepend(icon(ICON_DOWNLOAD));b.classList.add('with-icon');return b;}
async function api(path,body){
const r=await fetch('/api/operator'+path,{headers:{'Content-Type':'application/json'},...(body===undefined?{}:{method:'POST',body:JSON.stringify(body)})});
const d=await r.json();if(!r.ok){const error=new Error(typeof d.detail==='string'?d.detail:'Confira os campos ('+r.status+').');error.status=r.status;throw error;}return d;
@@ -200,7 +208,8 @@ function card(order){
const status=node('span',p.text,p.tone);status.style.display='flex';status.style.alignItems='center';status.style.gap='5px';
if(p.tone==='ok')status.prepend(icon(ICON_OK));if(p.tone==='warn')status.prepend(icon(ICON_WARN));
const dest=order.snapshot.destination;
foot.append(status,node('span',dest?'Entrega · '+dest.state:'Retirada','where'));
const where=node('span',dest?'Entrega · '+dest.state:'Retirada','where');where.prepend(icon(dest?ICON_TRUCK:ICON_PIN));
foot.append(status,where);
c.append(top,node('div',order.snapshot.customer.mail,'cust'),tags,foot);
if(['rec','tra','fil','imp'].includes(order.state)&&Date.now()-new Date(order.updated_at)>LATE_HOURS*3600000)
c.append(node('div','Parado há mais de '+LATE_HOURS+' h','late'));
@@ -336,8 +345,8 @@ function itemCard(order,item,index){
else body.append(file);
}else body.append(node('div','Arquivo de impressão ainda não gerado','line'));
const buttons=node('div',undefined,'buttons');
if(row?.status==='ready'&&row.detail?.source!=='original')buttons.append(button('Baixar PDF',download(row.upload_id)));
item.uploads.forEach((uid,i)=>buttons.append(button(item.uploads.length>1?'Original '+(i+1):'Baixar original',download(uid),'btn ghost')));
if(row?.status==='ready'&&row.detail?.source!=='original')buttons.append(downloadButton('Baixar PDF',download(row.upload_id)));
item.uploads.forEach((uid,i)=>buttons.append(downloadButton(item.uploads.length>1?'Original '+(i+1):'Baixar original',download(uid),'btn ghost')));
if(spec?.placements)buttons.append(button('Manifesto',()=>manifest(spec),'btn ghost'));
if(['rec','tra'].includes(order.state)&&(!row||row.status==='manual'||row.status==='failed'))
buttons.append(button('Gerar novamente',async()=>{await api('/orders/'+order.id+'/print-files',{});await load();},'btn ghost'));
@@ -350,7 +359,7 @@ function finalsSection(order,revisions,section){
const rev=node('div',undefined,'rev');
rev.append(node('b',(file.kind==='final'?'Final':'Correção do cliente')+' · item '+(file.item_index+1)+' · '+file.name),
node('span',(file.expired?'expirado':file.active?'atual':'substituído')+' · '+when(file.created_at)+' · '+file.note,'faint'));
if(!file.expired){const b=button('Baixar',download(file.upload_id),'btn ghost');b.style.alignSelf='flex-start';rev.append(b);}
if(!file.expired){const b=downloadButton('Baixar',download(file.upload_id),'btn ghost');b.style.alignSelf='flex-start';rev.append(b);}
section.append(rev);
}
if(!['rec','tra','cor'].includes(order.state)){if(!revisions.length)section.append(node('p','Nenhum arquivo final registrado.','faint'));return;}
@@ -504,7 +513,7 @@ function quoteItem(item,fields){
grid.append(asked,m,g);body.append(grid);
}
const buttons=node('div',undefined,'buttons');
item.uploads.forEach((uid,i)=>buttons.append(button(item.uploads.length>1?'Original '+(i+1):'Baixar original',download(uid),'btn ghost')));
item.uploads.forEach((uid,i)=>buttons.append(downloadButton(item.uploads.length>1?'Original '+(i+1):'Baixar original',download(uid),'btn ghost')));
if(item.production?.placements)buttons.append(button('Manifesto',()=>manifest(item.production),'btn ghost'));
body.append(buttons);box.append(preview,body);return box;
}

View File

@@ -30,7 +30,7 @@ function pintaPedido(){
$('itens').innerHTML = pedido.map((it,i)=>
'<div class="item"><div class="nm2"><b>'+escapeHTML(it.tit)+'</b><span>'+escapeHTML(it.desc)+'</span></div>'+
'<div class="vl2">'+rs(it.total)+'</div>'+
'<button type="button" class="x" data-rmi="'+i+'" aria-label="Remover '+escapeHTML(it.tit)+'">Remover</button></div>').join('');
'<button type="button" class="x" data-rmi="'+i+'" aria-label="Remover '+escapeHTML(it.tit)+'">'+icone('lixeira')+'<span>Remover</span></button></div>').join('');
$('itens').querySelectorAll('[data-rmi]').forEach(b=>b.addEventListener('click',()=>{
const i=+b.dataset.rmi, [item]=pedido.splice(i,1);
pintaPedido();
@@ -56,7 +56,7 @@ function pintaPedido(){
'<div class="l"><span>Revisão e reencaixe</span>'+
'<b><span style="color:var(--verde-ml)">Grátis</span></b></div>';
$('carrTot').textContent=rs(soma);
$('bPagar').textContent = (pedido.length + (itemAtual?1:0))>1
$('bPagarTx').textContent = (pedido.length + (itemAtual?1:0))>1
? 'Pagar os '+(pedido.length+(itemAtual?1:0))+' itens de uma vez'
: 'Ir para o pagamento';
$('bPagar').disabled = !cartPodeEnviar() || !entrega.cotado || !clienteOk() || !enderecoOk();

View File

@@ -15,6 +15,19 @@ const ICONES={
girar:'<path d="M21 12a9 9 0 1 1-9-9c2.52 0 4.93 1 6.74 2.74L21 8"/><path d="M21 3v5h-5"/>',
espelhar:'<path d="m3 7 5 5-5 5V7"/><path d="m21 7-5 5 5 5V7"/><path d="M12 20v2"/><path d="M12 14v2"/><path d="M12 8v2"/><path d="M12 2v2"/>',
fechar:'<path d="M18 6 6 18"/><path d="m6 6 12 12"/>',
adicionar:'<path d="M6 2 3 6v14a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2V6l-3-4Z"/><path d="M3 6h18"/><path d="M12 10v6"/><path d="M9 13h6"/>',
cadeado:'<path d="M5 11h14a2 2 0 0 1 2 2v7a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-7a2 2 0 0 1 2-2Z"/><path d="M7 11V7a5 5 0 0 1 10 0v4"/>',
lixeira:'<path d="M3 6h18"/><path d="M19 6v14c0 1-1 2-2 2H7c-1 0-2-1-2-2V6"/><path d="M8 6V4c0-1 1-2 2-2h4c1 0 2 1 2 2"/><path d="M10 11v6"/><path d="M14 11v6"/>',
local:'<path d="M20 10c0 4.99-5.54 10.19-7.4 11.8a1 1 0 0 1-1.2 0C9.54 20.19 4 14.99 4 10a8 8 0 0 1 16 0"/><circle cx="12" cy="10" r="3"/>',
caminhao:'<path d="M14 18V6a2 2 0 0 0-2-2H4a2 2 0 0 0-2 2v11a1 1 0 0 0 1 1h2"/><path d="M15 18H9"/><path d="M19 18h2a1 1 0 0 0 1-1v-3.65a1 1 0 0 0-.22-.62l-3.48-4.35A1 1 0 0 0 17.52 8H14"/><circle cx="17" cy="18" r="2"/><circle cx="7" cy="18" r="2"/>',
cartao:'<path d="M4 5h16a2 2 0 0 1 2 2v10a2 2 0 0 1-2 2H4a2 2 0 0 1-2-2V7a2 2 0 0 1 2-2Z"/><path d="M2 10h20"/>',
qrcode:'<path d="M3 3h5v5H3z"/><path d="M16 3h5v5h-5z"/><path d="M3 16h5v5H3z"/><path d="M21 16h-3a2 2 0 0 0-2 2v3"/><path d="M21 21v.01"/><path d="M12 7v3a2 2 0 0 1-2 2H7"/><path d="M3 12h.01"/><path d="M12 3h.01"/><path d="M12 16v.01"/><path d="M16 12h1"/><path d="M21 12v.01"/><path d="M12 21v-1"/>',
copiar:'<path d="M10 8h10a2 2 0 0 1 2 2v10a2 2 0 0 1-2 2H10a2 2 0 0 1-2-2V10a2 2 0 0 1 2-2Z"/><path d="M4 16a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h10a2 2 0 0 1 2 2"/>',
relogio:'<circle cx="12" cy="12" r="10"/><path d="M12 6v6l4 2"/>',
ok:'<path d="M20 6 9 17l-5-5"/>',
okCirculo:'<circle cx="12" cy="12" r="10"/><path d="m9 12 2 2 4-4"/>',
alerta:'<path d="M12 7v6"/><path d="M12 17h.01"/>',
refazer:'<path d="M3 12a9 9 0 1 0 9-9 9.75 9.75 0 0 0-6.74 2.74L3 8"/><path d="M3 3v5h5"/>',
};
const icone=n=>'<svg class="ic" viewBox="0 0 24 24" aria-hidden="true" focusable="false">'+ICONES[n]+'</svg>';
const fmt=b=>b<1048576?(b/1024).toFixed(0)+' KB'

View File

@@ -37,7 +37,7 @@ function pintaModo(){
$('larguraFolhaPronta').textContent=String(c.larg).replace('.',',');
$('foco2').classList.remove('sofila'); // a caixa de requisitos vale nos 4 modos
$('montcabTit').textContent = ehFolha(m) ? 'Sua folha' : 'Montagem ao vivo';
$('qOk').textContent = ehFolha(m) ? 'Adicionar ao carrinho' : 'Ver a folha montada';
$('qOk').innerHTML = ehFolha(m) ? icone('adicionar')+'<span>Adicionar ao carrinho</span>' : 'Ver a folha montada';
pintaTipoEnvio();
}
// O par folha montada / artes separadas fica visível e com preço nos dois lados,

View File

@@ -117,7 +117,7 @@ function mostraNota(itens,n,titulo,sub){
$('qbi').style.width=(semNota?0:n)+'%'; $('qbi').style.background=c;
$('qitens').innerHTML=itens.map(i=>
'<div class="qi '+i[0]+'"><span class="c">'+
(i[0]==='ok'?'✓':i[0]==='av'?'!':i[0]==='fix'?'↻':'×')+'</span>'+
icone(i[0]==='ok'?'ok':i[0]==='av'?'alerta':i[0]==='fix'?'refazer':'fechar')+'</span>'+
'<div><b>'+escapeHTML(i[1])+'</b><span>'+escapeHTML(i[2])+'</span></div></div>').join('');
$('qmsg').style.display='none';
ressalvaResolucao();

View File

@@ -97,6 +97,8 @@ function vaiPara(url){
passos.querySelectorAll('[data-passo]').forEach(a=>{
const n=Number(a.dataset.passo);
a.classList.toggle('atual',n===atual); a.classList.toggle('feito',n<atual);
const marca=a.querySelector('span');
if(marca) marca.innerHTML = n<atual ? icone('ok') : String(n);
if(n===atual) a.setAttribute('aria-current','step'); else a.removeAttribute('aria-current');
});
passos.querySelector('[data-passo="1"]').href =

View File

@@ -349,6 +349,22 @@ html:is([data-rota="pagamento"],[data-rota="pix"]) .pagGrid{display:grid;grid-te
.rep .l1 button .ic{width:17px;height:17px}
#ampliaFecha .ic{width:20px;height:20px}
@media (max-width:600px){.acts .actTx{display:none}.acts{gap:16px}}
.cAdd,#bPagar,#pOk,#qOk{display:flex;align-items:center;justify-content:center;gap:8px}
#pOk{display:inline-flex}
.cAdd .ic,#bPagar .ic{width:19px;height:19px}
.item .x{display:inline-flex;align-items:center;gap:6px}
.item .x .ic{width:15px;height:15px}
.opE b .ic{width:16px;height:16px;margin:-2px 6px 0 0;color:var(--laranja-escuro)}
.metodo .ic{width:20px;height:20px;color:var(--texto2)}
.codigo .pagBtn{display:inline-flex;align-items:center;gap:7px}
.codigo .pagBtn .ic{width:17px;height:17px}
.pixBox .prazo{display:flex;align-items:center;gap:8px}
.pixBox .prazo .ic{width:20px;height:20px}
.confirmado h4{display:flex;align-items:center;gap:8px}
.confirmado h4 .ic{width:24px;height:24px}
.envioArq .ic{width:16px;height:16px;margin:-2px 6px 0 0}
.qi .c .ic{width:12px;height:12px;stroke-width:3}
.passos a span .ic{width:14px;height:14px;stroke-width:3}
.envioArq{font-size:13px;color:var(--texto2);margin-top:14px;line-height:1.5}
.envioArq.ok{color:var(--verde)}
.envioArq.erro{color:#B42318}