TABLES: wp_snippets
COL: id bigint(20)
COL: name tinytext
COL: description text
COL: code longtext
COL: tags longtext
COL: scope varchar(15)
COL: condition_id bigint(20)
COL: priority smallint(6)
COL: active tinyint(1)
COL: modified datetime
COL: revision bigint(20)
COL: cloud_id varchar(255)
COL: created_by bigint(20) unsigned
COL: updated_by bigint(20) unsigned
===== SNIPPET 362 : JF DS — Package PDF (Queue Item 6) active=1 scope=global =====
if (!defined('ABSPATH')) { exit; }
/* JF DS — Package PDF (Queue Item 6). Companion to snippet 360.
* No PDF library is installed on this site, so the PDF is hand-rolled in PHP:
* core Helvetica fonts, drawn design-stage letterhead (dark band, gold rule),
* JPEG embedding for the save's wall photos and its Paint previews (PNGs are
* converted with GD when available). No street address appears anywhere.
* Fidelity limit vs the local reportlab reference: core Helvetica only (no
* embedded fonts) and no PNG transparency — the white logo is flattened onto
* the dark band colour. */
class JfDsPkgPdf {
public $W = 612; public $H = 792; public $M = 54; public $contentW = 504; public $bottom = 738;
public $y = 110; public $content = ''; public $pages = array(); public $imgs = array(); public $imgDims = array();
public $nextImg = 6; public $pageOpen = false; public $logoImgName = '';
public function esc($s) { $s = (string) $s; $map = array('—' => '--', '–' => '-', '·' => '-', '×' => 'x', '•' => '-', '’' => "'", '‘' => "'", '“' => '"', '”' => '"', '…' => '...'); $s = strtr($s, $map); $s = preg_replace('/[^x20-x7E]/', '?', $s); return str_replace(array('\', '(', ')'), array('\\', '\(', '\)'), $s); }
public function twidth($s, $size, $bold) { $s = $this->esc($s); $w = 0; $n = strlen($s); for ($i = 0; $i < $n; $i++) { $c = $s[$i]; if ($c === 'i' || $c === 'l' || $c === '.' || $c === "'" || $c === ' ') { $w += 0.28; } elseif ($c === 'W' || $c === 'M') { $w += 0.85; } else { $w += 0.5; } } return $w * $size * ($bold ? 1.04 : 1); }
public function wrap($text, $size, $bold, $maxw) { $words = preg_split('/s+/', trim($text)); $lines = array(); $cur = ''; foreach ($words as $wd) { $try = $cur === '' ? $wd : $cur . ' ' . $wd; if ($this->twidth($try, $size, $bold) <= $maxw) { $cur = $try; } else { if ($cur !== '') { $lines[] = $cur; } $cur = $wd; } } if ($cur !== '') { $lines[] = $cur; } return $lines; }
public function ensure($need) { if ($this->y + $need > $this->bottom) { $this->newPage(); } }
public function rectTop($x, $yTop, $w, $h, $r, $g, $b) { $y = $this->H - $yTop - $h; $this->content .= $r . ' ' . $g . ' ' . $b . ' rg ' . $x . ' ' . $y . ' ' . $w . ' ' . $h . ' re f' . "n"; }
public function textAt($x, $yTop, $size, $font, $text, $r = 0.13, $g = 0.13, $b = 0.13) { $y = $this->H - $yTop; $this->content .= 'BT /' . $font . ' ' . $size . ' Tf ' . $r . ' ' . $g . ' ' . $b . ' rg 1 0 0 1 ' . $x . ' ' . $y . ' Tm (' . $this->esc($text) . ') Tj ET' . "n"; }
public function line($text, $size = 11, $bold = false, $lh = 1.45) { $this->ensure($size * $lh); $this->textAt($this->M, $this->y + $size, $size, $bold ? 'F2' : 'F1', $text); $this->y += $size * $lh; }
public function para($text, $size = 11, $bold = false) { foreach ($this->wrap($text, $size, $bold, $this->contentW) as $ln) { $this->line($ln, $size, $bold); } $this->y += 4; }
public function heading($text, $size = 15) { $this->y += 8; $this->ensure($size + 12); $this->textAt($this->M, $this->y + $size, $size, 'F2', $text); $this->y += $size + 8; $this->rectTop($this->M, $this->y, 60, 2, 0.79, 0.64, 0.15); $this->y += 8; }
public function amountRow($space, $item, $area, $gals, $bold = false) { $this->ensure(18); $this->textAt($this->M, $this->y + 11, 10, $bold ? 'F2' : 'F1', $space); $this->textAt(190, $this->y + 11, 10, $bold ? 'F2' : 'F1', $item); $this->textAt(360, $this->y + 11, 10, $bold ? 'F2' : 'F1', $area); $this->textAt(470, $this->y + 11, 10, $bold ? 'F2' : 'F1', $gals); $this->y += 18; }
public function addImage($data, $iw, $ih) { $id = $this->nextImg++; $name = 'Im' . $id; $this->imgs[$id] = array('name' => $name, 'w' => (int) $iw, 'h' => (int) $ih, 'data' => $data); $this->imgDims[$name] = array((int) $iw, (int) $ih); return $name; }
public function imageBlock($name, $maxW, $maxH, $caption = '') { if (!isset($this->imgDims[$name])) { return; } list($iw, $ih) = $this->imgDims[$name]; $scale = min($maxW / $iw, $maxH / $ih, 1); $dw = (int) ($iw * $scale); $dh = (int) ($ih * $scale); if ($dw < 10 || $dh < 10) { return; } $need = $dh + ($caption !== '' ? 18 : 0) + 8; $this->ensure($need); $x = $this->M + (int) (($this->contentW - $dw) / 2); $yBottom = $this->H - $this->y - $dh; $this->content .= 'q ' . $dw . ' 0 0 ' . $dh . ' ' . $x . ' ' . $yBottom . ' cm /' . $name . ' Do Q' . "n"; $this->y += $dh + 4; if ($caption !== '') { $this->line($caption, 9); } $this->y += 6; }
public function drawHeader() {
$this->rectTop(0, 0, 612, 84, 0.08, 0.08, 0.08);
$this->rectTop(0, 84, 612, 4, 0.79, 0.64, 0.15);
$tx = $this->M;
if ($this->logoImgName !== '' && isset($this->imgDims[$this->logoImgName])) {
list($iw, $ih) = $this->imgDims[$this->logoImgName];
$lh = 56; $lw = (int) ($lh * $iw / max(1, $ih)); if ($lw > 170) { $lw = 170; $lh = (int) (170 * $ih / max(1, $iw)); }
$ly = $this->H - (22 + $lh);
$this->content .= 'q ' . $lw . ' 0 0 ' . $lh . ' ' . $this->M . ' ' . $ly . ' cm /' . $this->logoImgName . ' Do Q' . "n";
$tx = $this->M + $lw + 16;
}
$this->textAt($tx, 40, 14, 'F2', 'FINEST REMODELING & CONSTRUCTION', 0.96, 0.96, 0.96);
$this->textAt($tx, 60, 11, 'F3', 'Building Dreams', 0.79, 0.64, 0.15);
$this->textAt($tx, 76, 9, 'F1', jf_ds_pkg_contact(), 0.96, 0.96, 0.96);
}
public function newPage() { if ($this->pageOpen) { $this->pages[] = array('content' => $this->content); } $this->content = ''; $this->pageOpen = true; $this->y = 110; $this->drawHeader(); }
public function output($filename) {
if ($this->pageOpen) { $this->pages[] = array('content' => $this->content); $this->pageOpen = false; }
$n = count($this->pages); if ($n < 1) { $n = 1; $this->pages[] = array('content' => ''); }
foreach ($this->pages as $i => &$pg) { $pg['content'] .= 'BT /F1 8 Tf 0.5 0.5 0.5 rg 1 0 0 1 ' . $this->M . ' 36 Tm (Page ' . ($i + 1) . ' of ' . $n . ') Tj ET' . "n"; }
unset($pg);
$base = 6 + count($this->imgs);
$objects = array();
$objects[1] = '<< /Type /Catalog /Pages 2 0 R >>';
$kids = array(); for ($i = 0; $i < $n; $i++) { $kids[] = ($base + 2 * $i) . ' 0 R'; }
$objects[2] = '<< /Type /Pages /Kids [' . implode(' ', $kids) . '] /Count ' . $n . ' >>';
$objects[3] = '<< /Type /Font /Subtype /Type1 /BaseFont /Helvetica >>';
$objects[4] = '<< /Type /Font /Subtype /Type1 /BaseFont /Helvetica-Bold >>';
$objects[5] = '<< /Type /Font /Subtype /Type1 /BaseFont /Helvetica-Oblique >>';
foreach ($this->imgs as $id => $im) { $objects[$id] = '<< /Type /XObject /Subtype /Image /Width ' . $im['w'] . ' /Height ' . $im['h'] . ' /ColorSpace /DeviceRGB /BitsPerComponent 8 /Filter /DCTDecode /Length ' . strlen($im['data']) . ' >>' . "n" . 'stream' . "n" . $im['data'] . "n" . 'endstream'; }
for ($i = 0; $i < $n; $i++) {
$xobj = ''; foreach ($this->imgs as $id => $im) { $xobj .= '/' . $im['name'] . ' ' . $id . ' 0 R '; }
$objects[$base + 2 * $i] = '<< /Type /Page /Parent 2 0 R /MediaBox [0 0 612 792] /Resources << /Font << /F1 3 0 R /F2 4 0 R /F3 5 0 R >> /XObject << ' . $xobj . '>> >> /Contents ' . ($base + 2 * $i + 1) . ' 0 R >>';
$c = $this->pages[$i]['content'];
$objects[$base + 2 * $i + 1] = '<< /Length ' . strlen($c) . ' >>' . "n" . 'stream' . "n" . $c . "n" . 'endstream';
}
$infoId = $base + 2 * $n;
$objects[$infoId] = '<< /Title (Your Finest design package) /Author (Finest Remodeling & Construction) /Creator (Finest Remodeling & Construction) >>';
$pdf = "%PDF-1.4n";
$offsets = array(); $max = max(array_keys($objects));
for ($id = 1; $id <= $max; $id++) { if (!isset($objects[$id])) { continue; } $offsets[$id] = strlen($pdf); $pdf .= $id . " 0 objn" . $objects[$id] . "nendobjn"; }
$xref = strlen($pdf);
$pdf .= "xrefn0 " . ($max + 1) . "n0000000000 65535 f n";
for ($id = 1; $id <= $max; $id++) { $pdf .= isset($offsets[$id]) ? sprintf("%010d 00000 n n", $offsets[$id]) : "0000000000 65535 f n"; }
$pdf .= "trailern<< /Size " . ($max + 1) . " /Root 1 0 R /Info " . $infoId . " 0 R >>nstartxrefn" . $xref . "n%%EOF";
while (ob_get_level()) { ob_end_clean(); }
header('Content-Type: application/pdf');
header('Content-Disposition: attachment; filename="' . $filename . '"');
header('Content-Length: ' . strlen($pdf));
echo $pdf;
exit;
}
}
function jf_ds_pkg_pdf_local_path($url) {
if (!$url) { return false; }
$id = attachment_url_to_postid($url);
if ($id) { $p = get_attached_file($id); if ($p && file_exists($p)) { return $p; } }
$up = wp_upload_dir(); $base = $up['basedir']; $baseurl = $up['baseurl'];
if ($baseurl && strpos($url, $baseurl) === 0) { $p = $base . substr($url, strlen($baseurl)); if (file_exists($p)) { return $p; } }
return false;
}
function jf_ds_pkg_pdf_img($pdf, $path, $bg = array(255, 255, 255)) {
if (!$path || !file_exists($path)) { return ''; }
$info = @getimagesize($path); if (!$info) { return ''; }
$data = false;
if ($info[2] === IMAGETYPE_JPEG) { $data = file_get_contents($path); }
elseif ($info[2] === IMAGETYPE_PNG && function_exists('imagecreatefrompng') && function_exists('imagejpeg')) {
$im = @imagecreatefrompng($path); if (!$im) { return ''; }
$w = imagesx($im); $h = imagesy($im);
$flat = imagecreatetruecolor($w, $h); $col = imagecolorallocate($flat, (int) $bg[0], (int) $bg[1], (int) $bg[2]); imagefill($flat, 0, 0, $col);
imagecopy($flat, $im, 0, 0, 0, 0, $w, $h); imagedestroy($im);
ob_start(); imagejpeg($flat, null, 90); $data = ob_get_clean(); imagedestroy($flat);
$info = array(0 => $w, 1 => $h);
}
if ($data === false || $data === '') { return ''; }
return $pdf->addImage($data, (int) $info[0], (int) $info[1]);
}
function jf_ds_pkg_render_pdf($rec) {
if (!function_exists('jf_ds_pkg_gold_line')) { status_header(503); echo 'The PDF download is being prepared.'; exit; }
$pdf = new JfDsPkgPdf();
$logo_path = jf_ds_pkg_pdf_local_path(jf_ds_pkg_logo());
if ($logo_path) { $ln = jf_ds_pkg_pdf_img($pdf, $logo_path, array(21, 21, 21)); if ($ln !== '') { $pdf->logoImgName = $ln; } }
$media = jf_ds_pkg_live_media($rec);
$amounts = jf_ds_pkg_amounts($rec['rooms'], isset($rec['door_count']) ? $rec['door_count'] : null, !empty($rec['doors_both_sides']), isset($rec['spaces']) ? $rec['spaces'] : array());
$is_paint = ($rec['project'] === 'Painting' || !empty($rec['selections']['Paint code']));
$pdf->newPage();
$pdf->heading('Finest Design Studio', 20);
$pdf->para('Your design package', 13);
$pdf->para(jf_ds_pkg_gold_line(), 11);
$pdf->para('Saved ' . $rec['time'] . (!empty($rec['name']) ? ' - For ' . $rec['name'] : '') . ' - Project: ' . $rec['project'], 11);
$pdf->heading('Your selections', 15);
foreach ((array) $rec['selections'] as $k => $v) { $pdf->para($k . ': ' . $v, 11); }
if (!empty($rec['rooms'])) {
$pdf->heading('Your rooms', 15);
foreach ((array) $rec['rooms'] as $r) {
$nm = !empty($r['name']) ? $r['name'] : ('Room ' . $r['index']);
$dim = ($r['length'] !== '' || $r['width'] !== '' || $r['height'] !== '') ? ($r['length'] . ' ft long x ' . $r['width'] . ' ft wide, ' . $r['height'] . ' ft high') : 'Dimensions not given';
$pdf->para($nm . ': ' . $dim . (empty($r['surfaces']) ? '' : ' - ' . implode(', ', $r['surfaces'])), 11);
}
}
if (!empty($rec['notes'])) { $pdf->heading('Notes', 15); $pdf->para($rec['notes'], 11); }
if (!empty($rec['designer_note'])) { $pdf->heading('Designer note', 15); $pdf->para($rec['designer_note'], 11); }
$mats = array();
foreach (array('Cabinet collection', 'Cabinet door / finish', 'Countertop / slab', 'Tile', 'Paint code', 'Paint color path', 'Paint scope', 'Wall finish', 'Ceiling finish', 'Trim finish') as $mk) { if (!empty($rec['selections'][$mk])) { $mats[$mk] = $rec['selections'][$mk]; } }
if ($mats || !empty($rec['standards'])) {
$pdf->heading('Materials', 15);
foreach ($mats as $k => $v) { $pdf->para($k . ': ' . $v, 11); }
if (!empty($rec['standards'])) { $pdf->para($rec['standards'], 11); }
}
if ($is_paint && $amounts) {
$pdf->heading('Approximate amounts', 15);
$pdf->para('Approximate - figured at 350 sq ft per gallon per coat, 2 coats, rounded up to whole gallons.', 11);
$pdf->amountRow('Space', 'Item', 'Area', 'Paint', true);
$door_notes = array();
foreach ($amounts as $a) { if (!empty($a['note'])) { $door_notes[] = $a['note']; } $pdf->amountRow($a['space'], $a['item'], number_format($a['sqft'], 0) . ' sq ft', $a['gallons'] . ' gal'); }
$pdf->para('Final quantities are confirmed at field measure on your written proposal.', 11);
foreach (array_unique($door_notes) as $dn) { $pdf->para($dn, 11); }
}
if (!empty($rec['dossier_line'])) { $pdf->heading('Paint product dossier', 15); $pdf->para($rec['dossier_line'], 11); $pdf->para('The exact paint line is confirmed on your written proposal.', 11); }
if (!empty($rec['photos'])) {
$pdf->heading('Your photos', 15);
foreach ((array) $rec['photos'] as $label => $url) {
$path = jf_ds_pkg_pdf_local_path($url);
$nm = $path ? jf_ds_pkg_pdf_img($pdf, $path) : '';
if ($nm !== '') { $pdf->imageBlock($nm, 252, 252, $label); } else { $pdf->para($label . ' - view it on your package page.', 11); }
}
}
if (!empty($media['slides'])) {
$pdf->heading('Your paint previews', 15);
foreach ($media['slides'] as $s) {
if (!empty($s['orig_url'])) { $p = jf_ds_pkg_pdf_local_path($s['orig_url']); $nm = $p ? jf_ds_pkg_pdf_img($pdf, $p) : ''; if ($nm !== '') { $pdf->imageBlock($nm, 252, 252, 'Your wall - ' . $s['label'] . ' (before)'); } }
if (!empty($s['prev_url'])) { $p = jf_ds_pkg_pdf_local_path($s['prev_url']); $nm = $p ? jf_ds_pkg_pdf_img($pdf, $p) : ''; if ($nm !== '') { $pdf->imageBlock($nm, 252, 252, 'Paint preview - ' . $s['label'] . ' (after)'); } }
}
}
$pdf->heading('Next steps', 15);
$pdf->para('1. Check your selections and room details above - tell us if anything needs changing.', 11);
$pdf->para('2. We will call you to confirm your design and arrange your field measure.', 11);
$pdf->para('3. Your written proposal confirms final quantities and the exact paint line.', 11);
$pdf->para(jf_ds_pkg_warranty(), 11);
$pdf->para('With thanks,', 11);
$pdf->para('Finest Remodeling & Construction', 11, true);
$pdf->para('Building Dreams', 11);
$pdf->para(jf_ds_pkg_contact(), 11);
$pdf->para(jf_ds_pkg_privacy(), 11);
$pdf->output('Finest-design-package.pdf');
}
===== SNIPPET 372 : JF DS — Package Auto-Delivery (Queue Item 6) active=1 scope=global =====
if (!defined('ABSPATH')) { exit; }
/* JF DS — Package Auto-Delivery (Queue Item 6).
* Automatic per-save delivery: private token package page, real-PDF download
* (renderer lives in the companion snippet "JF DS — Package PDF (Queue Item 6)"),
* and a save-confirmation email to the customer.
* Hooks update_option_jf_ds_last_send at priority 20 — after the installer hook
* in snippet 137 — and template_redirect for the package route.
* New options: jf_ds_pkg_packages, jf_ds_pkg_email_log, jf_ds_pkg_log.
* Does NOT edit snippets 110/135/137/250/251, page 448, the portal or the calculator.
* Preview pairing: finished previews are ds_preview_ids, originals are
* ds_photo_ids, paired by index. ds_derivative_ids is deliberately NOT used:
* it holds flat upright derivatives of the originals (intermediate processing),
* not the finished previews. */
function jf_ds_pkg_gold_line() { return 'An original design document — created in-house by Finest Remodeling & Construction.'; }
function jf_ds_pkg_warranty() { return 'Workmanship warranty: 1-year limited. Materials under the manufacturer's warranty.'; }
function jf_ds_pkg_privacy() { return 'Your private link opens only your package. Finest never asks for or stores card or bank information — you only tell us how you paid.'; }
function jf_ds_pkg_contact() { return '(832) 472-9735 · FinestRemodeling@gmail.com · jfremodel.com'; }
function jf_ds_pkg_logo() { return 'https://jfremodel.com/wp-content/uploads/2026/11/finest-logo-original.png'; }
function jf_ds_pkg_url($token) { return home_url('/?jf_ds_package=' . rawurlencode($token)); }
function jf_ds_pkg_pdf_url($token) { return home_url('/?jf_ds_package=' . rawurlencode($token) . '&jf_ds_pdf=1'); }
function jf_ds_pkg_log($msg) { $l = get_option('jf_ds_pkg_log', array()); if (!is_array($l)) { $l = array(); } $l[] = array('time' => current_time('mysql'), 'msg' => substr($msg, 0, 500)); if (count($l) > 50) { $l = array_slice($l, -50); } update_option('jf_ds_pkg_log', $l); }
function jf_ds_pkg_parse_dims($d) {
if (is_array($d)) { $L = isset($d['length']) ? (float) $d['length'] : 0; $W = isset($d['width']) ? (float) $d['width'] : 0; $H = isset($d['height']) ? (float) $d['height'] : 0; if ($L > 0 && $W > 0 && $H > 0) { return array($L, $W, $H); } return false; }
if (is_string($d) && preg_match('/(d+(?:.d+)?)s*xs*(d+(?:.d+)?)s*xs*(d+(?:.d+)?)/i', $d, $m)) { return array((float) $m[1], (float) $m[2], (float) $m[3]); }
return false;
}
function jf_ds_pkg_norm_spaces($raw) {
$out = array();
if (!is_array($raw)) { return $out; }
foreach (array_slice($raw, 0, 6) as $entry) {
if (!is_array($entry)) { continue; }
$nm = isset($entry['name']) ? sanitize_text_field($entry['name']) : '';
if ($nm === '') { $nm = 'Room ' . (count($out) + 1); }
$dm = isset($entry['dimensions']) ? $entry['dimensions'] : '';
if (is_array($dm)) { $p = jf_ds_pkg_parse_dims($dm); $dm = $p ? ($p[0] . ' x ' . $p[1] . ' x ' . $p[2] . ' ft') : 'not provided'; } else { $dm = sanitize_text_field((string) $dm); if ($dm === '') { $dm = 'not provided'; } }
$out[] = array('name' => $nm, 'dimensions' => $dm);
}
return $out;
}
function jf_ds_pkg_amounts($rooms, $door_count = null, $doors_both_sides = false, $spaces = array()) {
$out = array();
$dc = ($door_count !== null && $door_count !== '' && is_numeric($door_count)) ? (int) $door_count : null;
$both = ($doors_both_sides === true || $doors_both_sides === 1 || $doors_both_sides === '1' || $doors_both_sides === 'true');
foreach ((array) $rooms as $r) {
$L = (float) $r['length']; $W = (float) $r['width']; $H = (float) $r['height'];
if (!($L > 0 && $W > 0 && $H > 0)) { continue; }
$label = !empty($r['name']) ? $r['name'] : ('Room ' . $r['index']);
$sur = isset($r['surfaces']) && is_array($r['surfaces']) ? $r['surfaces'] : array();
if (empty($sur) || in_array('Walls', $sur, true)) { $sq = 2 * ($L + $W) * $H; $out[] = array('space' => $label, 'item' => 'Walls', 'sqft' => $sq, 'gallons' => (int) ceil($sq / 350 * 2)); }
if (empty($sur) || in_array('Ceiling', $sur, true)) { $sq = $L * $W; $out[] = array('space' => $label, 'item' => 'Ceiling', 'sqft' => $sq, 'gallons' => (int) ceil($sq / 350 * 2)); }
if (!empty(array_intersect($sur, array('Doors', 'Trim', 'Baseboards', 'Crown molding')))) {
$sq = 42; $note = 'Doors figured for the room side — both sides available on request.';
if ($dc !== null && $dc > 0 && in_array('Doors', $sur, true)) {
$sq = $dc * 21 * ($both ? 2 : 1);
$note = $both ? ('Doors figured for ' . $dc . ($dc == 1 ? ' door' : ' doors') . ' — both sides.') : ('Doors figured for ' . $dc . ($dc == 1 ? ' door' : ' doors') . ' — room side only; both sides available on request.');
}
$out[] = array('space' => $label, 'item' => 'Doors, trim and baseboards', 'sqft' => $sq, 'gallons' => (int) ceil($sq / 350 * 2), 'note' => $note);
}
}
$main_sur = array();
foreach ((array) $rooms as $r) { if (isset($r['surfaces']) && is_array($r['surfaces'])) { $main_sur = $r['surfaces']; break; } }
$sp_list = is_array($spaces) ? $spaces : array();
foreach (array_slice($sp_list, 1) as $sp) {
if (!is_array($sp) || empty($sp['name'])) { continue; }
$dims = jf_ds_pkg_parse_dims(isset($sp['dimensions']) ? $sp['dimensions'] : '');
if ($dims === false) { continue; }
list($L, $W, $H) = $dims;
$label = $sp['name'];
if (empty($main_sur) || in_array('Walls', $main_sur, true)) { $sq = 2 * ($L + $W) * $H; $out[] = array('space' => $label, 'item' => 'Walls', 'sqft' => $sq, 'gallons' => (int) ceil($sq / 350 * 2)); }
if (empty($main_sur) || in_array('Ceiling', $main_sur, true)) { $sq = $L * $W; $out[] = array('space' => $label, 'item' => 'Ceiling', 'sqft' => $sq, 'gallons' => (int) ceil($sq / 350 * 2)); }
}
return $out;
}
function jf_ds_pkg_meta_ids($post_id, $key) { $v = get_post_meta($post_id, $key, true); if (is_array($v)) { return array_values(array_filter(array_map('intval', $v))); } if (is_numeric($v)) { return array((int) $v); } return array(); }
function jf_ds_pkg_live_media($rec) {
$prev_ids = array(); $orig_ids = array(); $status = ''; $orig_rid = 0;
$saved = !empty($rec['saved_post_id']) ? (int) $rec['saved_post_id'] : 0;
if ($saved) {
$prev_ids = array_merge($prev_ids, jf_ds_pkg_meta_ids($saved, 'ds_preview_ids'));
$orig_ids = array_merge($orig_ids, jf_ds_pkg_meta_ids($saved, 'ds_photo_ids'));
}
if (!empty($rec['prev_token'])) {
$q = get_posts(array('post_type' => 'ds_request', 'post_status' => 'any', 'posts_per_page' => 1, 'meta_query' => array(array('key' => 'ds_token', 'value' => $rec['prev_token'])), 'fields' => 'ids'));
if ($q) { $orig_rid = (int) $q[0]; $st = get_post_meta($orig_rid, 'ds_status', true); if (is_string($st)) { $status = $st; }
$prev_ids = array_merge($prev_ids, jf_ds_pkg_meta_ids($orig_rid, 'ds_preview_ids'));
$orig_ids = array_merge($orig_ids, jf_ds_pkg_meta_ids($orig_rid, 'ds_photo_ids'));
}
}
$prev_ids = array_values(array_unique(array_filter(array_map('intval', $prev_ids))));
$orig_ids = array_values(array_unique(array_filter(array_map('intval', $orig_ids))));
$photo_ids = !empty($rec['photo_ids']) && is_array($rec['photo_ids']) ? array_values(array_map('intval', $rec['photo_ids'])) : array();
if ($orig_ids) { $orig_ids = array_values(array_unique(array_merge($orig_ids, $photo_ids))); } else { $orig_ids = $photo_ids; }
$labels = array('Back wall', 'Left wall', 'Right wall', 'Wall');
$slides = array();
foreach ($prev_ids as $i => $pid) {
$oid = isset($orig_ids[$i]) ? (int) $orig_ids[$i] : 0;
$slides[] = array('label' => isset($labels[$i]) ? $labels[$i] : ('Wall ' . ($i + 1)), 'orig_id' => $oid, 'orig_url' => $oid ? wp_get_attachment_url($oid) : '', 'prev_id' => $pid, 'prev_url' => wp_get_attachment_url($pid));
}
return array('slides' => $slides, 'status' => $status, 'orig_rid' => $orig_rid, 'preview_ids' => $prev_ids, 'orig_ids' => $orig_ids, 'photo_ids' => $photo_ids);
}
function jf_ds_pkg_build_record($new) {
$t = function ($k) { return isset($_POST[$k]) ? sanitize_text_field(wp_unslash($_POST[$k])) : ''; };
$name = $t('ds_name'); $phone = $t('ds_phone'); $email = isset($_POST['ds_email']) ? sanitize_email(wp_unslash($_POST['ds_email'])) : '';
$project = $t('ds_project'); $cabinet = $t('ds_cabinet'); $door = $t('ds_door'); $slab = $t('ds_slab'); $tile = $t('ds_tile');
$paint_code = $t('ds_paint'); $paint_path = $t('ds_paint_path'); $paint_scope = $t('ds_paint_scope');
$wall_finish = $t('ds_wall_finish'); $ceiling_finish = $t('ds_ceiling_finish');
$trim_finish = $t('ds_trim_finish'); if (!in_array($trim_finish, array('Satin', 'Semi-Gloss', 'Gloss', 'High Gloss'), true)) { $trim_finish = ''; }
$door_count_raw = $t('ds_door_count'); $door_count = ($door_count_raw !== '' && is_numeric($door_count_raw)) ? (int) $door_count_raw : null;
$doors_both = isset($_POST['ds_doors_both_sides']) && in_array(wp_unslash($_POST['ds_doors_both_sides']), array('1', 'true', 'yes', 'on'), true);
$spaces = array();
if (isset($_POST['spaces']) && is_array($_POST['spaces'])) { $spaces = jf_ds_pkg_norm_spaces(wp_unslash($_POST['spaces'])); } else {
$dimf = function ($lk, $wk, $hk) use ($t) { $a = $t($lk); $b = $t($wk); $c = $t($hk); if ($a === '' || $b === '' || $c === '') { return 'not provided'; } return $a . ' x ' . $b . ' x ' . $c . ' ft'; };
$main_name = $t('ds_room1_name'); if ($main_name === '') { $main_name = 'Room 1'; }
$spaces[] = array('name' => $main_name, 'dimensions' => $dimf('ds_room1_length', 'ds_room1_width', 'ds_room1_height'));
if ($t('ds_closet_included') === '1' || $dimf('ds_closet_length', 'ds_closet_width', 'ds_closet_height') !== 'not provided') { $spaces[] = array('name' => 'Closet', 'dimensions' => $dimf('ds_closet_length', 'ds_closet_width', 'ds_closet_height')); }
if ($t('ds_attached_bath_included') === '1' || $dimf('ds_attached_bath_length', 'ds_attached_bath_width', 'ds_attached_bath_height') !== 'not provided') { $spaces[] = array('name' => 'Attached bathroom', 'dimensions' => $dimf('ds_attached_bath_length', 'ds_attached_bath_width', 'ds_attached_bath_height')); }
}
$measure = $t('ds_measure'); $notes = isset($_POST['ds_notes']) ? sanitize_textarea_field(wp_unslash($_POST['ds_notes'])) : '';
$installer = $t('ds_installer'); if ($installer === '' && isset($_GET['installer'])) { $installer = sanitize_text_field(wp_unslash($_GET['installer'])); }
$sel = array(); $sel['Project type'] = $project;
if ($cabinet !== '') { $sel['Cabinet collection'] = $cabinet; }
if ($door !== '') { $sel['Cabinet door / finish'] = $door; }
if ($slab !== '') { $sel['Countertop / slab'] = $slab; }
if ($tile !== '') { $sel['Tile'] = $tile; }
if ($paint_code !== '') { $sel['Paint code'] = $paint_code; }
if ($paint_path !== '') { $sel['Paint color path'] = $paint_path; }
if ($paint_scope !== '') { $sel['Paint scope'] = $paint_scope; }
if ($wall_finish !== '') { $sel['Wall finish'] = $wall_finish; }
if ($ceiling_finish !== '') { $sel['Ceiling finish'] = $ceiling_finish; }
if ($trim_finish !== '') { $sel['Trim finish'] = $trim_finish; }
if ($door_count !== null && $door_count > 0) { $sel['Doors'] = $door_count . ($door_count == 1 ? ' door' : ' doors') . ' — ' . ($doors_both ? 'both sides' : 'one side (room side)'); }
if ($measure !== '') { $sel['Measured by'] = $measure; }
$rooms = array(); $surface_options = array('Walls', 'Ceiling', 'Doors', 'Trim', 'Baseboards', 'Crown molding');
for ($i = 1; $i <= 6; $i++) {
$rn = $t('ds_room' . $i . '_name'); $rl = $t('ds_room' . $i . '_length'); $rw = $t('ds_room' . $i . '_width'); $rh = $t('ds_room' . $i . '_height');
$sur = array(); $sk = 'ds_room' . $i . '_surfaces';
if (isset($_POST[$sk]) && is_array($_POST[$sk])) { foreach (wp_unslash($_POST[$sk]) as $sv) { $sv = sanitize_text_field($sv); if (in_array($sv, $surface_options, true)) { $sur[] = $sv; } } }
if ($rn === '' && $rl === '' && $rw === '' && $rh === '' && empty($sur)) { continue; }
$rooms[] = array('name' => $rn, 'length' => $rl, 'width' => $rw, 'height' => $rh, 'surfaces' => $sur, 'index' => $i);
}
$standards = '';
if (is_array($new) && isset($new['lines']) && is_string($new['lines'])) { foreach (explode("n", $new['lines']) as $ln) { if (strpos($ln, 'Standard kitchen parts') === 0) { $standards = $ln; break; } } }
$saved_id = 0; $token = ''; $prev_token = '';
$posts = get_posts(array('post_type' => 'ds_request', 'post_status' => 'any', 'posts_per_page' => 3, 'orderby' => 'date', 'order' => 'DESC', 'meta_query' => array(array('key' => 'ds_status', 'value' => 'saved')), 'fields' => 'ids'));
if ($posts) { $saved_id = (int) $posts[0]; $tk = get_post_meta($saved_id, 'ds_token', true); if (is_string($tk) && preg_match('/^[A-Za-z0-9]{32}$/', $tk)) { $token = $tk; } $pt = get_post_meta($saved_id, 'ds_prev_token', true); if (is_string($pt)) { $prev_token = $pt; } }
if ($token === '') { $token = bin2hex(random_bytes(16)); }
$photos = (is_array($new) && isset($new['photos']) && is_array($new['photos'])) ? $new['photos'] : array();
$photo_ids = (is_array($new) && isset($new['photo_ids']) && is_array($new['photo_ids'])) ? array_values(array_map('intval', $new['photo_ids'])) : array();
$designer_note = $saved_id ? get_post_meta($saved_id, 'ds_designer_note', true) : '';
$dossier_line = $saved_id ? get_post_meta($saved_id, 'ds_dossier_line', true) : '';
if ($saved_id) { $saved_sel = get_post_meta($saved_id, 'ds_selections', true); if (is_array($saved_sel)) { if (!empty($saved_sel['doors_both_sides'])) { $doors_both = true; } if ($trim_finish === '' && !empty($saved_sel['trim_finish'])) { $trim_finish = sanitize_text_field($saved_sel['trim_finish']); $sel['Trim finish'] = $trim_finish; } if ($door_count === null && !empty($saved_sel['door_count']) && is_numeric($saved_sel['door_count'])) { $door_count = (int) $saved_sel['door_count']; } if ($door_count !== null && $door_count > 0) { $sel['Doors'] = $door_count . ($door_count == 1 ? ' door' : ' doors') . ' — ' . ($doors_both ? 'both sides' : 'one side (room side)'); } if (empty($spaces) || (count($spaces) === 1 && isset($spaces[0]['dimensions']) && $spaces[0]['dimensions'] === 'not provided')) { $spaces = jf_ds_pkg_norm_spaces(isset($saved_sel['spaces']) ? $saved_sel['spaces'] : array()); } } }
return array('token' => $token, 'created' => current_time('mysql'), 'time' => isset($new['time']) ? $new['time'] : current_time('mysql'), 'name' => $name, 'phone' => $phone, 'email' => $email, 'project' => $project, 'installer' => $installer, 'selections' => $sel, 'rooms' => $rooms, 'door_count' => $door_count, 'doors_both_sides' => $doors_both, 'spaces' => $spaces, 'notes' => $notes, 'standards' => $standards, 'photos' => $photos, 'photo_ids' => $photo_ids, 'saved_post_id' => $saved_id, 'prev_token' => $prev_token, 'designer_note' => is_string($designer_note) ? $designer_note : '', 'dossier_line' => is_string($dossier_line) ? $dossier_line : '', 'email_sent' => false, 'email_result' => 'not attempted');
}
function jf_ds_pkg_send_email($rec) {
$url = jf_ds_pkg_url($rec['token']);
$facts = '';
foreach ((array) $rec['selections'] as $k => $v) { $facts .= '<li><strong>' . esc_html($k) . ':</strong> ' . esc_html($v) . '</li>'; }
$rooms_html = '';
foreach ((array) $rec['rooms'] as $r) { $rooms_html .= '<li><strong>' . esc_html(!empty($r['name']) ? $r['name'] : ('Room ' . $r['index'])) . ':</strong> ' . esc_html($r['length'] . ' ft x ' . $r['width'] . ' ft, ' . $r['height'] . ' ft high') . (empty($r['surfaces']) ? '' : ' — ' . esc_html(implode(', ', $r['surfaces']))) . '</li>'; }
if ($rooms_html !== '') { $facts .= '<li><strong>Rooms:</strong><ul>' . $rooms_html . '</ul></li>'; }
$body = '<div style="background:#151515;color:#f4f4f6;border-bottom:4px solid #c9a227;padding:20px 24px;font-family:Georgia,serif">' . '<div style="font-size:20px;letter-spacing:2px">FINEST REMODELING & CONSTRUCTION</div>' . '<div style="color:#c9a227;font-style:italic;margin-top:4px">Building Dreams</div>' . '<div style="font-size:13px;margin-top:8px">' . esc_html(jf_ds_pkg_contact()) . '</div></div>' . '<div style="font-family:Georgia,serif;color:#222;padding:8px 4px">' . '<p>Dear ' . esc_html($rec['name']) . ',</p>' . '<p>Thank you for designing with Finest Remodeling & Construction. Your design package is ready — it brings together your selections, your room details and your next steps in one private place.</p>' . '<h3 style="font-family:Georgia,serif">Your design at a glance</h3>' . '<ul>' . $facts . '</ul>' . '<p><a href="' . esc_url($url) . '" style="display:inline-block;background:#151515;color:#fff;padding:12px 20px;text-decoration:none">Open my design package</a></p>' . '<p>Your package page also has a “Download my package (PDF)” button if you would like a copy to keep.</p>' . '<p>With thanks,<br>Finest Remodeling & Construction<br><em>Building Dreams</em><br>' . esc_html(jf_ds_pkg_contact()) . '</p>' . '<p>' . esc_html(jf_ds_pkg_privacy()) . '</p></div>';
$headers = array('From: Finest Remodeling & Construction <FinestRemodeling@gmail.com>', 'Content-Type: text/html; charset=UTF-8');
$sent = wp_mail($rec['email'], 'Your Finest design package', $body, $headers);
$log = get_option('jf_ds_pkg_email_log', array()); if (!is_array($log)) { $log = array(); }
$log[] = array('time' => current_time('mysql'), 'token' => $rec['token'], 'to' => $rec['email'], 'subject' => 'Your Finest design package', 'result' => ($sent ? 'sent' : 'failed'), 'url' => $url, 'body' => $body);
if (count($log) > 100) { $log = array_slice($log, -100); }
update_option('jf_ds_pkg_email_log', $log);
return $sent;
}
function jf_ds_pkg_on_last_send($old, $new, $option) {
try {
if (!is_array($new) || empty($new['time']) || empty($new['name'])) { return; }
$pkgs = get_option('jf_ds_pkg_packages', array()); if (!is_array($pkgs)) { $pkgs = array(); }
foreach ($pkgs as $ex) { if (is_array($ex) && !empty($ex['time']) && $ex['time'] === $new['time'] && isset($ex['name']) && $ex['name'] === $new['name']) { return; } }
$rec = jf_ds_pkg_build_record($new);
$pkgs[$rec['token']] = $rec;
update_option('jf_ds_pkg_packages', $pkgs);
$inst = get_option('jf_ds_installer_saves', array());
if (is_array($inst)) {
$changed = false;
foreach ($inst as $idx => $sv) { if (is_array($sv) && isset($sv['time']) && $sv['time'] === $new['time'] && empty($sv['package_url'])) { $inst[$idx]['package_url'] = jf_ds_pkg_url($rec['token']); $inst[$idx]['package_status'] = 'Ready'; $changed = true; } }
if (!$changed) { $keys = array_keys($inst); for ($k = count($keys) - 1; $k >= 0; $k--) { $idx = $keys[$k]; $sv = $inst[$idx]; if (is_array($sv) && !empty($sv['name']) && $sv['name'] === $rec['name'] && empty($sv['package_url'])) { $inst[$idx]['package_url'] = jf_ds_pkg_url($rec['token']); $inst[$idx]['package_status'] = 'Ready'; $changed = true; break; } } }
if ($changed) { update_option('jf_ds_installer_saves', $inst); }
}
if (!empty($rec['email']) && is_email($rec['email'])) {
$sent = jf_ds_pkg_send_email($rec);
$pkgs = get_option('jf_ds_pkg_packages', array());
if (isset($pkgs[$rec['token']])) { $pkgs[$rec['token']]['email_sent'] = (bool) $sent; $pkgs[$rec['token']]['email_result'] = $sent ? 'sent' : 'failed'; update_option('jf_ds_pkg_packages', $pkgs); }
}
} catch (Throwable $e) { jf_ds_pkg_log('on_last_send failed: ' . $e->getMessage()); }
}
add_action('update_option_jf_ds_last_send', 'jf_ds_pkg_on_last_send', 20, 3);
function jf_ds_pkg_record_from_post($pid) {
$token = get_post_meta($pid, 'ds_token', true); if (!is_string($token) || !preg_match('/^[A-Za-z0-9]{32}$/', $token)) { return false; }
$sel = get_post_meta($pid, 'ds_selections', true); if (!is_array($sel)) { $sel = array(); }
$map = array('cabinet' => 'Cabinet collection', 'door' => 'Cabinet door / finish', 'slab' => 'Countertop / slab', 'tile' => 'Tile', 'paint' => 'Paint code', 'paint_path' => 'Paint color path', 'scope' => 'Paint scope', 'wall_finish' => 'Wall finish', 'ceiling_finish' => 'Ceiling finish', 'trim_finish' => 'Trim finish');
$selections = array('Project type' => get_post_meta($pid, 'ds_tab', true));
foreach ($map as $k => $label) { if (!empty($sel[$k])) { $selections[$label] = $sel[$k]; } }
$door_count = (!empty($sel['door_count']) && is_numeric($sel['door_count'])) ? (int) $sel['door_count'] : null;
$doors_both = !empty($sel['doors_both_sides']);
if ($door_count !== null && $door_count > 0) { $selections['Doors'] = $door_count . ($door_count == 1 ? ' door' : ' doors') . ' — ' . ($doors_both ? 'both sides' : 'one side (room side)'); }
$spaces = jf_ds_pkg_norm_spaces(isset($sel['spaces']) ? $sel['spaces'] : array());
$photo_ids = jf_ds_pkg_meta_ids($pid, 'ds_photo_ids'); $photos = array();
foreach ($photo_ids as $id) { $u = wp_get_attachment_url($id); if ($u) { $photos['Photo ' . $id] = $u; } }
$dn = get_post_meta($pid, 'ds_designer_note', true); $dl = get_post_meta($pid, 'ds_dossier_line', true);
return array('token' => $token, 'created' => get_the_date('Y-m-d H:i:s', $pid), 'time' => get_the_date('Y-m-d H:i:s', $pid), 'name' => '', 'phone' => '', 'email' => '', 'project' => get_post_meta($pid, 'ds_tab', true), 'installer' => '', 'selections' => $selections, 'rooms' => array(), 'door_count' => $door_count, 'doors_both_sides' => $doors_both, 'spaces' => $spaces, 'notes' => '', 'standards' => '', 'photos' => $photos, 'photo_ids' => $photo_ids, 'saved_post_id' => (int) $pid, 'prev_token' => get_post_meta($pid, 'ds_prev_token', true), 'designer_note' => is_string($dn) ? $dn : '', 'dossier_line' => is_string($dl) ? $dl : '', 'email_sent' => false, 'email_result' => 'not attempted', 'partial' => true);
}
function jf_ds_pkg_get($token) {
$pkgs = get_option('jf_ds_pkg_packages', array());
if (is_array($pkgs) && isset($pkgs[$token]) && is_array($pkgs[$token])) { return $pkgs[$token]; }
$q = get_posts(array('post_type' => 'ds_request', 'post_status' => 'any', 'posts_per_page' => 1, 'meta_query' => array(array('key' => 'ds_token', 'value' => $token), array('key' => 'ds_status', 'value' => 'saved')), 'fields' => 'ids'));
if ($q) { $rec = jf_ds_pkg_record_from_post((int) $q[0]); if ($rec) { return $rec; } }
return false;
}
function jf_ds_pkg_not_found() {
status_header(404); nocache_headers(); header('X-Robots-Tag: noindex, nofollow');
echo '<!DOCTYPE html><html lang="en-US"><head><meta charset="utf-8"><title>Package not found — Finest Remodeling & Construction</title></head><body style="font-family:Georgia,serif;margin:0">' . '<div style="background:#151515;color:#f4f4f6;border-bottom:4px solid #c9a227;padding:20px 24px"><div style="font-size:20px;letter-spacing:2px">FINEST REMODELING & CONSTRUCTION</div><div style="color:#c9a227;font-style:italic">Building Dreams</div><div style="font-size:13px;margin-top:6px">' . esc_html(jf_ds_pkg_contact()) . '</div></div>' . '<div style="padding:28px"><h1>Your design package</h1><p>This package link is not valid. If you saved a design, please use the link from your confirmation email, or call us and we will help.</p><p>' . esc_html(jf_ds_pkg_privacy()) . '</p></div></body></html>';
exit;
}
add_action('template_redirect', 'jf_ds_pkg_route');
function jf_ds_pkg_route() {
if (empty($_GET['jf_ds_package'])) { return; }
$token = sanitize_text_field(wp_unslash($_GET['jf_ds_package']));
if (!preg_match('/^[A-Za-z0-9]{32}$/', $token)) { jf_ds_pkg_not_found(); }
$rec = jf_ds_pkg_get($token);
if (!$rec) { jf_ds_pkg_not_found(); }
if (!empty($_GET['jf_ds_pdf'])) {
if (function_exists('jf_ds_pkg_render_pdf')) { jf_ds_pkg_render_pdf($rec); exit; }
status_header(503); echo 'The PDF download is being prepared. Please use the package page for now.'; exit;
}
nocache_headers(); header('X-Robots-Tag: noindex, nofollow');
echo jf_ds_pkg_render_page($rec);
exit;
}
function jf_ds_pkg_render_page($rec) {
$media = jf_ds_pkg_live_media($rec);
$slides = $media['slides'];
$amounts = jf_ds_pkg_amounts($rec['rooms'], isset($rec['door_count']) ? $rec['door_count'] : null, !empty($rec['doors_both_sides']), isset($rec['spaces']) ? $rec['spaces'] : array());
$is_paint = ($rec['project'] === 'Painting' || !empty($rec['selections']['Paint code']));
$css = <<<'CSS'
body{margin:0;font-family:Georgia,serif;color:#222;background:#fff}
.jf-pkg-head{background:#151515;color:#f4f4f6;border-bottom:4px solid #c9a227;padding:22px 28px}
.jf-pkg-head img{height:64px;width:auto;display:block}
.jf-pkg-co{font-size:22px;letter-spacing:2px;margin-top:10px}
.jf-pkg-tag{color:#c9a227;font-style:italic;margin-top:4px}
.jf-pkg-contact{font-size:13px;margin-top:8px}
.jf-pkg-main{max-width:860px;margin:0 auto;padding:28px 24px 60px}
.jf-pkg-sub{font-size:20px;color:#555;margin-top:-10px}
.jf-pkg-meta{color:#666;font-size:14px}
.jf-pkg-gold{font-style:italic;border-left:4px solid #c9a227;padding-left:12px}
.jf-pkg-btn{display:inline-block;background:#151515;color:#fff;padding:12px 20px;text-decoration:none;border-radius:4px}
.jf-pkg-facts dt{font-weight:bold}
.jf-pkg-facts dd{margin:0 0 8px}
table.jf-pkg-amt{border-collapse:collapse;width:100%}
table.jf-pkg-amt th,table.jf-pkg-amt td{border:1px solid #ccc;padding:6px 8px;text-align:left}
.jf-pkg-photos{display:grid;grid-template-columns:repeat(auto-fill,minmax(240px,1fr));gap:16px}
.jf-pkg-photos img{width:100%;height:auto;display:block}
.jf-pkg-stage{position:relative;overflow:hidden;background:#111;height:480px;border:1px solid #ccc}
.jf-pkg-stage img{width:100%;height:100%;object-fit:contain}
.jf-pkg-thumbs{display:flex;gap:8px;margin-top:10px;flex-wrap:wrap}
.jf-pkg-thumb{border:2px solid transparent;padding:0;cursor:pointer;background:none}
.jf-pkg-thumb.active{border-color:#c9a227}
.jf-pkg-thumb img{width:90px;height:64px;object-fit:cover;display:block}
.jf-pkg-controls{display:flex;gap:8px;flex-wrap:wrap;margin-top:10px}
.jf-pkg-controls button{padding:8px 12px;cursor:pointer}
.jf-pkg-privacy{border-top:1px solid #ccc;padding-top:14px;color:#555}
.jf-pkg-sig{margin-top:20px}
h2{margin-top:32px}
CSS;
$h = '<!DOCTYPE html><html lang="en-US"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width, initial-scale=1"><meta name="robots" content="noindex, nofollow"><title>Your Finest design package — Finest Remodeling & Construction</title><style>' . $css . '</style></head><body id="jf-pkg-page" data-preview-status="' . esc_attr($media['status']) . '">';
$h .= '<header class="jf-pkg-head"><img src="' . esc_url(jf_ds_pkg_logo()) . '" alt="Finest Remodeling & Construction"><div class="jf-pkg-co">FINEST REMODELING & CONSTRUCTION</div><div class="jf-pkg-tag">Building Dreams</div><div class="jf-pkg-contact">' . esc_html(jf_ds_pkg_contact()) . '</div></header>';
$h .= '<main class="jf-pkg-main">';
$h .= '<h1>Finest Design Studio</h1><p class="jf-pkg-sub">Your design package</p>';
$h .= '<p class="jf-pkg-meta">Saved ' . esc_html($rec['time']) . (!empty($rec['name']) ? ' · For ' . esc_html($rec['name']) : '') . ' · Project: ' . esc_html($rec['project']) . '</p>';
$h .= '<p class="jf-pkg-gold">' . esc_html(jf_ds_pkg_gold_line()) . '</p>';
$h .= '<p><a class="jf-pkg-btn" href="' . esc_url(jf_ds_pkg_pdf_url($rec['token'])) . '">Download my package (PDF)</a></p>';
$h .= '<section><h2>Your selections</h2><dl class="jf-pkg-facts">';
foreach ((array) $rec['selections'] as $k => $v) { $h .= '<dt>' . esc_html($k) . '</dt><dd>' . esc_html($v) . '</dd>'; }
$h .= '</dl></section>';
if (!empty($rec['rooms'])) {
$h .= '<section><h2>Your rooms</h2><ul>';
foreach ((array) $rec['rooms'] as $r) {
$nm = !empty($r['name']) ? $r['name'] : ('Room ' . $r['index']);
$dim = ($r['length'] !== '' || $r['width'] !== '' || $r['height'] !== '') ? ($r['length'] . ' ft long x ' . $r['width'] . ' ft wide, ' . $r['height'] . ' ft high') : 'Dimensions not given';
$h .= '<li><strong>' . esc_html($nm) . ':</strong> ' . esc_html($dim) . (empty($r['surfaces']) ? '' : ' — ' . esc_html(implode(', ', $r['surfaces']))) . '</li>';
}
$h .= '</ul></section>';
}
if (!empty($rec['notes'])) { $h .= '<section><h2>Notes</h2><p>' . esc_html($rec['notes']) . '</p></section>'; }
if ($is_paint && $amounts) {
$h .= '<section><h2>Approximate amounts</h2><p>Approximate — figured at 350 sq ft per gallon per coat, 2 coats, rounded up to whole gallons.</p><table class="jf-pkg-amt"><thead><tr><th>Space</th><th>Item</th><th>Area</th><th>Paint</th></tr></thead><tbody>';
$door_notes = array();
foreach ($amounts as $a) { if (!empty($a['note'])) { $door_notes[] = $a['note']; } $h .= '<tr><td>' . esc_html($a['space']) . '</td><td>' . esc_html($a['item']) . '</td><td>' . esc_html(number_format($a['sqft'], 0) . ' sq ft') . '</td><td>' . esc_html($a['gallons'] . ' gal') . '</td></tr>'; }
$h .= '</tbody></table><p>Final quantities are confirmed at field measure on your written proposal.</p>';
foreach (array_unique($door_notes) as $dn) { $h .= '<p>' . esc_html($dn) . '</p>'; }
$h .= '</section>';
}
$mats = array();
foreach (array('Cabinet collection', 'Cabinet door / finish', 'Countertop / slab', 'Tile', 'Paint code', 'Paint color path', 'Paint scope', 'Wall finish', 'Ceiling finish', 'Trim finish') as $mk) { if (!empty($rec['selections'][$mk])) { $mats[$mk] = $rec['selections'][$mk]; } }
if ($mats || !empty($rec['standards'])) {
$h .= '<section><h2>Materials</h2><ul>';
foreach ($mats as $k => $v) { $h .= '<li><strong>' . esc_html($k) . ':</strong> ' . esc_html($v) . '</li>'; }
if (!empty($rec['standards'])) { $h .= '<li>' . esc_html($rec['standards']) . '</li>'; }
$h .= '</ul></section>';
}
if (!empty($rec['photos'])) {
$h .= '<section><h2>Your photos</h2><div class="jf-pkg-photos">';
foreach ((array) $rec['photos'] as $label => $url) { $h .= '<figure style="margin:0"><img src="' . esc_url($url) . '" alt="' . esc_attr($label) . '" loading="lazy"><figcaption>' . esc_html($label) . '</figcaption></figure>'; }
$h .= '</div></section>';
}
if ($slides) {
$h .= '<section><h2>Your paint previews</h2><p>Drag to look around. Use the buttons to zoom, rotate, switch walls, or compare with your original wall photo.</p>';
$h .= '<div class="jf-pkg-stage" id="jf-pkg-stage"><img id="jf-pkg-view-img" src="' . esc_url($slides[0]['prev_url']) . '" alt="Paint preview"></div>';
$h .= '<p id="jf-pkg-view-label"></p>';
$h .= '<div class="jf-pkg-controls"><button type="button" id="jf-pkg-prev">← Previous wall</button><button type="button" id="jf-pkg-next">Next wall →</button><button type="button" id="jf-pkg-mode">Show original wall</button><button type="button" id="jf-pkg-zin">Zoom in</button><button type="button" id="jf-pkg-zout">Zoom out</button><button type="button" id="jf-pkg-zreset">Reset view</button><button type="button" id="jf-pkg-rotl">Rotate left</button><button type="button" id="jf-pkg-rotr">Rotate right</button></div>';
$h .= '<div class="jf-pkg-thumbs">';
foreach ($slides as $i => $s) { $h .= '<button type="button" class="jf-pkg-thumb' . ($i === 0 ? ' active' : '') . '" data-i="' . $i . '"><img src="' . esc_url($s['prev_url']) . '" alt="' . esc_attr($s['label']) . '"></button>'; }
$h .= '</div></section>';
} elseif ($is_paint && !empty($rec['prev_token'])) {
$h .= '<section><h2>Your paint previews</h2><p>Your previews are still being prepared. They will appear here as soon as they are ready — please refresh this page.</p></section>';
}
if (!empty($rec['designer_note'])) { $h .= '<section><h2>Designer note</h2><p>' . esc_html($rec['designer_note']) . '</p></section>'; }
if (!empty($rec['dossier_line'])) { $h .= '<section><h2>Paint product dossier</h2><p>' . esc_html($rec['dossier_line']) . '</p><p>The exact paint line is confirmed on your written proposal.</p></section>'; }
$h .= '<section><h2>Next steps</h2><ol><li>Check your selections and room details above — tell us if anything needs changing.</li><li>We will call you to confirm your design and arrange your field measure.</li><li>Your written proposal confirms final quantities and the exact paint line.</li></ol></section>';
$h .= '<p>' . esc_html(jf_ds_pkg_warranty()) . '</p>';
$h .= '<div class="jf-pkg-sig"><p>With thanks,<br>Finest Remodeling & Construction<br><em>Building Dreams</em><br>' . esc_html(jf_ds_pkg_contact()) . '</p></div>';
$h .= '<p class="jf-pkg-privacy">' . esc_html(jf_ds_pkg_privacy()) . '</p>';
$h .= '</main>';
$h .= '<script src="https://jfremodel.com/wp-content/plugins/panzoom/panzoom.min.js"></script>';
$js = <<<'JS'
(function(){
var slides=__SLIDES__;
var stage=document.getElementById('jf-pkg-stage');
if(!stage||!slides.length){return;}
var img=document.getElementById('jf-pkg-view-img');
var label=document.getElementById('jf-pkg-view-label');
var idx=0,mode='prev',rot=0,pz=null;
function show(){
var s=slides[idx];if(!s){return;}
var src=(mode==='orig'&&s.orig_url)?s.orig_url:s.prev_url;
img.src=src;
img.style.transform='rotate('+rot+'deg)';
label.textContent=s.label+' — '+(mode==='orig'?'Original wall':'Paint preview');
var th=document.querySelectorAll('.jf-pkg-thumb');
for(var i=0;i<th.length;i++){th[i].classList.toggle('active',i===idx);}
var mt=document.getElementById('jf-pkg-mode');
if(mt){mt.textContent=(mode==='orig')?'Show paint preview':'Show original wall';mt.style.display=s.orig_url?'':'none';}
}
function resetView(){rot=0;if(pz){pz.reset();}show();}
if(window.Panzoom){try{pz=Panzoom(stage,{maxScale:6,minScale:1,contain:false});stage.addEventListener('wheel',pz.zoomWithWheel);}catch(e){}}
document.getElementById('jf-pkg-prev').addEventListener('click',function(){idx=(idx-1+slides.length)%slides.length;resetView();});
document.getElementById('jf-pkg-next').addEventListener('click',function(){idx=(idx+1)%slides.length;resetView();});
document.getElementById('jf-pkg-mode').addEventListener('click',function(){mode=(mode==='orig')?'prev':'orig';show();});
document.getElementById('jf-pkg-zin').addEventListener('click',function(){if(pz){pz.zoomIn();}});
document.getElementById('jf-pkg-zout').addEventListener('click',function(){if(pz){pz.zoomOut();}});
document.getElementById('jf-pkg-zreset').addEventListener('click',function(){resetView();});
document.getElementById('jf-pkg-rotl').addEventListener('click',function(){rot-=90;show();});
document.getElementById('jf-pkg-rotr').addEventListener('click',function(){rot+=90;show();});
var thumbs=document.querySelectorAll('.jf-pkg-thumb');
for(var i=0;i<thumbs.length;i++){thumbs[i].addEventListener('click',function(){idx=parseInt(this.getAttribute('data-i'),10)||0;resetView();});}
var rid='__RID__';
var st=document.getElementById('jf-pkg-page').getAttribute('data-preview-status');
if(rid&&rid!=='0'&&st!=='ready'){
setInterval(function(){
fetch('/wp-admin/admin-ajax.php?action=jf_ds_preview_status&rid='+encodeURIComponent(rid)).then(function(r){return r.json();}).then(function(d){
if(d&&d.success&&d.data){var c=(d.data.preview_ids||[]).length;if(c>slides.length||d.data.status==='ready'){location.reload();}}
}).catch(function(){});
},20000);
}
show();
})();
JS;
$h .= '<script>' . str_replace(array('__SLIDES__', '__RID__'), array(wp_json_encode($slides), (string) (int) $media['orig_rid']), $js) . '</script>';
$h .= '</body></html>';
return $h;
}