<?php
/* ═══════════════════════════════════════════════════════════════
   PetBlip · Product Design Editor
   ─────────────────────────────────────────────────────────────
   SETUP:
   1. Set BGBUSTER_KEY
   2. Set DB credentials
   3. Upload /mockups/ folder alongside this file
   4. chmod 755 /uploads/editor/

   CALL FROM PRODUCT PAGE:
   product-editor.php?product=tshirt&return=/shop/tshirt
   product-editor.php?product=mug&gallery_image=/uploads/gallery/photo.jpg
   ═══════════════════════════════════════════════════════════════ */

session_start();

define('BGBUSTER_KEY', 'LlYuinpMHieaNejGfRwiqoDptECgqcjxpMUxIXBiclXzBNiMtNdzvGiFPhRBxiua');
define('DB_HOST',      'localhost');
define('DB_USER',      'qxgxnceu_photou');
define('DB_PASS',      '#Bullshit124');
define('DB_NAME',      'qxgxnceu_photo');
define('UPLOAD_DIR',   __DIR__ . '/uploads/editor/');
define('UPLOAD_URL',   '/uploads/editor/');
define('MOCKUP_URL',   '/mockups/');
define('ALLOWED_MIME', ['image/jpeg','image/png','image/webp']);
define('MAX_BYTES',    20 * 1024 * 1024);

if (!is_dir(UPLOAD_DIR)) mkdir(UPLOAD_DIR, 0755, true);

/* ══════════════════════════════════════════════════════════════
   PRODUCT DEFINITIONS
   Exact print zones derived from Fancy Product Designer plugin
   product.json files (stage coords converted to mockup % coords)

   zone = [left%, top%, width%, height%] as % of mockup image size
   print_px = final export resolution in pixels at print DPI
   ══════════════════════════════════════════════════════════════ */
$PRODUCTS = [
  'tshirt' => [
    'label'      => 'T-Shirt',
    'mockup_f'   => 'tshirt_front.png',
    'mockup_b'   => 'tshirt_back.png',
    'img_w'      => 240, 'img_h' => 264,
    'zone_f'     => ['l'=>29.7,'t'=>21.1,'w'=>40.3,'h'=>61.1],
    'zone_b'     => ['l'=>31.7,'t'=>22.7,'w'=>37.3,'h'=>56.7],
    'print_in'   => ['w'=>10,'h'=>16.7],
    'print_px'   => ['w'=>3000,'h'=>5010],
    'dpi'        => 300,
    'color'      => '#1A56FF',
    'note'       => 'Center chest · 10×16.7 in · 300 DPI',
    'has_back'   => true,
    'colors'     => ['#FFFFFF','#000000','#1A56FF','#FF3B4E','#10B981','#FF9500','#7C3AED','#6B7592'],
  ],
  'hoodie' => [
    'label'      => 'Hoodie',
    'mockup_f'   => 'hoodie_front.png',
    'mockup_b'   => 'hoodie_back.png',
    'img_w'      => 240, 'img_h' => 264,
    'zone_f'     => ['l'=>33.7,'t'=>23.9,'w'=>34.2,'h'=>26.7],
    'zone_b'     => ['l'=>34.0,'t'=>27.9,'w'=>33.3,'h'=>43.2],
    'print_in'   => ['w'=>10,'h'=>7.8],
    'print_px'   => ['w'=>3000,'h'=>2340],
    'dpi'        => 300,
    'color'      => '#7C3AED',
    'note'       => 'Center chest · 10×7.8 in · 300 DPI',
    'has_back'   => true,
    'colors'     => ['#FFFFFF','#000000','#7C3AED','#1A56FF','#FF3B4E','#6B7592','#354060','#FF9500'],
  ],
  'mug' => [
    'label'      => 'Mug 11oz',
    'mockup_f'   => 'mug.png',
    'mockup_b'   => null,
    'img_w'      => 240, 'img_h' => 200,
    'zone_f'     => ['l'=>22,'t'=>18,'w'=>56,'h'=>62],
    'zone_b'     => null,
    'print_in'   => ['w'=>8.5,'h'=>3.75],
    'print_px'   => ['w'=>2550,'h'=>1125],
    'dpi'        => 300,
    'color'      => '#DB2777',
    'note'       => 'Wrap area · 8.5×3.75 in · 300 DPI',
    'has_back'   => false,
    'colors'     => ['#FFFFFF','#000000','#DB2777','#1A56FF','#10B981','#FF9500'],
  ],
  'phonecase' => [
    'label'      => 'Phone Case',
    'mockup_f'   => 'phonecase.png',
    'mockup_b'   => null,
    'img_w'      => 318, 'img_h' => 600,
    'zone_f'     => ['l'=>18.4,'t'=>7.6,'w'=>63.0,'h'=>85.2],
    'zone_b'     => null,
    'print_in'   => ['w'=>2.4,'h'=>5.0],
    'print_px'   => ['w'=>720,'h'=>1500],
    'dpi'        => 300,
    'color'      => '#0EA5E9',
    'note'       => 'Full back · 2.4×5 in · 300 DPI',
    'has_back'   => false,
    'colors'     => ['#FFFFFF','#000000','#1A56FF','#FF3B4E','#0EA5E9'],
  ],
  'pillow' => [
    'label'      => 'Pillow 18×18',
    'mockup_f'   => 'pillow.png',
    'mockup_b'   => null,
    'img_w'      => 240, 'img_h' => 240,
    'zone_f'     => ['l'=>10,'t'=>10,'w'=>80,'h'=>80],
    'zone_b'     => null,
    'print_in'   => ['w'=>18,'h'=>18],
    'print_px'   => ['w'=>5400,'h'=>5400],
    'dpi'        => 300,
    'color'      => '#10B981',
    'note'       => 'Full face · 18×18 in · 300 DPI',
    'has_back'   => false,
    'colors'     => ['#FFFFFF','#F5F5F5','#0A0F1E','#1A56FF','#10B981','#DB2777'],
  ],
  'cap' => [
    'label'      => 'Cap',
    'mockup_f'   => 'cap.png',
    'mockup_b'   => null,
    'img_w'      => 280, 'img_h' => 200,
    'zone_f'     => ['l'=>28,'t'=>22,'w'=>44,'h'=>42],
    'zone_b'     => null,
    'print_in'   => ['w'=>2.5,'h'=>2.5],
    'print_px'   => ['w'=>750,'h'=>750],
    'dpi'        => 300,
    'color'      => '#FF9500',
    'note'       => 'Front panel · 2.5×2.5 in · 300 DPI',
    'has_back'   => false,
    'colors'     => ['#FFFFFF','#000000','#1A56FF','#FF3B4E','#FF9500','#10B981','#7C3AED'],
  ],
  'mousepad' => [
    'label'      => 'Mouse Pad',
    'mockup_f'   => 'mousepad.png',
    'mockup_b'   => null,
    'img_w'      => 280, 'img_h' => 240,
    'zone_f'     => ['l'=>6,'t'=>6,'w'=>88,'h'=>88],
    'zone_b'     => null,
    'print_in'   => ['w'=>7.5,'h'=>9.25],
    'print_px'   => ['w'=>2250,'h'=>2775],
    'dpi'        => 300,
    'color'      => '#EF4444',
    'note'       => 'Full surface · 7.5×9.25 in · 300 DPI',
    'has_back'   => false,
    'colors'     => ['#000000','#FFFFFF','#1A56FF','#FF3B4E','#10B981'],
  ],
  'bag' => [
    'label'      => 'Drawstring Bag',
    'mockup_f'   => 'bag.png',
    'mockup_b'   => null,
    'img_w'      => 240, 'img_h' => 240,
    'zone_f'     => ['l'=>20,'t'=>22,'w'=>60,'h'=>55],
    'zone_b'     => null,
    'print_in'   => ['w'=>10,'h'=>12],
    'print_px'   => ['w'=>3000,'h'=>3600],
    'dpi'        => 300,
    'color'      => '#64748B',
    'note'       => 'Center front · 10×12 in · 300 DPI',
    'has_back'   => false,
    'colors'     => ['#000000','#FFFFFF','#1A56FF','#FF3B4E','#10B981','#7C3AED'],
  ],
];

function siteUrl(): string {
    $s = (!empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] !== 'off') ? 'https' : 'http';
    return $s . '://' . $_SERVER['HTTP_HOST'];
}

/* ── AJAX ──────────────────────────────────────────────────── */
$action = $_POST['action'] ?? $_GET['action'] ?? '';

if ($action === 'upload') {
    header('Content-Type: application/json');
    $f = $_FILES['image'] ?? null;
    if (!$f || !in_array(mime_content_type($f['tmp_name']), ALLOWED_MIME))
        die(json_encode(['error'=>'Invalid file type']));
    if ($f['size'] > MAX_BYTES) die(json_encode(['error'=>'File too large (max 20 MB)']));
    $ext  = strtolower(pathinfo($f['name'], PATHINFO_EXTENSION)) ?: 'jpg';
    $name = 'up_'.uniqid().'.'.$ext;
    $path = UPLOAD_DIR.$name;
    move_uploaded_file($f['tmp_name'], $path);
    $bytes = file_get_contents($path);
    $mime  = mime_content_type($path);
    echo json_encode(['ok'=>true,'b64'=>'data:'.$mime.';base64,'.base64_encode($bytes),'filename'=>$name]);
    exit;
}

if ($action === 'remove_bg') {
    header('Content-Type: application/json');
    $filename = basename($_POST['filename'] ?? '');
    $path = UPLOAD_DIR.$filename;
    if (!$filename || !file_exists($path)) die(json_encode(['error'=>'File not found']));
    $mime = mime_content_type($path);

    $ch = curl_init('https://api.bgbuster.com/images/remove-background');
    curl_setopt_array($ch, [
        CURLOPT_RETURNTRANSFER => true, CURLOPT_POST => true,
        CURLOPT_POSTFIELDS => ['input'=>new CURLFile($path,$mime,$filename),'output'=>'raw','format'=>'png'],
        CURLOPT_HTTPHEADER => ['X-Api-Key: '.trim(BGBUSTER_KEY)],
        CURLOPT_TIMEOUT    => 60,
    ]);
    $res  = curl_exec($ch);
    $code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
    $ct   = curl_getinfo($ch, CURLINFO_CONTENT_TYPE);
    curl_close($ch);

    if ($code !== 200) { $e=json_decode($res,true); die(json_encode(['error'=>$e['message']??'BGBuster error '.$code])); }
    if (!str_starts_with($ct,'image/')) die(json_encode(['error'=>'Unexpected response: '.$ct]));
    $out = 'rmbg_'.uniqid().'.png';
    file_put_contents(UPLOAD_DIR.$out, $res);
    echo json_encode(['ok'=>true,'b64'=>'data:image/png;base64,'.base64_encode($res),'filename'=>$out]);
    exit;
}

if ($action === 'place_order') {
    header('Content-Type: application/json');
    $dataUrl  = $_POST['dataUrl']  ?? '';
    $product  = $_POST['product']  ?? '';
    $label    = $_POST['label']    ?? '';
    $notes    = $_POST['notes']    ?? '';
    $imgId    = (int)($_POST['image_id'] ?? 0);
    if (!$dataUrl || !str_starts_with($dataUrl,'data:image/'))
        die(json_encode(['error'=>'No image data']));

    $bytes  = base64_decode(explode(',', $dataUrl, 2)[1] ?? '');
    $name   = 'design_'.uniqid().'.png';
    $path   = UPLOAD_DIR.$name;
    file_put_contents($path, $bytes);
    $pubUrl = siteUrl().UPLOAD_URL.$name;

    try {
        $db  = new mysqli(DB_HOST, DB_USER, DB_PASS, DB_NAME);
        $uid = $_SESSION['user_id'] ?? $_SESSION['uid'] ?? 0;
        $stmt = $db->prepare("INSERT INTO product_orders
            (user_id,source_image_id,product_type,label,notes,design_file_path,design_file_url,status,created_at)
            VALUES (?,?,?,?,?,?,?,'pending',NOW())");
        if ($stmt) {
            $stmt->bind_param('iisssss',$uid,$imgId,$product,$label,$notes,$path,$pubUrl);
            $stmt->execute(); $orderId=$db->insert_id; $stmt->close();
        }
        $db->close();
    } catch(\Exception $e) { $orderId=0; }

    echo json_encode(['ok'=>true,'url'=>$pubUrl,'order_id'=>$orderId??0]);
    exit;
}

// Determine active product
$productKey  = $_GET['product']       ?? 'tshirt';
if (!isset($PRODUCTS[$productKey])) $productKey = 'tshirt';
$P           = $PRODUCTS[$productKey];
$returnUrl   = $_GET['return']        ?? '';
$galleryImg  = $_GET['gallery_image'] ?? '';
$imageId     = (int)($_GET['image_id'] ?? 0);

// Pre-load gallery image if provided
$preloadB64      = '';
$preloadFilename = '';
if ($galleryImg) {
    $localPath = ltrim($galleryImg, '/');
    if (file_exists($localPath)) {
        $bytes = file_get_contents($localPath);
        $mime  = mime_content_type($localPath);
        $preloadB64 = 'data:'.$mime.';base64,'.base64_encode($bytes);
        $ext  = pathinfo($localPath, PATHINFO_EXTENSION) ?: 'jpg';
        $preloadFilename = 'gal_'.uniqid().'.'.$ext;
        copy($localPath, UPLOAD_DIR.$preloadFilename);
    }
}

$productsJson = json_encode($PRODUCTS);
?>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width,initial-scale=1">
<title><?= htmlspecialchars($P['label']) ?> Designer · PetBlip</title>
<link rel="preconnect" href="https://fonts.googleapis.com">
<link href="https://fonts.googleapis.com/css2?family=Syne:wght@600;700;800&family=DM+Sans:ital,wght@0,400;0,500;0,600;1,400&display=swap" rel="stylesheet">
<script src="https://cdnjs.cloudflare.com/ajax/libs/fabric.js/5.3.1/fabric.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/qrcodejs/1.0.0/qrcode.min.js"></script>
<style>
:root{
  --ink:#0A0F1E;--ink2:#354060;--ink3:#7B8BAA;
  --line:#E4E9F4;--line2:#C9D3E8;--surf:#F5F7FC;--white:#FFF;
  --blue:#1A56FF;--blue-d:#1040D6;--blue-l:#EEF2FF;--blue-r:rgba(26,86,255,.16);
  --green:#00C896;--green-l:#E4FBF5;
  --red:#FF3B4E;--red-l:#FFF0F1;
  --amber:#FF9500;--amber-l:#FFF4E0;
  --nav-h:52px;--lw:265px;--rw:270px;
  --r:10px;--rs:7px;--rxs:5px;
  --sh-s:0 1px 3px rgba(10,15,30,.07),0 1px 2px rgba(10,15,30,.04);
  --sh-m:0 4px 16px rgba(10,15,30,.10);
  --sh-l:0 20px 60px rgba(10,15,30,.22),0 4px 20px rgba(10,15,30,.10);
  --prod-color:<?= $P['color'] ?>;
  --fd:'Syne',sans-serif;--fb:'DM Sans',sans-serif;
}
*,*::before,*::after{box-sizing:border-box;margin:0;padding:0}
html,body{height:100%;font-family:var(--fb);font-size:13px;color:var(--ink);background:var(--surf);-webkit-font-smoothing:antialiased}

/* ── NAV ──────────────────────────────────────────────────── */
.nav{height:var(--nav-h);background:var(--ink);display:flex;align-items:center;padding:0 14px;gap:8px;position:sticky;top:0;z-index:300;border-bottom:1px solid rgba(255,255,255,.05);box-shadow:0 1px 0 rgba(0,0,0,.3)}
.nav-logo{display:flex;align-items:center;gap:8px;text-decoration:none;flex-shrink:0}
.nav-mark{width:28px;height:28px;background:var(--blue);border-radius:7px;display:grid;place-items:center;flex-shrink:0}
.nav-mark svg{width:15px;height:15px}
.nav-name{font-family:var(--fd);font-size:14.5px;color:#fff;letter-spacing:-.015em}
.nav-pipe{width:1px;height:16px;background:rgba(255,255,255,.1);margin:0 2px}
.nav-sub{font-size:11.5px;font-weight:500;color:rgba(255,255,255,.35)}
/* product badge */
.nav-prod{display:flex;align-items:center;gap:6px;padding:3px 10px 3px 7px;background:rgba(255,255,255,.08);border:1px solid rgba(255,255,255,.1);border-radius:20px;font-size:11.5px;font-weight:700;color:#fff}
.nav-prod-dot{width:7px;height:7px;border-radius:50%;background:var(--prod-color);flex-shrink:0}
/* ai badge */
.nav-badge{display:inline-flex;align-items:center;gap:5px;padding:3px 8px;background:rgba(0,200,150,.12);border:1px solid rgba(0,200,150,.25);border-radius:20px;font-size:10.5px;font-weight:700;color:#00C896}
.nav-dot{width:5px;height:5px;border-radius:50%;background:#00C896;animation:pulse 2s infinite}
@keyframes pulse{0%,100%{opacity:1}50%{opacity:.4}}
.nav-r{margin-left:auto;display:flex;align-items:center;gap:5px}
.nb{display:inline-flex;align-items:center;gap:5px;padding:5px 10px;border-radius:6px;font-size:12px;font-weight:600;font-family:var(--fb);cursor:pointer;border:none;transition:all .12s;line-height:1}
.nb-ico{padding:6px}
.nb-ghost{background:rgba(255,255,255,.07);color:rgba(255,255,255,.6)}
.nb-ghost:hover{background:rgba(255,255,255,.14);color:#fff}
.nb-ghost:disabled{opacity:.3;cursor:not-allowed}
.nb-order{background:var(--prod-color);color:#fff}
.nb-order:hover{filter:brightness(.9)}
.nb-order:disabled{opacity:.4;cursor:not-allowed}
.nav-sep{width:1px;height:16px;background:rgba(255,255,255,.08)}

/* ── SHELL ────────────────────────────────────────────────── */
.shell{display:grid;grid-template-columns:var(--lw) 1fr var(--rw);height:calc(100vh - var(--nav-h));overflow:hidden}

/* ── PANELS ───────────────────────────────────────────────── */
.panel{background:var(--white);display:flex;flex-direction:column;overflow:hidden;border-right:1px solid var(--line)}
.panel.right{border-right:none;border-left:1px solid var(--line)}
.pscroll{flex:1;overflow-y:auto;padding:8px 8px 28px;display:flex;flex-direction:column;gap:1px}
.pscroll::-webkit-scrollbar{width:3px}
.pscroll::-webkit-scrollbar-thumb{background:var(--line2);border-radius:3px}

/* sections */
.shd{display:flex;align-items:center;gap:7px;padding:8px 7px 6px;cursor:pointer;border-radius:var(--rs);transition:background .1s;user-select:none}
.shd:hover{background:var(--surf)}
.shd-ic{color:var(--ink3);display:flex;flex-shrink:0}
.shd-lb{font-size:10px;font-weight:700;letter-spacing:.08em;text-transform:uppercase;color:var(--ink3);flex:1}
.schev{color:var(--ink3);display:flex;transition:transform .15s}
.schev.dn{transform:rotate(180deg)}
.sbd{padding:2px 4px 10px;display:flex;flex-direction:column;gap:7px}
.sbd.hidden{display:none}
.sdiv{height:1px;background:var(--line);margin:2px 0}

/* form */
.f{display:flex;flex-direction:column;gap:3px}
.flb{font-size:11px;font-weight:600;color:var(--ink2)}
.inp{width:100%;padding:6px 8px;border:1.5px solid var(--line2);border-radius:var(--rs);font-size:12.5px;font-family:var(--fb);color:var(--ink);background:#fff;outline:none;transition:border-color .12s,box-shadow .12s;appearance:none}
.inp:focus{border-color:var(--blue);box-shadow:0 0 0 3px var(--blue-r)}
.inp::placeholder{color:var(--ink3)}
.inp2{display:grid;grid-template-columns:1fr 1fr;gap:6px}
.csw{width:100%;height:32px;padding:2px;border:1.5px solid var(--line2);border-radius:var(--rs);cursor:pointer;background:none}

/* swatches */
.sws{display:flex;flex-wrap:wrap;gap:4px}
.sw{width:20px;height:20px;border-radius:50%;cursor:pointer;flex-shrink:0;border:2.5px solid transparent;transition:transform .1s,box-shadow .1s}
.sw:hover{transform:scale(1.18)}
.sw.on{box-shadow:0 0 0 2px #fff,0 0 0 4px var(--blue)}
.sw-tp{background:linear-gradient(45deg,#bbb 25%,transparent 25%,transparent 75%,#bbb 75%),linear-gradient(45deg,#bbb 25%,transparent 25%,transparent 75%,#bbb 75%);background-size:8px 8px;background-position:0 0,4px 4px;background-color:#e8e8e8}

/* buttons */
.btn{display:inline-flex;align-items:center;justify-content:center;gap:5px;padding:6px 11px;border-radius:var(--rs);font-size:12px;font-weight:600;cursor:pointer;border:1.5px solid transparent;font-family:var(--fb);transition:all .13s;line-height:1;white-space:nowrap}
.btn:disabled{opacity:.36;cursor:not-allowed}
.btn svg{flex-shrink:0}
.btn-p{background:var(--blue);color:#fff;border-color:var(--blue)}
.btn-p:hover:not(:disabled){background:var(--blue-d)}
.btn-s{background:#fff;color:var(--ink);border-color:var(--line2)}
.btn-s:hover:not(:disabled){border-color:var(--blue);color:var(--blue);background:var(--blue-l)}
.btn-g{background:transparent;color:var(--ink2);border-color:transparent}
.btn-g:hover:not(:disabled){background:var(--surf);color:var(--ink)}
.btn-dng{background:#fff;color:var(--red);border-color:#FECDD3}
.btn-dng:hover:not(:disabled){background:var(--red);color:#fff}
.btn-prod{background:var(--prod-color);color:#fff;border-color:var(--prod-color)}
.btn-prod:hover:not(:disabled){filter:brightness(.9)}
.btn-w{width:100%}
.btn-sm{padding:4px 8px;font-size:11px}
.btn-ic{padding:5px}

/* upload zone */
.upzone{border:1.5px dashed var(--line2);border-radius:var(--r);padding:16px 10px;text-align:center;cursor:pointer;background:var(--surf);transition:all .15s}
.upzone input{display:none}
.upzone:hover,.upzone.drag{border-color:var(--prod-color);background:rgba(26,86,255,.05)}
.upzone:hover .upz-ic,.upzone.drag .upz-ic{color:var(--prod-color)}
.upz-ic{display:flex;justify-content:center;color:var(--ink3);margin-bottom:6px;transition:color .12s}
.upz-t{font-size:12px;font-weight:600;color:var(--ink2);margin-bottom:2px}
.upz-h{font-size:11px;color:var(--ink3)}

/* gallery pill */
.gallery-pill{display:flex;align-items:center;gap:8px;padding:8px 10px;background:var(--blue-l);border:1.5px solid rgba(26,86,255,.2);border-radius:var(--rs);font-size:12px;font-weight:600;color:var(--blue)}
.gallery-pill svg{flex-shrink:0}

/* status */
.st{display:none;align-items:center;gap:6px;padding:7px 9px;border-radius:var(--rs);font-size:11.5px;font-weight:500}
.st.show{display:flex}
.st.ld{background:var(--blue-l);color:var(--blue)}
.st.ok{background:var(--green-l);color:#065F46}
.st.er{background:var(--red-l);color:#991B1B}
.st.warn{background:var(--amber-l);color:#92400E}
.spin{width:12px;height:12px;border:2px solid currentColor;border-top-color:transparent;border-radius:50%;animation:spin .5s linear infinite;flex-shrink:0}
@keyframes spin{to{transform:rotate(360deg)}}

/* sliders */
.slr{display:flex;align-items:center;gap:7px}
.sllb{font-size:11px;font-weight:600;color:var(--ink2);width:66px;flex-shrink:0}
.slr input[type=range]{flex:1;accent-color:var(--prod-color);cursor:pointer;height:4px}
.slv{font-size:11px;color:var(--ink3);width:30px;text-align:right;flex-shrink:0;font-variant-numeric:tabular-nums}

/* toggles */
.togrow{display:flex;gap:3px}
.tog{flex:1;padding:5px 0;border:1.5px solid var(--line2);border-radius:var(--rxs);background:#fff;color:var(--ink2);cursor:pointer;display:grid;place-items:center;transition:all .1s}
.tog.on{background:var(--blue-l);border-color:var(--blue);color:var(--blue)}
.tog:hover:not(.on){border-color:var(--ink3)}

/* product color chips */
.col-chips{display:flex;flex-wrap:wrap;gap:5px}
.col-chip{width:24px;height:24px;border-radius:50%;cursor:pointer;border:2.5px solid transparent;transition:transform .1s,box-shadow .1s;flex-shrink:0}
.col-chip:hover{transform:scale(1.15)}
.col-chip.on{box-shadow:0 0 0 2px #fff,0 0 0 4px var(--ink)}

/* filter grid */
.filter-grid{display:grid;grid-template-columns:repeat(4,1fr);gap:5px}
.filter-chip{border:1.5px solid var(--line2);border-radius:var(--rs);overflow:hidden;cursor:pointer;transition:all .13s;background:#fff;text-align:center}
.filter-chip:hover{border-color:var(--prod-color);transform:translateY(-1px);box-shadow:var(--sh-s)}
.filter-chip.on{border-color:var(--prod-color);box-shadow:0 0 0 2px var(--blue-l)}
.filter-thumb{height:40px;display:flex;align-items:center;justify-content:center;font-size:10px;font-weight:700;letter-spacing:.03em;color:rgba(255,255,255,.9);text-shadow:0 1px 2px rgba(0,0,0,.5)}
.filter-thumb.f-normal{background:linear-gradient(135deg,#e8ecf4,#d0d8e8)}
.filter-thumb.f-grayscale{background:linear-gradient(135deg,#888,#444);color:#fff}
.filter-thumb.f-sepia{background:linear-gradient(135deg,#c8a87a,#8b6a3e)}
.filter-thumb.f-bw{background:linear-gradient(135deg,#fff 50%,#000 50%)}
.filter-thumb.f-vintage{background:linear-gradient(135deg,#d4a56a,#7a5c3a)}
.filter-thumb.f-kodachrome{background:linear-gradient(135deg,#e8b84b,#c44a2a)}
.filter-thumb.f-polaroid{background:linear-gradient(135deg,#e8e0d4,#b4a090)}
.filter-thumb.f-brownie{background:linear-gradient(135deg,#a07850,#5c3820)}
.filter-thumb.f-technicolor{background:linear-gradient(135deg,#e84848,#48b448,#4848e8)}
.filter-name{font-size:9px;font-weight:700;color:var(--ink2);padding:3px 0}
.filter-chip.on .filter-name{color:var(--blue)}

/* stickers */
.sticker-grid{display:grid;grid-template-columns:repeat(4,1fr);gap:5px}
.sticker-btn{border:1.5px solid var(--line2);border-radius:var(--rs);padding:8px 4px;cursor:pointer;text-align:center;background:#fff;transition:all .13s;display:flex;align-items:center;justify-content:center}
.sticker-btn svg,.sticker-btn img{width:30px;height:30px}
.sticker-btn:hover{border-color:var(--prod-color);background:var(--blue-l);transform:scale(1.05)}

/* text templates */
.tmpl-list{display:flex;flex-direction:column;gap:4px}
.tmpl-btn{padding:7px 10px;border:1.5px solid var(--line2);border-radius:var(--rs);background:#fff;cursor:pointer;font-family:var(--fb);font-size:12px;font-weight:600;color:var(--ink2);text-align:left;transition:all .13s;display:flex;align-items:center;justify-content:space-between}
.tmpl-btn:hover{border-color:var(--prod-color);background:var(--blue-l);color:var(--blue)}
.tmpl-btn small{font-size:10px;font-weight:400;color:var(--ink3)}

/* QR */
.qr-preview{width:70px;height:70px;border-radius:var(--rs);border:1.5px solid var(--line2);overflow:hidden;background:#fff;display:flex;align-items:center;justify-content:center;flex-shrink:0}

/* ── CANVAS STAGE ─────────────────────────────────────────── */
.stage{background:#BEC6D4;display:flex;flex-direction:column;overflow:hidden;position:relative}
.ctb{display:flex;align-items:center;gap:3px;padding:5px 12px;background:rgba(255,255,255,.9);backdrop-filter:blur(12px) saturate(180%);border-bottom:1px solid rgba(0,0,0,.07);flex-shrink:0;min-height:44px;flex-wrap:wrap}
.ctb-sep{width:1px;height:18px;background:var(--line);margin:0 3px;flex-shrink:0}
.ctb-r{margin-left:auto;display:flex;align-items:center;gap:3px}
.tb{display:inline-flex;align-items:center;gap:4px;padding:4px 8px;border-radius:var(--rxs);background:transparent;border:1px solid transparent;color:var(--ink2);cursor:pointer;font-size:11px;font-weight:500;font-family:var(--fb);transition:all .11s}
.tb:hover{background:rgba(255,255,255,.95);border-color:var(--line);color:var(--ink);box-shadow:var(--sh-s)}
.tb:disabled{opacity:.25;cursor:not-allowed;box-shadow:none;background:transparent;border-color:transparent}
.tb.dng:hover{background:var(--red-l);color:var(--red);border-color:#FECDD3}
.tb.active{background:var(--blue-l);border-color:var(--blue);color:var(--blue)}
.tb svg{flex-shrink:0}

/* canvas wrap */
.cwrap{flex:1;display:flex;align-items:center;justify-content:center;padding:24px;overflow:auto;position:relative}
.cempty{position:absolute;inset:0;display:flex;flex-direction:column;align-items:center;justify-content:center;gap:10px;pointer-events:none;transition:opacity .2s}
.cempty.gone{opacity:0;pointer-events:none}
.cempty-ic{color:rgba(0,0,0,.15)}
.cempty-t{font-family:var(--fd);font-size:15px;color:rgba(0,0,0,.2)}
.cempty-h{font-size:12px;color:rgba(0,0,0,.18)}

/*
 * MOCKUP SYSTEM
 * .mhost  — sized to mockup image dimensions
 *   .mbg  — product PNG (base layer)
 *   .mzone — Fabric.js canvas (editable print area)
 *   .mover — product PNG again (multiply blend for fold shadows)
 *   .mring — dashed print zone indicator
 */
.mhost{position:relative;display:block;box-shadow:var(--sh-l);border-radius:6px;overflow:hidden}
.mbg{position:absolute;inset:0;width:100%;height:100%;object-fit:fill;pointer-events:none;z-index:1;display:block}
.mzone{position:absolute;z-index:2;overflow:visible}
#fc{display:block}
.mover{position:absolute;inset:0;width:100%;height:100%;object-fit:fill;pointer-events:none;z-index:3;mix-blend-mode:multiply;opacity:.5;display:block}
.mring{position:absolute;z-index:4;pointer-events:none;border:1.5px dashed rgba(26,86,255,.55);border-radius:3px;transition:opacity .3s}
.mring.hide{opacity:0}

/* product color overlay on shirt */
.mcolor{position:absolute;inset:0;z-index:0;pointer-events:none;mix-blend-mode:multiply;opacity:.0;transition:opacity .2s}

/* DPI warning */
.dpi-warn{display:none;align-items:center;gap:6px;padding:6px 9px;background:var(--amber-l);border:1.5px solid var(--amber);border-radius:var(--rs);font-size:11px;font-weight:600;color:#92400E}
.dpi-warn.show{display:flex}

/* smart guide lines */
.guide-h,.guide-v{position:absolute;pointer-events:none;z-index:10;opacity:0;transition:opacity .1s}
.guide-h{height:1px;left:0;right:0;background:#FF3B4E}
.guide-v{width:1px;top:0;bottom:0;background:#FF3B4E}
.guide-h.show,.guide-v.show{opacity:1}

/* view toggle */
.view-toggle{display:flex;gap:4px}
.vtb{flex:1;padding:5px 0;border:1.5px solid var(--line2);border-radius:var(--rxs);background:#fff;color:var(--ink2);cursor:pointer;font-size:11px;font-weight:700;font-family:var(--fb);text-align:center;transition:all .12s}
.vtb:hover{border-color:var(--prod-color);color:var(--prod-color)}
.vtb.on{background:var(--prod-color);border-color:var(--prod-color);color:#fff}

/* ── LAYERS ───────────────────────────────────────────────── */
.ly-item{display:flex;align-items:center;gap:6px;padding:5px 7px;border:1.5px solid var(--line2);border-radius:var(--rs);background:#fff;cursor:pointer;font-size:11.5px;font-weight:500;color:var(--ink2);transition:all .11s}
.ly-item:hover{border-color:var(--blue);color:var(--ink)}
.ly-item.on{border-color:var(--blue);background:var(--blue-l);color:var(--blue)}
.ly-ic{width:24px;height:24px;border-radius:4px;background:var(--surf);border:1px solid var(--line);display:grid;place-items:center;color:var(--ink3);flex-shrink:0}
.ly-nm{flex:1;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;font-size:11px}
.ly-mt{text-align:center;padding:12px 0;font-size:11.5px;color:var(--ink3)}

/* ── PRODUCT INFO CARD ────────────────────────────────────── */
.prod-info-card{
  background:linear-gradient(135deg,rgba(26,86,255,.06) 0%,rgba(26,86,255,.02) 100%);
  border:1.5px solid rgba(26,86,255,.15);border-radius:var(--r);
  padding:12px;display:flex;flex-direction:column;gap:8px;
}
.prod-info-title{font-family:var(--fd);font-size:13px;font-weight:700;color:var(--prod-color)}
.prod-info-dim{font-size:11px;color:var(--ink3)}
.prod-info-dpi{display:inline-flex;align-items:center;gap:4px;padding:2px 7px;background:var(--prod-color);color:#fff;border-radius:20px;font-size:10px;font-weight:700}

/* ── EXPORT ───────────────────────────────────────────────── */
.expbox{background:linear-gradient(135deg,#EEF2FF 0%,#F5F7FF 100%);border:1.5px solid rgba(26,86,255,.13);border-radius:var(--r);padding:11px;display:flex;flex-direction:column;gap:8px}
.exp-ttl{font-family:var(--fd);font-size:10px;letter-spacing:.08em;text-transform:uppercase;color:var(--blue)}
.fmt-row{display:flex;gap:4px}
.fmt{flex:1;padding:5px 0;border:1.5px solid var(--line2);border-radius:var(--rxs);background:#fff;text-align:center;font-size:11px;font-weight:700;cursor:pointer;color:var(--ink2);transition:all .11s}
.fmt:hover{border-color:var(--blue);color:var(--blue)}
.fmt.on{border-color:var(--blue);background:var(--blue);color:#fff}

/* prop panel */
.prop-g{background:var(--surf);border-radius:var(--rs);padding:8px 10px;display:flex;flex-direction:column;gap:7px}

/* ── ORDER MODAL ──────────────────────────────────────────── */
.mbg-modal{position:fixed;inset:0;background:rgba(10,15,30,.55);backdrop-filter:blur(5px);z-index:500;display:flex;align-items:center;justify-content:center;opacity:0;pointer-events:none;transition:opacity .2s}
.mbg-modal.open{opacity:1;pointer-events:auto}
.mbox{background:#fff;border-radius:16px;padding:26px;width:460px;max-width:92vw;box-shadow:var(--sh-l);transform:translateY(14px);transition:transform .2s}
.mbg-modal.open .mbox{transform:translateY(0)}
.m-title{font-family:var(--fd);font-size:18px;color:var(--ink);margin-bottom:3px}
.m-sub{font-size:12.5px;color:var(--ink3);margin-bottom:18px}
/* order summary */
.order-summary{display:flex;gap:12px;align-items:flex-start;padding:12px;background:var(--surf);border-radius:var(--rs);margin-bottom:16px;border:1.5px solid var(--line)}
.order-thumb{width:80px;height:80px;border-radius:var(--rs);overflow:hidden;background:#ddd;flex-shrink:0;position:relative;border:1px solid var(--line)}
.order-thumb-chk{position:absolute;inset:0;background-image:linear-gradient(45deg,#ccc 25%,transparent 25%,transparent 75%,#ccc 75%),linear-gradient(45deg,#ccc 25%,transparent 25%,transparent 75%,#ccc 75%);background-size:10px 10px;background-position:0 0,5px 5px;background-color:#f0f0f0;z-index:0}
.order-thumb img{position:relative;z-index:1;width:100%;height:100%;object-fit:contain}
.order-info{flex:1}
.order-prod{font-family:var(--fd);font-size:14px;font-weight:700;color:var(--ink);margin-bottom:2px}
.order-dim{font-size:11.5px;color:var(--ink3);margin-bottom:6px}
.order-badge{display:inline-flex;align-items:center;gap:4px;padding:2px 8px;background:var(--green-l);border:1px solid rgba(0,200,150,.3);border-radius:20px;font-size:10.5px;font-weight:700;color:#065F46}
/* mockup preview in modal */
.m-mockup-preview{width:100%;aspect-ratio:1;border-radius:var(--rs);overflow:hidden;background:#e0e4ed;margin-bottom:14px;position:relative;border:1.5px solid var(--line)}
.m-mockup-preview-chk{position:absolute;inset:0;background-image:linear-gradient(45deg,#ccc 25%,transparent 25%,transparent 75%,#ccc 75%),linear-gradient(45deg,#ccc 25%,transparent 25%,transparent 75%,#ccc 75%);background-size:14px 14px;background-position:0 0,7px 7px;background-color:#f0f0f0;z-index:0}
.m-mockup-preview img{position:relative;z-index:1;width:100%;height:100%;object-fit:contain}
.m-actions{display:flex;gap:8px}
.m-actions .btn{flex:1}
</style>
</head>
<body>

<!-- ═══ NAV ════════════════════════════════════════════════════ -->
<nav class="nav">
  <a href="/" class="nav-logo">
    <div class="nav-mark">
      <svg viewBox="0 0 24 24" fill="none" stroke="#fff" stroke-width="2.2" stroke-linecap="round" stroke-linejoin="round">
        <circle cx="11" cy="4" r="2"/><circle cx="18" cy="8" r="2"/><circle cx="20" cy="16" r="2"/>
        <path d="M9 10a5 5 0 0 1 5 5v3.5a3.5 3.5 0 0 1-6.84 1.045Q6.52 17.48 4.46 16.84A3.5 3.5 0 0 1 5.5 10Z"/>
      </svg>
    </div>
    <span class="nav-name">PetBlip</span>
  </a>
  <div class="nav-pipe"></div>
  <span class="nav-sub">Design Studio</span>
  <div class="nav-prod">
    <span class="nav-prod-dot"></span>
    <?= htmlspecialchars($P['label']) ?>
  </div>
  <div class="nav-badge"><span class="nav-dot"></span>BGBuster AI</div>

  <div class="nav-r">
    <button class="nb nb-ghost nb-ico" id="btnUndo" disabled title="Undo Ctrl+Z">
      <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.2" stroke-linecap="round" stroke-linejoin="round"><path d="M3 7v6h6"/><path d="M21 17a9 9 0 0 0-9-9 9 9 0 0 0-6 2.3L3 13"/></svg>
    </button>
    <button class="nb nb-ghost nb-ico" id="btnRedo" disabled title="Redo Ctrl+Y">
      <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.2" stroke-linecap="round" stroke-linejoin="round"><path d="M21 7v6h-6"/><path d="M3 17a9 9 0 0 1 9-9 9 9 0 0 1 6 2.3L21 13"/></svg>
    </button>
    <div class="nav-sep"></div>
    <button class="nb nb-ghost" id="btnZoneToggle" title="Toggle print zone">
      <svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect x="3" y="3" width="18" height="18" rx="2"/><circle cx="8.5" cy="8.5" r="1.5"/><polyline points="21 15 16 10 5 21"/></svg>
      Zone
    </button>
    <button class="nb nb-ghost" id="btnDownload" disabled>
      <svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.2" stroke-linecap="round" stroke-linejoin="round"><path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"/><polyline points="7 10 12 15 17 10"/><line x1="12" y1="15" x2="12" y2="3"/></svg>
      Download
    </button>
    <button class="nb nb-order" id="btnOrder" disabled>
      <svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M6 2 3 6v14a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2V6l-3-4z"/><line x1="3" y1="6" x2="21" y2="6"/><path d="M16 10a4 4 0 0 1-8 0"/></svg>
      Place Order
    </button>
  </div>
</nav>

<!-- ═══ SHELL ════════════════════════════════════════════════════ -->
<div class="shell">

<!-- ══ LEFT PANEL ═════════════════════════════════════════════════ -->
<aside class="panel">
<div class="pscroll">

  <!-- PHOTO UPLOAD -->
  <div>
    <div class="shd" data-sec="ph">
      <span class="shd-ic"><svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect x="3" y="3" width="18" height="18" rx="2"/><circle cx="9" cy="9" r="2"/><path d="m21 15-3.086-3.086a2 2 0 0 0-2.828 0L6 21"/></svg></span>
      <span class="shd-lb">Your Pet Photo</span>
      <span class="schev" id="cv-ph"><svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"><polyline points="18 15 12 9 6 15"/></svg></span>
    </div>
    <div class="sbd" id="sb-ph">
      <?php if($preloadB64): ?>
      <div class="gallery-pill">
        <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect x="3" y="3" width="18" height="18" rx="2"/><circle cx="9" cy="9" r="2"/><path d="m21 15-3.086-3.086a2 2 0 0 0-2.828 0L6 21"/></svg>
        Gallery photo loaded
      </div>
      <?php else: ?>
      <div class="upzone" id="upZone">
        <input type="file" id="fileIn" accept="image/*">
        <div class="upz-ic"><svg width="26" height="26" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"><polyline points="16 16 12 12 8 16"/><line x1="12" y1="12" x2="12" y2="21"/><path d="M20.39 18.39A5 5 0 0 0 18 9h-1.26A8 8 0 1 0 3 16.3"/></svg></div>
        <div class="upz-t">Upload your pet photo</div>
        <div class="upz-h">JPG · PNG · WEBP · max 20 MB</div>
      </div>
      <?php endif; ?>
      <div id="stUp" class="st"></div>
      <div id="dpiWarn" class="dpi-warn">
        <svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M10.29 3.86L1.82 18a2 2 0 0 0 1.71 3h16.94a2 2 0 0 0 1.71-3L13.71 3.86a2 2 0 0 0-3.42 0z"/><line x1="12" y1="9" x2="12" y2="13"/><line x1="12" y1="17" x2="12.01" y2="17"/></svg>
        <span id="dpiWarnTxt">Low resolution — may print blurry</span>
      </div>

      <button class="btn btn-p btn-w" id="btnRmBg" disabled>
        <svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M12 22C6.5 22 2 17.5 2 12S6.5 2 12 2s10 4.5 10 10"/><circle cx="17" cy="17" r="4"/><path d="m19 15-4 4"/><path d="m15 15 4 4"/></svg>
        Remove Background
      </button>
      <button class="btn btn-s btn-w btn-sm" id="btnRestore" disabled>
        <svg width="11" height="11" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M3 12a9 9 0 1 0 9-9 9.75 9.75 0 0 0-6.74 2.74L3 8"/><path d="M3 3v5h5"/></svg>
        Restore Original
      </button>
      <div id="stBg" class="st"></div>
    </div>
  </div>

  <div class="sdiv"></div>

  <!-- PHOTO FILTERS -->
  <div>
    <div class="shd" data-sec="fi">
      <span class="shd-ic"><svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="3"/><path d="M3 9c0-1.1.9-2 2-2h1l1-2h6l1 2h1a2 2 0 0 1 2 2v8a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V9z"/></svg></span>
      <span class="shd-lb">Photo Filters</span>
      <span class="schev" id="cv-fi"><svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"><polyline points="18 15 12 9 6 15"/></svg></span>
    </div>
    <div class="sbd" id="sb-fi">
      <div class="filter-grid" id="filterGrid"></div>
    </div>
  </div>

  <div class="sdiv"></div>

  <!-- ADJUSTMENTS -->
  <div>
    <div class="shd" data-sec="aj">
      <span class="shd-ic"><svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><line x1="4" y1="21" x2="4" y2="14"/><line x1="4" y1="10" x2="4" y2="3"/><line x1="12" y1="21" x2="12" y2="12"/><line x1="12" y1="8" x2="12" y2="3"/><line x1="20" y1="21" x2="20" y2="16"/><line x1="20" y1="12" x2="20" y2="3"/><line x1="1" y1="14" x2="7" y2="14"/><line x1="9" y1="8" x2="15" y2="8"/><line x1="17" y1="16" x2="23" y2="16"/></svg></span>
      <span class="shd-lb">Adjustments</span>
      <span class="schev" id="cv-aj"><svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"><polyline points="18 15 12 9 6 15"/></svg></span>
    </div>
    <div class="sbd" id="sb-aj">
      <div class="slr"><span class="sllb">Brightness</span><input type="range" id="sBr" min="-1" max="1" step=".05" value="0"><span class="slv" id="vBr">0</span></div>
      <div class="slr"><span class="sllb">Contrast</span><input type="range" id="sCo" min="-1" max="1" step=".05" value="0"><span class="slv" id="vCo">0</span></div>
      <div class="slr"><span class="sllb">Saturation</span><input type="range" id="sSa" min="-1" max="1" step=".05" value="0"><span class="slv" id="vSa">0</span></div>
      <div class="slr"><span class="sllb">Opacity</span><input type="range" id="sOp" min="0" max="1" step=".05" value="1"><span class="slv" id="vOp">1.0</span></div>
      <button class="btn btn-g btn-sm" id="btnRstAdj" style="align-self:flex-start">
        <svg width="11" height="11" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.2" stroke-linecap="round" stroke-linejoin="round"><path d="M3 12a9 9 0 1 0 9-9 9.75 9.75 0 0 0-6.74 2.74L3 8"/><path d="M3 3v5h5"/></svg>
        Reset
      </button>
    </div>
  </div>

  <div class="sdiv"></div>

  <!-- TEXT -->
  <div>
    <div class="shd" data-sec="tx">
      <span class="shd-ic"><svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polyline points="4 7 4 4 20 4 20 7"/><line x1="9" y1="20" x2="15" y2="20"/><line x1="12" y1="4" x2="12" y2="20"/></svg></span>
      <span class="shd-lb">Text</span>
      <span class="schev" id="cv-tx"><svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"><polyline points="18 15 12 9 6 15"/></svg></span>
    </div>
    <div class="sbd" id="sb-tx">
      <div class="f"><label class="flb">Content</label><input class="inp" type="text" id="txCt" placeholder="Rex · Good Boy · 2020–2025"></div>
      <div class="f">
        <label class="flb">Font</label>
        <select class="inp" id="txFn">
          <option value="Syne">Syne (Bold)</option>
          <option value="Georgia">Georgia (Classic)</option>
          <option value="Impact">Impact (Strong)</option>
          <option value="Verdana">Verdana (Clean)</option>
          <option value="DM Sans">DM Sans (Modern)</option>
          <option value="Courier New">Courier New (Retro)</option>
          <option value="Arial Black">Arial Black (Heavy)</option>
        </select>
      </div>
      <div class="inp2">
        <div class="f"><label class="flb">Size</label><input class="inp" type="number" id="txSz" value="42" min="6" max="300"></div>
        <div class="f"><label class="flb">Color</label><input type="color" class="csw" id="txCol" value="#0A0F1E"></div>
      </div>
      <div class="togrow">
        <button class="tog" id="togB" title="Bold"><svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"><path d="M6 4h8a4 4 0 0 1 4 4 4 4 0 0 1-4 4H6z"/><path d="M6 12h9a4 4 0 0 1 4 4 4 4 0 0 1-4 4H6z"/></svg></button>
        <button class="tog" id="togI" title="Italic"><svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"><line x1="19" y1="4" x2="10" y2="4"/><line x1="14" y1="20" x2="5" y2="20"/><line x1="15" y1="4" x2="9" y2="20"/></svg></button>
        <button class="tog on" id="togAC" title="Center"><svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"><line x1="21" y1="6" x2="3" y2="6"/><line x1="17" y1="12" x2="7" y2="12"/><line x1="19" y1="18" x2="5" y2="18"/></svg></button>
        <button class="tog" id="togAL" title="Left"><svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"><line x1="21" y1="6" x2="3" y2="6"/><line x1="15" y1="12" x2="3" y2="12"/><line x1="17" y1="18" x2="3" y2="18"/></svg></button>
        <button class="tog" id="togAR" title="Right"><svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"><line x1="21" y1="6" x2="3" y2="6"/><line x1="21" y1="12" x2="9" y2="12"/><line x1="21" y1="18" x2="7" y2="18"/></svg></button>
      </div>
      <div class="slr"><span class="sllb">Spacing</span><input type="range" id="txLs" min="-200" max="800" step="10" value="0"><span class="slv" id="vLs">0</span></div>
      <div class="slr"><span class="sllb">Line H</span><input type="range" id="txLh" min="0.5" max="3" step="0.1" value="1.2"><span class="slv" id="vLh">1.2</span></div>
      <div class="f">
        <label class="flb">Outline</label>
        <div class="inp2">
          <input type="color" class="csw" id="txStrokeCol" value="#FFFFFF">
          <div class="slr" style="flex:1"><input type="range" id="txStrokeW" min="0" max="20" step="1" value="0" style="flex:1"><span class="slv" id="vSw">0</span></div>
        </div>
      </div>
      <div class="f">
        <label class="flb">Curve (0 = straight)</label>
        <div class="slr"><input type="range" id="txCurve" min="0" max="400" step="10" value="0"><span class="slv" id="vCurve">0</span></div>
      </div>
      <button class="btn btn-p btn-w" id="btnAddTx">
        <svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"><line x1="12" y1="5" x2="12" y2="19"/><line x1="5" y1="12" x2="19" y2="12"/></svg>
        Add Text
      </button>
    </div>
  </div>

  <div class="sdiv"></div>

  <!-- SHADOW (shown when layer selected) -->
  <div id="secShadow" style="display:none">
    <div>
      <div class="shd" data-sec="sd">
        <span class="shd-ic"><svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect x="3" y="3" width="18" height="18" rx="2"/><path d="M7 21v-4a2 2 0 0 1 2-2h6"/><path d="M11 7h6v6"/></svg></span>
        <span class="shd-lb">Shadow</span>
        <span class="schev" id="cv-sd"><svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"><polyline points="18 15 12 9 6 15"/></svg></span>
      </div>
      <div class="sbd" id="sb-sd">
        <div class="f"><label class="flb">Color</label><input type="color" class="csw" id="sdCol" value="#000000"></div>
        <div class="slr"><span class="sllb">Blur</span><input type="range" id="sdBlur" min="0" max="30" step="1" value="0"><span class="slv" id="vSdB">0</span></div>
        <div class="slr"><span class="sllb">Offset X</span><input type="range" id="sdX" min="-20" max="20" step="1" value="0"><span class="slv" id="vSdX">0</span></div>
        <div class="slr"><span class="sllb">Offset Y</span><input type="range" id="sdY" min="-20" max="20" step="1" value="0"><span class="slv" id="vSdY">0</span></div>
      </div>
    </div>
    <div class="sdiv"></div>
  </div>

  <!-- STICKERS -->
  <div>
    <div class="shd" data-sec="cl">
      <span class="shd-ic"><svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M20.84 4.61a5.5 5.5 0 0 0-7.78 0L12 5.67l-1.06-1.06a5.5 5.5 0 0 0-7.78 7.78l1.06 1.06L12 21.23l7.78-7.78 1.06-1.06a5.5 5.5 0 0 0 0-7.78z"/></svg></span>
      <span class="shd-lb">Clipart</span>
      <span class="schev" id="cv-cl"><svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"><polyline points="18 15 12 9 6 15"/></svg></span>
    </div>
    <div class="sbd" id="sb-cl">
      <div class="sticker-grid" id="stickerGrid"></div>
    </div>
  </div>

  <div class="sdiv"></div>

  <!-- TEXT TEMPLATES -->
  <div>
    <div class="shd" data-sec="tmpl">
      <span class="shd-ic"><svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"/><polyline points="14 2 14 8 20 8"/><line x1="16" y1="13" x2="8" y2="13"/><line x1="16" y1="17" x2="8" y2="17"/></svg></span>
      <span class="shd-lb">Quick Text</span>
      <span class="schev" id="cv-tmpl"><svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"><polyline points="18 15 12 9 6 15"/></svg></span>
    </div>
    <div class="sbd" id="sb-tmpl">
      <div class="tmpl-list" id="tmplList">
        <?php
        $tmpls=[
          ['text'=>'Good Boy','font'=>'Syne','size'=>52,'color'=>'#1A56FF'],
          ['text'=>'Good Girl','font'=>'Syne','size'=>52,'color'=>'#DB2777'],
          ['text'=>'Best Dog Mom','font'=>'Georgia','size'=>38,'color'=>'#0A0F1E'],
          ['text'=>'Rescue Dog','font'=>'Impact','size'=>48,'color'=>'#FF3B4E'],
          ['text'=>"Rachel's Pet Supply",'font'=>'Syne','size'=>36,'color'=>'#1A56FF'],
          ['text'=>'Adopt Don\'t Shop','font'=>'Impact','size'=>40,'color'=>'#FF9500'],
          ['text'=>'✦ Good Pup ✦','font'=>'Syne','size'=>46,'color'=>'#0A0F1E'],
          ['text'=>'Dog Dad','font'=>'Syne','size'=>52,'color'=>'#7C3AED'],
        ];
        foreach($tmpls as $t):
        ?>
        <button class="tmpl-btn" data-text="<?=htmlspecialchars($t['text'])?>" data-font="<?=$t['font']?>" data-size="<?=$t['size']?>" data-color="<?=$t['color']?>">
          <?=htmlspecialchars($t['text'])?><small><?=$t['font']?></small>
        </button>
        <?php endforeach; ?>
      </div>
    </div>
  </div>

  <div class="sdiv"></div>

  <!-- QR CODE -->
  <div>
    <div class="shd" data-sec="qr">
      <span class="shd-ic"><svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect x="3" y="3" width="7" height="7"/><rect x="14" y="3" width="7" height="7"/><rect x="14" y="14" width="7" height="7"/><rect x="3" y="14" width="7" height="7"/><rect x="5" y="5" width="3" height="3" fill="currentColor"/><rect x="16" y="5" width="3" height="3" fill="currentColor"/><rect x="16" y="16" width="3" height="3" fill="currentColor"/><rect x="5" y="16" width="3" height="3" fill="currentColor"/></svg></span>
      <span class="shd-lb">QR Code</span>
      <span class="schev" id="cv-qr"><svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"><polyline points="18 15 12 9 6 15"/></svg></span>
    </div>
    <div class="sbd" id="sb-qr">
      <div class="f"><label class="flb">URL</label><input class="inp" type="text" id="qrUrl" placeholder="https://petblip.com/..."></div>
      <div class="inp2">
        <div class="f"><label class="flb">Foreground</label><input type="color" class="csw" id="qrFg" value="#0A0F1E"></div>
        <div class="f"><label class="flb">Background</label><input type="color" class="csw" id="qrBg" value="#FFFFFF"></div>
      </div>
      <div style="display:flex;gap:8px;align-items:center">
        <div class="qr-preview" id="qrPreview"></div>
        <button class="btn btn-p btn-sm" id="btnGenQr" style="flex:1">Generate & Add</button>
      </div>
      <div id="stQr" class="st"></div>
    </div>
  </div>

</div>
</aside>

<!-- ══ CANVAS ════════════════════════════════════════════════════ -->
<main class="stage">
  <div class="ctb">
    <button class="tb" id="tbFH" disabled><svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M8 3H5a2 2 0 0 0-2 2v14c0 1.1.9 2 2 2h3"/><path d="M16 3h3a2 2 0 0 1 2 2v14a2 2 0 0 1-2 2h-3"/><line x1="12" y1="20" x2="12" y2="4"/></svg> Flip H</button>
    <button class="tb" id="tbFV" disabled><svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M21 8V5a2 2 0 0 0-2-2H5a2 2 0 0 0-2 2v3"/><path d="M21 16v3a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-3"/><line x1="4" y1="12" x2="20" y2="12"/></svg> Flip V</button>
    <div class="ctb-sep"></div>
    <button class="tb" id="tbRL" disabled><svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M3 12a9 9 0 1 0 9-9 9.75 9.75 0 0 0-6.74 2.74L3 8"/><path d="M3 3v5h5"/></svg> −15°</button>
    <button class="tb" id="tbRR" disabled><svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M21 12a9 9 0 1 1-9-9 9.75 9.75 0 0 1 6.74 2.74L21 8"/><path d="M21 3v5h-5"/></svg> +15°</button>
    <div class="ctb-sep"></div>
    <button class="tb" id="tbDup" disabled title="Duplicate"><svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect x="8" y="8" width="13" height="13" rx="2"/><path d="M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1"/></svg> Dup</button>
    <button class="tb" id="tbCenter" disabled title="Center in zone"><svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="10"/><line x1="22" y1="12" x2="2" y2="12"/><line x1="12" y1="22" x2="12" y2="2"/></svg> Center</button>
    <button class="tb" id="tbFill" disabled title="Fill zone"><svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect x="3" y="3" width="18" height="18" rx="1.5"/></svg> Fill</button>
    <div class="ctb-sep"></div>
    <button class="tb active" id="tbGuides" title="Smart guides"><svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><line x1="3" y1="12" x2="21" y2="12"/><line x1="12" y1="3" x2="12" y2="21"/></svg> Guides</button>
    <div class="ctb-r">
      <button class="tb dng" id="tbDel" disabled><svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polyline points="3 6 5 6 21 6"/><path d="M19 6l-1 14H6L5 6"/><path d="M10 11v6"/><path d="M14 11v6"/><path d="M9 6V4h6v2"/></svg> Delete</button>
    </div>
  </div>

  <div class="cwrap" id="cwrap">
    <div class="cempty" id="cEmpty">
      <div class="cempty-ic"><svg width="52" height="52" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.1" stroke-linecap="round" stroke-linejoin="round"><rect x="3" y="3" width="18" height="18" rx="2"/><circle cx="9" cy="9" r="2"/><path d="m21 15-3.086-3.086a2 2 0 0 0-2.828 0L6 21"/></svg></div>
      <div class="cempty-t">Upload your pet photo to begin</div>
      <div class="cempty-h">It will be placed on the <?= htmlspecialchars($P['label']) ?> print area</div>
    </div>

    <!-- MOCKUP HOST -->
    <div class="mhost" id="mHost" style="">
      <img class="mbg" id="mBg" src="" alt="">
      <div class="mzone" id="mZone">
        <canvas id="fc"></canvas>
        <div class="guide-h" id="guideH"></div>
        <div class="guide-v" id="guideV"></div>
      </div>
      <img class="mover" id="mOver" src="" alt="">
      <div class="mring" id="mRing"></div>
    </div>
  </div>
</main>

<!-- ══ RIGHT PANEL ════════════════════════════════════════════════ -->
<aside class="panel right">
<div class="pscroll">

  <!-- PRODUCT INFO -->
  <div>
    <div class="shd" data-sec="pi">
      <span class="shd-ic"><svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="m7.5 4.27 9 5.15"/><path d="M21 8a2 2 0 0 0-1-1.73l-7-4a2 2 0 0 0-2 0l-7 4A2 2 0 0 0 3 8v8a2 2 0 0 0 1 1.73l7 4a2 2 0 0 0 2 0l7-4A2 2 0 0 0 21 16Z"/><path d="m3.3 7 8.7 5 8.7-5"/><path d="M12 22V12"/></svg></span>
      <span class="shd-lb">Product Specs</span>
      <span class="schev" id="cv-pi"><svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"><polyline points="18 15 12 9 6 15"/></svg></span>
    </div>
    <div class="sbd" id="sb-pi">
      <div class="prod-info-card">
        <div class="prod-info-title"><?= htmlspecialchars($P['label']) ?></div>
        <div class="prod-info-dim"><?= htmlspecialchars($P['note']) ?></div>
        <div>
          <span class="prod-info-dpi">
            <svg width="10" height="10" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"><path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"/><polyline points="7 10 12 15 17 10"/><line x1="12" y1="15" x2="12" y2="3"/></svg>
            <?= $P['print_px']['w'] ?>×<?= $P['print_px']['h'] ?>px
          </span>
        </div>
        <div style="font-size:11px;color:var(--ink3)">Print file exports at <?= $P['dpi'] ?> DPI — ready for DTF printing</div>
      </div>

      <!-- Front/Back toggle (when product has both) -->
      <?php if($P['has_back']): ?>
      <div class="view-toggle">
        <button class="vtb on" id="vtbFront">Front</button>
        <button class="vtb" id="vtbBack">Back</button>
      </div>
      <?php endif; ?>

      <!-- Product color picker -->
      <div class="f">
        <label class="flb">Product Color</label>
        <div class="col-chips" id="colChips">
          <?php foreach($P['colors'] as $i=>$c): ?>
          <div class="col-chip<?=$i===0?' on':''?>" style="background:<?=$c?>" data-color="<?=$c?>" title="<?=$c?>"></div>
          <?php endforeach; ?>
        </div>
      </div>
    </div>
  </div>

  <div class="sdiv"></div>

  <!-- LAYERS -->
  <div>
    <div class="shd" data-sec="ly">
      <span class="shd-ic"><svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polygon points="12 2 2 7 12 12 22 7 12 2"/><polyline points="2 17 12 22 22 17"/><polyline points="2 12 12 17 22 12"/></svg></span>
      <span class="shd-lb">Layers</span>
      <span class="schev" id="cv-ly"><svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"><polyline points="18 15 12 9 6 15"/></svg></span>
    </div>
    <div class="sbd" id="sb-ly">
      <div id="lyList"><div class="ly-mt">No layers yet</div></div>
      <div style="display:flex;gap:4px">
        <button class="btn btn-g btn-sm" style="flex:1" id="btnLyUp"><svg width="10" height="10" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"><line x1="12" y1="19" x2="12" y2="5"/><polyline points="5 12 12 5 19 12"/></svg>Up</button>
        <button class="btn btn-g btn-sm" style="flex:1" id="btnLyDn"><svg width="10" height="10" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"><line x1="12" y1="5" x2="12" y2="19"/><polyline points="19 12 12 19 5 12"/></svg>Down</button>
      </div>
    </div>
  </div>

  <div class="sdiv"></div>

  <!-- LAYER PROPERTIES -->
  <div id="secProp" style="display:none">
    <div>
      <div class="shd" data-sec="pr">
        <span class="shd-ic"><svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="3"/><path d="M19.07 4.93 17.66 6.34"/><path d="M10.5 2h3"/><path d="M10.5 22h3"/><path d="M4.93 4.93l1.41 1.41"/><path d="M2 10.5v3"/><path d="M22 10.5v3"/><path d="M6.34 17.66l-1.41 1.41"/><path d="M19.07 19.07l-1.41-1.41"/></svg></span>
        <span class="shd-lb">Properties</span>
        <span class="schev" id="cv-pr"><svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"><polyline points="18 15 12 9 6 15"/></svg></span>
      </div>
      <div class="sbd prop-g" id="sb-pr">
        <div class="slr"><span class="sllb">Scale X</span><input type="range" id="pSX" min=".05" max="4" step=".05" value="1"><span class="slv" id="vpSX">1.0</span></div>
        <div class="slr"><span class="sllb">Scale Y</span><input type="range" id="pSY" min=".05" max="4" step=".05" value="1"><span class="slv" id="vpSY">1.0</span></div>
        <div class="slr"><span class="sllb">Rotation</span><input type="range" id="pRt" min="-180" max="180" step="1" value="0"><span class="slv" id="vpRt">0°</span></div>
      </div>
    </div>
    <div class="sdiv"></div>
  </div>

  <!-- EXPORT -->
  <div>
    <div class="shd" data-sec="ex">
      <span class="shd-ic"><svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"/><polyline points="7 10 12 15 17 10"/><line x1="12" y1="15" x2="12" y2="3"/></svg></span>
      <span class="shd-lb">Download Print File</span>
      <span class="schev" id="cv-ex"><svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"><polyline points="18 15 12 9 6 15"/></svg></span>
    </div>
    <div class="sbd" id="sb-ex">
      <div class="expbox">
        <div class="exp-ttl">Export Settings</div>
        <div class="f">
          <label class="flb">Mode</label>
          <select class="inp" id="exMode">
            <option value="print" selected>Print file only (no mockup)</option>
            <option value="mockup">With product mockup</option>
          </select>
        </div>
        <div class="f">
          <label class="flb">Resolution</label>
          <select class="inp" id="exSc">
            <option value="screen">Screen (1×)</option>
            <option value="2x">High res (2×)</option>
            <option value="print" selected>Full print DPI</option>
          </select>
        </div>
        <div class="fmt-row">
          <div class="fmt on" data-fmt="png">PNG</div>
          <div class="fmt" data-fmt="jpeg">JPEG</div>
        </div>
        <button class="btn btn-p btn-w" id="btnDl" disabled>
          <svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"/><polyline points="7 10 12 15 17 10"/><line x1="12" y1="15" x2="12" y2="3"/></svg>
          Download
        </button>
        <div id="stEx" class="st"></div>
      </div>
    </div>
  </div>

</div>
</aside>
</div><!-- /shell -->

<!-- ═══ ORDER MODAL ═══════════════════════════════════════════════ -->
<div class="mbg-modal" id="orderModal">
  <div class="mbox">
    <div class="m-title">Place Your Order</div>
    <div class="m-sub">Your design will be sent to our team for production.</div>

    <div class="order-summary">
      <div class="order-thumb">
        <div class="order-thumb-chk"></div>
        <img id="orderThumb" src="" alt="">
      </div>
      <div class="order-info">
        <div class="order-prod"><?= htmlspecialchars($P['label']) ?></div>
        <div class="order-dim"><?= htmlspecialchars($P['note']) ?></div>
        <span class="order-badge">
          <svg width="10" height="10" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"><polyline points="20 6 9 17 4 12"/></svg>
          Print-ready PNG included
        </span>
      </div>
    </div>

    <!-- Mockup composite preview -->
    <div class="m-mockup-preview">
      <div class="m-mockup-preview-chk"></div>
      <img id="orderMockupImg" src="" alt="">
    </div>

    <div class="f" style="margin-bottom:10px">
      <label class="flb">Design Label</label>
      <input class="inp" type="text" id="orderLabel" placeholder="e.g. Rex T-Shirt — Blue">
    </div>
    <div class="f" style="margin-bottom:14px">
      <label class="flb">Notes for production (optional)</label>
      <textarea class="inp" id="orderNotes" rows="2" placeholder="Any special instructions…" style="resize:vertical"></textarea>
    </div>

    <div class="m-actions">
      <button class="btn btn-s" id="btnOrderCancel">Cancel</button>
      <button class="btn btn-prod" id="btnOrderConfirm">
        <svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M6 2 3 6v14a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2V6l-3-4z"/><line x1="3" y1="6" x2="21" y2="6"/><path d="M16 10a4 4 0 0 1-8 0"/></svg>
        Submit Order
      </button>
    </div>
    <div id="stOrder" class="st" style="margin-top:8px"></div>
  </div>
</div>

<div id="qrHidden" style="position:absolute;left:-9999px;top:-9999px;width:200px;height:200px"></div>

<script>
'use strict';

/* ══ PRODUCTS DATA ══════════════════════════════════════════════ */
const PRODS = <?= $productsJson ?>;
const PROD_KEY = <?= json_encode($productKey) ?>;
const P = PRODS[PROD_KEY];
const MOCKUP_BASE = <?= json_encode(MOCKUP_URL) ?>;

/* ══ PHOTO FILTERS ══════════════════════════════════════════════ */
const FILTERS = {
  Normal:      null,
  Grayscale:   () => new fabric.Image.filters.Grayscale(),
  Sepia:       () => new fabric.Image.filters.Sepia(),
  'B&W':       () => new fabric.Image.filters.BlackWhite({threshold:0.5}),
  Vintage:     () => new fabric.Image.filters.Vintage(),
  Kodachrome:  () => new fabric.Image.filters.Kodachrome(),
  Polaroid:    () => new fabric.Image.filters.Polaroid(),
  Brownie:     () => new fabric.Image.filters.Brownie(),
  Technicolor: () => new fabric.Image.filters.Technicolor(),
};

/* ══ CLIPART ════════════════════════════════════════════════════ */
const CLIPART=[
  {name:'Paw',svg:`<svg viewBox="0 0 100 100" fill="#1A56FF" xmlns="http://www.w3.org/2000/svg"><ellipse cx="50" cy="65" rx="22" ry="20"/><ellipse cx="28" cy="42" rx="10" ry="12"/><ellipse cx="72" cy="42" rx="10" ry="12"/><ellipse cx="18" cy="28" rx="8" ry="9"/><ellipse cx="82" cy="28" rx="8" ry="9"/></svg>`},
  {name:'Bone',svg:`<svg viewBox="0 0 100 40" fill="#FF9500" xmlns="http://www.w3.org/2000/svg"><rect x="20" y="14" width="60" height="12" rx="4"/><circle cx="16" cy="12" r="9"/><circle cx="16" cy="28" r="9"/><circle cx="84" cy="12" r="9"/><circle cx="84" cy="28" r="9"/></svg>`},
  {name:'Heart',svg:`<svg viewBox="0 0 100 90" fill="#FF3B4E" xmlns="http://www.w3.org/2000/svg"><path d="M50 80 C50 80 5 50 5 25 C5 10 18 2 30 8 C40 13 50 25 50 25 C50 25 60 13 70 8 C82 2 95 10 95 25 C95 50 50 80 50 80Z"/></svg>`},
  {name:'Star',svg:`<svg viewBox="0 0 100 100" fill="#FF9500" xmlns="http://www.w3.org/2000/svg"><polygon points="50,5 61,35 95,35 68,57 79,91 50,70 21,91 32,57 5,35 39,35"/></svg>`},
  {name:'Crown',svg:`<svg viewBox="0 0 100 70" fill="#FF9500" xmlns="http://www.w3.org/2000/svg"><polygon points="5,65 20,20 50,45 80,20 95,65"/><circle cx="5" cy="65" r="6"/><circle cx="95" cy="65" r="6"/><circle cx="20" cy="20" r="6"/><circle cx="50" cy="45" r="6"/><circle cx="80" cy="20" r="6"/><rect x="5" y="60" width="90" height="10" rx="3"/></svg>`},
  {name:'Ribbon',svg:`<svg viewBox="0 0 100 100" fill="#DB2777" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="50" r="18"/><path d="M50 32 Q30 10 15 20 Q5 30 30 48Z"/><path d="M50 32 Q70 10 85 20 Q95 30 70 48Z"/><path d="M50 68 Q30 90 15 80 Q5 70 30 52Z"/><path d="M50 68 Q70 90 85 80 Q95 70 70 52Z"/></svg>`},
  {name:'Shield',svg:`<svg viewBox="0 0 100 110" fill="#7C3AED" xmlns="http://www.w3.org/2000/svg"><path d="M50 5 L95 20 L95 55 C95 78 74 97 50 105 C26 97 5 78 5 55 L5 20 Z"/></svg>`},
  {name:'PB',svg:`<svg viewBox="0 0 100 100" fill="none" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="50" r="48" fill="#1A56FF"/><text x="50" y="65" text-anchor="middle" font-family="Syne,sans-serif" font-weight="800" font-size="30" fill="#fff">PB</text></svg>`},
  {name:'Paw2',svg:`<svg viewBox="0 0 80 100" fill="#0A0F1E" xmlns="http://www.w3.org/2000/svg"><ellipse cx="35" cy="70" rx="15" ry="20"/><ellipse cx="18" cy="50" rx="8" ry="10"/><ellipse cx="52" cy="50" rx="8" ry="10"/><ellipse cx="10" cy="38" rx="7" ry="8"/><ellipse cx="60" cy="38" rx="7" ry="8"/></svg>`},
  {name:'Spark',svg:`<svg viewBox="0 0 100 100" fill="#FF9500" xmlns="http://www.w3.org/2000/svg"><polygon points="50,5 53,44 92,47 53,50 56,89 50,52 8,55 47,50 44,11"/></svg>`},
  {name:'Banner',svg:`<svg viewBox="0 0 120 40" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="M5 5 L115 5 L115 30 L60 38 L5 30 Z" fill="#1A56FF"/><path d="M5 5 L5 30 L0 35 L5 5Z" fill="#1040D6"/><path d="M115 5 L115 30 L120 35 L115 5Z" fill="#1040D6"/></svg>`},
  {name:'Circle',svg:`<svg viewBox="0 0 100 100" fill="none" stroke="#1A56FF" stroke-width="8" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="50" r="42"/></svg>`},
];

/* ══ STATE ══════════════════════════════════════════════════════ */
const S = {
  cv:null, mainImg:null,
  origB64:null, origFilename:null,
  rmBgB64:null, rmBgFilename:null,
  busy:false,
  hist:[],histIdx:-1,
  fmt:'png',
  bold:false,italic:false,align:'center',
  activeFilter:'Normal',
  smartGuides:true,
  zoneVisible:true,
  view:'front',
  prodColor:P.colors?.[0]||'#FFFFFF',
  imageId:<?= $imageId ?>,
  returnUrl:<?= json_encode($returnUrl) ?>,
  // mockup geometry
  hostW:0,hostH:0,
  zoneL:0,zoneT:0,zoneW:0,zoneH:0,
};

/* ══ MOCKUP SETUP ═══════════════════════════════════════════════ */
function setupMockup(view='front') {
  const zone = view==='back' && P.zone_b ? P.zone_b : P.zone_f;
  const mockupFile = view==='back' && P.mockup_b ? P.mockup_b : P.mockup_f;
  const wrap = document.getElementById('cwrap');
  const maxW = Math.min(wrap.clientWidth-48, 520);
  const maxH = Math.min(wrap.clientHeight-48, 560);
  const sc   = Math.min(maxW/P.img_w, maxH/P.img_h);
  const hW   = Math.round(P.img_w*sc);
  const hH   = Math.round(P.img_h*sc);
  S.hostW=hW; S.hostH=hH;

  // Zone in display pixels
  const zL=Math.round(zone.l/100*hW);
  const zT=Math.round(zone.t/100*hH);
  const zW=Math.round(zone.w/100*hW);
  const zH=Math.round(zone.h/100*hH);
  S.zoneL=zL;S.zoneT=zT;S.zoneW=zW;S.zoneH=zH;

  const host=document.getElementById('mHost');
  host.style.cssText=`width:${hW}px;height:${hH}px;display:block;`;

  const mockSrc=MOCKUP_BASE+mockupFile;
  document.getElementById('mBg').src=mockSrc;
  document.getElementById('mOver').src=mockSrc;

  const mzone=document.getElementById('mZone');
  mzone.style.cssText=`left:${zL}px;top:${zT}px;width:${zW}px;height:${zH}px;`;

  const ring=document.getElementById('mRing');
  ring.style.cssText=`left:${zL}px;top:${zT}px;width:${zW}px;height:${zH}px;opacity:${S.zoneVisible?1:0}`;

  setupCanvas(zW,zH);
}

function setupCanvas(zW,zH) {
  let savedJson=null;
  if(S.cv){savedJson=JSON.stringify(S.cv.toJSON(['data']));S.cv.dispose();}

  S.cv=new fabric.Canvas('fc',{width:zW,height:zH,backgroundColor:null,preserveObjectStacking:true});
  S.cv._lW=zW;S.cv._lH=zH;
  // logical print size for DPI export
  S.cv._printW=P.print_px.w;S.cv._printH=P.print_px.h;

  S.cv.on('object:modified',saveH);
  S.cv.on('object:added',()=>{mkLy();saveH();});
  S.cv.on('object:removed',()=>{mkLy();saveH();});
  S.cv.on('selection:created',onSel);
  S.cv.on('selection:updated',onSel);
  S.cv.on('selection:cleared',onDesel);
  if(S.smartGuides)attachGuides();

  if(savedJson){S.cv.loadFromJSON(savedJson,()=>{S.cv.renderAll();mkLy();});}
  saveH();
}

/* ══ SMART GUIDES ═══════════════════════════════════════════════ */
function attachGuides(){
  const gH=document.getElementById('guideH'),gV=document.getElementById('guideV');
  const THRESH=7;
  S.cv.on('object:moving',({target:obj})=>{
    const cx=S.cv.width/2,cy=S.cv.height/2;
    const ob=obj.getBoundingRect(true);
    const ox=ob.left+ob.width/2,oy=ob.top+ob.height/2;
    let sX=false,sY=false;
    if(Math.abs(ox-cx)<THRESH){obj.left+=cx-ox;sX=true;}
    if(Math.abs(oy-cy)<THRESH){obj.top+=cy-oy;sY=true;}
    gV.style.left=(S.cv.width/2)+'px';gV.classList.toggle('show',sX);
    gH.style.top=(S.cv.height/2)+'px';gH.classList.toggle('show',sY);
    S.cv.renderAll();
  });
  S.cv.on('object:modified',()=>{gH.classList.remove('show');gV.classList.remove('show');});
}

/* ══ UPLOAD ══════════════════════════════════════════════════════ */
const fileIn=document.getElementById('fileIn');
const upZone=document.getElementById('upZone');
if(upZone){
  upZone.addEventListener('click',()=>fileIn.click());
  upZone.addEventListener('dragover',e=>{e.preventDefault();upZone.classList.add('drag');});
  upZone.addEventListener('dragleave',()=>upZone.classList.remove('drag'));
  upZone.addEventListener('drop',e=>{e.preventDefault();upZone.classList.remove('drag');if(e.dataTransfer.files[0])upload(e.dataTransfer.files[0]);});
  fileIn?.addEventListener('change',e=>{if(e.target.files[0])upload(e.target.files[0]);});
}

function upload(file){
  if(file.size>20*1024*1024){st('stUp','er','File too large (max 20 MB)');return;}
  st('stUp','ld','Uploading…');
  const fd=new FormData();fd.append('image',file);fd.append('action','upload');
  fetch(location.pathname,{method:'POST',body:fd}).then(r=>r.json()).then(d=>{
    if(d.error){st('stUp','er',d.error);return;}
    S.origB64=d.b64;S.origFilename=d.filename;S.rmBgB64=null;
    loadImg(d.b64,false,()=>checkDPI());
    hideSt('stUp');showEditor();
    document.getElementById('btnRmBg').disabled=false;
  }).catch(()=>st('stUp','er','Upload failed'));
}

function checkDPI(){
  if(!S.mainImg)return;
  const natW=S.mainImg.getOriginalSize().width;
  const natH=S.mainImg.getOriginalSize().height;
  // compare natural px to print px at 150 DPI minimum
  const minPx150W=P.print_px.w/2,minPx150H=P.print_px.h/2;
  const warn=(natW<minPx150W||natH<minPx150H);
  document.getElementById('dpiWarn').classList.toggle('show',warn);
  if(warn){
    const eff=Math.round(natW/P.print_px.w*P.dpi);
    document.getElementById('dpiWarnTxt').textContent=`Low resolution (~${eff} DPI) — may print blurry at ${P.print_in.w}×${P.print_in.h} in`;
  }
}

function showEditor(){
  document.getElementById('cEmpty').classList.add('gone');
  // mockup already visible
  document.getElementById('btnDl').disabled=false;
  document.getElementById('btnDownload').disabled=false;
  document.getElementById('btnOrder').disabled=false;
  setObjBtns(false);
}

function loadImg(src,keepRest=false,cb=null){
  fabric.Image.fromURL(src,img=>{
    if(!keepRest){
      S.cv.clear();
    } else {
      // Remove ALL image layers that are the base photo (not stickers/text)
      const toRemove=S.cv.getObjects().filter(o=>o.type==='image'&&!o.data?.isSticker&&!o.data?.isQR);
      toRemove.forEach(o=>S.cv.remove(o));
      S.mainImg=null;
    }
    const cw=S.cv.width,ch=S.cv.height;
    const sc=Math.min(cw/img.width,ch/img.height)*.9;
    img.set({left:cw/2,top:ch/2,originX:'center',originY:'center',scaleX:sc,scaleY:sc});
    img.applyFilters();
    S.cv.insertAt(img,0);S.cv.setActiveObject(img);
    S.mainImg=img;S.cv.renderAll();rstSliders();mkLy();
    if(cb)cb();
  },{crossOrigin:'anonymous'});
}

/* ══ REMOVE BG ═══════════════════════════════════════════════════ */
document.getElementById('btnRmBg').addEventListener('click',()=>{
  if(!S.origFilename||S.busy)return;
  S.busy=true;st('stBg','ld','Removing background…');
  document.getElementById('btnRmBg').disabled=true;
  const fd=new FormData();fd.append('action','remove_bg');fd.append('filename',S.origFilename);
  fetch(location.pathname,{method:'POST',body:fd}).then(r=>r.json()).then(d=>{
    S.busy=false;document.getElementById('btnRmBg').disabled=false;
    if(d.error){st('stBg','er',d.error);return;}
    S.rmBgB64=d.b64;S.rmBgFilename=d.filename;
    loadImg(d.b64,true);
    st('stBg','ok','Background removed!');
    document.getElementById('btnRestore').disabled=false;
    setTimeout(()=>hideSt('stBg'),4000);
  }).catch(()=>{S.busy=false;document.getElementById('btnRmBg').disabled=false;st('stBg','er','Request failed');});
});
document.getElementById('btnRestore').addEventListener('click',()=>{if(S.origB64){loadImg(S.origB64,true);S.rmBgB64=null;}});

/* ══ FRONT / BACK VIEW ═══════════════════════════════════════════ */
const vtbFront=document.getElementById('vtbFront');
const vtbBack=document.getElementById('vtbBack');
if(vtbFront){
  vtbFront.addEventListener('click',()=>{
    S.view='front';vtbFront.classList.add('on');vtbBack.classList.remove('on');
    document.getElementById('mBg').src=MOCKUP_BASE+P.mockup_f;
    document.getElementById('mOver').src=MOCKUP_BASE+P.mockup_f;
    setupMockup('front');
    if(S.origB64)loadImg(S.rmBgB64||S.origB64);
  });
  vtbBack.addEventListener('click',()=>{
    S.view='back';vtbBack.classList.add('on');vtbFront.classList.remove('on');
    const bf=P.mockup_b||P.mockup_f;
    document.getElementById('mBg').src=MOCKUP_BASE+bf;
    document.getElementById('mOver').src=MOCKUP_BASE+bf;
    setupMockup('back');
    if(S.origB64)loadImg(S.rmBgB64||S.origB64);
  });
}

/* ══ PRODUCT COLOR ═══════════════════════════════════════════════ */
document.getElementById('colChips')?.addEventListener('click',e=>{
  const chip=e.target.closest('.col-chip');if(!chip)return;
  document.querySelectorAll('.col-chip').forEach(c=>c.classList.remove('on'));
  chip.classList.add('on');S.prodColor=chip.dataset.color;
  // tint mockup by changing CSS filter on the bg image
  const bg=document.getElementById('mBg');
  if(chip.dataset.color==='#FFFFFF'||chip.dataset.color==='#F5F5F5'){
    bg.style.filter='none';
  } else if(chip.dataset.color==='#000000'){
    bg.style.filter='brightness(0.25)';
  } else {
    // subtle hue shift via sepia + hue-rotate
    bg.style.filter='sepia(0.5) hue-rotate(180deg) saturate(1.5)';
    // In production you'd use SVG color blend or actual colored mockup images
  }
});

/* ══ FILTERS ═════════════════════════════════════════════════════ */
const FILTER_CLASSES = {
  'Normal':'f-normal','Grayscale':'f-grayscale','Sepia':'f-sepia',
  "B&W":'f-bw','Vintage':'f-vintage','Kodachrome':'f-kodachrome',
  'Polaroid':'f-polaroid','Brownie':'f-brownie','Technicolor':'f-technicolor'
};
function buildFilterGrid(){
  const grid=document.getElementById('filterGrid');
  Object.keys(FILTERS).forEach(name=>{
    const cls=FILTER_CLASSES[name]||'f-normal';
    const chip=document.createElement('div');
    chip.className='filter-chip'+(name==='Normal'?' on':'');
    chip.dataset.filter=name;
    chip.innerHTML=`<div class="filter-thumb ${cls}">${name==='Normal'?'':''}${name==='B&W'?'B&W':''}</div><div class="filter-name">${name}</div>`;
    grid.appendChild(chip);
    chip.addEventListener('click',()=>applyPhotoFilter(name));
  });
}
buildFilterGrid();

function applyPhotoFilter(name){
  const obj=S.mainImg;if(!obj)return;
  S.activeFilter=name;
  document.querySelectorAll('.filter-chip').forEach(c=>c.classList.toggle('on',c.dataset.filter===name));
  const photoF=name!=='Normal'?[FILTERS[name]()]:[];
  const b=+document.getElementById('sBr').value,c=+document.getElementById('sCo').value,sat=+document.getElementById('sSa').value;
  const adjF=[];
  if(b!==0)adjF.push(new fabric.Image.filters.Brightness({brightness:b}));
  if(c!==0)adjF.push(new fabric.Image.filters.Contrast({contrast:c}));
  if(sat!==0)adjF.push(new fabric.Image.filters.Saturation({saturation:sat}));
  obj.filters=[...photoF,...adjF];obj.applyFilters();S.cv.renderAll();
}

/* ══ ADJUSTMENTS ═════════════════════════════════════════════════ */
[['sBr','vBr'],['sCo','vCo'],['sSa','vSa'],['sOp','vOp']].forEach(([s,v])=>{
  document.getElementById(s).addEventListener('input',e=>{
    const n=parseFloat(e.target.value);
    document.getElementById(v).textContent=Number.isInteger(n)?n:n.toFixed(2).replace(/\.?0+$/,'');
    applyAdj();
  });
});
function applyAdj(){
  const o=S.cv.getActiveObject()||S.mainImg;if(!o||o.type!=='image')return;
  const photoF=S.activeFilter!=='Normal'?[FILTERS[S.activeFilter]()]:[];
  const b=+document.getElementById('sBr').value,c=+document.getElementById('sCo').value,sat=+document.getElementById('sSa').value;
  const adjF=[];
  if(b!==0)adjF.push(new fabric.Image.filters.Brightness({brightness:b}));
  if(c!==0)adjF.push(new fabric.Image.filters.Contrast({contrast:c}));
  if(sat!==0)adjF.push(new fabric.Image.filters.Saturation({saturation:sat}));
  o.filters=[...photoF,...adjF];
  o.opacity=+document.getElementById('sOp').value;
  o.applyFilters();S.cv.renderAll();
}
document.getElementById('btnRstAdj').addEventListener('click',()=>{
  [['sBr','vBr',0],['sCo','vCo',0],['sSa','vSa',0],['sOp','vOp',1]].forEach(([s,v,n])=>{document.getElementById(s).value=n;document.getElementById(v).textContent=n;});applyAdj();
});
function rstSliders(){[['sBr','vBr',0],['sCo','vCo',0],['sSa','vSa',0],['sOp','vOp',1]].forEach(([s,v,n])=>{document.getElementById(s).value=n;document.getElementById(v).textContent=n;});}

/* ══ TEXT ════════════════════════════════════════════════════════ */
// Live update selected text when controls change
['txCol','txFn','txSz'].forEach(id=>{
  document.getElementById(id).addEventListener('input',()=>{
    const o=S.cv.getActiveObject();
    if(!o||o.type!=='text')return;
    const col=document.getElementById('txCol').value;
    const fn=document.getElementById('txFn').value;
    const sz=+document.getElementById('txSz').value||42;
    o.set({fill:col,fontFamily:fn,fontSize:Math.round(sz*(S.cv.width/400))});
    S.cv.renderAll();
  });
  // also on change for select
  document.getElementById(id).addEventListener('change',()=>{
    const o=S.cv.getActiveObject();
    if(!o||o.type!=='text')return;
    const col=document.getElementById('txCol').value;
    const fn=document.getElementById('txFn').value;
    const sz=+document.getElementById('txSz').value||42;
    o.set({fill:col,fontFamily:fn,fontSize:Math.round(sz*(S.cv.width/400))});
    S.cv.renderAll();
  });
});

document.getElementById('togB').addEventListener('click',()=>{S.bold=!S.bold;document.getElementById('togB').classList.toggle('on',S.bold);const o=S.cv.getActiveObject();if(o&&o.type==='text'){o.set('fontWeight',S.bold?'bold':'normal');S.cv.renderAll();}});
document.getElementById('togI').addEventListener('click',()=>{S.italic=!S.italic;document.getElementById('togI').classList.toggle('on',S.italic);const o=S.cv.getActiveObject();if(o&&o.type==='text'){o.set('fontStyle',S.italic?'italic':'normal');S.cv.renderAll();}});
[['togAL','left'],['togAC','center'],['togAR','right']].forEach(([id,a])=>{document.getElementById(id).addEventListener('click',()=>{['togAL','togAC','togAR'].forEach(i=>document.getElementById(i).classList.remove('on'));document.getElementById(id).classList.add('on');S.align=a;const o=S.cv.getActiveObject();if(o&&o.type==='text'){o.set('textAlign',a);S.cv.renderAll();}});});
['txLs','txLh','txStrokeW','txStrokeCol'].forEach(id=>{document.getElementById(id).addEventListener('input',()=>{const o=S.cv.getActiveObject();if(!o||o.type!=='text')return;o.set({charSpacing:+document.getElementById('txLs').value,lineHeight:+document.getElementById('txLh').value,stroke:document.getElementById('txStrokeCol').value,strokeWidth:+document.getElementById('txStrokeW').value});document.getElementById('vLs').textContent=document.getElementById('txLs').value;document.getElementById('vLh').textContent=parseFloat(document.getElementById('txLh').value).toFixed(1);document.getElementById('vSw').textContent=document.getElementById('txStrokeW').value;S.cv.renderAll();});});
['sdCol','sdBlur','sdX','sdY'].forEach(id=>{document.getElementById(id).addEventListener('input',()=>{const o=S.cv.getActiveObject();if(!o)return;const blur=+document.getElementById('sdBlur').value,ox=+document.getElementById('sdX').value,oy=+document.getElementById('sdY').value;document.getElementById('vSdB').textContent=blur;document.getElementById('vSdX').textContent=ox;document.getElementById('vSdY').textContent=oy;o.set('shadow',new fabric.Shadow({color:document.getElementById('sdCol').value,blur,offsetX:ox,offsetY:oy}));S.cv.renderAll();});});
document.getElementById('txCurve').addEventListener('input',e=>{const v=+e.target.value;document.getElementById('vCurve').textContent=v;const o=S.cv.getActiveObject();if(o&&o.type==='text'){if(v>0){const p=new fabric.Path(`M ${-Math.PI*v} 0 A ${v} ${v} 0 1 1 ${Math.PI*v} 0`,{visible:false,fill:'',stroke:''});o.set('path',p);}else o.set('path',null);S.cv.renderAll();}});

document.getElementById('btnAddTx').addEventListener('click',()=>{
  const txt=document.getElementById('txCt').value.trim()||'My Pet';
  const size=+document.getElementById('txSz').value||42;
  const curveR=+document.getElementById('txCurve').value;
  const sw=+document.getElementById('txStrokeW').value;
  const t=new fabric.Text(txt,{
    left:S.cv.width/2,top:S.cv.height*.7,originX:'center',originY:'center',
    fontSize:Math.round(size*(S.cv.width/400)),
    fill:document.getElementById('txCol').value,
    fontFamily:document.getElementById('txFn').value,
    fontWeight:S.bold?'bold':'normal',fontStyle:S.italic?'italic':'normal',
    textAlign:S.align,charSpacing:+document.getElementById('txLs').value,
    lineHeight:+document.getElementById('txLh').value,
    stroke:sw>0?document.getElementById('txStrokeCol').value:'',strokeWidth:sw,paintFirst:'stroke',
  });
  if(curveR>0){const p=new fabric.Path(`M ${-Math.PI*curveR} 0 A ${curveR} ${curveR} 0 1 1 ${Math.PI*curveR} 0`,{visible:false,fill:'',stroke:''});t.set('path',p);}
  S.cv.add(t);S.cv.setActiveObject(t);S.cv.renderAll();
});

document.getElementById('tmplList').querySelectorAll('.tmpl-btn').forEach(btn=>{
  btn.addEventListener('click',()=>{
    const t=new fabric.Text(btn.dataset.text,{left:S.cv.width/2,top:S.cv.height/2,originX:'center',originY:'center',fontSize:Math.round(+btn.dataset.size*(S.cv.width/400)),fill:btn.dataset.color,fontFamily:btn.dataset.font,fontWeight:'bold',textAlign:'center'});
    S.cv.add(t);S.cv.setActiveObject(t);S.cv.renderAll();
  });
});

/* ══ CLIPART ═════════════════════════════════════════════════════ */
function buildStickerGrid(){
  const grid=document.getElementById('stickerGrid');
  CLIPART.forEach(item=>{
    const btn=document.createElement('div');btn.className='sticker-btn';btn.title=item.name;btn.innerHTML=item.svg;
    btn.addEventListener('click',()=>{
      const blob=new Blob([item.svg],{type:'image/svg+xml'});
      const url=URL.createObjectURL(blob);
      fabric.Image.fromURL(url,img=>{
        const sz=Math.round(Math.min(S.cv.width,S.cv.height)*.25);
        img.scaleToWidth(sz);
        img.set({left:S.cv.width/2+Math.random()*30-15,top:S.cv.height/2+Math.random()*30-15,originX:'center',originY:'center'});
        img.data={name:item.name,isSticker:true};
        S.cv.add(img);S.cv.setActiveObject(img);S.cv.renderAll();URL.revokeObjectURL(url);
      });
    });
    grid.appendChild(btn);
  });
}
buildStickerGrid();

/* ══ QR CODE ═════════════════════════════════════════════════════ */
document.getElementById('btnGenQr').addEventListener('click',()=>{
  const url=document.getElementById('qrUrl').value.trim();if(!url){st('stQr','er','Enter URL first');return;}
  st('stQr','ld','Generating…');
  const fg=document.getElementById('qrFg').value,bg=document.getElementById('qrBg').value;
  const div=document.getElementById('qrHidden');div.innerHTML='';
  try{
    new QRCode(div,{text:url,width:200,height:200,colorDark:fg,colorLight:bg,correctLevel:QRCode.CorrectLevel.H});
    setTimeout(()=>{
      const c=div.querySelector('canvas')||div.querySelector('img');if(!c){st('stQr','er','Failed');return;}
      const src=c.tagName==='CANVAS'?c.toDataURL('image/png'):c.src;
      document.getElementById('qrPreview').innerHTML=`<img src="${src}" style="width:100%;height:100%;object-fit:contain">`;
      fabric.Image.fromURL(src,img=>{const sz=Math.round(Math.min(S.cv.width,S.cv.height)*.28);img.scaleToWidth(sz);img.set({left:S.cv.width/2,top:S.cv.height/2,originX:'center',originY:'center'});img.data={name:'QR Code',isQR:true};S.cv.add(img);S.cv.setActiveObject(img);S.cv.renderAll();st('stQr','ok','QR added!');setTimeout(()=>hideSt('stQr'),3000);});
    },200);
  }catch(e){st('stQr','er','Error');}
});

/* ══ TOOLBAR ═════════════════════════════════════════════════════ */
document.getElementById('tbFH').addEventListener('click',()=>{const o=S.cv.getActiveObject();if(o){o.set('flipX',!o.flipX);S.cv.renderAll();}});
document.getElementById('tbFV').addEventListener('click',()=>{const o=S.cv.getActiveObject();if(o){o.set('flipY',!o.flipY);S.cv.renderAll();}});
document.getElementById('tbRL').addEventListener('click',()=>{const o=S.cv.getActiveObject();if(o){o.rotate((o.angle||0)-15);S.cv.renderAll();}});
document.getElementById('tbRR').addEventListener('click',()=>{const o=S.cv.getActiveObject();if(o){o.rotate((o.angle||0)+15);S.cv.renderAll();}});
document.getElementById('tbDel').addEventListener('click',()=>{const o=S.cv.getActiveObject();if(o){if(o===S.mainImg)S.mainImg=null;S.cv.remove(o);S.cv.renderAll();}});
document.getElementById('tbDup').addEventListener('click',()=>{const o=S.cv.getActiveObject();if(o){o.clone(cl=>{cl.set({left:(o.left||0)+20,top:(o.top||0)+20});S.cv.add(cl);S.cv.setActiveObject(cl);S.cv.renderAll();});}});
document.getElementById('tbCenter').addEventListener('click',()=>{const o=S.cv.getActiveObject();if(o){o.set({left:S.cv.width/2,top:S.cv.height/2,originX:'center',originY:'center'});S.cv.renderAll();}});
document.getElementById('tbFill').addEventListener('click',()=>{const o=S.cv.getActiveObject();if(o){const sc=Math.min(S.cv.width/(o.width||1),S.cv.height/(o.height||1));o.set({scaleX:sc,scaleY:sc,left:S.cv.width/2,top:S.cv.height/2,originX:'center',originY:'center'});S.cv.renderAll();}});
document.getElementById('tbGuides').addEventListener('click',()=>{S.smartGuides=!S.smartGuides;document.getElementById('tbGuides').classList.toggle('active',S.smartGuides);if(S.smartGuides)attachGuides();else S.cv.off('object:moving');document.getElementById('guideH').classList.remove('show');document.getElementById('guideV').classList.remove('show');});
document.getElementById('btnZoneToggle').addEventListener('click',()=>{S.zoneVisible=!S.zoneVisible;document.getElementById('mRing').style.opacity=S.zoneVisible?'1':'0';});
function setObjBtns(on){['tbFH','tbFV','tbRL','tbRR','tbDel','tbDup','tbCenter','tbFill'].forEach(id=>document.getElementById(id).disabled=!on);}

/* ══ SELECTION ═══════════════════════════════════════════════════ */
function onSel(e){
  const o=e.selected?.[0]||S.cv.getActiveObject();if(!o)return;
  setObjBtns(true);document.getElementById('secProp').style.display='';
  document.getElementById('secShadow').style.display='';
  // Sync text controls to selected text layer
  if(o.type==='text'){
    document.getElementById('txCol').value=o.fill||'#0A0F1E';
    document.getElementById('txFn').value=o.fontFamily||'Syne';
    document.getElementById('txSz').value=Math.round((o.fontSize||42)/(S.cv.width/400));
    document.getElementById('togB').classList.toggle('on',o.fontWeight==='bold');
    document.getElementById('togI').classList.toggle('on',o.fontStyle==='italic');
    S.bold=o.fontWeight==='bold';S.italic=o.fontStyle==='italic';
  }
  document.getElementById('pSX').value=(o.scaleX||1).toFixed(2);document.getElementById('vpSX').textContent=(o.scaleX||1).toFixed(1);
  document.getElementById('pSY').value=(o.scaleY||1).toFixed(2);document.getElementById('vpSY').textContent=(o.scaleY||1).toFixed(1);
  document.getElementById('pRt').value=Math.round(o.angle||0);document.getElementById('vpRt').textContent=Math.round(o.angle||0)+'°';
  if(o.type==='text'&&o.shadow){document.getElementById('sdBlur').value=o.shadow.blur||0;document.getElementById('vSdB').textContent=o.shadow.blur||0;document.getElementById('sdX').value=o.shadow.offsetX||0;document.getElementById('vSdX').textContent=o.shadow.offsetX||0;document.getElementById('sdY').value=o.shadow.offsetY||0;document.getElementById('vSdY').textContent=o.shadow.offsetY||0;}
  mkLy();
}
function onDesel(){setObjBtns(false);document.getElementById('secProp').style.display='none';document.getElementById('secShadow').style.display='none';mkLy();}
[['pSX','vpSX'],['pSY','vpSY'],['pRt','vpRt']].forEach(([id,vid])=>{document.getElementById(id).addEventListener('input',e=>{const o=S.cv.getActiveObject();if(!o)return;const v=+e.target.value;if(id==='pSX'){o.set('scaleX',v);document.getElementById(vid).textContent=v.toFixed(1);}if(id==='pSY'){o.set('scaleY',v);document.getElementById(vid).textContent=v.toFixed(1);}if(id==='pRt'){o.rotate(v);document.getElementById(vid).textContent=v+'°';}S.cv.renderAll();});});

/* ══ LAYERS ══════════════════════════════════════════════════════ */
const IMG_IC=`<svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect x="3" y="3" width="18" height="18" rx="2"/><circle cx="9" cy="9" r="2"/><path d="m21 15-3.086-3.086a2 2 0 0 0-2.828 0L6 21"/></svg>`;
const TXT_IC=`<svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polyline points="4 7 4 4 20 4 20 7"/><line x1="9" y1="20" x2="15" y2="20"/><line x1="12" y1="4" x2="12" y2="20"/></svg>`;
const X_IC=`<svg width="10" height="10" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"><line x1="18" y1="6" x2="6" y2="18"/><line x1="6" y1="6" x2="18" y2="18"/></svg>`;
function mkLy(){
  const list=document.getElementById('lyList'),objs=S.cv.getObjects();
  if(!objs.length){list.innerHTML='<div class="ly-mt">No layers yet</div>';return;}
  const act=S.cv.getActiveObject();
  list.innerHTML=[...objs].reverse().map((obj,ri)=>{const idx=objs.length-1-ri;const isTx=obj.type==='text';const lbl=isTx?(obj.text||'').slice(0,18):obj.data?.name||'Photo';return `<div class="ly-item${obj===act?' on':''}" data-i="${idx}"><div class="ly-ic">${isTx?TXT_IC:IMG_IC}</div><span class="ly-nm">${lbl}</span><button class="btn btn-g btn-ic btn-sm ly-del" data-i="${idx}">${X_IC}</button></div>`;}).join('');
  list.querySelectorAll('.ly-item').forEach(item=>{item.addEventListener('click',e=>{if(e.target.closest('.ly-del'))return;const o=S.cv.getObjects()[+item.dataset.i];if(o){S.cv.setActiveObject(o);S.cv.renderAll();mkLy();}});});
  list.querySelectorAll('.ly-del').forEach(btn=>{btn.addEventListener('click',()=>{const o=S.cv.getObjects()[+btn.dataset.i];if(o){if(o===S.mainImg)S.mainImg=null;S.cv.remove(o);S.cv.renderAll();}});});
}
document.getElementById('btnLyUp').addEventListener('click',()=>{const o=S.cv.getActiveObject();if(o){S.cv.bringForward(o);mkLy();}});
document.getElementById('btnLyDn').addEventListener('click',()=>{const o=S.cv.getActiveObject();if(o){S.cv.sendBackwards(o);mkLy();}});

/* ══ HISTORY ═════════════════════════════════════════════════════ */
function saveH(){if(!S.cv)return;const j=JSON.stringify(S.cv.toJSON(['data']));S.hist=S.hist.slice(0,S.histIdx+1);S.hist.push(j);S.histIdx=S.hist.length-1;document.getElementById('btnUndo').disabled=S.histIdx<=0;document.getElementById('btnRedo').disabled=true;}
function undo(){if(S.histIdx<=0)return;S.histIdx--;S.cv.loadFromJSON(S.hist[S.histIdx],()=>{S.cv.renderAll();mkLy();});document.getElementById('btnUndo').disabled=S.histIdx<=0;document.getElementById('btnRedo').disabled=false;}
function redo(){if(S.histIdx>=S.hist.length-1)return;S.histIdx++;S.cv.loadFromJSON(S.hist[S.histIdx],()=>{S.cv.renderAll();mkLy();});document.getElementById('btnRedo').disabled=S.histIdx>=S.hist.length-1;document.getElementById('btnUndo').disabled=false;}
document.getElementById('btnUndo').addEventListener('click',undo);
document.getElementById('btnRedo').addEventListener('click',redo);
document.addEventListener('keydown',e=>{const tag=document.activeElement.tagName;if(tag==='INPUT'||tag==='TEXTAREA'||tag==='SELECT')return;if((e.ctrlKey||e.metaKey)&&!e.shiftKey&&e.key.toLowerCase()==='z'){e.preventDefault();undo();}if((e.ctrlKey||e.metaKey)&&(e.key.toLowerCase()==='y'||(e.shiftKey&&e.key.toLowerCase()==='z'))){e.preventDefault();redo();}if(e.key==='Delete'||e.key==='Backspace'){const o=S.cv.getActiveObject();if(o){if(o===S.mainImg)S.mainImg=null;S.cv.remove(o);S.cv.renderAll();}}});

/* ══ EXPORT ══════════════════════════════════════════════════════ */
document.querySelectorAll('.fmt').forEach(f=>{f.addEventListener('click',()=>{document.querySelectorAll('.fmt').forEach(x=>x.classList.remove('on'));f.classList.add('on');S.fmt=f.dataset.fmt;});});

function getPrintDataUrl(){
  const mode=document.getElementById('exSc').value;
  let mult;
  if(mode==='print'){mult=S.cv._printW/S.cv.width;}
  else if(mode==='2x'){mult=2;}
  else{mult=1;}
  const q=S.fmt==='jpeg'?0.92:1;
  return {dataUrl:S.cv.toDataURL({format:S.fmt==='jpeg'?'jpeg':'png',quality:q,multiplier:mult}),w:Math.round(S.cv.width*mult),h:Math.round(S.cv.height*mult)};
}

async function getMockupDataUrl(){
  return new Promise(resolve=>{
    const oc=document.createElement('canvas');oc.width=S.hostW;oc.height=S.hostH;
    const ctx=oc.getContext('2d');
    const bg=new Image();bg.crossOrigin='anonymous';bg.src=document.getElementById('mBg').src;
    bg.onload=()=>{
      ctx.drawImage(bg,0,0,S.hostW,S.hostH);
      const zd=S.cv.toDataURL({format:'png',multiplier:1});
      const zi=new Image();zi.crossOrigin='anonymous';zi.src=zd;
      zi.onload=()=>{
        ctx.drawImage(zi,S.zoneL,S.zoneT,S.zoneW,S.zoneH);
        const ov=new Image();ov.crossOrigin='anonymous';ov.src=document.getElementById('mOver').src;
        ov.onload=()=>{ctx.save();ctx.globalCompositeOperation='multiply';ctx.globalAlpha=.5;ctx.drawImage(ov,0,0,S.hostW,S.hostH);ctx.restore();resolve(oc.toDataURL('image/png'));};
        ov.onerror=()=>resolve(oc.toDataURL('image/png'));
      };
    };
  });
}

async function doDownload(){
  st('stEx','ld','Generating…');
  const mode=document.getElementById('exMode').value;
  if(mode==='mockup'){
    const url=await getMockupDataUrl();const a=document.createElement('a');a.href=url;a.download=`petblip-${P.label.replace(/\s/g,'-').toLowerCase()}-mockup.png`;a.click();st('stEx','ok','Mockup downloaded!');
  }else{
    const {dataUrl,w,h}=getPrintDataUrl();const a=document.createElement('a');a.href=dataUrl;a.download=`petblip-${P.label.replace(/\s/g,'-').toLowerCase()}-${w}x${h}.${S.fmt}`;a.click();st('stEx','ok',`Print file · ${w}×${h}px`);
  }
  setTimeout(()=>hideSt('stEx'),5000);
}
document.getElementById('btnDl').addEventListener('click',doDownload);
document.getElementById('btnDownload').addEventListener('click',doDownload);

/* ══ ORDER MODAL ════════════════════════════════════════════════ */
const orderModal=document.getElementById('orderModal');
document.getElementById('btnOrder').addEventListener('click',async()=>{
  // Show print file preview
  const {dataUrl}=getPrintDataUrl();
  document.getElementById('orderThumb').src=dataUrl;
  // Generate mockup composite preview
  const mockUrl=await getMockupDataUrl();
  document.getElementById('orderMockupImg').src=mockUrl;
  orderModal.classList.add('open');
});
document.getElementById('btnOrderCancel').addEventListener('click',()=>orderModal.classList.remove('open'));
orderModal.addEventListener('click',e=>{if(e.target===orderModal)orderModal.classList.remove('open');});

document.getElementById('btnOrderConfirm').addEventListener('click',()=>{
  st('stOrder','ld','Submitting order…');
  document.getElementById('btnOrderConfirm').disabled=true;
  const {dataUrl}=getPrintDataUrl();
  const fd=new FormData();
  fd.append('action','place_order');fd.append('dataUrl',dataUrl);
  fd.append('product',PROD_KEY);fd.append('image_id',S.imageId);
  fd.append('label',document.getElementById('orderLabel').value);
  fd.append('notes',document.getElementById('orderNotes').value);
  fetch(location.pathname,{method:'POST',body:fd}).then(r=>r.json()).then(d=>{
    document.getElementById('btnOrderConfirm').disabled=false;
    if(d.error){st('stOrder','er',d.error);return;}
    st('stOrder','ok','Order submitted! We\'ll be in touch.');
    setTimeout(()=>{orderModal.classList.remove('open');if(S.returnUrl)window.location.href=S.returnUrl;},2200);
  }).catch(()=>{document.getElementById('btnOrderConfirm').disabled=false;st('stOrder','er','Submission failed');});
});

/* ══ COLLAPSIBLE ════════════════════════════════════════════════ */
document.querySelectorAll('.shd').forEach(hd=>{hd.addEventListener('click',()=>{const id=hd.dataset.sec;const bd=document.getElementById('sb-'+id);const cv=document.getElementById('cv-'+id);if(!bd)return;const c=bd.classList.toggle('hidden');if(cv)cv.classList.toggle('dn',c);});});

/* ══ STATUS ══════════════════════════════════════════════════════ */
function st(id,type,msg){const el=document.getElementById(id);if(!el)return;el.className='st show '+type;el.innerHTML=type==='ld'?`<div class="spin"></div><span>${msg}</span>`:`<span>${msg}</span>`;}
function hideSt(id){const el=document.getElementById(id);if(el)el.className='st';}

/* ══ BOOT ════════════════════════════════════════════════════════ */
setupMockup('front');
setObjBtns(false);
document.getElementById('btnRestore').disabled=true;
// Show mockup immediately on load
document.getElementById('mHost').style.display='block';
document.getElementById('cEmpty').classList.add('gone');

// Pre-load from gallery
<?php if($preloadB64 && $preloadFilename): ?>
(()=>{
  S.origB64=<?= json_encode($preloadB64) ?>;
  S.origFilename=<?= json_encode($preloadFilename) ?>;
  loadImg(S.origB64,false,()=>checkDPI());
  showEditor();
  document.getElementById('btnRmBg').disabled=false;
  setTimeout(()=>document.getElementById('btnRmBg').click(),700);
})();
<?php endif; ?>
</script>
</body>
</html>