← back to Hollywood Wallcoverings

customcreator/configure.html

490 lines

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Custom Mural Configurator — Hollywood Wallcoverings</title>
<style>
  :root{
    --ink:#1a1a1a; --muted:#6b6b6b; --line:#e6e2db; --bg:#fbfaf7; --paper:#fff;
    --accent:#c9a865; --accent-ink:#a98b50; --radius:4px;
    --serif:'Cormorant Garamond',Georgia,'Times New Roman',serif;
    --sans:-apple-system,BlinkMacSystemFont,'Segoe UI',Helvetica,Arial,sans-serif;
  }
  *{box-sizing:border-box;margin:0;padding:0}
  body{font-family:var(--sans);color:var(--ink);background:var(--bg);line-height:1.55;-webkit-font-smoothing:antialiased}
  .wrap{max-width:1320px;margin:0 auto;padding:30px 28px 90px}
  .crumb{font-size:12px;letter-spacing:.08em;text-transform:uppercase;color:var(--muted);margin-bottom:22px}
  .crumb a{color:var(--muted);text-decoration:none}
  .pdp{display:grid;grid-template-columns:1.18fr .82fr;gap:46px;align-items:start}
  @media(max-width:920px){.pdp{grid-template-columns:1fr;gap:30px}}

  .stage{position:sticky;top:18px}
  .tabs{display:inline-flex;border:1px solid var(--line);border-radius:22px;overflow:hidden;margin-bottom:12px}
  .tabs button{border:0;background:var(--paper);padding:7px 16px;font-size:12px;letter-spacing:.05em;cursor:pointer;color:var(--muted)}
  .tabs button.on{background:var(--ink);color:#fff}
  .photos .hero-img{width:100%;aspect-ratio:4/5;object-fit:cover;background:#eee;border-radius:var(--radius);display:block}
  .thumbs{display:flex;gap:9px;margin-top:11px;flex-wrap:wrap}
  .thumbs img{width:64px;height:80px;object-fit:cover;border-radius:3px;cursor:pointer;opacity:.5;border:1px solid var(--line);transition:opacity .15s}
  .thumbs img.active,.thumbs img:hover{opacity:1}
  .hide{display:none!important}

  /* technical wall preview */
  .preview-frame{background:#f3f1ec;border:1px solid var(--line);border-radius:var(--radius);padding:22px;display:flex;align-items:center;justify-content:center;min-height:360px}
  .preview{position:relative;max-width:100%;max-height:60vh;box-shadow:0 6px 26px rgba(0,0,0,.16);background-size:cover;background-position:center;outline:1px solid rgba(0,0,0,.12)}
  .preview .seam{position:absolute;top:0;bottom:0;width:0;border-left:1px dashed rgba(255,255,255,.85);box-shadow:0 0 0 .5px rgba(0,0,0,.25)}
  .preview .dropnum{position:absolute;top:8px;transform:translateX(-50%);font-size:11px;font-weight:600;color:#fff;background:rgba(0,0,0,.45);padding:1px 6px;border-radius:10px}
  .preview .trim{position:absolute;border:1px dashed rgba(255,80,80,.95);pointer-events:none}
  .preview .trim-lbl{position:absolute;font-size:10px;color:#c0392b;background:rgba(255,255,255,.85);padding:0 4px;border-radius:3px}
  .ov{display:flex;gap:8px;margin-top:12px;flex-wrap:wrap}
  .ov button{border:1px solid var(--line);background:#fff;border-radius:20px;padding:5px 13px;font-size:11.5px;cursor:pointer;color:var(--muted)}
  .ov button.on{border-color:var(--ink);background:var(--ink);color:#fff}

  /* PHOTOREAL room visualizer */
  .room-frame{border:1px solid var(--line);border-radius:var(--radius);overflow:hidden;background:var(--bg)}
  .room{position:relative;width:100%;aspect-ratio:3/2;overflow:hidden;background-size:cover;background-position:center;background-color:#e9e2d4}
  .room-wall{position:absolute;overflow:hidden}
  .room-mural{position:absolute;bottom:0;left:50%;transform:translateX(-50%);background-size:cover;background-position:center bottom;transition:width .1s,height .1s}
  .room-fg{position:absolute;inset:0;width:100%;height:100%;object-fit:cover;pointer-events:none}
  .room-cap{position:absolute;left:12px;bottom:10px;font-size:11px;letter-spacing:.04em;color:#fff;background:rgba(0,0,0,.42);padding:3px 9px;border-radius:12px}
  .room-sel{display:flex;gap:8px;margin-top:12px;flex-wrap:wrap}
  .room-sel button{border:1px solid var(--line);background:#fff;border-radius:20px;padding:6px 14px;font-size:12px;cursor:pointer;color:var(--muted)}
  .room-sel button.on{border-color:var(--ink);background:var(--ink);color:#fff}
  .rz{position:absolute;right:-10px;bottom:-10px;width:20px;height:20px;border-radius:50%;background:var(--accent);border:2px solid #fff;box-shadow:0 1px 5px rgba(0,0,0,.35);cursor:nwse-resize;display:none;touch-action:none;z-index:3}
  .room-mural.custom{cursor:grab;bottom:auto;background-position:center;box-shadow:0 0 0 1px rgba(255,255,255,.75),0 8px 24px rgba(0,0,0,.32);touch-action:none}
  .room-mural.custom:active{cursor:grabbing}
  .room-mural.custom .rz{display:block}
  .cust-ctrl{display:flex;align-items:center;gap:16px;margin-top:11px;font-size:12px;color:var(--muted);flex-wrap:wrap}
  .cust-ctrl label{display:flex;align-items:center;gap:7px}
  .cust-ctrl input[type=range]{width:110px;-webkit-appearance:none;appearance:none;height:4px;border-radius:3px;background:var(--line)}
  .cust-ctrl input[type=range]::-webkit-slider-thumb{-webkit-appearance:none;width:15px;height:15px;border-radius:50%;background:var(--accent);border:2px solid #fff;cursor:pointer}
  .cust-ctrl button{border:1px solid var(--line);background:#fff;border-radius:3px;padding:5px 11px;font-size:12px;cursor:pointer;color:var(--ink)}
  .room-sel button.upload{border-style:dashed;color:var(--accent-ink)}
  .room-pick{display:flex;gap:8px;margin-top:10px;overflow-x:auto;padding-bottom:4px}
  .room-pick button{flex:0 0 auto;width:52px;height:52px;border-radius:5px;border:2px solid transparent;background-size:cover;background-position:center;cursor:pointer;opacity:.8}
  .room-pick button.on{border-color:var(--ink);opacity:1}

  .panel{max-width:470px}
  .vendor{font-size:12px;letter-spacing:.14em;text-transform:uppercase;color:var(--accent-ink);font-weight:600}
  h1{font-family:var(--serif);font-weight:500;font-size:38px;line-height:1.05;margin:8px 0 6px}
  .colors{display:flex;align-items:center;gap:10px;margin:12px 0 16px;flex-wrap:wrap}
  .chip{display:flex;align-items:center;gap:6px;font-size:12px;color:var(--muted)}
  .chip i{width:15px;height:15px;border-radius:50%;border:1px solid var(--line);display:inline-block}
  .desc{font-size:14.5px;color:#3a3a3a;line-height:1.6;margin:0 0 22px}
  .calc{background:var(--paper);border:1px solid var(--line);border-radius:var(--radius);padding:22px}
  .calc h2{font-size:12px;letter-spacing:.1em;text-transform:uppercase;color:var(--muted);font-weight:600;margin-bottom:14px}
  .unit-tog{display:inline-flex;border:1px solid var(--line);border-radius:20px;overflow:hidden;margin-bottom:16px}
  .unit-tog button{border:0;background:var(--paper);padding:5px 15px;font-size:12px;cursor:pointer;color:var(--muted)}
  .unit-tog button.on{background:var(--ink);color:#fff}
  .dims{display:grid;grid-template-columns:1fr auto 1fr;align-items:end;gap:10px}
  .field label{display:block;font-size:11px;letter-spacing:.05em;text-transform:uppercase;color:var(--muted);margin-bottom:5px}
  .field input{width:100%;border:1px solid var(--line);border-radius:3px;padding:11px 12px;font-size:16px;background:#fff;font-family:var(--sans)}
  .field input:focus{outline:none;border-color:var(--accent)}
  .times{padding-bottom:12px;color:var(--muted)}
  .sliders{display:flex;flex-direction:column;gap:18px}
  .srow .shead{display:flex;justify-content:space-between;align-items:baseline;margin-bottom:8px}
  .srow label{font-size:11px;letter-spacing:.05em;text-transform:uppercase;color:var(--muted)}
  .srow .sval{font-family:var(--serif);font-size:21px;color:var(--ink)}
  input[type=range]{-webkit-appearance:none;appearance:none;width:100%;height:4px;border-radius:3px;background:var(--line);outline:none;cursor:pointer}
  input[type=range]::-webkit-slider-thumb{-webkit-appearance:none;appearance:none;width:21px;height:21px;border-radius:50%;background:var(--accent);cursor:pointer;border:2px solid #fff;box-shadow:0 1px 5px rgba(0,0,0,.28)}
  input[type=range]::-moz-range-thumb{width:21px;height:21px;border-radius:50%;background:var(--accent);cursor:pointer;border:2px solid #fff;box-shadow:0 1px 5px rgba(0,0,0,.28)}
  input[type=range]:focus{outline:none}
  .presets{display:flex;flex-wrap:wrap;gap:7px;margin:14px 0 4px}
  .presets button{border:1px solid var(--line);background:#fff;border-radius:3px;padding:6px 11px;font-size:12px;cursor:pointer;color:var(--ink)}
  .presets button:hover{border-color:var(--ink)}
  .hint{font-size:11.5px;color:var(--muted);margin:8px 2px 0;line-height:1.5}
  .readout{margin:16px 0 4px;border-top:1px solid var(--line)}
  .readout div{display:flex;justify-content:space-between;padding:8px 2px;font-size:13px;border-bottom:1px solid var(--line)}
  .readout dt{color:var(--muted)} .readout dd{font-weight:600}
  .price-block{display:flex;align-items:baseline;justify-content:space-between;margin:16px 0 2px}
  .price-block .lbl{font-size:12px;letter-spacing:.08em;text-transform:uppercase;color:var(--muted)}
  .price{font-family:var(--serif);font-size:40px;font-weight:500;line-height:1}
  .price small{font-size:15px;color:var(--muted)}
  .perm2{font-size:12px;color:var(--muted);text-align:right;margin-bottom:16px}
  .cta{display:block;width:100%;text-align:center;border:0;border-radius:3px;padding:15px;font-size:14px;letter-spacing:.05em;cursor:pointer;font-family:var(--sans)}
  .cta.primary{background:var(--accent);color:#fff;font-weight:600;margin-bottom:10px}
  .cta.primary:hover{background:var(--accent-ink)} .cta.primary:disabled{opacity:.6;cursor:wait}
  .cta.ghost{background:#fff;color:var(--ink);border:1px solid var(--line)} .cta.ghost:hover{border-color:var(--ink)}
  .note{font-size:12px;color:var(--muted);margin-top:13px;line-height:1.5}
  .sec-title{font-family:var(--serif);font-size:22px;margin:32px 0 4px}
  .specs{border-top:1px solid var(--line)}
  .specs div{display:flex;justify-content:space-between;padding:12px 2px;border-bottom:1px solid var(--line);font-size:13.5px}
  .specs dt{color:var(--muted)} .specs dd{text-align:right;max-width:62%}
  .loading{opacity:.4}

  /* ===== Above-the-fold fit — whole configurator on one screen, no page scroll ===== */
  html,body{height:100%}
  body{overflow:hidden}
  /* render the entire UI permanently at 80% (reduced 20% from original, like a browser zoom-out) */
  :root{--page-zoom:.8}
  html{zoom:var(--page-zoom)}
  /* divide the fold height by the zoom so the 80%-scaled page still fills the screen (no bottom gap) */
  .wrap{height:calc(100svh / var(--page-zoom));padding:14px 28px 16px;display:flex;flex-direction:column;overflow:hidden}
  .crumb{margin-bottom:10px}
  .pdp{flex:1;min-height:0;gap:34px;align-items:stretch}
  /* left column: stack tabs + active media region (fills) + selectors */
  .stage{position:static;min-height:0;display:flex;flex-direction:column;overflow:hidden}
  #roomFrameWrap,#photos,#previewFrame{flex:1 1 auto;min-height:0;display:flex;flex-direction:column;overflow:hidden}
  .room-frame{flex:1;min-height:0;display:flex;align-items:center;justify-content:center;overflow:hidden}
  .room{width:100%;height:100%;aspect-ratio:auto;max-height:none;max-width:100%}
  .preview-frame{padding:16px}
  .preview{max-height:56svh}
  .photos .hero-img{aspect-ratio:auto;height:100%;width:auto;max-width:100%;max-height:60svh;margin:0 auto}
  /* right column: scrolls internally only as a last resort on very short screens */
  .panel{min-height:0;overflow-y:auto;max-width:none;padding-right:6px}
  h1{font-size:27px;margin:4px 0 4px}
  .colors{margin:8px 0 10px}
  .desc{font-size:13px;line-height:1.45;margin:0 0 10px;display:-webkit-box;-webkit-line-clamp:1;-webkit-box-orient:vertical;overflow:hidden}
  .calc{padding:13px}
  .calc h2{margin-bottom:9px}
  .unit-tog{margin-bottom:10px}
  .sliders{gap:10px}
  .hint{display:none}
  .note{display:none}
  .srow .shead{margin-bottom:5px}
  .srow .sval{font-size:18px}
  .presets{gap:6px;margin:10px 0 2px}
  .presets button{padding:5px 9px;font-size:11.5px}
  .hint{font-size:11px;margin:6px 2px 0}
  .readout{margin:10px 0 2px;display:grid;grid-template-columns:1fr 1fr;column-gap:20px}
  .readout div{padding:5px 2px;font-size:12px}
  .price-block{margin:10px 0 2px}
  .price{font-size:31px}
  .perm2{margin-bottom:10px}
  .cta{padding:12px}
  .cta.primary{margin-bottom:8px}
  .note{margin-top:8px;font-size:11px;line-height:1.4}
  /* specs as a compact disclosure so it's available without pushing the fold */
  .specs-wrap{margin-top:14px;border-top:1px solid var(--line);padding-top:10px}
  .specs-wrap>summary{font-family:var(--serif);font-size:20px;cursor:pointer;list-style:none;display:flex;align-items:center;justify-content:space-between}
  .specs-wrap>summary::-webkit-details-marker{display:none}
  .specs-wrap>summary::after{content:'+';color:var(--muted);font-family:var(--sans);font-size:20px;font-weight:300}
  .specs-wrap[open]>summary::after{content:'\2013'}
  .specs-wrap .specs{margin-top:6px}
  /* ===== user-resizable split — drag the corner hamburger grip to grow/shrink the image ===== */
  .pdp{position:relative}
  .stage{position:relative}
  .grip{position:absolute;bottom:8px;width:28px;height:22px;border-radius:6px;background:var(--paper);
    border:1px solid var(--line);box-shadow:0 1px 5px rgba(0,0,0,.16);cursor:ew-resize;
    display:flex;flex-direction:column;justify-content:center;align-items:center;gap:3px;
    z-index:6;touch-action:none;opacity:.5;transition:opacity .15s,border-color .15s;user-select:none}
  .grip:hover,.grip.active{opacity:1;border-color:var(--accent)}
  .grip span{display:block;width:15px;height:1.5px;border-radius:2px;background:var(--muted)}
  .grip.active span{background:var(--accent-ink)}
  .grip.left{right:8px}    /* bottom-right corner of the image (left) panel */
  .grip.right{right:0}     /* bottom-right corner of the page = right panel corner */
  @media(max-width:920px){.grip{display:none}}

  /* on phones, stop locking the viewport — let it stack and scroll normally */
  @media(max-width:920px){
    body{overflow:auto}
    .wrap{height:auto;overflow:visible;padding-bottom:60px}
    .pdp{display:block}
    .panel{overflow:visible;max-width:470px}
    .room{max-height:none;height:auto;width:100%}
    .photos .hero-img{height:auto;width:100%;max-height:none}
  }
</style>
</head>
<body>
<div class="wrap">
  <nav class="crumb"><a href="https://hollywoodwallcoverings.com">Hollywood Wallcoverings</a> / <a href="/">Custom Creator</a> / <a href="#">Made to Measure</a> / <span id="bcTitle">—</span></nav>
  <div class="pdp" id="pdp">
    <div class="stage">
      <div class="tabs">
        <button id="tabRoom" class="on">In a Room</button>
        <button id="tabPhotos">Photos</button>
        <button id="tabPreview">Wall Preview</button>
      </div>

      <div id="roomFrameWrap">
        <div class="room-frame"><div class="room" id="room">
          <div class="room-wall" id="roomWall"><div class="room-mural" id="roomMural"><div class="rz" id="rzHandle"></div></div></div>
          <img class="room-fg" id="roomFg2" alt="">
          <div class="room-cap" id="roomCap">—</div>
        </div></div>
        <div class="room-sel" id="roomSel"></div>
        <input type="file" id="roomUpload" accept="image/*" capture="environment" style="display:none">
        <div class="cust-ctrl hide" id="custCtrl">
          <span>Drag to position · corner handle to resize</span>
          <label>Opacity <input type="range" id="muralOpacity" min="0.4" max="1" step="0.05" value="1"></label>
          <button id="reuploadBtn">Change photo</button>
        </div>
        <div class="room-pick" id="roomPick"></div>
      </div>

      <div class="photos hide" id="photos"><img class="hero-img" id="hero" alt=""><div class="thumbs" id="thumbs"></div></div>

      <div class="preview-frame hide" id="previewFrame"><div class="preview" id="preview"></div></div>
      <div class="ov hide" id="ovBar">
        <button class="on" data-ov="seams">Roll seams</button>
        <button class="on" data-ov="nums">Roll numbering</button>
        <button class="on" data-ov="trim">Trim area</button>
        <button data-ov="flip">Flip</button>
      </div>
    </div>

    <div class="panel">
      <div class="vendor" id="vendor">Made to Measure · Custom Mural</div>
      <h1 id="title">Loading…</h1>
      <div class="colors" id="colors"></div>
      <div class="desc" id="desc"></div>
      <div class="calc">
        <h2>Configure Your Mural</h2>
        <div class="unit-tog" id="unitTog"><button data-u="ft" class="on">Feet</button><button data-u="in">Inches</button></div>
        <div class="sliders">
          <div class="srow"><div class="shead"><label>Wall width</label><span class="sval" id="wv">13′ 1″</span></div><input id="w" type="range" min="20" max="470" step="1" value="157"></div>
          <div class="srow"><div class="shead"><label>Wall height</label><span class="sval" id="hv">9′ 2″</span></div><input id="h" type="range" min="20" max="470" step="1" value="110"></div>
        </div>
        <div class="presets" id="presets"></div>
        <p class="hint">Tip: add ~4″ to your true wall size for installation trim. <span id="trimEg"></span></p>
        <dl class="readout" id="readout"></dl>
        <div class="price-block"><span class="lbl">Your mural</span><span class="price" id="price">—</span></div>
        <div class="perm2" id="perm2"></div>
        <button class="cta primary" id="addMural">Add Custom Mural to Cart</button>
        <button class="cta ghost" id="addSample">Order a Sample — $4.25</button>
        <p class="note">Pick a pattern and room, set your wall size, and see it scale on a real wall live. Printed to your exact size on premium non-woven in <span id="dropW">20</span>″ drops; trade pricing at checkout.</p>
      </div>
      <details class="specs-wrap">
        <summary>Specifications</summary>
        <dl class="specs" id="specs"></dl>
      </details>
    </div>
  </div>
</div>

<script>
const CM_PER_IN=2.54, ROLL_W_CM=50, TRIM_CM=5;
// Photoreal room templates: real photo (floor/sides/ceiling) + furniture cutout overlay (occlusion)
// + the flat feature-wall rectangle (inset %, front-facing) it represents in cm.
const ROOMS=[
  {id:'living', name:'Living Room', bg:'/CustomCreator/rooms/living.jpg',  fg:'/CustomCreator/rooms/living-fg.png',  wall:{l:2,t:4,r:8,b:17}, realW:430, realH:250},
  {id:'bedroom',name:'Bedroom',     bg:'/CustomCreator/rooms/bedroom.jpg', fg:'/CustomCreator/rooms/bedroom-fg.png', wall:{l:1,t:3,r:1,b:20}, realW:440, realH:250},
  {id:'dining', name:'Dining Room', bg:'/CustomCreator/rooms/dining.jpg',  fg:'/CustomCreator/rooms/dining-fg.png',  wall:{l:4,t:13,r:13,b:18},realW:430, realH:225},
  {id:'office', name:'Office',      bg:'/CustomCreator/rooms/office.jpg',  fg:'/CustomCreator/rooms/office-fg.png',  wall:{l:5,t:3,r:1,b:22},  realW:440, realH:250},
  {id:'nursery',name:'Nursery',     bg:'/CustomCreator/rooms/nursery.jpg', fg:'/CustomCreator/rooms/nursery-fg.png', wall:{l:1,t:2,r:1,b:20},  realW:440, realH:255},
  {id:'hallway',name:'Entryway',    bg:'/CustomCreator/rooms/hallway.jpg', fg:'/CustomCreator/rooms/hallway-fg.png', wall:{l:18,t:13,r:18,b:17},realW:380,realH:240},
  {id:'lobby',  name:'Lobby',       bg:'/CustomCreator/rooms/lobby.jpg',   fg:'/CustomCreator/rooms/lobby-fg.png',   wall:{l:6,t:4,r:12,b:28}, realW:600, realH:300},
];
let RATE=83.38;
let state={unit:'ft',product:null,view:'room',flip:false,ov:{seams:true,nums:true,trim:true},roomIdx:0,list:[],
  custom:false,userRoom:null,cbox:{l:22,t:12,w:56,h:64},opacity:1};
const $=id=>document.getElementById(id);
const fmt=n=>'$'+n.toLocaleString('en-US',{minimumFractionDigits:2,maximumFractionDigits:2});
const val=id=>parseFloat($(id).value)||0;
// sliders always hold INCHES; the unit toggle only changes how lengths read (feet-then-inches vs inches). No cm anywhere.
const toCm=v=>v*CM_PER_IN;
const ftIn=inch=>{let f=Math.floor(inch/12),i=Math.round(inch-f*12);if(i===12){f++;i=0;}return f+'′ '+i+'″';};
const fmtLen=inch=>state.unit==='ft'?ftIn(inch):(Math.round(inch)+'″');
const cmToLen=cm=>fmtLen(cm/CM_PER_IN);
const muralUrl=()=>state.product?(state.product.flatImage||(state.product.images[0]&&state.product.images[0].url)||''):'';

function metrics(){
  const w=toCm(val('w')),h=toCm(val('h'));
  const netA=(w/100)*(h/100), pw=w+2*TRIM_CM, ph=h+2*TRIM_CM, printA=(pw/100)*(ph/100);
  const drops=w>0?Math.ceil(pw/ROLL_W_CM):0;
  return {w,h,pw,ph,netA,printA,drops,price:netA>0?RATE*netA:0};
}
function updateSval(){ $('wv').textContent=fmtLen(val('w')); $('hv').textContent=fmtLen(val('h')); }
function relabelPresets(){document.querySelectorAll('#presets button').forEach(b=>{const w=b._w,h=b._h;if(w==null)return;b.textContent=state.unit==='ft'?(Math.round(w/12)+'×'+Math.round(h/12)+' ft'):(w+'×'+h+'″');});}
function recalc(){
  updateSval();
  const m=metrics();
  $('readout').innerHTML='';
  if(m.netA>0)[['Net wall area',(m.netA*10.7639).toFixed(1)+' sq ft'],
    ['Printed area (incl. trim)',(m.printA*10.7639).toFixed(1)+' sq ft'],['Drops / panels',m.drops+' panels'],
    ['Trim margin','2″ each edge']].forEach(([k,v])=>{const d=document.createElement('div');d.innerHTML='<dt>'+k+'</dt><dd>'+v+'</dd>';$('readout').appendChild(d);});
  if(state.product&&state.product.bespoke){
    $('price').innerHTML=m.netA>0?fmt(m.price)+' <small>USD</small>':'—';
    $('perm2').textContent=m.netA>0?'Bespoke · '+fmt(RATE)+'/m² · '+cmToLen(m.w)+' × '+cmToLen(m.h)+' · inquire to order':'';
  } else {
    $('price').innerHTML=m.netA>0?fmt(m.price)+' <small>USD</small>':'—';
    $('perm2').textContent=m.netA>0?fmt(RATE)+'/m² · '+cmToLen(m.w)+' × '+cmToLen(m.h)+' · '+m.drops+' drops':'';
  }
  renderPreview(m); renderRoom(m);
}

function renderPreview(m){
  if(state.view!=='preview'||!m||m.netA<=0)return;
  const box=$('preview'),frame=$('previewFrame');
  const maxW=frame.clientWidth-44,maxH=Math.min(window.innerHeight*0.6,540),ar=m.pw/m.ph;
  let dw=maxW,dh=dw/ar; if(dh>maxH){dh=maxH;dw=dh*ar;}
  box.style.width=dw+'px';box.style.height=dh+'px';
  box.style.backgroundImage='url("'+muralUrl()+'")';box.style.transform=state.flip?'scaleX(-1)':'none';
  box.innerHTML='';
  if(state.ov.trim){const tx=(TRIM_CM/m.pw)*100,ty=(TRIM_CM/m.ph)*100;const t=document.createElement('div');t.className='trim';t.style.left=tx+'%';t.style.top=ty+'%';t.style.right=tx+'%';t.style.bottom=ty+'%';box.appendChild(t);
    const lbl=document.createElement('div');lbl.className='trim-lbl';lbl.style.left=tx+'%';lbl.style.top='calc('+ty+'% - 14px)';lbl.textContent='wall '+cmToLen(m.w)+'×'+cmToLen(m.h);box.appendChild(lbl);}
  if(state.ov.seams||state.ov.nums)for(let i=0;i<m.drops;i++){const lp=((i*ROLL_W_CM)/m.pw)*100;
    if(state.ov.seams&&i>0){const s=document.createElement('div');s.className='seam';s.style.left=lp+'%';box.appendChild(s);}
    if(state.ov.nums){const cx=(((i+0.5)*ROLL_W_CM)/m.pw)*100;const n=document.createElement('div');n.className='dropnum';n.style.left=Math.min(cx,98)+'%';n.textContent=(i+1);box.appendChild(n);}}
}

function applyCustomMural(){
  const mu=$('roomMural');
  mu.style.left=state.cbox.l+'%'; mu.style.top=state.cbox.t+'%'; mu.style.right='auto';
  mu.style.width=state.cbox.w+'%'; mu.style.height=state.cbox.h+'%';
  mu.style.transform=state.flip?'scaleX(-1)':'none'; mu.style.opacity=state.opacity;
  mu.style.backgroundImage='url("'+muralUrl()+'")';
}
function renderRoom(m){
  if(state.view!=='room'||!m||m.netA<=0)return;
  const mu=$('roomMural');
  if(state.custom){
    $('room').style.backgroundImage='url("'+state.userRoom+'")';
    $('roomFg2').style.display='none';
    const wall=$('roomWall'); wall.style.left='0';wall.style.top='0';wall.style.right='0';wall.style.bottom='0';wall.style.overflow='visible';
    mu.classList.add('custom'); applyCustomMural();
    $('roomCap').textContent='Your room · drag & resize the mural onto your wall';
    return;
  }
  const room=ROOMS[state.roomIdx];
  $('roomFg2').style.display=''; mu.classList.remove('custom'); mu.style.opacity=1; mu.style.left='50%'; mu.style.top='auto';
  $('room').style.backgroundImage='url("'+room.bg+'")';
  $('roomFg2').src=room.fg;
  const wall=$('roomWall'); wall.style.overflow='hidden';
  wall.style.left=room.wall.l+'%'; wall.style.top=room.wall.t+'%'; wall.style.right=room.wall.r+'%'; wall.style.bottom=room.wall.b+'%';
  const wPct=Math.max(8,(m.w/room.realW)*100), hPct=Math.max(8,(m.h/room.realH)*100);
  mu.style.width=wPct+'%'; mu.style.height=Math.min(hPct,135)+'%';
  mu.style.backgroundImage='url("'+muralUrl()+'")';
  mu.style.transform='translateX(-50%)'+(state.flip?' scaleX(-1)':'');
  $('roomCap').textContent=cmToLen(m.w)+' × '+cmToLen(m.h)+' · shown to scale in the '+room.name.toLowerCase();
}
// pointer drag + resize for the uploaded-room mural (works mouse + touch on desktop/mobile/iPad)
let _drag=null;
function muralDown(e){
  if(!state.custom)return;
  const isRz=e.target.id==='rzHandle';
  _drag={mode:isRz?'resize':'move',sx:e.clientX,sy:e.clientY,box:{...state.cbox}};
  try{$('roomMural').setPointerCapture(e.pointerId);}catch(_){}
  e.preventDefault();
}
function muralMove(e){
  if(!_drag)return;
  const r=$('room').getBoundingClientRect();
  const dx=(e.clientX-_drag.sx)/r.width*100, dy=(e.clientY-_drag.sy)/r.height*100;
  if(_drag.mode==='move'){ state.cbox.l=Math.max(-30,Math.min(95,_drag.box.l+dx)); state.cbox.t=Math.max(-30,Math.min(95,_drag.box.t+dy)); }
  else { state.cbox.w=Math.max(8,Math.min(160,_drag.box.w+dx)); state.cbox.h=Math.max(8,Math.min(180,_drag.box.h+dy)); }
  applyCustomMural(); e.preventDefault();
}
function muralUp(){ _drag=null; }

function setView(v){
  state.view=v;
  $('tabRoom').classList.toggle('on',v==='room'); $('tabPhotos').classList.toggle('on',v==='photos'); $('tabPreview').classList.toggle('on',v==='preview');
  $('roomFrameWrap').classList.toggle('hide',v!=='room');
  $('photos').classList.toggle('hide',v!=='photos');
  $('previewFrame').classList.toggle('hide',v!=='preview'); $('ovBar').classList.toggle('hide',v!=='preview');
  recalc();
}

function render(p){
  state.product=p; RATE=p.rate||83.38;
  $('title').textContent=p.pattern; $('bcTitle').textContent=p.pattern;
  $('vendor').textContent=(p.vendor||'Made to Measure')+' · Custom Mural';
  const imgs=p.images.length?p.images:[{url:'',alt:''}];
  $('hero').src=imgs[0].url; $('hero').alt=imgs[0].alt;
  const tw=$('thumbs'); tw.innerHTML='';
  imgs.forEach((im,k)=>{const t=new Image();t.src=im.url;t.alt=im.alt;t.className=k===0?'active':'';t.onclick=()=>{$('hero').src=im.url;[...tw.children].forEach(c=>c.classList.remove('active'));t.classList.add('active');};tw.appendChild(t);});
  const cw=$('colors'); cw.innerHTML='';
  (p.colors||[]).forEach(c=>{const s=document.createElement('span');s.className='chip';s.innerHTML='<i style="background:'+c.hex+'"></i>'+(c.name||c.hex)+(c.pct?' · '+c.pct+'%':'');cw.appendChild(s);});
  $('desc').innerHTML=p.descriptionHtml||'';
  const dl=$('specs'); dl.innerHTML='';
  (p.specs||[]).forEach(s=>{const d=document.createElement('div');d.innerHTML='<dt>'+s.label+'</dt><dd>'+s.value+'</dd>';dl.appendChild(d);});
  if(p.bespoke){ $('addMural').textContent='Request a Quote →'; $('addSample').style.display='none'; $('vendor').textContent='Bespoke Mural'; }
  else { $('addMural').textContent='Add Custom Mural to Cart'; $('addSample').style.display=''; $('addSample').textContent='Order a Sample — '+fmt(parseFloat(p.samplePrice||4.25)); }
  [...$('roomPick').children].forEach(b=>b.classList.toggle('on',b.dataset.h===(p.sku||'').toLowerCase()));
  recalc();
}

async function load(handle){
  $('pdp').classList.add('loading');
  try{
    const r=await fetch('/CustomCreator/api/product'+(handle?('?handle='+encodeURIComponent(handle)):''));
    const p=await r.json(); if(p.error)throw new Error(p.error);
    render(p);
    history.replaceState(null,'',location.pathname+'?handle='+p.sku.toLowerCase());
  }catch(e){ console.error(e); location.replace('/CustomCreator'); return; }  // unknown/retired design → land on the gallery, never a dead end
  $('pdp').classList.remove('loading');
}
async function postCart(mode){
  const p=state.product; if(!p)return;
  if(p.bespoke){ const wcm=toCm(val('w')), hcm=toCm(val('h')), area=((wcm/100)*(hcm/100)*10.7639).toFixed(1), sz=ftIn(val('w'))+' × '+ftIn(val('h'));
    window.location.href='mailto:info@hollywoodwallcoverings.com?subject='+encodeURIComponent('Mural Quote: '+p.pattern)+'&body='+encodeURIComponent('I would like a quote for the '+p.pattern+' mural at '+sz+' (approx '+area+' sq ft).'); return; }
  const btn=$(mode==='sample'?'addSample':'addMural'); const orig=btn.textContent; btn.disabled=true; btn.textContent='Creating checkout…';
  try{
    const r=await fetch('/CustomCreator/api/mural-cart',{method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify({mode,sku:p.sku,pattern:p.pattern,unit:'in',width:val('w'),height:val('h')})});
    const j=await r.json();
    if(j.invoiceUrl){window.location.href=j.invoiceUrl;return;}
    alert(j.scopeHint?'Checkout not yet enabled — token needs write_draft_orders.':'Could not create checkout: '+(j.error||'unknown'));
  }catch(e){alert('Network error: '+e.message);}
  btn.disabled=false; btn.textContent=orig;
}

// ===== resizable split: corner hamburger grips on both panels adjust the image size =====
const DEFAULT_SPLIT=0.59;            // matches the original 1.18fr : .82fr column ratio
let split=Math.max(0.32,Math.min(0.80,parseFloat(localStorage.getItem('muralSplit'))||DEFAULT_SPLIT));
function applySplit(){ // persists via localStorage; two fr values that always sum to 2 (like 1.18+.82)
  $('pdp').style.gridTemplateColumns=(split*2).toFixed(3)+'fr '+((1-split)*2).toFixed(3)+'fr';
}
function makeGrip(cls,host){
  const g=document.createElement('div'); g.className='grip '+cls;
  g.title='Drag to resize the image · double-click to reset';
  g.innerHTML='<span></span><span></span><span></span>';
  let dragging=false;
  g.addEventListener('pointerdown',e=>{dragging=true;g.classList.add('active');try{g.setPointerCapture(e.pointerId);}catch(_){}e.preventDefault();});
  g.addEventListener('pointermove',e=>{ if(!dragging)return;
    const r=$('pdp').getBoundingClientRect();
    split=Math.max(0.32,Math.min(0.80,(e.clientX-r.left)/r.width));
    applySplit(); renderPreview(metrics()); renderRoom(metrics()); e.preventDefault(); });
  const end=()=>{ if(!dragging)return; dragging=false; g.classList.remove('active'); localStorage.setItem('muralSplit',split.toFixed(4)); };
  g.addEventListener('pointerup',end); g.addEventListener('pointercancel',end);
  g.addEventListener('dblclick',()=>{ split=DEFAULT_SPLIT; applySplit(); localStorage.setItem('muralSplit',split.toFixed(4)); renderPreview(metrics()); renderRoom(metrics()); });
  host.appendChild(g);
}
function setupGrips(){ applySplit(); makeGrip('left',document.querySelector('.stage')); makeGrip('right',$('pdp')); }

function init(){
  $('dropW').textContent=ROLL_W_CM;
  // room selector (predefined rooms + upload-your-own)
  const rs=$('roomSel');
  const clearSel=()=>[...rs.children].forEach(c=>c.classList.remove('on'));
  ROOMS.forEach((rm,i)=>{const b=document.createElement('button');b.textContent=rm.name;b.className=i===0?'on':'';
    b.onclick=()=>{state.custom=false;state.roomIdx=i;clearSel();b.classList.add('on');$('custCtrl').classList.add('hide');renderRoom(metrics());};rs.appendChild(b);});
  const up=document.createElement('button'); up.textContent='+ Your Room'; up.className='upload'; up.onclick=()=>$('roomUpload').click(); rs.appendChild(up);
  // upload handler — file input is camera-capable on mobile/iPad, gallery on desktop
  $('roomUpload').onchange=e=>{const f=e.target.files&&e.target.files[0]; if(!f)return;
    const rd=new FileReader(); rd.onload=()=>{ state.userRoom=rd.result; state.custom=true; state.cbox={l:22,t:12,w:56,h:64}; state.opacity=1; $('muralOpacity').value=1;
      clearSel(); up.classList.add('on'); $('custCtrl').classList.remove('hide'); if(state.view!=='room')setView('room'); renderRoom(metrics()); };
    rd.readAsDataURL(f); e.target.value=''; };
  $('reuploadBtn').onclick=()=>$('roomUpload').click();
  $('muralOpacity').oninput=()=>{state.opacity=parseFloat($('muralOpacity').value);applyCustomMural();};
  // drag + resize (pointer events = mouse + touch)
  const mu=$('roomMural');
  mu.addEventListener('pointerdown',muralDown); mu.addEventListener('pointermove',muralMove); mu.addEventListener('pointerup',muralUp); mu.addEventListener('pointercancel',muralUp);
  document.querySelectorAll('#unitTog button').forEach(btn=>btn.onclick=()=>{
    const u=btn.dataset.u; if(u===state.unit)return;
    // display-only toggle — the slider already holds inches, so just re-render the labels
    state.unit=u; document.querySelectorAll('#unitTog button').forEach(b=>b.classList.toggle('on',b===btn)); relabelPresets(); recalc();
  });
  $('trimEg').textContent='[e.g. a 10′×8′ wall → add a few inches each side for trim]';
  [[120,96],[144,108],[168,108],[96,96],[192,108],[144,120]].forEach(([w,h])=>{const b=document.createElement('button');b._w=w;b._h=h;b.onclick=()=>{$('w').value=w;$('h').value=h;recalc();};$('presets').appendChild(b);});
  relabelPresets();
  document.querySelectorAll('#ovBar button').forEach(b=>b.onclick=()=>{const k=b.dataset.ov;if(k==='flip'){state.flip=!state.flip;b.classList.toggle('on',state.flip);}else{state.ov[k]=!state.ov[k];b.classList.toggle('on',state.ov[k]);}renderPreview(metrics());renderRoom(metrics());});
  $('tabRoom').onclick=()=>setView('room'); $('tabPhotos').onclick=()=>setView('photos'); $('tabPreview').onclick=()=>setView('preview');
  $('w').oninput=recalc; $('h').oninput=recalc;
  $('addMural').onclick=()=>postCart('mural'); $('addSample').onclick=()=>postCart('sample');
  window.addEventListener('resize',()=>{renderPreview(metrics());renderRoom(metrics());});
  setupGrips();
  const params=new URLSearchParams(location.search), start=params.get('handle');
  fetch('/CustomCreator/api/products').then(r=>r.json()).then(list=>{
    state.list=list;
    const pk=$('roomPick'); pk.innerHTML='';
    list.slice(0,18).forEach(it=>{const b=document.createElement('button');b.dataset.h=it.handle;b.title=it.title;if(it.image)b.style.backgroundImage='url("'+it.image+'")';b.onclick=()=>load(it.handle);pk.appendChild(b);});
    load(start||(list[0]&&list[0].handle));
  }).catch(()=>load(start));
}
init();
</script>
</body>
</html>