feat: rebuild the Site product page around a buy box
All checks were successful
Build and deploy / Validate source (push) Successful in 10s
Build and deploy / Integration suite on a real stack (push) Successful in 2m46s
Build and deploy / Secret scan and release gate (push) Successful in 6s
Build and deploy / Publish images (push) Successful in 1m46s

Artworks on the left; on the right a box that stays in view with the live
sheet, the grade, the price per metre, the metres charged, the total, the
resolution note and "Adicionar ao carrinho". The separate quality and
preview panels, the second sheet preview, the per-row mini sheets, the
summary box and the repeated findings list are gone: each piece of
information now appears once.

Each artwork row carries at most one hint (resolution first, otherwise a
width that saves film), the ready-sheet/loose-artwork choice is a toggle
beside the title, the upload area is one bar and the tips are collapsed.
The box only shows the item the page already priced, so it always matches
the cart.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Cauê Faleiros
2026-09-24 18:02:45 -03:00
parent b81ff8d03d
commit cbbbdb351a
6 changed files with 230 additions and 147 deletions

View File

@@ -748,7 +748,7 @@ footer a:hover{color:var(--laranja2)}
justify-content:space-between;gap:12px;flex-wrap:wrap;font-size:11px;color:#5A6570} justify-content:space-between;gap:12px;flex-wrap:wrap;font-size:11px;color:#5A6570}
@media(prefers-reduced-motion:reduce){*{transition:none!important;scroll-behavior:auto}} @media(prefers-reduced-motion:reduce){*{transition:none!important;scroll-behavior:auto}}
</style> </style>
<link rel="stylesheet" href="/site-v2.css?v=4"> <link rel="stylesheet" href="/site-v2.css?v=5">
<script src="/site-pages.js"></script> <script src="/site-pages.js"></script>
</head> </head>
<body> <body>
@@ -924,33 +924,33 @@ footer a:hover{color:var(--laranja2)}
<!-- painel focado --> <!-- painel focado -->
<div class="foco" id="foco"> <div class="foco" id="foco">
<button class="voltar" id="bVoltar"><span>‹</span>Trocar de produto</button> <button class="voltar" id="bVoltar"><span>‹</span>Todos os produtos</button>
<div class="focoGrid"> <div class="focoGrid">
<div class="cat" id="catFoco"> <div class="cat" id="catFoco">
<div class="cabfoco" style="padding:20px 22px 14px;margin:0"> <div class="cabfoco" style="padding:20px 22px 14px;margin:0">
<div><h3 id="fTit">—</h3><span class="lg" id="fLg" style="display:block"></span></div> <div><h3 id="fTit">—</h3><span class="lg" id="fLg" style="display:block"></span></div>
<span class="pr2" id="fPreco"></span> <span class="pr2" id="fPreco"></span>
<p id="fSub"></p> <p id="fSub"></p>
</div> <div class="caminhos tipoEnvio" id="tipoEnvio" hidden>
<div class="in" style="padding:0 22px 22px">
<div class="foco2" id="foco2">
<div class="fila">
<div class="trocouCam" id="trocouCam" style="display:none"></div>
<div class="caminhos" id="tipoEnvio" hidden>
<button type="button" class="cam on" data-tipo="folha"> <button type="button" class="cam on" data-tipo="folha">
<span class="mk2"></span> <span class="mk2"></span>
<div><b>Já é uma folha montada</b> <div><b>Folha já montada</b>
<span>a imagem ocupa a largura inteira do filme de <span>a imagem ocupa a largura inteira do filme de
<span id="larguraFolhaPronta">57</span> cm</span> <span id="larguraFolhaPronta">57</span> cm</span>
<em id="tipoFolhaPreco">—</em></div> <em id="tipoFolhaPreco">—</em></div>
</button> </button>
<button type="button" class="cam" data-tipo="avulsa"> <button type="button" class="cam" data-tipo="avulsa">
<span class="mk2"></span> <span class="mk2"></span>
<div><b>São artes separadas</b> <div><b>Artes separadas</b>
<span>você informa a largura de cada uma e nós montamos a folha</span> <span>você informa a largura de cada uma e nós montamos a folha</span>
<em id="tipoAvulsaPreco">—</em></div> <em id="tipoAvulsaPreco">—</em></div>
</button> </button>
</div> </div>
</div>
<div class="in" style="padding:0 22px 22px">
<div class="foco2" id="foco2">
<div class="fila">
<div class="trocouCam" id="trocouCam" style="display:none"></div>
<div class="trocouCam" id="avisoTipo" style="display:none"></div> <div class="trocouCam" id="avisoTipo" style="display:none"></div>
<div class="caminhos" id="caminhos"> <div class="caminhos" id="caminhos">
<button class="cam on" data-cam="auto"> <button class="cam on" data-cam="auto">
@@ -976,28 +976,10 @@ footer a:hover{color:var(--laranja2)}
<div id="lista"></div> <div id="lista"></div>
<div class="resumoA" id="rA" style="display:none"></div> <div class="resumoA" id="rA" style="display:none"></div>
<input type="file" id="inp" hidden> <input type="file" id="inp" hidden>
<div class="aoVivo" id="aoVivo"></div>
</div>
<aside class="mont" id="mont">
<div class="montcab"><b id="montcabTit">Montagem ao vivo</b>
<div class="zoomB"><button id="zMenos" title="Diminuir">−</button>
<span id="zTxt">100%</span>
<button id="zMais" title="Aumentar">+</button></div>
<span id="vMt">—</span></div>
<div id="vArea"></div>
<div id="vUso"></div>
<p class="montpe">Prévia aproximada. No servidor o encaixe é refeito do zero —
o resultado sai igual ou melhor, nunca pior.</p>
</aside>
</div>
</div>
</div>
</div>
<aside class="req" id="req"> <aside class="req" id="req">
<h3 class="reqcab">O que devo saber<br>sobre a arte</h3> <h3 class="reqcab">Dicas sobre a arte</h3>
<details open> <details>
<summary>Como pagar o menor metro</summary> <summary>Como pagar o menor metro</summary>
<ul> <ul>
<li class="s"><b>300 DPI no tamanho que vai imprimir.</b> É a nota máxima <li class="s"><b>300 DPI no tamanho que vai imprimir.</b> É a nota máxima
@@ -1060,6 +1042,41 @@ footer a:hover{color:var(--laranja2)}
</details> </details>
</aside> </aside>
</div> </div>
<aside class="mont" id="mont">
<div class="montcab"><b id="montcabTit">Montagem ao vivo</b>
<div class="zoomB"><button id="zMenos" title="Diminuir">−</button>
<span id="zTxt">100%</span>
<button id="zMais" title="Aumentar">+</button></div>
<span id="vMt">—</span></div>
<div id="vArea"></div>
<div id="vUso"></div>
<p class="montpe">Prévia. O encaixe final é refeito no servidor e nunca sai pior.</p>
<div class="compra" id="compra">
<div class="aoVivo" id="aoVivo"></div>
<div id="compraVals" hidden>
<div class="cNota"><span>Nota da arte</span><i><i id="cNotaBar"></i></i><b id="cNota">—</b></div>
<div class="cLin"><span>Preço do metro</span><span><s id="cTabela"></s><b id="cMetro">—</b></span></div>
<div class="cLin"><span>Metragem cobrada</span><b id="cMetragem">—</b></div>
<p class="cObs" id="cObs"></p>
<div class="cTot"><span>Total</span><b id="cTotal">—</b></div>
<div id="qmsg" style="display:none"></div>
<div class="ciente" id="ciente">
<h4 id="cienteTit">Ressalva de resolução</h4>
<div id="cienteTxt"></div>
<label><input type="checkbox" id="cienteOk">
<span>Li e entendi. Quero imprimir assim mesmo.</span></label>
</div>
</div>
<button type="button" class="cAdd" id="bAdd" disabled>Adicionar ao carrinho</button>
<p class="cPe">Revisão de 5 pontos e reencaixe grátis</p>
</div>
</aside>
</div>
</div>
</div>
</div>
</div>
</div> </div>
<!-- nota --> <!-- nota -->
@@ -1072,16 +1089,9 @@ footer a:hover{color:var(--laranja2)}
</div> </div>
<div class="qbar"><i id="qbi" style="width:0%"></i></div> <div class="qbar"><i id="qbi" style="width:0%"></i></div>
<div class="qitens" id="qitens"></div> <div class="qitens" id="qitens"></div>
<div class="ciente" id="ciente">
<h4 id="cienteTit">Ressalva de resolução</h4>
<div id="cienteTxt"></div>
<label><input type="checkbox" id="cienteOk">
<span>Li e entendi. Quero imprimir assim mesmo.</span></label>
</div>
<div class="qbts"> <div class="qbts">
<button class="qb chama" id="qOk">Seguir para o pedido</button> <button class="qb chama" id="qOk">Seguir para o pedido</button>
</div> </div>
<div id="qmsg" style="margin-top:12px;font-size:12.5px;display:none"></div>
</div> </div>
<!-- prévia da montagem --> <!-- prévia da montagem -->
@@ -1279,6 +1289,7 @@ footer a:hover{color:var(--laranja2)}
<script src="/site-packing.js"></script> <script src="/site-packing.js"></script>
<script src="/site-cart.js"></script> <script src="/site-cart.js"></script>
<script src="/site-flow.js"></script> <script src="/site-flow.js"></script>
<script src="/site-compra.js"></script>
<script src="/privacy.js"></script> <script src="/privacy.js"></script>
<script src="/upload.js"></script> <script src="/upload.js"></script>
<script src="/checkout.js"></script> <script src="/checkout.js"></script>

30
web/site-compra.js Normal file
View File

@@ -0,0 +1,30 @@
/* Site DTF — the product page's buy box: grade, price per metre, billed length,
total and the add-to-cart button, beside the live sheet. It shows the item
the other site-*.js files already priced (itemAtual) and never computes a
price of its own, so the box and the cart always agree. */
(function(){
const vals=$('compraVals'), add=$('bAdd');
function pintaCompra(){
const it=itemAtual;
vals.hidden=!it;
add.disabled=!it || !itemPodeEnviar(it);
if(!it) return;
const semNota=!it.nota;
$('cNota').textContent = semNota ? 'sem nota' : it.nota;
$('cNotaBar').style.width=(semNota?0:it.nota)+'%';
$('cNotaBar').className = semNota ? '' : it.nota>=85 ? 'ok' : it.nota>=50 ? 'av' : 'er';
$('cTabela').textContent = TABELA[it.modo]-it.unit>0.001 ? rs(TABELA[it.modo]) : '';
$('cMetro').textContent=rs(it.unit);
$('cMetragem').textContent=fmtM(it.cob)+' m';
const sobraCm=Math.round((it.cob-it.metros)*100);
$('cObs').textContent =
it.cob===MINIMO_M && it.metros<MINIMO_M
? (ehFolha(it.modo) ? 'Pedido mínimo de 1 m.'
: 'Pedido mínimo de 1 m: ainda cabem '+sobraCm+' cm de artes sem custo.')
: sobraCm>0 ? fmtM(it.metros)+' m de folha, arredondado a cada 10 cm.' : '';
$('cTotal').textContent=rs(it.total);
}
add.addEventListener('click',()=>adicionaAoCarrinho());
addEventListener('dtf-cart-changed',pintaCompra);
pintaCompra();
})();

View File

@@ -21,7 +21,7 @@ $('qOk').addEventListener('click',()=>{
// The finished item joins the order and the product page starts over empty. // The finished item joins the order and the product page starts over empty.
function guardaItem(){ function guardaItem(){
if(itemAtual && !itemPodeEnviar(itemAtual)){ if(itemAtual && !itemPodeEnviar(itemAtual)){
$('qual').scrollIntoView({behavior:'smooth',block:'nearest'}); $('ciente').scrollIntoView({behavior:'smooth',block:'center'});
return false; return false;
} }
if(itemAtual){ pedido.push(itemAtual); itemAtual=null; } if(itemAtual){ pedido.push(itemAtual); itemAtual=null; }
@@ -33,7 +33,7 @@ function guardaItem(){
return true; return true;
} }
function adicionaAoCarrinho(){ function adicionaAoCarrinho(){
if(!itemAtual){ $('qual').scrollIntoView({behavior:'smooth',block:'nearest'}); return; } if(!itemAtual){ $('compra').scrollIntoView({behavior:'smooth',block:'nearest'}); return; }
if(guardaItem()) vaiPara(CARRINHO); if(guardaItem()) vaiPara(CARRINHO);
} }
$('pOk').addEventListener('click',adicionaAoCarrinho); $('pOk').addEventListener('click',adicionaAoCarrinho);

View File

@@ -319,36 +319,6 @@ function sugestao(a){
if(economiaCm<0.5) return null; // menos de meio centímetro não é notícia if(economiaCm<0.5) return null; // menos de meio centímetro não é notícia
return {alvo, de:n, para:n+1, fileiras:fa-fd, cm:economiaCm}; return {alvo, de:n, para:n+1, fileiras:fa-fd, cm:economiaCm};
} }
// A cota em escala: o cliente ve a peca dentro da largura do filme, quantas cabem
// lado a lado e quanto sobra. Numero em texto ninguem le; retangulo todo mundo entende.
function vizPeca(a){
const BASE=300, L=larguraFilme();
const e0=BASE/L, alt0=a.cm*propDe(a)*e0;
const k = alt0>132 ? 132/alt0 : 1; // tira nunca fica alta demais
const e = e0*k;
const n=cabemNaLinha(a.cm), pw=a.cm*e, ph=a.cm*propDe(a)*e, gp=GAP_CM*e;
const sobra=L-(n*a.cm+(n-1)*GAP_CM);
const ext=escapeHTML(a.f.name.split('.').pop().toUpperCase());
let pecas='';
for(let i=0;i<n;i++) pecas+='<div class="pc" style="width:'+pw.toFixed(1)+'px;height:'+
ph.toFixed(1)+'px">'+(a.src?'<img src="'+a.src+'" alt="" style="transform:'+
(a.giro?'rotate(90deg) ':'')+(a.esp?'scaleX(-1)':'')+'">':'<span>'+ext+'</span>')+'</div>';
const Wt=(L*e+6).toFixed(1); // +6 = borda e respiro da tira
return '<div class="viz">'+
'<div class="vizRow">'+
'<div class="vizH" style="height:'+ph.toFixed(1)+'px"><span>'+n1(a.cm*propDe(a))+' cm</span></div>'+
'<div class="vizTira" style="width:'+Wt+'px">'+
'<div class="vizL"><i></i><b>'+n1(L)+' cm de filme</b><i></i></div>'+
'<div class="tira" style="height:'+ph.toFixed(1)+'px;gap:'+gp.toFixed(1)+'px">'+pecas+
(sobra>0.25?'<div class="folga"><span>sobra '+n1(sobra)+' cm</span></div>':'')+
'</div>'+
'</div>'+
'</div>'+
'<div class="vizPe"><b>'+n1(a.cm)+' cm</b> de largura · <b>'+n+
(n>1?' peças':' peça')+'</b> por fileira'+
(sobra>0.25?' · sobram <b>'+n1(sobra)+' cm</b>':' · largura toda aproveitada')+'</div>'+
'</div>';
}
const n1=v=>v.toFixed(1).replace('.',','); const n1=v=>v.toFixed(1).replace('.',',');
function pintaArtes(){ function pintaArtes(){
@@ -362,17 +332,20 @@ function pintaArtes(){
}else if(a.cm>0 && a.src){ }else if(a.cm>0 && a.src){
const dpi=Math.round(dpiDe(a)), n=Math.max(6,Math.min(100,Math.round(dpi/300*100))); const dpi=Math.round(dpiDe(a)), n=Math.max(6,Math.min(100,Math.round(dpi/300*100)));
barra='<div class="qb2"><i class="'+cls(n)+'" style="width:'+n+'%"></i></div>'; barra='<div class="qb2"><i class="'+cls(n)+'" style="width:'+n+'%"></i></div>';
const fila=cabemNaLinha(a.cm);
calc='<span class="'+cls(n)+'"><b>'+n+'%</b> · '+dpi+' DPI</span><span>'+ calc='<span class="'+cls(n)+'"><b>'+n+'%</b> · '+dpi+' DPI</span><span>'+
a.q+' × '+n1(a.cm)+' × '+n1(a.cm*propDe(a))+' cm</span>'; a.q+' × '+n1(a.cm)+' × '+n1(a.cm*propDe(a))+' cm · '+fila+' por fileira</span>';
const sug=sugestao(a); const sug=sugestao(a);
dica = sug // One hint per row, on the row it is about: resolution first, since it
? '<div class="dica"><b>Reduzindo para '+n1(sug.alvo)+' cm você economiza '+ // changes the price; otherwise a width that saves sheet.
n1(sug.cm)+' cm de folha</b>Passam de '+sug.de+' para '+sug.para+ const maxCm=Math.floor(a.px/DPI_AVISA*2.54*10)/10;
' por fileira, e as suas '+a.q+' peças ocupam '+sug.fileiras+ dica = dpi<DPI_AVISA && maxCm>0 && maxCm<a.cm
' fileira'+(sug.fileiras>1?'s':'')+' a menos.<button data-usar="'+i+'" data-cm="'+ ? '<div class="dica av">Pequena para '+n1(a.cm)+' cm. Em '+DPI_AVISA+' DPI ela sai com até '+
sug.alvo+'">Usar sugerido</button></div>' n1(maxCm)+' cm, ou envie um arquivo maior.</div>'
: sug
? '<div class="dica">Com '+n1(sug.alvo)+' cm cabem '+sug.para+' por fileira e a folha encolhe '+
n1(sug.cm)+' cm.<button data-usar="'+i+'" data-cm="'+sug.alvo+'">Usar '+n1(sug.alvo)+' cm</button></div>'
: ''; : '';
dica = vizPeca(a) + dica;
} }
return '<div class="art'+(a===recemChegada?' nova':'')+'"><div class="l1"><b>'+escapeHTML(a.f.name)+'</b>'+ return '<div class="art'+(a===recemChegada?' nova':'')+'"><div class="l1"><b>'+escapeHTML(a.f.name)+'</b>'+
'<span style="color:var(--fraco);font-size:10.5px">'+ '<span style="color:var(--fraco);font-size:10.5px">'+

View File

@@ -71,11 +71,10 @@ function vaiPara(url){
const r=el.getBoundingClientRect(); const r=el.getBoundingClientRect();
return r.height>0 && r.top<innerHeight && r.bottom>0; return r.height>0 && r.top<innerHeight && r.bottom>0;
} }
// Where the phone bar's button leads: the button that finishes the item on a // Where the phone bar's button leads: the add-to-cart button on a product
// product page, the order summary in the cart. // page, the order summary in the cart.
function alvoBarra(){ function alvoBarra(){
if(rota()==='produto' && itemAtual) if(rota()==='produto' && itemAtual) return $('bAdd');
return $('prev').classList.contains('on') ? $('pOk') : $('qOk');
if(rota()==='carrinho' && (pedido.length || itemAtual) && !temCheckout()) if(rota()==='carrinho' && (pedido.length || itemAtual) && !temCheckout())
return document.querySelector('.resumo'); return document.querySelector('.resumo');
return null; return null;

View File

@@ -93,28 +93,98 @@ section+section{border-top:0}
.passos a.feito span{background:var(--verde-fundo);border-color:var(--verde-fundo);color:var(--verde)} .passos a.feito span{background:var(--verde-fundo);border-color:var(--verde-fundo);color:var(--verde)}
.passos i{width:48px;height:2px;background:#E0E0E0} .passos i{width:48px;height:2px;background:#E0E0E0}
/* Montagem: artworks on the left, the live sheet fixed on the right. */ /* Product page (Montagem): artworks on the left; on the right a buy box that
.voltar{height:40px;padding:0 14px;border-radius:8px;border:1px solid #D6D6D6;font-size:14px;margin-bottom:16px} stays in view with the live sheet, the grade, the price, the total and the
add-to-cart button. The old quality and preview panels are not shown: their
parts live in the buy box, and the rest repeated it (the findings list too:
each row already says what was checked). */
#qual,#prev,#rA,.qb2,#qitens{display:none!important}
.voltar{height:auto;padding:0;border:0;background:none;box-shadow:none;font-size:14px;font-weight:500;color:var(--fraco);margin:24px 0 14px}
.voltar:hover{color:var(--laranja-escuro)}
.focoGrid{grid-template-columns:minmax(0,1fr);gap:20px} .focoGrid{grid-template-columns:minmax(0,1fr);gap:20px}
.foco2{grid-template-columns:minmax(0,1fr) 440px;gap:28px} .cat,.cat.file{border:0;background:transparent;box-shadow:none;overflow:visible}
.mont{top:var(--topo-passos,128px)} .cabfoco,.cat.file .cabfoco{padding:0 0 20px!important;background:transparent;border:0;display:grid;
.req{position:static;padding:22px 24px} grid-template-columns:minmax(0,1fr) auto;grid-template-areas:"t e" "s e" "p e";column-gap:24px;align-items:end}
.req .reqcab br{display:none} .cabfoco>div:first-child{grid-area:t;display:flex;flex-direction:column-reverse;gap:4px}
@media(max-width:1100px){.foco2{grid-template-columns:1fr}.mont{position:static}} .cabfoco .lg{font-size:12px;color:#707070;letter-spacing:.05em}
.cat,.cat.file{border:1px solid var(--linha);border-width:1px;border-radius:16px} .cabfoco h3{font-size:32px;font-weight:800;letter-spacing:-.02em;line-height:1.1}
.cabfoco,.cat.file .cabfoco{background:#fff} .cabfoco p{grid-area:s;margin-top:6px;font-size:15px;color:var(--texto2)}
.cabfoco h3{font-size:26px;font-weight:800;letter-spacing:-.02em} .cabfoco .pr2{grid-area:p;margin-top:4px;font-size:14px;font-weight:600;color:var(--fraco);white-space:normal}
.cabfoco .pr2{font-size:15px;font-weight:700;color:var(--texto2)} .tipoEnvio{grid-area:e;margin:0}
.zona{border:2px dashed #F2B25A;background:var(--aviso-fundo);border-radius:var(--raio)} .tipoEnvio:not([hidden]){display:inline-flex;gap:4px;padding:4px;border-radius:10px;background:#EFEFEF}
.zona .ico{background:var(--laranja);color:var(--preto)} .tipoEnvio .cam,.tipoEnvio .cam.on{border:0;box-shadow:none;background:transparent;padding:0 14px;min-height:40px;border-radius:8px;align-items:center}
.zona:hover,.zona.drag{border-color:var(--laranja2)} .tipoEnvio .cam.on{background:#fff;box-shadow:0 1px 2px rgba(3,6,11,.14)}
.tipoEnvio .cam .mk2,.tipoEnvio .cam span,.tipoEnvio .cam em{display:none}
.tipoEnvio .cam b{margin:0;font-size:13.5px;font-weight:600;white-space:nowrap}
.tipoEnvio .cam.on b{font-weight:800}
.tipoEnvio .cam:disabled{cursor:default}
.tipoEnvio .cam:disabled:not(.on){opacity:.5}
.cat .in{padding:0!important}
.foco2{grid-template-columns:minmax(0,1fr) 420px;gap:32px}
.fila{display:flex;flex-direction:column;gap:12px}
.fila>*{margin:0}
.cam{border-radius:var(--raio2)} .cam{border-radius:var(--raio2)}
.cam.on{border-color:var(--laranja);background:var(--aviso-fundo)} .cam.on{border-color:var(--laranja);background:var(--aviso-fundo)}
#larguraFolhaPronta{display:inline} #larguraFolhaPronta{display:inline}
.mont{border-radius:var(--raio)} .zona{display:grid;grid-template-columns:44px minmax(0,1fr);column-gap:16px;row-gap:2px;align-items:center;text-align:left;padding:16px 20px;margin:0;border:2px dashed #F2B25A;background:var(--aviso-fundo);border-radius:var(--raio)}
.req{border-radius:var(--raio)} .zona .ico{grid-row:1/4;width:44px;height:44px;line-height:44px;margin:0;border-radius:10px;background:var(--laranja);color:var(--preto);box-shadow:none}
.qual,.prev{border-radius:16px;border-color:var(--linha)} .zona b{font-size:15px;margin:0}
.qb.chama,.pb.p{background:var(--laranja);color:var(--preto);border-color:var(--laranja);font-family:"Inter",sans-serif;font-weight:800;border-radius:var(--raio2)} .zona span{max-width:none;margin:0;font-size:13px;color:var(--fraco)}
.zona .bt{display:none}
.zona:hover,.zona.sobre{border-color:var(--laranja2);box-shadow:none}
#lista{display:flex;flex-direction:column;gap:10px}
#lista:empty{display:none}
.art{margin:0;padding:14px 16px;border-radius:12px;background:#fff;border:1px solid var(--linha)}
.art.nova{background:#fff;box-shadow:0 0 0 2px rgba(255,168,26,.45)}
.art .l1 b{font-size:15px;font-weight:700}
.art label{font-size:12px}
.art input{height:42px;font-size:15px;font-weight:600;border-radius:8px}
.art .cps{grid-template-columns:repeat(2,minmax(0,160px))}
.rep{margin:0;padding:14px 16px;border-radius:12px;background:#fff;border:1px solid var(--linha)}
.calc{font-size:13px;margin-top:8px}
.dica{display:flex;align-items:center;gap:12px;margin-top:10px;padding:10px 12px;border:0;border-radius:8px;background:var(--verde-fundo);font-size:13px;color:#14532D}
.dica.av{background:var(--aviso-fundo);color:#5A3A00}
.dica button{display:inline-flex;width:auto;margin:0 0 0 auto;flex:none;height:34px;align-items:center;padding:0 12px;border-radius:7px;border:1px solid var(--verde);background:#fff;color:var(--verde);box-shadow:none;font-size:13px}
.dica button:hover{background:var(--verde);color:#fff;box-shadow:none}
.req{position:static;padding:16px 20px 6px;border-radius:12px;box-shadow:none}
.reqcab{font-size:15px;margin-bottom:6px}
/* The buy box */
.mont{position:sticky;top:var(--topo-passos,128px);padding:20px;border-radius:16px;background:#fff;box-shadow:none;display:flex;flex-direction:column;gap:12px}
.mont>*{margin:0}
.montcab b{font-size:17px;font-weight:800}
.montpe{font-size:12px;text-align:center}
.compra{display:flex;flex-direction:column;gap:12px;padding-top:16px;border-top:1px solid #EFEFEF}
.compra .aoVivo{margin:0}
.compra .aoVivo.pronto{display:none}
#compraVals{display:flex;flex-direction:column;gap:10px;font-size:15px}
#compraVals[hidden]{display:none}
.cNota{display:flex;align-items:center;gap:12px}
.cNota span,.cLin>span:first-child{color:var(--texto2)}
.cNota>i{flex:1;height:6px;border-radius:999px;background:#EFEFEF;overflow:hidden}
.cNota>i i{display:block;height:100%;border-radius:999px;background:#C9CCD1}
.cNota>i i.ok{background:var(--verde)}.cNota>i i.av{background:var(--laranja)}.cNota>i i.er{background:var(--vermelho)}
.cNota b{font-family:"Inter",sans-serif;font-weight:800}
.cLin{display:flex;align-items:baseline;gap:8px}
.cLin>span:last-child,.cLin>b{margin-left:auto;display:flex;gap:8px;align-items:baseline}
.cLin s{font-size:13px;color:#707070}
.cObs{margin:-4px 0 0;font-size:13px;color:var(--verde)}
.cObs:empty{display:none}
.cTot{display:flex;align-items:baseline;padding-top:12px;border-top:1px solid #EFEFEF}
.cTot span{font-family:"Inter",sans-serif;font-weight:700}
.cTot b{margin-left:auto;font-family:"Inter",sans-serif;font-size:30px;font-weight:900;letter-spacing:-.01em}
#qmsg{font-size:13px;line-height:1.45}
.compra .ciente{margin:0;padding:12px 14px}
.compra .ciente h4{font-size:13.5px}
.compra .ciente p,.compra .ciente .lista{font-size:13px}
.cAdd{height:54px;border-radius:var(--raio2);border:0;background:var(--laranja);color:var(--preto);font-family:"Inter",sans-serif;font-weight:800;font-size:16px;cursor:pointer}
.cAdd:hover:not(:disabled){background:#FFB53D}
.cAdd:disabled{background:#EDEDED;color:#8A8D92;cursor:not-allowed}
.cPe{margin:0;font-size:13px;color:var(--fraco);text-align:center}
@media(max-width:1100px){.foco2{grid-template-columns:1fr}.mont{position:static}}
@media(max-width:620px){.montcab b{font-size:15px}
.cabfoco,.cat.file .cabfoco{grid-template-columns:1fr;grid-template-areas:"t" "s" "p" "e"}
.tipoEnvio:not([hidden]){display:grid;grid-template-columns:1fr 1fr;margin-top:14px}}
/* The quality, preview, cart and checkout panels sit directly in #envio, /* The quality, preview, cart and checkout panels sit directly in #envio,
outside .w; they share the page column so nothing runs edge to edge. */ outside .w; they share the page column so nothing runs edge to edge. */