← back to Wallpaperestimator

public/index.html

623 lines

<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<!-- GA4 gtag (auto) -->
<script async src="https://www.googletagmanager.com/gtag/js?id=G-T3TWEXYWQX"></script>
<script>
  window.dataLayer = window.dataLayer || [];
  function gtag(){dataLayer.push(arguments);}
  gtag('js', new Date());
  gtag('config', 'G-T3TWEXYWQX');
</script>
<!-- /GA4 gtag -->
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Wallpaper Estimator — How much wallpaper do I need?</title>
<meta name="description" content="Free wallpaper yardage calculator. Enter your room's dimensions, pattern repeat, and roll size — we'll compute the exact bolts and total square footage you need, with closing recommendations from Designer Wallcoverings.">
<meta name="theme-color" content="#faf8f3">
<link rel="canonical" href="https://wallpaperestimator.com/">
<link rel="icon" href="data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Ctext y='20' font-size='22'%3E%E2%9F%82%3C/text%3E%3C/svg%3E">

<meta property="og:type" content="website">
<meta property="og:url" content="https://wallpaperestimator.com/">
<meta property="og:image" content="https://wallpaperestimator.com/hero-bg.jpg">
<meta property="og:title" content="Wallpaper Estimator — How much wallpaper do I need?">
<meta property="og:description" content="Free yardage calculator. Pattern repeat, roll width, waste factor — all accounted for. Returns bolts, sq ft, and matching wallpaper picks.">
<meta name="twitter:card" content="summary_large_image">

<link href="https://fonts.googleapis.com/css2?family=Cormorant+Garamond:wght@400;500;600&family=Inter:wght@300;400;500;600;700&display=swap" rel="stylesheet">

<script>
(function(){ try { var t = localStorage.getItem('wpe_theme') || 'light'; document.documentElement.dataset.theme = t; } catch(e){} })();
</script>

<style>
:root{
  --bg:#faf8f3;--surface:#ffffff;--ink:#1a1a1a;--muted:#6b6b6b;--line:#e6e2d8;
  --accent:#1f3a2a;--accent-soft:#e9efe9;--bad:#a13030;--good:#1f3a2a;
  --serif:'Cormorant Garamond',Georgia,serif;
  --sans:'Inter',-apple-system,BlinkMacSystemFont,sans-serif;
  --cols:3;
}
:root[data-theme="dark"]{
  --bg:#15161a;--surface:#1f2127;--ink:#f4f3ee;--muted:#9c9a92;--line:#2c2f37;
  --accent:#bbe3c5;--accent-soft:#1d2a23;--bad:#e08585;--good:#bbe3c5;
}
*{box-sizing:border-box}
html,body{margin:0;padding:0;background:var(--bg);color:var(--ink);font-family:var(--sans);font-size:16px;line-height:1.55;-webkit-font-smoothing:antialiased}
a{color:var(--accent);text-decoration:none}
a:hover{text-decoration:underline}

header{padding:18px 24px;border-bottom:1px solid var(--line);display:flex;align-items:center;justify-content:space-between;gap:16px;background:var(--surface)}
.brand{font-family:var(--serif);font-size:24px;font-weight:600;letter-spacing:.5px}
.brand small{font-family:var(--sans);font-size:11px;text-transform:uppercase;letter-spacing:.2em;color:var(--muted);display:block;font-weight:500}
.theme-toggle{background:none;border:1px solid var(--line);color:var(--ink);width:38px;height:38px;border-radius:50%;cursor:pointer;font-size:18px;display:inline-flex;align-items:center;justify-content:center}
.theme-toggle:hover{background:var(--accent-soft)}

.hero{max-width:980px;margin:0 auto;padding:48px 24px 16px;text-align:center}
.hero .eyebrow{font-size:12px;text-transform:uppercase;letter-spacing:.22em;color:var(--muted);margin:0 0 14px;font-weight:500}
.hero h1{font-family:var(--serif);font-size:clamp(42px,6vw,68px);font-weight:500;margin:0 0 18px;line-height:1.02;letter-spacing:-.01em}
.hero h1 em{font-style:italic;color:var(--accent);font-weight:500}
.hero p.lede{font-size:18px;color:var(--muted);max-width:660px;margin:0 auto;line-height:1.6}

main{max-width:980px;margin:0 auto;padding:24px;display:grid;gap:32px}

.calc{background:var(--surface);border:1px solid var(--line);border-radius:14px;padding:28px;display:grid;gap:24px}
.calc h2{font-family:var(--serif);font-size:28px;margin:0 0 4px;font-weight:500}
.calc p.helper{margin:0;color:var(--muted);font-size:14px}

.form-grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(180px,1fr));gap:16px}
.field{display:flex;flex-direction:column;gap:6px}
.field label{font-size:12px;font-weight:600;text-transform:uppercase;letter-spacing:.08em;color:var(--muted)}
.field input,.field select{padding:11px 13px;border:1px solid var(--line);background:var(--bg);color:var(--ink);border-radius:8px;font-size:15px;font-family:var(--sans);width:100%}
.field input:focus,.field select:focus{outline:2px solid var(--accent);outline-offset:1px;border-color:transparent}
.field .hint{font-size:11px;color:var(--muted)}

.row-units{display:flex;gap:8px;align-items:center;font-size:13px;color:var(--muted)}
.row-units button{background:var(--bg);border:1px solid var(--line);color:var(--muted);padding:6px 14px;border-radius:999px;cursor:pointer;font-size:13px;font-family:var(--sans)}
.row-units button[aria-pressed="true"]{background:var(--accent);color:#fff;border-color:var(--accent)}

.calc-cta{display:flex;gap:12px;flex-wrap:wrap;align-items:center}
button.primary{background:var(--accent);color:#fff;border:none;padding:13px 28px;border-radius:8px;font-size:15px;font-weight:600;cursor:pointer;letter-spacing:.02em}
button.primary:hover{filter:brightness(1.1)}
button.ghost{background:transparent;border:1px solid var(--line);color:var(--ink);padding:13px 22px;border-radius:8px;font-size:14px;cursor:pointer;font-family:var(--sans)}

.results{background:var(--accent-soft);border-radius:12px;padding:24px;display:none;border:1px solid var(--line)}
.results.show{display:block}
.results h3{font-family:var(--serif);font-size:24px;margin:0 0 16px;font-weight:500}
.results-grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(160px,1fr));gap:16px}
.result-card{background:var(--surface);border-radius:10px;padding:16px;border:1px solid var(--line)}
.result-card .num{font-family:var(--serif);font-size:34px;font-weight:600;color:var(--accent);line-height:1.1}
.result-card .lbl{font-size:12px;text-transform:uppercase;letter-spacing:.08em;color:var(--muted);margin-top:6px}
.results .breakdown{margin-top:18px;font-size:14px;color:var(--ink);background:var(--surface);border-radius:8px;padding:14px;border:1px dashed var(--line)}
.results .breakdown div{padding:4px 0}
.results .breakdown strong{color:var(--accent);font-weight:600}
.results .warn{margin-top:14px;padding:12px;background:var(--surface);border-left:3px solid var(--bad);font-size:13px;border-radius:4px}

/* Closing CTA grid */
.closing{padding:32px 0 8px}
.closing h2{font-family:var(--serif);font-size:32px;margin:0 0 6px;font-weight:500;text-align:center}
.closing p.sub{text-align:center;color:var(--muted);margin:0 0 24px}

.controls{display:flex;gap:12px;justify-content:center;align-items:center;margin:0 0 18px;flex-wrap:wrap}
.controls label{font-size:12px;text-transform:uppercase;letter-spacing:.08em;color:var(--muted)}
.controls select{padding:8px 12px;border:1px solid var(--line);background:var(--surface);color:var(--ink);border-radius:6px;font-size:14px;font-family:var(--sans)}
.controls input[type="range"]{width:140px}

.product-grid{display:grid;grid-template-columns:repeat(var(--cols),1fr);gap:16px}
.product{background:var(--surface);border:1px solid var(--line);border-radius:10px;overflow:hidden;display:flex;flex-direction:column;transition:transform .15s ease,box-shadow .15s ease}
.product:hover{transform:translateY(-2px);box-shadow:0 8px 24px rgba(0,0,0,.06)}
.product img{width:100%;aspect-ratio:1/1;object-fit:cover;display:block;background:var(--accent-soft)}
.product .meta{padding:12px 14px;flex:1;display:flex;flex-direction:column;gap:4px}
.product .meta .ttl{font-family:var(--serif);font-size:17px;font-weight:600;line-height:1.25;color:var(--ink)}
.product .meta .vnd{font-size:11px;text-transform:uppercase;letter-spacing:.1em;color:var(--muted)}
.product .meta .cat{font-size:13px;color:var(--muted);margin-top:2px}
.product a.shop{display:block;text-align:center;background:var(--accent);color:#fff;padding:10px;font-size:13px;letter-spacing:.04em;text-transform:uppercase;font-weight:600}
.product a.shop:hover{filter:brightness(1.1);text-decoration:none}

footer{padding:48px 24px 32px;border-top:1px solid var(--line);text-align:center;color:var(--muted);font-size:13px;margin-top:48px;background:var(--surface)}
footer .links{margin-bottom:10px}
footer .links a{margin:0 10px;color:var(--ink)}
footer address{font-style:normal;margin-top:8px;line-height:1.7}

/* FAQ accordion — editorial card list */
.faq{padding:8px 0 4px}
.faq h2{font-family:var(--serif);font-size:32px;font-weight:500;margin:0 0 4px;text-align:center;letter-spacing:-.01em}
.faq p.sub{text-align:center;color:var(--muted);margin:0 0 24px;font-size:15px}
.faq-list{display:grid;gap:10px;max-width:780px;margin:0 auto}
.faq-item{background:var(--surface);border:1px solid var(--line);border-radius:10px;overflow:hidden;transition:border-color .15s ease}
.faq-item[open]{border-color:var(--accent)}
.faq-item summary{padding:18px 22px;cursor:pointer;font-family:var(--serif);font-size:19px;font-weight:500;color:var(--ink);list-style:none;display:flex;justify-content:space-between;align-items:center;gap:16px;line-height:1.35}
.faq-item summary::-webkit-details-marker{display:none}
.faq-item summary::after{content:'+';font-family:var(--sans);font-size:22px;font-weight:300;color:var(--accent);transition:transform .2s ease;flex-shrink:0;line-height:1}
.faq-item[open] summary::after{transform:rotate(45deg)}
.faq-item summary:hover{background:var(--accent-soft)}
.faq-body{padding:0 22px 20px;color:var(--ink);font-size:15px;line-height:1.65}
.faq-body p{margin:0 0 10px}
.faq-body p:last-child{margin-bottom:0}
.faq-body strong{color:var(--accent);font-weight:600}

@media (max-width: 640px){
  .product-grid{grid-template-columns:repeat(2,1fr)!important}
  .hero{padding:32px 16px 8px}
  main{padding:16px}
  .calc{padding:20px}
  .faq-item summary{font-size:17px;padding:16px 18px}
  .faq-body{padding:0 18px 18px}
}
</style>
<!-- Meta Pixel — INERT until set-fb-pixel.sh is run (does NOT load fbevents.js or hit Meta until flipped) -->
<script>
(function(){
  var PIXEL_ID = '{{FACEBOOK_PIXEL_ID}}';
  // Inert until flipped — the brace check prevents BOTH the loader IIFE
  // AND the init call from running, so fbevents.js is never fetched.
  if (PIXEL_ID.indexOf('{{') !== -1) return;
  !function(f,b,e,v,n,t,s){if(f.fbq)return;n=f.fbq=function(){n.callMethod?
  n.callMethod.apply(n,arguments):n.queue.push(arguments)};if(!f._fbq)f._fbq=n;
  n.push=n;n.loaded=!0;n.version='2.0';n.queue=[];t=b.createElement(e);t.async=!0;
  t.src=v;s=b.getElementsByTagName(e)[0];s.parentNode.insertBefore(t,s)}(window,
  document,'script','https://connect.facebook.net/en_US/fbevents.js');
  fbq('init', PIXEL_ID);
  fbq('track', 'PageView');
})();
</script>
<!-- noscript fallback is added by set-fb-pixel.sh on flip (omitted here to avoid unflipped img phone-home) -->
<!-- End Meta Pixel -->
<!-- corner-nav disabled (UL-hamburger rebuild) -->
</head>
<body>
<!-- Theme toggle (theme1 / theme2) — query-param-gated preview surface
     (Steve 2026-05-29). ?theme=2 in URL flips to theme2 and persists in
     localStorage. Toggle UI hidden until theme2 mode active. Same key
     ('wallco-page-theme') as wallco.ai for cross-site continuity. -->
<script>
(function(){
  var qs = new URLSearchParams(location.search);
  var fromUrl = qs.get('theme');
  var saved = null; try { saved = localStorage.getItem('wallco-page-theme'); } catch(e){}
  var theme = null;
  if (fromUrl === '2' || fromUrl === 'theme2') theme = 'theme2';
  else if (fromUrl === '1' || fromUrl === 'theme1') theme = 'theme1';
  else if (saved === 'theme2' || saved === 'theme1') theme = saved;
  if (theme) {
    document.documentElement.setAttribute('data-page-theme', theme);
    if (fromUrl) { try { localStorage.setItem('wallco-page-theme', theme); } catch(e){} }
  }
})();
</script>
<style id="page-theme-css-fleet">
  [data-page-theme="theme2"] body{
    background:#fff !important; color:#0a0a0a !important;
  }
  [data-page-theme="theme2"] h1,
  [data-page-theme="theme2"] h2,
  [data-page-theme="theme2"] h3{ letter-spacing:-0.01em; font-weight:500; }
  [data-page-theme="theme2"] .card,
  [data-page-theme="theme2"] [class*="card"]{
    background:#fafafa !important; border:1px solid #e5e5e5 !important; box-shadow:none !important;
  }
  #page-theme-toggle{ display:none; }
  [data-page-theme] #page-theme-toggle{ display:inline-flex; }
  #page-theme-toggle{
    position:fixed; top:96px; right:14px; z-index:90;
    gap:0; align-items:center;
    background:rgba(255,255,255,.92); border:1px solid #d8d2c5;
    border-radius:999px; padding:3px; backdrop-filter:blur(6px);
    box-shadow:0 2px 10px rgba(0,0,0,.08);
    font:600 11px ui-sans-serif,system-ui; letter-spacing:.06em; text-transform:uppercase;
  }
  #page-theme-toggle button{
    border:0; background:transparent; color:#5a5048; cursor:pointer;
    padding:6px 14px; border-radius:999px; font:inherit; transition:all .15s;
  }
  #page-theme-toggle button[aria-pressed="true"]{ background:#1a1714; color:#faf7f2; }
  #page-theme-toggle button:hover:not([aria-pressed="true"]){ color:#1a1714; }
</style>
<div id="page-theme-toggle" role="group" aria-label="Page theme">
  <button type="button" data-theme="theme1" aria-pressed="false">Theme 1</button>
  <button type="button" data-theme="theme2" aria-pressed="false">Theme 2</button>
</div>
<script>
(function(){
  var cur = document.documentElement.getAttribute('data-page-theme') || 'theme1';
  var btns = document.querySelectorAll('#page-theme-toggle button');
  function sync(t){ btns.forEach(function(b){ b.setAttribute('aria-pressed', b.dataset.theme === t ? 'true' : 'false'); }); }
  sync(cur);
  btns.forEach(function(b){
    b.addEventListener('click', function(){
      var t = b.dataset.theme;
      document.documentElement.setAttribute('data-page-theme', t);
      try { localStorage.setItem('wallco-page-theme', t); } catch(e){}
      sync(t);
    });
  });
})();
</script>


<header>
  <div class="brand">
    Wallpaper Estimator
    <small>by Designer Wallcoverings</small>
  </div>
  <button class="theme-toggle" id="themeBtn" aria-label="Toggle dark mode" title="Toggle theme">☼</button>
</header>

<section class="hero">
  <p class="eyebrow">How much wallpaper do I&nbsp;need?</p>
  <h1>Measure twice.<br><em>Order once.</em></h1>
  <p class="lede">Pattern repeat, bolt size, and waste factor — the three variables most online calculators quietly skip, and the reason most DIY rooms come up half a roll short.</p>
</section>

<main>

  <div class="calc">
    <div>
      <h2>Yardage calculator</h2>
      <p class="helper">All fields are required. Defaults assume a standard American double-roll bolt (27" × 27 ft).</p>
    </div>

    <div class="row-units" role="group" aria-label="Measurement units">
      <span>Units:</span>
      <button id="unitFt" type="button" aria-pressed="true">Feet / inches</button>
      <button id="unitM" type="button" aria-pressed="false">Meters / cm</button>
    </div>

    <div class="form-grid">
      <div class="field">
        <label for="length">Room length</label>
        <input id="length" type="number" step="0.1" min="1" value="14" inputmode="decimal">
        <span class="hint" id="lenUnit">feet</span>
      </div>
      <div class="field">
        <label for="width">Room width</label>
        <input id="width" type="number" step="0.1" min="1" value="12" inputmode="decimal">
        <span class="hint" id="widUnit">feet</span>
      </div>
      <div class="field">
        <label for="height">Wall height</label>
        <input id="height" type="number" step="0.1" min="1" value="8" inputmode="decimal">
        <span class="hint" id="hgtUnit">feet</span>
      </div>
      <div class="field">
        <label for="repeat">Pattern repeat</label>
        <input id="repeat" type="number" step="0.5" min="0" value="0" inputmode="decimal">
        <span class="hint" id="repUnit">inches (0 = no repeat)</span>
      </div>
      <div class="field">
        <label for="rollWidth">Roll width</label>
        <select id="rollWidth">
          <option value="20.5">20.5" (US single)</option>
          <option value="27" selected>27" (US double)</option>
          <option value="36">36" (commercial Type II)</option>
          <option value="40">40" (Euro 1m)</option>
          <option value="52">52" (commercial 54")</option>
        </select>
        <span class="hint">most residential bolts are 27"</span>
      </div>
      <div class="field">
        <label for="rollLen">Roll length</label>
        <select id="rollLen">
          <option value="13.5">13.5 ft (US single)</option>
          <option value="27" selected>27 ft (US double bolt)</option>
          <option value="33">33 ft (Euro 10m)</option>
          <option value="30">30 yd (commercial)</option>
        </select>
        <span class="hint">double bolts are most common</span>
      </div>
      <div class="field">
        <label for="openings">Doors + windows</label>
        <input id="openings" type="number" step="1" min="0" value="2" inputmode="numeric">
        <span class="hint">subtracts ~21 sq ft each</span>
      </div>
      <div class="field">
        <label for="waste">Waste factor</label>
        <select id="waste">
          <option value="0.10">10% (no repeat / experienced installer)</option>
          <option value="0.15" selected>15% (standard)</option>
          <option value="0.20">20% (large repeat / DIY)</option>
        </select>
        <span class="hint">extra to absorb cuts &amp; mistakes</span>
      </div>
    </div>

    <div class="calc-cta">
      <button class="primary" id="calcBtn" type="button">Calculate yardage</button>
      <button class="ghost" id="resetBtn" type="button">Reset to defaults</button>
    </div>

    <div class="results" id="results" aria-live="polite">
      <h3>Your estimate</h3>
      <div class="results-grid">
        <div class="result-card"><div class="num" id="resBolts">—</div><div class="lbl">Double-roll bolts</div></div>
        <div class="result-card"><div class="num" id="resSqft">—</div><div class="lbl">Sq ft total (with waste)</div></div>
        <div class="result-card"><div class="num" id="resStrips">—</div><div class="lbl">Strips needed</div></div>
        <div class="result-card"><div class="num" id="resStripLen">—</div><div class="lbl">Strip length</div></div>
      </div>
      <div class="breakdown" id="resBreakdown"></div>
      <div class="warn" id="resWarn" style="display:none"></div>
    </div>
  </div>

  <section class="faq" id="faq">
    <h2>Questions installers hear most</h2>
    <p class="sub">Four things worth understanding before you place an order.</p>
    <div class="faq-list">
      <details class="faq-item">
        <summary>Why does the pattern repeat add to my total?</summary>
        <div class="faq-body">
          <p>Every strip of wallpaper has to <strong>line up horizontally</strong> with the strip next to it. If a pattern repeats every 21 inches, the installer has to slide the next strip up or down within that 21-inch window until the motif matches.</p>
          <p>That slid-off length becomes cut waste at the top or bottom. A wall with a 21" repeat can lose a foot of usable material per strip — which is why our calculator adds the repeat into the strip-length calc, not just sticks a flat 15% on at the end.</p>
        </div>
      </details>
      <details class="faq-item">
        <summary>What's the difference between a single and a double roll?</summary>
        <div class="faq-body">
          <p>In the US, almost everything is <strong>sold as a double roll</strong> — a single bolt that's roughly 27" wide × 27 ft long, or about 56 square feet of usable paper. A "single roll" is half of that, and was the old standard before wider rolls became common.</p>
          <p>European rolls run narrower and shorter (about 20.5" × 33 ft). Commercial Type II vinyl runs 36"–54" wide and is sold by the yard. The calculator handles all five common sizes — pick whatever your specific pattern lists on its product page.</p>
        </div>
      </details>
      <details class="faq-item">
        <summary>Should I order all of my bolts from the same dye lot?</summary>
        <div class="faq-body">
          <p><strong>Yes — without exception.</strong> Each production run of wallpaper goes through the printer as a single "dye lot" or "run number." Different lots can have subtle but visible color shifts that show up as a vertical seam line on your wall.</p>
          <p>Always order one or two extra bolts up front, all from the same lot. Re-orders placed a week later almost always ship from a different lot and can't be matched. The estimate above already factors a 15% buffer — but order another bolt anyway if the room is large.</p>
        </div>
      </details>
      <details class="faq-item">
        <summary>How much waste should I budget for?</summary>
        <div class="faq-body">
          <p>The default <strong>15%</strong> is the right number for most rooms with a standard repeat and a competent installer. It covers trim at the ceiling and baseboard, mistakes around outlets, and one or two unusable strips.</p>
          <p>Bump it to <strong>20%</strong> if the pattern repeat is over 21", if you're a first-time DIYer, or if the room has lots of angles and built-ins. Drop it to <strong>10%</strong> only for plain or random-match papers (grasscloth, solid linens) where strips don't have to align.</p>
        </div>
      </details>
    </div>
  </section>

  <section class="closing" id="closing">
    <h2>Wallpapers we'd suggest at this size</h2>
    <p class="sub">Curated from our catalog of 11,000+ patterns. Click any one to see the full collection.</p>

    <div class="controls">
      <label for="sortSel">Sort</label>
      <select id="sortSel">
        <option value="newest">Newest</option>
        <option value="title">Title A→Z</option>
        <option value="vendor">Vendor A→Z</option>
        <option value="category">Category</option>
      </select>
      <label for="density">Density</label>
      <input id="density" type="range" min="2" max="6" step="1" value="3">
    </div>

    <div class="product-grid" id="productGrid"></div>

    <div style="text-align:center;margin-top:32px">
      <a href="https://designerwallcoverings.com" class="primary" style="display:inline-block;background:var(--accent);color:#fff;padding:14px 32px;border-radius:8px;font-weight:600;text-decoration:none">Browse the full catalog at Designer Wallcoverings →</a>
    </div>
  </section>
</main>

<footer>
  <div class="links">
    <a href="https://designerwallcoverings.com">Designer Wallcoverings</a> ·
    <a href="mailto:info@wallpaperestimator.com">info@wallpaperestimator.com</a> ·
    <a href="tel:8883734564">888-373-4564</a>
  </div>
  <address>
    Designer Wallcoverings<br>
    15442 Ventura Bl #102, Sherman Oaks, CA 91403
  </address>
  <div style="margin-top:14px;font-size:11px">© <span id="yr"></span> Designer Wallcoverings. Estimates are approximate — measure twice, order once.</div>
</footer>

<script>
/* ---------- theme toggle ---------- */
(function(){
  var btn = document.getElementById('themeBtn');
  function setBtn(){ btn.textContent = document.documentElement.dataset.theme === 'dark' ? '☾' : '☼'; }
  setBtn();
  btn.addEventListener('click', function(){
    var cur = document.documentElement.dataset.theme || 'light';
    var nxt = cur === 'dark' ? 'light' : 'dark';
    document.documentElement.dataset.theme = nxt;
    try { localStorage.setItem('wpe_theme', nxt); } catch(e){}
    setBtn();
  });
})();

document.getElementById('yr').textContent = new Date().getFullYear();

/* ---------- units toggle ---------- */
var UNIT = 'ft';
var unitFt = document.getElementById('unitFt');
var unitM  = document.getElementById('unitM');
function setUnits(u){
  UNIT = u;
  unitFt.setAttribute('aria-pressed', u === 'ft');
  unitM .setAttribute('aria-pressed', u === 'm');
  var lenLbl = u === 'ft' ? 'feet' : 'meters';
  var repLbl = u === 'ft' ? 'inches (0 = no repeat)' : 'cm (0 = no repeat)';
  document.getElementById('lenUnit').textContent = lenLbl;
  document.getElementById('widUnit').textContent = lenLbl;
  document.getElementById('hgtUnit').textContent = lenLbl;
  document.getElementById('repUnit').textContent = repLbl;
  if (u === 'm') {
    document.getElementById('length').value = 4.3;
    document.getElementById('width').value  = 3.7;
    document.getElementById('height').value = 2.4;
  } else {
    document.getElementById('length').value = 14;
    document.getElementById('width').value  = 12;
    document.getElementById('height').value = 8;
  }
}
unitFt.addEventListener('click', function(){ setUnits('ft'); });
unitM .addEventListener('click', function(){ setUnits('m'); });

/* ---------- calculator ---------- */
function calc(){
  var L = parseFloat(document.getElementById('length').value)  || 0;
  var W = parseFloat(document.getElementById('width').value)   || 0;
  var H = parseFloat(document.getElementById('height').value)  || 0;
  var R = parseFloat(document.getElementById('repeat').value)  || 0;          // pattern repeat
  var rollW = parseFloat(document.getElementById('rollWidth').value); // inches
  var rollLraw = document.getElementById('rollLen').value;
  var openings = parseInt(document.getElementById('openings').value, 10) || 0;
  var waste = parseFloat(document.getElementById('waste').value);

  // Normalize all dimensions to FEET internally
  if (UNIT === 'm'){ L *= 3.28084; W *= 3.28084; H *= 3.28084; R = R / 2.54; /* cm → in */ }

  // Roll length normalization
  var rollL_ft;
  if (rollLraw === '13.5') rollL_ft = 13.5;
  else if (rollLraw === '27') rollL_ft = 27;
  else if (rollLraw === '33') rollL_ft = 33;          // Euro 10m
  else if (rollLraw === '30') rollL_ft = 90;          // 30 yd → ft
  else rollL_ft = parseFloat(rollLraw) || 27;

  // Perimeter in feet
  var perimeter_ft = 2 * (L + W);
  // Strip length = wall height + pattern repeat allowance
  var stripLen_ft = H + (R / 12); // R is in inches
  // Strips per roll = floor(rollL_ft / stripLen_ft)
  var stripsPerRoll = Math.max(1, Math.floor(rollL_ft / stripLen_ft));
  // Total strips needed = perimeter / roll-width
  var rollW_ft = rollW / 12;
  var totalStrips = Math.ceil(perimeter_ft / rollW_ft);
  // Doors/windows reduce strip count slightly (each opening ≈ 1 strip saved)
  var openingsSavings = Math.min(openings, Math.floor(totalStrips / 4));
  var effectiveStrips = Math.max(1, totalStrips - openingsSavings);
  // Bolts = ceil(strips / strips per roll) × waste
  var bolts = Math.ceil(effectiveStrips / stripsPerRoll);
  var boltsWithWaste = Math.ceil(bolts * (1 + waste));

  // Sq ft (with waste)
  var wallArea = perimeter_ft * H;
  var openingsArea = openings * 21; // door+window avg
  var netArea = Math.max(0, wallArea - openingsArea);
  var totalSqft = Math.ceil(netArea * (1 + waste));

  document.getElementById('resBolts').textContent     = boltsWithWaste;
  document.getElementById('resSqft').textContent      = totalSqft.toLocaleString();
  document.getElementById('resStrips').textContent    = effectiveStrips;
  document.getElementById('resStripLen').textContent  = stripLen_ft.toFixed(1) + ' ft';

  var bd = '';
  bd += '<div>Perimeter: <strong>' + perimeter_ft.toFixed(1) + ' ft</strong> · Wall area before openings: <strong>' + Math.ceil(wallArea) + ' sq ft</strong></div>';
  bd += '<div>Doors/windows deducted: <strong>' + openingsArea + ' sq ft</strong> · Net wall area: <strong>' + Math.ceil(netArea) + ' sq ft</strong></div>';
  bd += '<div>Strips per ' + rollL_ft + '-ft roll at ' + stripLen_ft.toFixed(1) + '-ft strip length: <strong>' + stripsPerRoll + '</strong></div>';
  bd += '<div>Bolts before waste: <strong>' + bolts + '</strong> · After ' + Math.round(waste*100) + '% waste: <strong>' + boltsWithWaste + ' bolts</strong></div>';
  document.getElementById('resBreakdown').innerHTML = bd;

  var warn = document.getElementById('resWarn');
  if (R > 21){
    warn.style.display = 'block';
    warn.innerHTML = '<strong>Heads up:</strong> with a ' + R.toFixed(1) + '" pattern repeat we recommend bumping waste to 20% — large repeats need more cut-off length to keep patterns aligned across strips.';
  } else if (boltsWithWaste < 2){
    warn.style.display = 'block';
    warn.innerHTML = '<strong>Note:</strong> always order at least <strong>1 extra bolt</strong> from the same dye lot. Re-orders can come from a different production run with subtle color shift.';
  } else {
    warn.style.display = 'none';
  }

  document.getElementById('results').classList.add('show');

  if (window.gtag) {
    gtag('event', 'estimate_calculated', {
      bolts: boltsWithWaste,
      sqft:  totalSqft,
      repeat_in: R.toFixed(1),
      unit: UNIT
    });
  }

  // Smooth scroll to the closing CTA so user sees product picks
  setTimeout(function(){
    document.getElementById('closing').scrollIntoView({ behavior: 'smooth', block: 'start' });
  }, 400);
}

document.getElementById('calcBtn').addEventListener('click', calc);
document.getElementById('resetBtn').addEventListener('click', function(){
  if (UNIT === 'm') setUnits('m'); else setUnits('ft');
  document.getElementById('repeat').value = 0;
  document.getElementById('openings').value = 2;
  document.getElementById('rollWidth').value = '27';
  document.getElementById('rollLen').value = '27';
  document.getElementById('waste').value = '0.15';
  document.getElementById('results').classList.remove('show');
});

/* ---------- closing CTA grid ---------- */
var PICKS = [];
fetch('/picks.json').then(function(r){return r.json();}).then(function(d){
  PICKS = d;
  renderPicks();
});

function renderPicks(){
  var sort = (document.getElementById('sortSel').value) || 'newest';
  var list = PICKS.slice();
  if (sort === 'title')    list.sort(function(a,b){ return a.title.localeCompare(b.title); });
  if (sort === 'vendor')   list.sort(function(a,b){ return a.vendor.localeCompare(b.vendor); });
  if (sort === 'category') list.sort(function(a,b){ return a.category.localeCompare(b.category); });

  var html = '';
  for (var i = 0; i < list.length; i++){
    var p = list[i];
    var url = 'https://designerwallcoverings.com/products/' + p.handle;
    html += '<article class="product">';
    html += '<a href="'+url+'" target="_blank" rel="noopener noreferrer">';
    html += '<img src="'+p.image_url+'" alt="'+p.title+'" loading="lazy">';
    html += '</a>';
    html += '<div class="meta">';
    html += '<div class="vnd">'+p.vendor+'</div>';
    html += '<div class="ttl">'+p.title+'</div>';
    html += '<div class="cat">'+p.category+'</div>';
    html += '</div>';
    html += '<a class="shop" href="'+url+'" target="_blank" rel="noopener noreferrer">View on DW →</a>';
    html += '</article>';
  }
  document.getElementById('productGrid').innerHTML = html;
}

/* sort persistence */
var sortSel = document.getElementById('sortSel');
try { sortSel.value = localStorage.getItem('wpe_sort') || 'newest'; } catch(e){}
sortSel.addEventListener('change', function(){
  try { localStorage.setItem('wpe_sort', sortSel.value); } catch(e){}
  renderPicks();
});

/* density slider persistence */
var density = document.getElementById('density');
try {
  var saved = localStorage.getItem('wpe_cols');
  if (saved) density.value = saved;
} catch(e){}
function applyDensity(){
  document.documentElement.style.setProperty('--cols', density.value);
}
applyDensity();
density.addEventListener('input', function(){
  applyDensity();
  try { localStorage.setItem('wpe_cols', density.value); } catch(e){}
});
</script>



<!-- Big Red customer chat — lower-left, woman-avatar, vendor-redacted retail persona. -->
<script data-bigred-widget src="https://chat.designerwallcoverings.com/widget.js"
        data-host="https://chat.designerwallcoverings.com"
        data-mode="retail"
        defer onerror="this.remove()"></script>

<script>window.DwHeaderConfig={siteName:"Wallpaperestimator",accent:"#9B7B4F",navLinks:[{name:'Home',url:'/'},{name:'Collections',url:'#shop'},{name:'About',url:'/about'},{name:'Care',url:'/care.html'},{name:'History',url:'/history.html'}],adminLinks:[{name:'Shopify admin',url:'https://admin.shopify.com',external:true},{name:'Edit hero',url:'#'},{name:'Analytics',url:'#'}]};</script>
<script src="/dw-header.js" defer></script>
</body>
</html>