← back to NationalPaperHangers

views/public/installer.ejs

286 lines

<%- include('../partials/head', { title }) %>
<%- include('../partials/header') %>
<%
  function safeWebsite(u) {
    if (!u) return null;
    try { var x = new URL(u); return (x.protocol === 'http:' || x.protocol === 'https:') ? x.toString() : null; }
    catch (e) { return null; }
  }
  var safeSiteUrl = safeWebsite(installer.website);
%>

<article class="installer-profile">
  <%
    var _calendarTier = ['pro','signature','enterprise'].indexOf(installer.tier) !== -1;
    var _selfBookable = _calendarTier && installer.subscription_status === 'active';
    var _depositCents = parseInt(process.env.NPH_DEFAULT_DEPOSIT_CENTS || '9900', 10);
    var _depositLabel = '$' + (_depositCents / 100).toFixed(0);
    var _feeBps = parseInt(process.env.NPH_PLATFORM_FEE_BPS || '2500', 10);
    var _feePct = Math.round(_feeBps / 100);
  %>
  <% if (_selfBookable) { %>
    <aside class="reserve-banner" role="region" aria-label="Reserve a visit">
      <div class="reserve-banner-inner">
        <div class="reserve-copy">
          <p class="reserve-kicker">Reserve a visit · <%= _depositLabel %> deposit</p>
          <p class="reserve-body">Pay through NPH — your share goes straight to <%= installer.business_name %>. NPH retains a <%= _feePct %>% lead-coordination fee. The installation contract is between you and <%= installer.business_name %> directly.</p>
          <p class="reserve-fineprint" style="margin:6px 0 0;font-size:11px;color:var(--fg-muted);line-height:1.5">Verify CA contractor licenses at <a href="https://search.cslb.ca.gov/" target="_blank" rel="noopener">search.cslb.ca.gov</a> before signing.</p>
        </div>
        <a href="/installer/<%= installer.slug %>/book" class="btn btn-primary btn-lg reserve-cta">Book a consultation →</a>
      </div>
    </aside>
  <% } %>
  <% var _heroImg = (portfolio.length === 0) ? pickSegmentImage(installer) : null; %>
  <% if (_heroImg) {
    var _attr = imageAttribution(_heroImg.file);
    var _firstSeg = String((installer.market_segments && installer.market_segments[0]) || 'interior');
  %>
    <div class="installer-hero-image">
      <img src="<%= _heroImg.file %>" alt="Wallcovering installation, <%= _firstSeg.replace(/_/g,' ') %>" loading="eager" decoding="async">
      <% if (_attr) { %>
        <a class="photo-credit" href="<%= _attr.source_url %>" target="_blank" rel="noopener">
          Photo<% if (_attr.creator) { %>: <%= _attr.creator.slice(0, 60) %><% } %> · <%= _attr.license %> · <%= _attr.source %>
        </a>
      <% } %>
    </div>
  <% } %>
  <header class="profile-hero">
    <div class="profile-hero-inner">
      <p class="kicker"><%= installer.city %>, <%= installer.state %> · est. <%= installer.founded_year || '—' %></p>
      <h1 class="display-sm"><%= installer.business_name %></h1>
      <% if (installer.headline) { %><p class="profile-headline"><%= installer.headline %></p><% } %>
      <div class="profile-badges">
        <% if (installer.claim_status === 'unclaimed') { %>
          <span class="directory-badge" title="Public directory listing — studio has not yet claimed.">Directory listing</span>
        <% } %>
        <% if (installer.verified) { %><span class="verified-badge">Verified</span><% } %>
        <% if (installer.insurance_on_file) { %><span class="badge">Insurance on file</span><% } %>
        <% if (installer.travel_available) { %><span class="badge">Travels for projects</span><% } %>
        <% if (installer.tier === 'signature' || installer.tier === 'enterprise') { %><span class="signature-badge">Signature</span><% } %>
        <% if (installer.claim_status !== 'unclaimed') { %><span class="badge">Responds in &lt; <%= installer.response_time_hours %>h</span><% } %>
      </div>

      <% if (installer.claim_status === 'unclaimed') { %>
        <div class="callout" style="margin:20px 0">
          <strong>Trade buyers are finding this studio on NPH.</strong>
          If you operate <%= installer.business_name %>, claim your listing in 2 minutes to enable direct bookings, the Verified badge, and reply-tracking on inbound leads.
          <a href="/installer/<%= installer.slug %>/claim" class="btn btn-primary btn-sm" style="margin-left:8px">Claim this listing</a>
          <p class="muted" style="margin:8px 0 0;font-size:13px">Seeded from <%= installer.source_name === 'wia' ? 'the WIA public directory' : 'a public source' %>. <a href="mailto:info@nationalpaperhangers.com?subject=Opt-out%3A%20<%= encodeURIComponent(installer.business_name) %>">Request removal →</a></p>
        </div>
      <% } %>

      <div class="profile-actions">
        <% if (installer.claim_status === 'unclaimed') { %>
          <% if (safeSiteUrl) { %><a href="<%= safeSiteUrl %>" target="_blank" rel="noopener" class="btn btn-primary btn-lg">Visit studio site ↗</a><% } %>
          <% if (installer.instagram_handle) { %><a href="https://instagram.com/<%= encodeURIComponent(installer.instagram_handle) %>" target="_blank" rel="noopener" class="btn btn-ghost btn-lg">@<%= installer.instagram_handle %></a><% } %>
        <% } else { %>
          <a href="/installer/<%= installer.slug %>/book" class="btn btn-primary btn-lg">Book a consultation</a>
          <% if (safeSiteUrl) { %><a href="<%= safeSiteUrl %>" target="_blank" rel="noopener" class="btn btn-ghost">Visit studio site ↗</a><% } %>
          <% if (installer.instagram_handle) { %><a href="https://instagram.com/<%= encodeURIComponent(installer.instagram_handle) %>" target="_blank" rel="noopener" class="btn btn-ghost">@<%= installer.instagram_handle %></a><% } %>
        <% } %>
      </div>
    </div>
  </header>

  <div class="profile-body">
    <section class="profile-bio">
      <h2 class="section-title">About the studio</h2>
      <p><%= installer.bio %></p>
    </section>

    <aside class="profile-sidebar">
      <h3>At a glance</h3>
      <dl>
        <dt>Service radius</dt>
        <dd><%= installer.service_radius_miles %> miles<%= installer.travel_available ? ' · travels for projects' : '' %></dd>
        <% if (installer.team_size) { %>
          <dt>Team</dt><dd><%= installer.team_size %> installer<%= installer.team_size === 1 ? '' : 's' %></dd>
        <% } %>
        <% if ((installer.market_segments || []).length) { %>
          <dt>Segments</dt><dd><%= installer.market_segments.map(s => String(s||'').replace(/_/g,' ')).filter(Boolean).join(', ') %></dd>
        <% } %>
        <% if ((installer.materials || []).length) { %>
          <dt>Materials</dt><dd><%= installer.materials.map(s => String(s||'').replace(/_/g,' ')).filter(Boolean).join(', ') %></dd>
        <% } %>
        <% if ((installer.brands_handled || []).length) { %>
          <dt>Brands installed</dt><dd><%= installer.brands_handled.join(', ') %></dd>
        <% } %>
        <% if ((installer.accreditations || []).length) { %>
          <dt>Accreditations</dt><dd><%= installer.accreditations.join(', ') %></dd>
        <% } %>
      </dl>

      <%
        var eq = installer.equipment;
        var hasEq = eq && (eq.max_reach_ft || eq.lift_type || eq.paper_table || eq.vehicle || eq.dust_extraction || eq.notes);
        var liftLabel = {
          extension_ladder: 'Extension ladder',
          scaffold:         'Scaffold',
          scissor_lift:     'Scissor lift',
          boom_lift:        'Boom lift'
        };
        var tableLabel = {
          none: 'No dedicated table',
          folding: 'Folding table',
          dedicated_60: 'Dedicated 60" pasting table',
          dedicated_72_plus: 'Dedicated 72"+ pasting table'
        };
        var vehicleLabel = { van: 'Van', box_truck: 'Box truck', trailer: 'Trailer-equipped' };
      %>
      <% if (hasEq) { %>
        <h3 style="margin-top:24px">Studio capacity</h3>
        <dl>
          <% if (eq.max_reach_ft) { %><dt>Reach</dt><dd>Up to <%= eq.max_reach_ft %> ft</dd><% } %>
          <% if (eq.lift_type) { %><dt>Lift</dt><dd><%= liftLabel[eq.lift_type] || eq.lift_type %></dd><% } %>
          <% if (eq.paper_table) { %><dt>Pasting</dt><dd><%= tableLabel[eq.paper_table] || eq.paper_table %></dd><% } %>
          <% if (eq.vehicle) { %><dt>Vehicle</dt><dd><%= vehicleLabel[eq.vehicle] || eq.vehicle %></dd><% } %>
          <% if (eq.dust_extraction) { %><dt>Dust</dt><dd>HEPA extraction on-site</dd><% } %>
          <% if (eq.notes) { %><dt>Notes</dt><dd><%= eq.notes %></dd><% } %>
        </dl>
      <% } %>

      <% if (typeof credentials !== 'undefined' && credentials && credentials.length) {
         var _credLabel = {
           brand_trained:        'trained',
           brand_certified:      'certified',
           brand_approved:       'approved',
           manufacturer_partner: 'partner',
           trade_member:         'member'
         };
      %>
        <h3 style="margin-top:24px">Brand credentials</h3>
        <ul class="credential-list" style="list-style:none;padding:0;margin:0">
          <% credentials.forEach(function(c){ %>
            <li style="padding:8px 0;border-bottom:1px solid var(--border)">
              <strong><%= c.brand %></strong>
              <span class="muted">· <%= _credLabel[c.credential_type] || c.credential_type %><% if (c.year_issued) { %> <%= c.year_issued %><% } %></span>
              <% if (c.certificate_url) { %>
                <a href="<%= c.certificate_url %>" target="_blank" rel="noopener" style="font-size:12px;margin-left:6px">cert ↗</a>
              <% } %>
            </li>
          <% }); %>
        </ul>
      <% } %>

      <% if (typeof acceptance !== 'undefined' && acceptance) { %>
        <h3 style="margin-top:24px">Selectivity</h3>
        <p style="margin:0">
          <strong>Accepts <%= acceptance.rate %>%</strong> of inquiries
          <span class="muted">(<%= acceptance.accepted %> taken / <%= acceptance.declined %> declined, last 12 mo)</span>
        </p>
        <p class="muted" style="font-size:12px;margin:6px 0 0">A studio booked solid enough to be selective.</p>
      <% } %>

      <%- include('../partials/paper-contributions') %>
    </aside>
  </div>

  <%- include('../partials/coi-request') %>

  <%- include('../partials/call-installer') %>

  <% if (portfolio.length > 0) { %>
    <section class="profile-portfolio">
      <h2 class="section-title">Selected projects</h2>
      <p class="muted" style="margin:-12px 0 18px;font-size:13px">Tap <em>Seams</em>, <em>Corners</em>, or <em>Ceiling</em> on any project to see craft detail — where the difference shows.</p>
      <div class="portfolio-grid">
        <% portfolio.forEach(function(p, idx){
          var shots = [
            { key: 'hero',    label: 'Project',  url: p.image_url },
            { key: 'seam',    label: 'Seams',    url: p.detail_seam_url },
            { key: 'corner',  label: 'Corners',  url: p.detail_corner_url },
            { key: 'ceiling', label: 'Ceiling',  url: p.detail_ceiling_url }
          ].filter(s => s.url);
        %>
          <article class="portfolio-card portfolio-card-tabbed" data-portfolio-card="<%= idx %>">
            <div class="portfolio-image-wrap">
              <% if (shots.length === 0) { %>
                <span class="portfolio-image-placeholder"><%= (p.brand || '').slice(0,2).toUpperCase() %></span>
              <% } else { %>
                <% shots.forEach(function(s, i){ %>
                  <img class="portfolio-shot <%= i === 0 ? 'is-active' : '' %>"
                       data-shot="<%= s.key %>"
                       src="<%= s.url %>"
                       alt="<%= p.title %> — <%= s.label %>"
                       loading="lazy">
                <% }); %>
              <% } %>
            </div>
            <% if (shots.length > 1) { %>
              <nav class="portfolio-tabs" aria-label="Project detail">
                <% shots.forEach(function(s, i){ %>
                  <button type="button"
                          class="portfolio-tab <%= i === 0 ? 'is-active' : '' %>"
                          data-shot-target="<%= s.key %>"
                          data-card="<%= idx %>"><%= s.label %></button>
                <% }); %>
              </nav>
            <% } %>
            <h3><%= p.title %></h3>
            <p class="portfolio-meta">
              <%= [p.city, p.state, p.year].filter(Boolean).join(' · ') %>
              <% if (p.brand) { %> · <%= p.brand %><% } %>
            </p>
            <% if (p.description) { %><p class="portfolio-desc"><%= p.description %></p><% } %>
          </article>
        <% }); %>
      </div>
    </section>
  <% } %>

  <%- include('../partials/social-videos') %>

  <% if (reviews.length > 0) { %>
    <section class="profile-reviews">
      <h2 class="section-title">Verified reviews</h2>
      <div class="reviews-grid">
        <% reviews.forEach(function(r){ %>
          <article class="review-card">
            <div class="review-rating"><%= '★'.repeat(r.rating) %><%= '☆'.repeat(5 - r.rating) %></div>
            <% if (r.title) { %><h4><%= r.title %></h4><% } %>
            <p><%= r.body %></p>
            <p class="review-author"><%= r.customer_name || 'Verified client' %><% if (r.verified) { %> · Verified<% } %></p>
          </article>
        <% }); %>
      </div>
    </section>
  <% } %>

  <section class="profile-cta">
    <h2 class="display-sm">Ready to book <%= installer.business_name %>?</h2>
    <a href="/installer/<%= installer.slug %>/book" class="btn btn-primary btn-lg">Open the calendar</a>
  </section>

  <%
    var _ldBase = (typeof publicUrl !== 'undefined' && publicUrl) ? publicUrl.replace(/\/+$/, '') : 'https://www.nationalpaperhangers.com';
    var _ld = {
      '@context': 'https://schema.org',
      '@type': 'LocalBusiness',
      name: installer.business_name,
      description: (installer.bio || '').slice(0, 500),
      url: _ldBase + '/installer/' + installer.slug,
      address: {
        '@type': 'PostalAddress',
        addressLocality: installer.city || undefined,
        addressRegion: installer.state || undefined,
        postalCode: installer.zip || undefined,
        addressCountry: installer.country || 'US'
      },
      knowsAbout: (installer.materials || []).map(function(m){ return String(m || '').replace(/_/g,' '); }).filter(Boolean),
      hasCredential: (installer.accreditations || [])
    };
    if (safeSiteUrl) _ld.sameAs = [safeSiteUrl];
  %>
  <script type="application/ld+json"><%- JSON.stringify(_ld) %></script>
  <script type="application/ld+json"><%- JSON.stringify({
    '@context':'https://schema.org','@type':'BreadcrumbList',
    itemListElement: [
      { '@type':'ListItem', position:1, name:'Find an installer', item: _ldBase + '/find' },
      { '@type':'ListItem', position:2, name: installer.business_name, item: _ldBase + '/installer/' + installer.slug }
    ]
  }) %></script>
</article>

<%- include('../partials/footer') %>
<script src="/js/portfolio-tabs.js" defer></script>