caterium-app/public/core/signature-offer-pdf-v18.js
pavlov346346-source 5ead4a7f0c feat: add visual heading to midnight-checklist menu section
Replace the plain white "МЕНЮ" text label with a gold icon badge,
item count, and divider line, matching the icon-badge language already
used elsewhere in the template instead of a bare heading.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-16 13:36:09 +03:00

291 lines
40 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

(()=>{
'use strict';
if(window.SunSignatureOfferPDFV18)return;
const VERSION='18.1.0';
const RELEASE='20260916-v18-1-0-signature-offer-pdf-polish';
const W=1000,H=1414,SCALE=1.6,M=54;
const SIGNATURE_IDS=['cream-elegance','neon-menu','emerald-circles','midnight-checklist','gourmet-hero','diamond-gold','premium-dark','premium-emerald'];
const SIGNATURE_SET=new Set(SIGNATURE_IDS);
const INNER_THEME={
'cream-elegance':'light',
'neon-menu':'midnight-glass',
'emerald-circles':'midnight-glass',
'midnight-checklist':'midnight-glass',
'gourmet-hero':'black-gold',
'diamond-gold':'black-gold',
'premium-dark':'black-gold',
'premium-emerald':'midnight-glass'
};
const PALETTES={
'cream-elegance':{bg:'#faf3e6',paper:'#ffffff',ink:'#22281f',muted:'#726c5f',accent:'#8f8536',accent2:'#4f6a3f',deep:'#274a34',line:'#ecdfc4',foot:'#faf3e6'},
'neon-menu':{bg:'#070908',paper:'#0d1210',ink:'#eafff1',muted:'#8fa89a',accent:'#39e27a',accent2:'#c9a24a',deep:'#0f2318',line:'#1f4430',foot:'#070908'},
'emerald-circles':{bg:'#08181c',paper:'#0d2226',ink:'#eef6f2',muted:'#94aca6',accent:'#c9a24a',accent2:'#2f9e78',deep:'#0a2a26',line:'#1f4642',foot:'#08181c'},
'midnight-checklist':{bg:'#06170f',paper:'#0b2417',ink:'#eef6ee',muted:'#8fac97',accent:'#dfb957',accent2:'#3f8f5c',deep:'#123822',line:'#1f4a30',foot:'#06170f'},
'gourmet-hero':{bg:'#0a0a09',paper:'#141311',ink:'#f5f1e8',muted:'#a89f8e',accent:'#d7ac55',accent2:'#8a6a2c',deep:'#1c1a15',line:'#3a352a',foot:'#0a0a09'},
'diamond-gold':{bg:'#071510',paper:'#0d251c',ink:'#f0f5ee',muted:'#93ab9c',accent:'#e0b95c',accent2:'#2f9e6f',deep:'#123d29',line:'#204b34',foot:'#071510'},
'premium-dark':{bg:'#0a0d12',paper:'#12161d',ink:'#f5f6f8',muted:'#8b93a1',accent:'#e0a545',accent2:'#c9863a',deep:'#171c24',line:'#252b35',foot:'#0a0d12'},
'premium-emerald':{bg:'#0a1210',paper:'#121d19',ink:'#f2f8f5',muted:'#8fa79c',accent:'#3fcf8e',accent2:'#2a9d6c',deep:'#17241f',line:'#233830',foot:'#0a1210'}
};
// Distinctive Cyrillic-friendly type pairing per polished template (falls back to Georgia/Arial elsewhere).
const FONT_STACKS={
'cream-elegance':{display:'"Playfair Display",Georgia,serif',label:'"Montserrat",Arial,sans-serif'},
'neon-menu':{display:'"Unbounded",Arial,sans-serif',label:'"Manrope",Arial,sans-serif'},
'emerald-circles':{display:'"Playfair Display",Georgia,serif',label:'"Montserrat",Arial,sans-serif'},
'midnight-checklist':{display:'"Unbounded",Arial,sans-serif',label:'"Manrope",Arial,sans-serif'},
'gourmet-hero':{display:'"Unbounded",Arial,sans-serif',label:'"Manrope",Arial,sans-serif'},
'diamond-gold':{display:'"Playfair Display",Georgia,serif',label:'"Montserrat",Arial,sans-serif'},
'premium-dark':{display:'"Unbounded",Arial,sans-serif',label:'"Manrope",Arial,sans-serif'},
'premium-emerald':{display:'"Unbounded",Arial,sans-serif',label:'"Manrope",Arial,sans-serif'}
};
function fontsFor(id){return FONT_STACKS[id]||{display:'Georgia,serif',label:'Arial,sans-serif'}}
function ensureFontLink(){
if(document.getElementById('sunSignatureFontsV18'))return;
const link=document.createElement('link');link.id='sunSignatureFontsV18';link.rel='stylesheet';
link.href='https://fonts.googleapis.com/css2?family=Playfair+Display:wght@500;600;700&family=Montserrat:wght@500;600;700;800&family=Unbounded:wght@600;700;800&family=Manrope:wght@500;600;700;800&display=swap';
document.head.appendChild(link);
}
ensureFontLink();
async function ensureFontsReady(id){
const f=FONT_STACKS[id];if(!f||!document.fonts)return;
try{
await Promise.all([
document.fonts.load(`700 40px ${f.display}`),
document.fonts.load(`600 16px ${f.label}`),
document.fonts.load(`800 16px ${f.label}`)
]);
}catch(_){}
}
const money=v=>Math.round(Number(v||0)).toLocaleString('ru-RU')+' ₽';
const classicBoxCount=s=>(s?.items||[]).filter(i=>[0,5].includes(Number(i?.categoryId))).reduce((sum,i)=>sum+Math.max(0,Number(i?.qty||0)),0);
const dateText=value=>{if(!value)return'';const d=new Date(String(value)+'T12:00:00');return Number.isNaN(d.getTime())?String(value):d.toLocaleDateString('ru-RU',{day:'numeric',month:'long',year:'numeric'})};
const plural=(n,a,b,c)=>{const x=Math.abs(Number(n)||0)%100,y=x%10;return x>10&&x<20?c:y===1?a:y>=2&&y<=4?b:c};
const safeImage=src=>/^data:image\/(?:png|jpe?g|webp);base64,/i.test(String(src||''));
function loadImage(src){return new Promise(resolve=>{if(!safeImage(src))return resolve(null);const im=new Image();let done=false;const finish=v=>{if(done)return;done=true;clearTimeout(timer);resolve(v)};const timer=setTimeout(()=>finish(null),3500);im.onload=()=>finish(im);im.onerror=()=>finish(null);im.src=String(src)})}
function roundRect(ctx,x,y,w,h,r,fill,stroke,width=1){const rr=Math.min(r,w/2,h/2);ctx.beginPath();ctx.moveTo(x+rr,y);ctx.arcTo(x+w,y,x+w,y+h,rr);ctx.arcTo(x+w,y+h,x,y+h,rr);ctx.arcTo(x,y+h,x,y,rr);ctx.arcTo(x,y,x+w,y,rr);ctx.closePath();if(fill){ctx.fillStyle=fill;ctx.fill()}if(stroke){ctx.strokeStyle=stroke;ctx.lineWidth=width;ctx.stroke()}}
function wrap(ctx,text,maxWidth){const words=String(text||'').trim().split(/\s+/).filter(Boolean),lines=[];let line='';for(const word of words){const test=line?line+' '+word:word;if(ctx.measureText(test).width<=maxWidth)line=test;else{if(line)lines.push(line);line=word}}if(line)lines.push(line);return lines}
function text(ctx,value,x,y,width,lineHeight,{font='16px Arial',color='#111',align='left',maxLines=0}={}){ctx.save();ctx.font=font;ctx.fillStyle=color||'#111';ctx.textAlign=align;ctx.textBaseline='top';let lines=wrap(ctx,value,width);if(maxLines&&lines.length>maxLines){lines=lines.slice(0,maxLines);let last=lines[lines.length-1]||'';while(last&&ctx.measureText(last+'…').width>width)last=last.slice(0,-1);lines[lines.length-1]=last+'…'}lines.forEach((line,i)=>ctx.fillText(line,x,y+i*lineHeight));ctx.restore();return lines.length*lineHeight}
function line(ctx,x1,y1,x2,y2,color,width=1,dash=[]){ctx.save();ctx.strokeStyle=color;ctx.lineWidth=width;ctx.setLineDash(dash);ctx.beginPath();ctx.moveTo(x1,y1);ctx.lineTo(x2,y2);ctx.stroke();ctx.restore()}
function coverImage(ctx,img,x,y,w,h,r=24){if(!img)return;const s=Math.max(w/img.naturalWidth,h/img.naturalHeight),sw=w/s,sh=h/s,sx=(img.naturalWidth-sw)/2,sy=(img.naturalHeight-sh)/2;ctx.save();roundRect(ctx,x,y,w,h,r);ctx.clip();ctx.drawImage(img,sx,sy,sw,sh,x,y,w,h);ctx.restore()}
function circleImage(ctx,img,cx,cy,r){if(!img)return;const d=r*2,s=Math.max(d/img.naturalWidth,d/img.naturalHeight),sw=d/s,sh=d/s,sx=(img.naturalWidth-sw)/2,sy=(img.naturalHeight-sh)/2;ctx.save();ctx.beginPath();ctx.arc(cx,cy,r,0,Math.PI*2);ctx.clip();ctx.drawImage(img,sx,sy,sw,sh,cx-r,cy-r,d,d);ctx.restore()}
function sun(ctx,cx,cy,r,color,rays=18){ctx.save();ctx.strokeStyle=color;ctx.fillStyle=color;ctx.lineWidth=2;for(let i=0;i<rays;i++){const a=i*Math.PI*2/rays,ri=r+8,ro=r+24+(i%2)*8;ctx.beginPath();ctx.moveTo(cx+Math.cos(a)*ri,cy+Math.sin(a)*ri);ctx.lineTo(cx+Math.cos(a)*ro,cy+Math.sin(a)*ro);ctx.stroke()}ctx.beginPath();ctx.arc(cx,cy,r,0,Math.PI*2);ctx.fill();ctx.restore()}
function logoOrBrand(ctx,logo,p,{x=M,y=46,dark=false,fonts}={},brandName='Солнце Кейтеринг'){if(logo){const s=Math.min(190/logo.naturalWidth,64/logo.naturalHeight),dw=logo.naturalWidth*s,dh=logo.naturalHeight*s;ctx.drawImage(logo,x,y,dw,dh);return}const df=fonts?.display||'Arial',lf=fonts?.label||'Arial';sun(ctx,x+28,y+29,15,p.accent,16);const parts=String(brandName||'Солнце Кейтеринг').trim().split(/\s+/),line1=(parts[0]||'Солнце').toUpperCase(),line2=(parts.slice(1).join(' ')||'Кейтеринг').toUpperCase();text(ctx,line1,x+60,y+11,220,28,{font:`700 23px ${df}`,color:dark?'#fff':p.ink,maxLines:1});text(ctx,line2,x+61,y+42,180,18,{font:`600 11px ${lf}`,color:p.accent,maxLines:1})}
function eventTitle(s){const event=String(s.event||'Персональное предложение').trim();return String(s.client||'').trim()?'Предложение для '+String(s.client).trim():event}
function footer(ctx,p,page,total,brandName='Солнце Кейтеринг',fonts){const lf=fonts?.label||'Arial';line(ctx,M,H-48,W-M,H-48,p.line,1);text(ctx,String(brandName||'Солнце Кейтеринг')+' · предложение клиенту',M,H-34,500,16,{font:`500 10px ${lf}`,color:p.muted,maxLines:1});text(ctx,String(page)+' / '+String(total),W-M,H-34,100,16,{font:`500 10px ${lf}`,color:p.muted,align:'right',maxLines:1})}
function iconCircle(ctx,cx,cy,r,glyph,{fill,stroke,glyphColor='#fff',size=20}={}){roundRect(ctx,cx-r,cy-r,r*2,r*2,r,fill,stroke,1.4);text(ctx,glyph,cx,cy-size/2-1,r*2,size+4,{font:`${size}px Arial`,color:glyphColor,align:'center',maxLines:1})}
// Small line-drawn icon badges (calendar/people/gauge/box) for the "premium" templates, so those stat cards use real iconography instead of emoji.
function iconBadge(ctx,x,y,size,p,draw){roundRect(ctx,x,y,size,size,size*0.28,p.deep,p.accent,1.4);ctx.save();ctx.translate(x+size/2,y+size/2);ctx.strokeStyle=p.accent;ctx.fillStyle=p.accent;ctx.lineWidth=Math.max(1.6,size*0.05);ctx.lineCap='round';ctx.lineJoin='round';draw(ctx,size*0.32);ctx.restore()}
function iconCalendar(ctx,r){roundRect(ctx,-r,-r*0.75,r*2,r*1.65,r*0.22,null,ctx.strokeStyle,ctx.lineWidth);ctx.beginPath();ctx.moveTo(-r,-r*0.3);ctx.lineTo(r,-r*0.3);ctx.stroke();ctx.beginPath();ctx.moveTo(-r*0.5,-r*1.05);ctx.lineTo(-r*0.5,-r*0.55);ctx.stroke();ctx.beginPath();ctx.moveTo(r*0.5,-r*1.05);ctx.lineTo(r*0.5,-r*0.55);ctx.stroke()}
function iconPeople(ctx,r){ctx.beginPath();ctx.arc(0,-r*0.4,r*0.4,0,Math.PI*2);ctx.stroke();ctx.beginPath();ctx.arc(0,r*0.75,r*0.82,Math.PI,0,true);ctx.stroke()}
function iconGauge(ctx,r){ctx.beginPath();ctx.arc(0,r*0.15,r*0.85,Math.PI,0);ctx.stroke();ctx.beginPath();ctx.moveTo(0,r*0.15);ctx.lineTo(r*0.48,-r*0.32);ctx.stroke();ctx.beginPath();ctx.arc(0,r*0.15,r*0.1,0,Math.PI*2);ctx.fill()}
function iconBox(ctx,r){roundRect(ctx,-r,-r*0.75,r*2,r*1.55,r*0.16,null,ctx.strokeStyle,ctx.lineWidth);ctx.beginPath();ctx.moveTo(-r,-r*0.05);ctx.lineTo(r,-r*0.05);ctx.stroke();ctx.beginPath();ctx.moveTo(0,-r*0.75);ctx.lineTo(0,r*0.8);ctx.stroke()}
function iconMenu(ctx,r){for(let i=-1;i<=1;i++){const yy=r*0.55*i;ctx.beginPath();ctx.arc(-r*0.72,yy,r*0.12,0,Math.PI*2);ctx.fill();ctx.beginPath();ctx.moveTo(-r*0.36,yy);ctx.lineTo(r,yy);ctx.stroke()}}
function statBadgeRow(ctx,p,f,x,y,w,h,cards){const gap=14,cw=(w-gap*(cards.length-1))/cards.length;const bs=Math.min(52,h*0.42);cards.forEach(([draw,value,label],i)=>{const xx=x+i*(cw+gap);roundRect(ctx,xx,y,cw,h,20,p.paper,p.line,1);iconBadge(ctx,xx+18,y+18,bs,p,draw);text(ctx,value,xx+18+bs+10,y+18+bs/2-12,cw-18-bs-10-12,24,{font:`700 16px ${f.display}`,color:'#fff',maxLines:1});text(ctx,label,xx+18,y+h-30,cw-36,16,{font:`500 12px ${f.label}`,color:p.muted,maxLines:1})})}
function bulletList(ctx,p,f,x,y,w,lineH,items,color){let yy=y;items.forEach(t=>{ctx.save();ctx.fillStyle=p.accent;ctx.beginPath();ctx.arc(x+4,yy+9,4,0,Math.PI*2);ctx.fill();ctx.restore();const used=text(ctx,t,x+20,yy,w-20,18,{font:`500 14px ${f.label}`,color:color||p.ink,maxLines:2});yy+=Math.max(lineH,used+6)});return yy-y}
function factChips(ctx,s,p,x,y,w,{dark=true,fonts}={}){const lf=fonts?.label||'Arial';const facts=[];if(s.date)facts.push(dateText(s.date));if(s.guests)facts.push(String(s.guests)+' '+plural(s.guests,'гость','гостя','гостей'));if(s.time)facts.push('Доставка '+String(s.time));const gap=8,cw=(w-gap*Math.max(0,facts.length-1))/Math.max(1,facts.length);facts.forEach((f,i)=>{const xx=x+i*(cw+gap);roundRect(ctx,xx,y,cw,44,13,dark?'rgba(255,255,255,.06)':p.paper,dark?'rgba(255,255,255,.2)':p.line);text(ctx,f,xx+cw/2,y+14,cw-14,17,{font:`600 12px ${lf}`,color:dark?'#f4f1e6':p.ink,align:'center',maxLines:1})})}
function menuList(ctx,s,p,x,y,w,rowH,count,{dark=true,numbered=true,fonts}={}){const df=fonts?.display||'Georgia',lf=fonts?.label||'Arial';const items=(s.items||[]).slice(0,count),gap=8;items.forEach((item,i)=>{const yy=y+i*(rowH+gap);roundRect(ctx,x,yy,w,rowH,13,dark?'rgba(255,255,255,.05)':p.paper,dark?'rgba(255,255,255,.14)':p.line);const badgeX=x+14;if(numbered){roundRect(ctx,badgeX,yy+rowH/2-13,26,26,13,p.accent,null);text(ctx,String(i+1),badgeX+13,yy+rowH/2-9,26,18,{font:`700 12px ${lf}`,color:p.deep,align:'center',maxLines:1})}const textX=numbered?badgeX+38:x+16;text(ctx,item.name||'Позиция меню',textX,yy+10,w-(textX-x)-70,20,{font:`600 14px ${df}`,color:dark?'#fff':p.ink,maxLines:1});const meta=[item.weight?String(item.weight):'',].filter(Boolean).join(' · ');if(meta)text(ctx,meta,textX,yy+rowH-24,w-(textX-x)-70,16,{font:`500 11px ${lf}`,color:p.muted,maxLines:1});text(ctx,'× '+Math.round(Number(item.qty||1)),x+w-16,yy+rowH/2-9,50,18,{font:`700 12px ${lf}`,color:p.accent,align:'right',maxLines:1})});return items.length*(rowH+gap)}
function photoGridSquare(ctx,s,p,x,y,w,h,cols,rows,{numbered=true,fonts}={}){const lf=fonts?.label||'Arial';const items=(s.items||[]).slice(0,cols*rows);const gap=7,cw=(w-gap*(cols-1))/cols,ch=(h-gap*(rows-1))/rows;return (async()=>{const imgs=await Promise.all(items.map(it=>loadImage(it.photoData||'')));imgs.forEach((img,i)=>{const col=i%cols,row=Math.floor(i/cols),xx=x+col*(cw+gap),yy=y+row*(ch+gap);roundRect(ctx,xx,yy,cw,ch,10,p.deep,null);if(img)coverImage(ctx,img,xx,yy,cw,ch,10);if(numbered){roundRect(ctx,xx+6,yy+6,22,22,11,'rgba(6,10,8,.72)',null);text(ctx,String(i+1),xx+17,yy+10,22,16,{font:`700 10px ${lf}`,color:'#fff',align:'center',maxLines:1})}})})()}
function photoGridCircles(ctx,s,p,x,y,w,cols,r,{numbered=true,fonts}={}){const lf=fonts?.label||'Arial';const items=(s.items||[]).slice(0,cols*2);const gapX=(w-cols*r*2)/(cols-1||1);return (async()=>{const imgs=await Promise.all(items.map(it=>loadImage(it.photoData||'')));imgs.forEach((img,i)=>{const col=i%cols,row=Math.floor(i/cols),cx=x+r+col*(r*2+gapX),cy=y+r+row*(r*2+22);ctx.save();ctx.beginPath();ctx.arc(cx,cy,r+3,0,Math.PI*2);ctx.strokeStyle=p.accent;ctx.lineWidth=2;ctx.stroke();ctx.restore();if(img)circleImage(ctx,img,cx,cy,r);else{ctx.save();ctx.beginPath();ctx.arc(cx,cy,r,0,Math.PI*2);ctx.fillStyle=p.deep;ctx.fill();ctx.restore()}if(numbered){roundRect(ctx,cx-13,cy+r-8,26,22,11,p.accent,null);text(ctx,String(i+1),cx,cy+r-4,26,16,{font:`700 10px ${lf}`,color:p.deep,align:'center',maxLines:1})}})})()}
function statCards(ctx,s,p,x,y,w,h,cards,{dark=true,fonts}={}){const lf=fonts?.label||'Arial';const gap=10,cw=(w-gap*(cards.length-1))/cards.length;cards.forEach((c,i)=>{const xx=x+i*(cw+gap);roundRect(ctx,xx,y,cw,h,14,dark?'rgba(255,255,255,.05)':p.paper,dark?'rgba(255,255,255,.16)':p.line);text(ctx,c[1],xx+16,y+14,cw-32,17,{font:`700 10px ${lf}`,color:p.muted,maxLines:1});text(ctx,String(c[0]),xx+16,y+38,cw-32,32,{font:`800 24px ${lf}`,color:dark?'#fff':p.deep,maxLines:1})})}
function priceBand(ctx,s,p,x,y,w,h,{dark=true,fonts}={}){
const df=fonts?.display||'Arial',lf=fonts?.label||'Arial';
const perGuest=s.guests?money((Number(s.pricing?.itemsTotal||s.pricing?.total||0))/Math.max(1,Number(s.guests))):'';
if(dark){
roundRect(ctx,x,y,w,h,16,p.accent,null);
text(ctx,'ИТОГОВАЯ СТОИМОСТЬ',x+22,y+16,w-44,16,{font:`700 11px ${lf}`,color:p.deep,maxLines:1});
text(ctx,money(s.pricing?.total),x+22,y+38,w-44,44,{font:`700 32px ${df}`,color:p.deep,maxLines:1});
if(perGuest)text(ctx,'на гостя: '+perGuest,x+22,y+h-26,w-44,18,{font:`500 12px ${lf}`,color:p.deep,maxLines:1});
}else{
roundRect(ctx,x,y,w,h,16,p.paper,p.accent,2);
text(ctx,'ИТОГОВАЯ СТОИМОСТЬ',x+22,y+18,w-44,16,{font:`700 11px ${lf}`,color:p.accent2,maxLines:1});
text(ctx,money(s.pricing?.total),x+22,y+38,w-44,46,{font:`700 34px ${df}`,color:p.deep,maxLines:1});
if(perGuest)text(ctx,'на гостя: '+perGuest,x+22,y+h-25,w-44,18,{font:`500 12px ${lf}`,color:p.muted,maxLines:1});
}
}
function checklistCard(ctx,p,x,y,w,h,title,lines,{dark=true,fonts}={}){const lf=fonts?.label||'Arial';roundRect(ctx,x,y,w,h,16,dark?'rgba(255,255,255,.04)':p.paper,dark?'rgba(255,255,255,.14)':p.line);text(ctx,title,x+18,y+16,w-36,18,{font:`700 13px ${lf}`,color:p.accent,maxLines:1});const cols=2,rowH=22,colW=(w-36)/cols;lines.forEach((t,i)=>{const col=i%cols,row=Math.floor(i/cols),xx=x+18+col*colW,yy=y+46+row*rowH;roundRect(ctx,xx,yy+2,14,14,7,p.accent,null);text(ctx,'✓',xx+7,yy+3,10,12,{font:`700 9px ${lf}`,color:p.deep,align:'center',maxLines:1});text(ctx,t,xx+20,yy,colW-24,14,{font:`500 10px ${lf}`,color:dark?'#eef':p.ink,maxLines:1})})}
function addonRow(ctx,p,x,y,w,h,items,{dark=true,fonts}={}){const lf=fonts?.label||'Arial';roundRect(ctx,x,y,w,h,16,dark?'rgba(255,255,255,.04)':p.paper,dark?'rgba(255,255,255,.14)':p.line);text(ctx,'Рекомендуем добавить',x+18,y+16,w-36,16,{font:`700 12px ${lf}`,color:p.accent,maxLines:1});const innerX=x+18,innerW=w-36,innerY=y+48,gap=8,cw=(innerW-gap*(items.length-1))/items.length;items.forEach((t,i)=>{const xx=innerX+i*(cw+gap);roundRect(ctx,xx+cw/2-11,innerY,22,22,11,p.accent,null);text(ctx,'+',xx+cw/2,innerY+2,22,18,{font:`700 13px ${lf}`,color:p.deep,align:'center',maxLines:1});text(ctx,t,xx+cw/2,innerY+32,cw,14,{font:`600 10px ${lf}`,color:dark?'#eef':p.ink,align:'center',maxLines:2})})}
const CHECK_LINES_DEFAULT=['Доставка в фирменных боксах','Свежие продукты каждый день','Аккуратная подача и упаковка','Готовность к сервировке'];
const ADDON_LINES_DEFAULT=['Напитки','Чайная станция','Официант','Текстиль'];
async function renderCover(s,id){
await ensureFontsReady(id);
const p=PALETTES[id]||PALETTES['cream-elegance'],f=fontsFor(id),canvas=document.createElement('canvas');canvas.width=Math.round(W*SCALE);canvas.height=Math.round(H*SCALE);const ctx=canvas.getContext('2d',{alpha:false});ctx.setTransform(SCALE,0,0,SCALE,0,0);ctx.fillStyle=p.bg;ctx.fillRect(0,0,W,H);
const logo=await loadImage(s.logo||''),heroSrc=(s.items||[]).find(x=>safeImage(x.photoData||x.photo))?.photoData||(s.finalGallery||[]).find(safeImage)||'',hero=await loadImage(heroSrc),title=eventTitle(s),event=String(s.event||'Персональное предложение').trim();
// Pull the user's editable "control"/"extras" lists from Settings when present (set in preparedSnapshot), falling back to sensible defaults otherwise.
const CHECK_LINES=Array.isArray(s.controlLines)&&s.controlLines.length?s.controlLines:CHECK_LINES_DEFAULT;
const ADDON_LINES=Array.isArray(s.extraServices)&&s.extraServices.length?s.extraServices:ADDON_LINES_DEFAULT;
if(id==='cream-elegance'){
logoOrBrand(ctx,logo,p,{fonts:f},s.brandName);
text(ctx,'МЕРОПРИЯТИЕ',M,168,390,18,{font:`700 11px ${f.label}`,color:p.accent2,maxLines:1});
text(ctx,title,M,198,405,52,{font:`600 40px ${f.display}`,color:p.ink,maxLines:2});
factChips(ctx,s,p,M,372,405,{dark:false,fonts:f});
line(ctx,M,440,445,440,p.accent,1);
text(ctx,'Меню составлено индивидуально для вашего мероприятия с учётом количества гостей и пожеланий.',M,464,395,24,{font:`500 14px ${f.label}`,color:p.muted,maxLines:4});
if(hero)coverImage(ctx,hero,500,150,446,600,28);
text(ctx,'МЕНЮ',W/2,795,W,26,{font:`600 24px ${f.display}`,color:p.deep,align:'center',maxLines:1});
line(ctx,M,814,410,814,p.line,1);line(ctx,590,814,W-M,814,p.line,1);
await photoGridSquare(ctx,s,p,M,850,W-2*M,300,3,2,{numbered:false,fonts:f});
statCards(ctx,s,p,M,1170,565,100,[[Math.round(classicBoxCount(s))||0,'КОРОБОК'],[(s.items||[]).length,'ПОЗИЦИЙ']],{dark:false,fonts:f});
priceBand(ctx,s,p,640,1170,306,100,{dark:false,fonts:f});
}else if(id==='neon-menu'){
ctx.save();roundRect(ctx,20,20,W-40,H-40,0,null,p.accent,1.4);ctx.restore();
logoOrBrand(ctx,logo,p,{x:M,y:52,dark:true,fonts:f},s.brandName);
roundRect(ctx,M,140,150,34,17,'rgba(57,226,122,.12)',p.accent,1);
text(ctx,dateText(s.date)||'Дата уточняется',M+14,150,200,16,{font:`700 10px ${f.label}`,color:p.accent,maxLines:1});
text(ctx,title,M,192,430,60,{font:`700 34px ${f.display}`,color:'#fff',maxLines:3});
text(ctx,event,M,340,400,26,{font:`500 14px ${f.label}`,color:p.muted,maxLines:2});
if(hero)coverImage(ctx,hero,520,140,426,300,20);
if(s.guests){roundRect(ctx,830,390,116,58,16,p.deep,p.accent,1.4);text(ctx,String(s.guests),888,402,116,26,{font:`700 20px ${f.display}`,color:p.accent,align:'center',maxLines:1});text(ctx,'ГОСТЕЙ',888,432,116,14,{font:`700 9px ${f.label}`,color:p.muted,align:'center',maxLines:1})}
const qual=['Свежие ингредиенты','Авторские рецепты','Эстетичная подача','Контроль качества'];
qual.forEach((t,i)=>{const xx=M+i*228;roundRect(ctx,xx,470,212,60,14,'rgba(57,226,122,.05)',p.line);text(ctx,t,xx+106,494,188,16,{font:`600 11px ${f.label}`,color:p.accent,align:'center',maxLines:2})});
menuList(ctx,s,p,M,560,470,64,5,{dark:true,fonts:f});
await photoGridSquare(ctx,s,p,540,560,406,470,3,4,{numbered:true,fonts:f});
text(ctx,'НАШИ ЗАКУСКИ',743,536,406,16,{font:`700 13px ${f.display}`,color:p.accent,align:'center',maxLines:1});
statCards(ctx,s,p,M,1060,306,80,[[Math.round(classicBoxCount(s))||0,'КОРОБОК'],[(s.items||[]).length,'ПОЗИЦИЙ']],{dark:true,fonts:f});
priceBand(ctx,s,p,376,1060,570,80,{dark:true,fonts:f});
checklistCard(ctx,p,M,1160,470,150,'ВСЁ ПОД КОНТРОЛЕМ',CHECK_LINES,{dark:true,fonts:f});
addonRow(ctx,p,540,1160,406,150,ADDON_LINES,{dark:true,fonts:f});
}else if(id==='emerald-circles'){
logoOrBrand(ctx,logo,p,{x:M,y:52,dark:true,fonts:f},s.brandName);roundRect(ctx,M,140,260,34,17,'rgba(201,162,74,.12)',p.accent,1);text(ctx,'ИНДИВИДУАЛЬНОЕ ПРЕДЛОЖЕНИЕ',M+14,150,240,14,{font:`700 9px ${f.label}`,color:p.accent,maxLines:1});text(ctx,title,M,192,430,58,{font:`600 38px ${f.display}`,color:'#fff',maxLines:3});text(ctx,'Составим идеальное гастрономическое решение для вашего мероприятия.',M,350,410,22,{font:`500 13px ${f.label}`,color:p.muted,maxLines:3});
const feats=['Разнообразие блюд','Свежие ингредиенты','Премиальное качество'];feats.forEach((t,i)=>{const xx=M+i*145;roundRect(ctx,xx,430,132,48,13,'rgba(255,255,255,.05)',p.line);text(ctx,t,xx+66,446,112,14,{font:`600 10px ${f.label}`,color:p.accent2,align:'center',maxLines:2})});
menuList(ctx,s,p,M,510,470,66,5,{dark:true,fonts:f});await photoGridCircles(ctx,s,p,565,520,380,3,58,{numbered:true,fonts:f});
statCards(ctx,s,p,M,940,W-2*M,78,[[Math.round(classicBoxCount(s))||0,'КОРОБОК'],[(s.items||[]).length,'ПОЗИЦИЙ ',],[s.guests||'—','ГОСТЕЙ']],{dark:true,fonts:f});priceBand(ctx,s,p,M,1032,W-2*M,90,{dark:true,fonts:f});
checklistCard(ctx,p,M,1140,470,170,'ВСЁ ПОД КОНТРОЛЕМ КОМАНДЫ СОЛНЦА',CHECK_LINES,{dark:true,fonts:f});addonRow(ctx,p,540,1140,406,170,ADDON_LINES,{dark:true,fonts:f});
}else if(id==='midnight-checklist'){
logoOrBrand(ctx,logo,p,{x:M,y:48,dark:true,fonts:f},s.brandName);if(s.client)text(ctx,String(s.client),W-M,58,300,20,{font:`700 15px ${f.display}`,color:'#fff',align:'right',maxLines:1});const metaLine=[dateText(s.date),s.guests?String(s.guests)+' гостей':''].filter(Boolean).join(' · ');if(metaLine)text(ctx,metaLine,W-M,84,300,16,{font:`500 11px ${f.label}`,color:p.muted,align:'right',maxLines:1});
roundRect(ctx,M,120,W-2*M,60,14,'rgba(255,255,255,.04)',p.line);text(ctx,'Меню составлено из расчёта количества гостей. Все позиции приедут готовыми к подаче на стол.',M+20,138,W-2*M-40,20,{font:`500 12px ${f.label}`,color:p.muted,maxLines:2});
iconBadge(ctx,M,192,34,p,iconMenu);text(ctx,'МЕНЮ',M+48,197,260,24,{font:`700 20px ${f.display}`,color:'#fff',maxLines:1});const menuCount=(s.items||[]).length;if(menuCount)text(ctx,String(menuCount)+' '+plural(menuCount,'позиция','позиции','позиций'),W-M,200,240,16,{font:`500 11px ${f.label}`,color:p.muted,align:'right',maxLines:1});line(ctx,M,234,W-M,234,p.line,1);
menuList(ctx,s,p,M,255,W-2*M,58,8,{dark:true,fonts:f});
await photoGridSquare(ctx,s,p,M,860,W-2*M,190,5,2,{numbered:false,fonts:f});
statCards(ctx,s,p,M,1075,470,74,[[Math.round(classicBoxCount(s))||0,'КОРОБОК'],[(s.items||[]).length,'ПОЗИЦИЙ']],{dark:true,fonts:f});priceBand(ctx,s,p,540,1075,406,74,{dark:true,fonts:f});
checklistCard(ctx,p,M,1170,470,150,'ВСЁ ПОД КОНТРОЛЕМ КОМАНДЫ СОЛНЦА',CHECK_LINES,{dark:true,fonts:f});addonRow(ctx,p,540,1170,406,150,ADDON_LINES,{dark:true,fonts:f});
}else if(id==='gourmet-hero'){
logoOrBrand(ctx,logo,p,{x:W-M-190,y:44,dark:true,fonts:f},s.brandName);text(ctx,'КЕЙТЕРИНГ ДЛЯ',M,96,460,44,{font:`700 32px ${f.display}`,color:'#fff',maxLines:1});text(ctx,'ВАШЕГО СОБЫТИЯ',M,140,460,44,{font:`700 32px ${f.display}`,color:'#fff',maxLines:1});text(ctx,'Индивидуальное меню, безупречная подача и сервис, о котором будут говорить ваши гости.',M,235,400,24,{font:`500 14px ${f.label}`,color:p.muted,maxLines:3});if(hero)coverImage(ctx,hero,520,60,426,340,18);roundRect(ctx,520,420,240,42,12,p.paper,p.line);text(ctx,String((s.items||[]).length)+' блюд в меню',540,432,220,18,{font:`700 12px ${f.label}`,color:p.accent,maxLines:1});
const realCats=[...new Set((s.items||[]).map(it=>it.categoryName).filter(Boolean))].slice(0,5);
if(realCats.length){const gap=10,cw=(W-2*M-gap*(realCats.length-1))/realCats.length;realCats.forEach((t,i)=>{const xx=M+i*(cw+gap);roundRect(ctx,xx,494,cw,32,10,'rgba(255,255,255,.06)',p.line);text(ctx,t,xx+cw/2,503,cw-10,14,{font:`600 10px ${f.label}`,color:p.accent,align:'center',maxLines:1})})}
const featured=(s.items||[]).slice(0,3);const cardW=(W-2*M-2*14)/3;
{const imgs=await Promise.all(featured.map(it=>loadImage(it.photoData||'')));featured.forEach((it,i)=>{const xx=M+i*(cardW+14);roundRect(ctx,xx,555,cardW,230,16,p.paper,p.line);if(imgs[i])coverImage(ctx,imgs[i],xx,555,cardW,150,16);text(ctx,String(i+1).padStart(2,'0'),xx+14,715,40,16,{font:`700 12px ${f.label}`,color:p.accent,maxLines:1});text(ctx,it.name||'Позиция меню',xx+14,735,cardW-28,20,{font:`600 13px ${f.display}`,color:'#fff',maxLines:2});if(it.weight)text(ctx,String(it.weight),xx+14,765,cardW-28,14,{font:`500 10px ${f.label}`,color:p.muted,maxLines:1})})}
roundRect(ctx,M,822,W-2*M,52,14,null,p.accent,1.4);text(ctx,'СМОТРЕТЬ ПОЛНОЕ МЕНЮ →',W/2,838,W,18,{font:`700 12px ${f.label}`,color:p.accent,align:'center',maxLines:1});
statCards(ctx,s,p,M,910,306,100,[[Math.round(classicBoxCount(s))||0,'КОРОБОК'],[(s.items||[]).length,'ПОЗИЦИЙ']],{dark:true,fonts:f});priceBand(ctx,s,p,376,910,306,100,{dark:true,fonts:f});checklistCard(ctx,p,698,910,248,100,'ДОСТАВКА',CHECK_LINES.slice(0,2),{dark:true,fonts:f});
addonRow(ctx,p,M,1045,W-2*M,150,ADDON_LINES,{dark:true,fonts:f});
}else if(id==='diamond-gold'){
logoOrBrand(ctx,logo,p,{x:M,y:50,dark:true,fonts:f},s.brandName);if(s.guests){roundRect(ctx,W-M-140,50,140,34,17,'rgba(255,255,255,.06)',p.line);text(ctx,String(s.guests)+' гостей',W-M-70,58,140,16,{font:`700 11px ${f.label}`,color:'#fff',align:'center',maxLines:1})}
text(ctx,'ИНДИВИДУАЛЬНОЕ МЕНЮ',M,150,440,34,{font:`700 28px ${f.display}`,color:'#fff',maxLines:1});text(ctx,'для вашего мероприятия',M,186,440,24,{font:`500 18px ${f.display}`,color:p.accent,maxLines:1});text(ctx,'Мы создаём гастрономические впечатления, которые запоминаются.',M,222,400,20,{font:`500 13px ${f.label}`,color:p.muted,maxLines:3});
const feats=['Индивидуальный подход','Только свежие ингредиенты','Разнообразие вкусов','Сделано с любовью'];feats.forEach((t,i)=>{const xx=M+(i%2)*220,yy=330+Math.floor(i/2)*46;roundRect(ctx,xx,yy,205,38,11,'rgba(255,255,255,.04)',p.line);text(ctx,t,xx+10,yy+12,185,14,{font:`500 10px ${f.label}`,color:p.accent2,maxLines:1})});
menuList(ctx,s,p,M,440,470,56,5,{dark:true,fonts:f});
text(ctx,'ПРИМЕРЫ ЗАКУСОК',540,428,406,16,{font:`700 12px ${f.label}`,color:p.accent,maxLines:1});await photoGridSquare(ctx,s,p,540,455,406,300,3,2,{numbered:true,fonts:f});
statCards(ctx,s,p,M,900,306,90,[[Math.round(classicBoxCount(s))||0,'КОРОБОК'],[(s.items||[]).length,'ПОЗИЦИЙ']],{dark:true,fonts:f});priceBand(ctx,s,p,376,900,306,90,{dark:true,fonts:f});
const dgx=698,dgy=900;roundRect(ctx,dgx,dgy,248,90,16,'rgba(255,255,255,.04)',p.line);ctx.save();ctx.translate(dgx+124,dgy+45);ctx.rotate(Math.PI/4);ctx.fillStyle=p.accent;ctx.fillRect(-16,-16,32,32);ctx.restore();text(ctx,'СКИДКА ВКЛЮЧЕНА',dgx+124,dgy+70,248,14,{font:`700 9px ${f.label}`,color:p.muted,align:'center',maxLines:1});
checklistCard(ctx,p,M,1010,470,170,'ДОСТАВКА И КОНТРОЛЬ КАЧЕСТВА',CHECK_LINES,{dark:true,fonts:f});addonRow(ctx,p,540,1010,406,170,ADDON_LINES.concat(['Посуда','Мебель']),{dark:true,fonts:f});
}else if(id==='premium-dark'||id==='premium-emerald'){
logoOrBrand(ctx,logo,p,{x:M,y:44,dark:true,fonts:f},s.brandName);
text(ctx,'ПЕРСОНАЛЬНОЕ ПРЕДЛОЖЕНИЕ',M,140,500,16,{font:`700 11px ${f.label}`,color:p.accent,maxLines:1});
text(ctx,title,M,164,W-2*M,40,{font:`700 32px ${f.display}`,color:'#fff',maxLines:2});
bulletList(ctx,p,f,M,256,W-2*M,30,CHECK_LINES,'#dfe3e8');
if(hero)coverImage(ctx,hero,M,396,W-2*M,230,26);
statBadgeRow(ctx,p,f,M,650,W-2*M,104,[[iconCalendar,dateText(s.date)||'Уточняется','Дата мероприятия'],[iconPeople,s.guests?String(s.guests):'—','Количество гостей'],[iconBox,String(Math.round(classicBoxCount(s))||0),'Коробок в заказе']]);
text(ctx,'МЕНЮ',W/2,786,W,26,{font:`700 22px ${f.display}`,color:'#fff',align:'center',maxLines:1});
line(ctx,M,812,410,812,p.line,1);line(ctx,590,812,W-M,812,p.line,1);
await photoGridSquare(ctx,s,p,M,846,W-2*M,272,3,2,{numbered:true,fonts:f});
statCards(ctx,s,p,M,1138,565,95,[[Math.round(classicBoxCount(s))||0,'КОРОБОК'],[(s.items||[]).length,'ПОЗИЦИЙ']],{dark:true,fonts:f});
priceBand(ctx,s,p,640,1138,306,95,{dark:true,fonts:f});
checklistCard(ctx,p,M,1253,470,113,'ВСЁ ПОД КОНТРОЛЕМ',CHECK_LINES.slice(0,2),{dark:true,fonts:f});
addonRow(ctx,p,540,1253,406,113,ADDON_LINES,{dark:true,fonts:f});
}
canvas.dataset.sunSignatureTemplate=id;canvas.dataset.sunSignatureCover='1';return canvas;
}
function correctPageNumbers(pages,start,total,innerId){const foot=(PALETTES[innerId]||{}).foot||'#fff';pages.forEach((canvas,i)=>{try{const scale=canvas.width/W,ctx=canvas.getContext('2d');ctx.save();ctx.setTransform(scale,0,0,scale,0,0);ctx.fillStyle=foot;ctx.fillRect(W-190,H-50,150,40);text(ctx,String(start+i)+' / '+String(total),W-M,H-34,100,16,{font:'10px Arial',color:'#888',align:'right',maxLines:1});ctx.restore()}catch(_){}})}
// ---- Fully-styled inner pages for the 2 polished templates, so the whole document reads as one design instead of a nice cover glued to the generic base pages. ----
function newPage(p,dark){const canvas=document.createElement('canvas');canvas.width=Math.round(W*SCALE);canvas.height=Math.round(H*SCALE);const ctx=canvas.getContext('2d',{alpha:false});ctx.setTransform(SCALE,0,0,SCALE,0,0);ctx.fillStyle=p.bg;ctx.fillRect(0,0,W,H);if(dark){ctx.save();roundRect(ctx,20,20,W-40,H-40,0,null,p.accent,1.4);ctx.restore()}return{canvas,ctx}}
function contentHeader(ctx,p,f,s,label,dark){text(ctx,String(s.brandName||'Солнце Кейтеринг').toUpperCase(),M,50,320,16,{font:`700 11px ${f.label}`,color:p.accent,maxLines:1});text(ctx,eventTitle(s),M,70,520,26,{font:`600 20px ${f.display}`,color:dark?'#fff':p.ink,maxLines:1});text(ctx,label,W-M,58,260,16,{font:`600 11px ${f.label}`,color:p.muted,align:'right',maxLines:1});line(ctx,M,108,W-M,108,p.line,1)}
async function itemRowsPage(ctx,p,f,items,y0,dark){
const rowH=dark?96:104,gap=dark?12:14;
const imgs=await Promise.all(items.map(it=>loadImage(it.photoData||'')));
items.forEach((item,i)=>{
const yy=y0+i*(rowH+gap),photoSize=rowH-20,px=M+10,py=yy+10,textX=px+photoSize+18,sum=Number(item.sum??(Number(item.unitPrice||0)*Number(item.qty||0)));
roundRect(ctx,M,yy,W-2*M,rowH,16,dark?'rgba(255,255,255,.04)':p.paper,dark?'rgba(255,255,255,.12)':p.line);
roundRect(ctx,px,py,photoSize,photoSize,12,p.deep,null);
if(imgs[i])coverImage(ctx,imgs[i],px,py,photoSize,photoSize,12);
text(ctx,item.name||'Позиция меню',textX,yy+14,W-2*M-(textX-M)-135,22,{font:`600 16px ${f.display}`,color:dark?'#fff':p.ink,maxLines:1});
const meta=[item.weight?String(item.weight):'',item.categoryName?String(item.categoryName):''].filter(Boolean).join(' · ');
if(meta)text(ctx,meta,textX,yy+42,W-2*M-(textX-M)-135,16,{font:`500 12px ${f.label}`,color:p.muted,maxLines:1});
text(ctx,'× '+Math.round(Number(item.qty||1)),W-M-16,yy+16,110,18,{font:`700 13px ${f.label}`,color:p.accent,align:'right',maxLines:1});
text(ctx,money(sum),W-M-16,yy+rowH-36,110,18,{font:`700 16px ${f.label}`,color:dark?'#fff':p.deep,align:'right',maxLines:1});
});
}
function pricingPage(ctx,p,f,s,dark){
text(ctx,'ИТОГОВАЯ СМЕТА',M,132,W-2*M,34,{font:`600 28px ${f.display}`,color:dark?'#fff':p.deep,maxLines:1});
const pr=s.pricing||{};let y=200;
const rows=[['Стоимость позиций',Number(pr.base||0),false]];
if(Number(pr.manual||0)>0)rows.push(['Скидка',Number(pr.manual),true]);
if(Number(pr.promo||0)>0)rows.push(['Промокод'+(s.promoCode?` «${s.promoCode}»`:''),Number(pr.promo),true]);
rows.push(['Меню после скидок',Number(pr.itemsTotal||0),false]);
rows.push(['Доставка',Number(pr.delivery||0),false]);
rows.forEach(([label,val,negative])=>{
roundRect(ctx,M,y,W-2*M,52,14,dark?'rgba(255,255,255,.04)':p.paper,dark?'rgba(255,255,255,.12)':p.line);
text(ctx,label,M+20,y+17,520,18,{font:`500 14px ${f.label}`,color:dark?'#eef2ee':p.ink,maxLines:1});
text(ctx,(negative?' ':'')+money(val),W-M-20,y+17,260,18,{font:`700 16px ${f.label}`,color:negative?'#c0554a':(dark?'#fff':p.deep),align:'right',maxLines:1});
y+=52+10;
});
y+=8;
roundRect(ctx,M,y,W-2*M,160,20,p.accent,null);
text(ctx,'ИТОГО К ОПЛАТЕ',M+28,y+24,400,16,{font:`700 12px ${f.label}`,color:p.deep,maxLines:1});
text(ctx,money(pr.total),M+28,y+48,W-2*M-56,56,{font:`700 44px ${f.display}`,color:p.deep,maxLines:1});
if(s.guests)text(ctx,'на гостя: '+money(Number(pr.itemsTotal||pr.total||0)/Math.max(1,Number(s.guests))),M+28,y+118,400,20,{font:`600 14px ${f.label}`,color:p.deep,maxLines:1});
y+=160+36;
text(ctx,'Спасибо, что выбираете нас — будем рады сделать ваше событие особенным.',M,y,W-2*M,22,{font:`500 15px ${f.label}`,color:p.muted,maxLines:2});
}
async function renderContentPages(s,id){
await ensureFontsReady(id);
const p=PALETTES[id],f=fontsFor(id),dark=id!=='cream-elegance';
const items=s.items||[],top=120,bottom=H-90,rowH=dark?96:104,gap=dark?12:14;
const perPage=Math.max(1,Math.floor((bottom-top+gap)/(rowH+gap)));
const pageCount=Math.max(1,Math.ceil(items.length/perPage));
const pages=[];
for(let pi=0;pi<pageCount;pi++){
const {canvas,ctx}=newPage(p,dark);
contentHeader(ctx,p,f,s,`Меню · стр. ${pi+1} из ${pageCount}`,dark);
await itemRowsPage(ctx,p,f,items.slice(pi*perPage,(pi+1)*perPage),top,dark);
pages.push(canvas);
}
const priced=newPage(p,dark);
contentHeader(priced.ctx,p,f,s,'Стоимость',dark);
pricingPage(priced.ctx,p,f,s,dark);
pages.push(priced.canvas);
return pages;
}
async function renderPages(snapshot,baseRenderer){
if(typeof baseRenderer!=='function')throw new Error('Base PDF renderer is unavailable');
const id=String(snapshot?.offerTemplateId||'');
if(!SIGNATURE_SET.has(id))return baseRenderer(snapshot);
const [cover,innerPages]=await Promise.all([renderCover(snapshot,id),renderContentPages(snapshot,id)]);
const pages=[cover,...(innerPages||[])],total=pages.length,p=PALETTES[id]||PALETTES['cream-elegance'];
try{const ctx=cover.getContext('2d');ctx.save();ctx.setTransform(SCALE,0,0,SCALE,0,0);footer(ctx,p,1,total,snapshot?.brandName,fontsFor(id));ctx.restore()}catch(_){}
const f=fontsFor(id);
(innerPages||[]).forEach((canvas,i)=>{try{const scale=canvas.width/W,ctx=canvas.getContext('2d');ctx.save();ctx.setTransform(scale,0,0,scale,0,0);footer(ctx,p,2+i,total,snapshot?.brandName,f);ctx.restore()}catch(_){}});
return pages;
}
function installPreviewStyles(){if(document.getElementById('sunSignatureOfferPreviewV18'))return;const style=document.createElement('style');style.id='sunSignatureOfferPreviewV18';style.textContent=`
.sun-offer-template-mini[data-template-mini="cream-elegance"] i,.sun-offer-template-mini[data-template-mini="neon-menu"] i,.sun-offer-template-mini[data-template-mini="emerald-circles"] i,.sun-offer-template-mini[data-template-mini="midnight-checklist"] i,.sun-offer-template-mini[data-template-mini="gourmet-hero"] i,.sun-offer-template-mini[data-template-mini="diamond-gold"] i,.sun-offer-template-mini[data-template-mini="premium-dark"] i,.sun-offer-template-mini[data-template-mini="premium-emerald"] i{display:none!important}
.sun-offer-template-mini[data-template-mini="cream-elegance"]{background:radial-gradient(circle at 78% 30%,#fff 0 18%,#8f8536 19% 21%,transparent 22%),linear-gradient(#fdf7ea,#f2e3bf)!important}
.sun-offer-template-mini[data-template-mini="neon-menu"]{background:linear-gradient(90deg,rgba(57,226,122,.35),transparent 45%),linear-gradient(135deg,#0d1210,#070908)!important;border-color:#1f4430!important}
.sun-offer-template-mini[data-template-mini="emerald-circles"]{background:radial-gradient(circle at 70% 55%,#c9a24a 0 6%,transparent 7%),radial-gradient(circle at 85% 55%,#c9a24a 0 6%,transparent 7%),linear-gradient(135deg,#0d2226,#08181c)!important;border-color:#1f4642!important}
.sun-offer-template-mini[data-template-mini="midnight-checklist"]{background:repeating-linear-gradient(0deg,#0b2417 0 11%,#123822 11% 12%),#06170f!important;border-color:#1f4a30!important}
.sun-offer-template-mini[data-template-mini="gourmet-hero"]{background:linear-gradient(90deg,#141311 0 55%,#3a352a 55% 58%,#0a0a09 58%)!important;border-color:#3a352a!important}
.sun-offer-template-mini[data-template-mini="diamond-gold"]{background:radial-gradient(circle at 75% 35%,#e0b95c 0 5%,transparent 6%),linear-gradient(135deg,#123d29,#071510)!important;border-color:#204b34!important}
.sun-offer-template-mini[data-template-mini="premium-dark"]{background:radial-gradient(circle at 18% 15%,rgba(224,165,69,.35),transparent 40%),linear-gradient(160deg,#12161d,#0a0d12)!important;border-color:#252b35!important}
.sun-offer-template-mini[data-template-mini="premium-emerald"]{background:radial-gradient(circle at 18% 15%,rgba(63,207,142,.35),transparent 40%),linear-gradient(160deg,#121d19,#0a1210)!important;border-color:#233830!important}
`;document.head.appendChild(style)}
if(document.readyState==='loading')document.addEventListener('DOMContentLoaded',installPreviewStyles,{once:true});else installPreviewStyles();
window.SunSignatureOfferPDFV18={VERSION,RELEASE,SIGNATURE_IDS:[...SIGNATURE_IDS],renderPages,renderCover,checks:()=>({version:VERSION,signature:SIGNATURE_IDS.length})};
})();