← back to Wallpapercontractor
views/index.ejs
189 lines
<%- include('partials/head', { title: site.name + ' — ' + site.tagline, description: site.description }) %>
<!-- TODO: nginx Cache-Control: no-store; per feedback_cloudflare_html_caching -->
<main class="wrap main">
<section class="hero hero-editorial">
<div class="hero-eyebrow">An industry directory · est. 2026</div>
<h1 class="hero-h1">
<span class="hero-line-1">Wallpaper at commercial scale</span>
<span class="hero-line-2">takes more than a paperhanger.</span>
</h1>
<p class="lead hero-sub"><%= total %> bonded, insured wallcovering installation contractors — vetted for the 30,000-square-foot job.</p>
<div class="hero-meta">
<span>Hospitality</span><span class="dot">·</span>
<span>Healthcare</span><span class="dot">·</span>
<span>Multifamily</span><span class="dot">·</span>
<span>Retail</span><span class="dot">·</span>
<span>Corporate</span><span class="dot">·</span>
<span>Government</span>
</div>
<p class="hero-foot muted">Smaller residential or single-installer job? <a href="/find-installer">Tell us about it</a> — free matching, 24-hour turnaround. Or browse single-installer paperhangers at <a href="https://nationalpaperhangers.com">NationalPaperHangers</a>.</p>
</section>
<!-- ─── How matching works (3-step) ─────────────────────────────── -->
<section class="how-it-works" aria-label="How matching works">
<div class="how-head">
<span class="how-eyebrow">For consumers & smaller jobs</span>
<h2 class="how-h2">How matching works</h2>
</div>
<ol class="how-steps">
<li class="how-step">
<div class="how-num">01</div>
<div class="how-step-body">
<h3>Tell us the project</h3>
<p>Wall area, project type, timeline, ZIP. Two minutes.</p>
</div>
</li>
<li class="how-step">
<div class="how-num">02</div>
<div class="how-step-body">
<h3>We route to the right installer</h3>
<p>Single-paperhanger residential goes to NationalPaperHangers; bonded crew work routes to a directory contractor in your region.</p>
</div>
</li>
<li class="how-step">
<div class="how-num">03</div>
<div class="how-step-body">
<h3>You hear back in 24 hours</h3>
<p>Direct contact from the installer — not a call-center. No fee to you, no auction, no resold leads.</p>
</div>
</li>
</ol>
<div class="how-cta">
<a href="/find-installer" class="btn-primary">Get matched →</a>
<span class="muted">or scroll for the contractor index ↓</span>
</div>
</section>
<form class="filter-bar" method="get" action="/">
<input type="search" name="q" value="<%= filters.q %>" placeholder="Search company, principal, city, specialty…" aria-label="Search">
<select name="region" aria-label="Region">
<option value="">All regions</option>
<% facets.regions.forEach(function(r){ %>
<option value="<%= r %>" <%= filters.region === r ? 'selected' : '' %>><%= r %></option>
<% }) %>
</select>
<select name="segment" aria-label="Project segment">
<option value="">All segments</option>
<% facets.segments.forEach(function(s){ %>
<option value="<%= s %>" <%= filters.segment === s ? 'selected' : '' %>><%= s %></option>
<% }) %>
</select>
<select name="specialty" aria-label="Specialty">
<option value="">All specialties</option>
<% facets.specialties.forEach(function(s){ %>
<option value="<%= s %>" <%= filters.specialty === s ? 'selected' : '' %>><%= s %></option>
<% }) %>
</select>
<label class="check">
<input type="checkbox" name="bonded" value="1" <%= filters.bonded === '1' ? 'checked' : '' %>>
<span>Bonded only</span>
</label>
<label class="check">
<input type="checkbox" name="insured" value="1" <%= filters.insured === '1' ? 'checked' : '' %>>
<span>Insured only</span>
</label>
<button type="submit" class="btn-primary">Filter</button>
<% if (filters.q || filters.region || filters.segment || filters.specialty || filters.bonded || filters.insured) { %>
<a class="btn-ghost" href="/">Reset</a>
<% } %>
</form>
<div class="result-bar">
<div class="muted"><%= matched %> of <%= total %> matching</div>
<div class="controls">
<label class="control">
<span>Sort</span>
<select id="sort-select" name="sort">
<option value="newest" <%= filters.sort === 'newest' ? 'selected' : '' %>>Newest</option>
<option value="company" <%= filters.sort === 'company' ? 'selected' : '' %>>Company A→Z</option>
<option value="city" <%= filters.sort === 'city' ? 'selected' : '' %>>City A→Z</option>
<option value="region" <%= filters.sort === 'region' ? 'selected' : '' %>>Region</option>
<option value="crew_max" <%= filters.sort === 'crew_max' ? 'selected' : '' %>>Crew size ↓</option>
<option value="years" <%= filters.sort === 'years' ? 'selected' : '' %>>Years in business ↓</option>
</select>
</label>
<label class="control">
<span>Density</span>
<input id="density-slider" type="range" min="240" max="440" step="20" value="320">
</label>
</div>
</div>
<% if (!matched) { %>
<div class="empty">No contractors match those filters. <a href="/">Reset</a>.</div>
<% } else { %>
<ul class="card-grid" id="rep-grid">
<% contractors.forEach(function(c){ %>
<li class="rep-card">
<a class="rep-link" href="/contractor/<%= c.id %>">
<div class="rep-card-head">
<div class="rep-avatar"><%= c.company.split(' ').map(s => s[0]).join('').slice(0,3).toUpperCase() %></div>
<div>
<div class="rep-name"><%= c.company %></div>
<div class="rep-agency"><%= c.principal %> · <%= c.city %></div>
</div>
</div>
<div class="rep-meta">
<% if (c.bonded) { %><span class="chip ok" title="Surety bonded">Bonded</span><% } %>
<% if (c.insured) { %><span class="chip ok" title="Carries general liability">Insured <%= c.liability_limit %></span><% } %>
<% if (c.union) { %><span class="chip union">Union</span><% } %>
<span class="chip">Crew up to <%= c.crew_max %></span>
<span class="chip"><%= c.years_in_business %> yrs</span>
</div>
<div class="rep-lines muted">
<%= c.specialties.slice(0,3).join(' · ') %>
</div>
<div class="rep-vertical">
<% c.segments.forEach(function(s){ %>
<span class="tag"><%= s %></span>
<% }) %>
</div>
</a>
</li>
<% }) %>
</ul>
<% } %>
<!-- ─── Procurement FAQ ─────────────────────────────────────────── -->
<section class="faq" aria-label="Procurement FAQ">
<div class="faq-head">
<span class="faq-eyebrow">For procurement & GCs</span>
<h2 class="faq-h2">Common procurement questions</h2>
</div>
<details class="faq-item">
<summary>What's the difference between a paperhanger and a wallcovering contractor?</summary>
<p>A paperhanger is a skilled single-installer trade — ideal for residential, boutique commercial, and detail-heavy work up to a few thousand square feet. A wallcovering contractor runs a bonded crew of 4–40 installers, carries general-liability insurance with limits sufficient for prime/sub agreements, files certified payroll on prevailing-wage jobs, and can mobilize for 30,000-square-foot scopes on hospitality, healthcare, and multifamily projects. Both have a place; they are not interchangeable on a commercial bid sheet.</p>
</details>
<details class="faq-item">
<summary>How do you verify bonding and insurance?</summary>
<p>Each listing reflects contractor-supplied surety bonding status, general-liability limit, and union affiliation as of the most recent update we received from the contractor. We do not act as a broker, and limits change. Before contracting work, request a current Certificate of Insurance and bond rider naming your project entity as additional insured, and verify directly with the surety on file. Use the filters above to narrow to bonded-only or insured-only listings as a starting point.</p>
</details>
<details class="faq-item">
<summary>Can a single installer take on a 30,000-square-foot job?</summary>
<p>Almost never on a commercial timeline. A solo paperhanger averages 80–120 linear yards per day in clean conditions. A 30,000 sqft scope is roughly 3,300 single-rolls; even at peak production, that's 4–6 months of one-installer labor, which collides with schedule, lift logistics, and prime-contract liability terms. Hospitality and healthcare GCs almost always require a crew contractor for scopes above ~5,000 sqft.</p>
</details>
<details class="faq-item">
<summary>What do Type II and Type III commercial wallcoverings mean for installer selection?</summary>
<p>Type II is medium-duty commercial vinyl (CCC-W-408D, 20–28 oz/lin yd) — the workhorse spec for hotel corridors, offices, and education. Type III is heavy-duty (28+ oz/lin yd) used in healthcare patient rooms, transit, and high-abrasion retail. Type III is heavier to hang, requires different adhesive open-time and seam treatment, and is less forgiving of substrate imperfection — confirm your contractor has recent Type III experience before awarding a Type III scope. Specialty installs (acoustic panels, leather, hand-painted, digital murals) are separate skill sets — filter by specialty above.</p>
</details>
<p class="faq-foot muted">Sourcing for an active bid? Email <a href="mailto:<%= site.email %>"><%= site.email %></a> with the scope sheet and we'll surface 3–5 contractors with the right region, segment, and bonding profile within one business day. Free service, no kickback.</p>
</section>
<section class="seo-block">
<h2>How this directory works</h2>
<p>Wallpaper Contractor is a curated index of commercial wallcovering installation contractors operating in the United States. Each listing maps the contractor's regional coverage, the project segments they actively work in (hospitality, luxury hotel, casino, resort, restaurant, multifamily, healthcare, education, corporate, retail, cruise, government), the wallcovering specialties they install (Type II commercial vinyl, Type III heavy-duty, antimicrobial, acoustic, grasscloth, silk, hand-painted murals, digital murals, leather wall panels, marine-grade), peak crew size, years in business, surety bonding status, general-liability insurance limits, and union affiliation. Use the filters above to narrow by region, segment, specialty, bonding status, or insurance status.</p>
<p>This directory exists because procurement teams, general contractors, FF&E purchasing groups, and design firms routinely struggle to identify wallcovering installers who can actually take on a 30,000-square-foot commercial scope. Single-installer paperhangers — ideal for residential and small commercial work — generally cannot bond, insure, or staff that volume. The contractors listed here can.</p>
<p>This directory is operated by <a href="https://designerwallcoverings.com">Designer Wallcoverings</a> as a free industry resource. Listings are not pay-to-play. To list your company, update an existing listing, or correct an entry, email <a href="mailto:<%= site.email %>"><%= site.email %></a>.</p>
</section>
</main>
<%- include('partials/foot') %>