← back to Stars of Design

views/public/designer.ejs

135 lines

<%- include('../partials/head', { title, meta_desc_override: typeof meta_desc_override !== 'undefined' ? meta_desc_override : undefined }) %>
<%
  // JSON-LD Person schema for richer SERP cards. Mention=DW collections that
  // pair with this designer; affiliation/image kept loose since we host neither
  // images nor employment data.
  var jsonld = {
    '@context': 'https://schema.org',
    '@type': 'Person',
    name: designer.name,
    description: designer.bio || designer.headline || ('Interior designer profile on Stars of Design.'),
    url: 'https://starsofdesign.com/designers/' + designer.slug,
    jobTitle: 'Interior Designer',
    knowsAbout: (designer.styles || []).slice(0, 8),
  };
  if (designer.city) {
    jsonld.workLocation = { '@type': 'Place', name: designer.city };
  }
  if (designer.preferred_dw && designer.preferred_dw.length) {
    jsonld.subjectOf = designer.preferred_dw.map(function (p) {
      return { '@type': 'CreativeWork', name: p.label, url: p.url };
    });
  }
%>
<%
  // Escape </script>, HTML comment markers, and U+2028/U+2029 so the JSON-LD
  // payload can't break out of the <script> tag if any data field ever contains
  // those substrings. Standard OWASP XSS Prevention Rule #3.1 for inline JSON.
  var jsonldSafe = JSON.stringify(jsonld)
    .replace(/<\/script/gi, '<\\/script')
    .replace(/<!--/g, '<\\!--')
    .replace(/\u2028/g, '\\u2028')
    .replace(/\u2029/g, '\\u2029');
%>
<script type="application/ld+json"><%- jsonldSafe %></script>
<%- include('../partials/header') %>

<main class="designer-detail">
  <section class="section">
    <div class="wrap narrow">
      <p class="muted"><a href="/designers">← Back to all designers</a></p>

      <header class="detail-header">
        <span class="detail-mark" aria-hidden="true"><%= (designer.name || '?').split(' ').slice(0,2).map(function (w) { return w[0]; }).join('') %></span>
        <div>
          <h1><%= designer.name %></h1>
          <% if (designer.headline) { %><p class="lede"><%= designer.headline %></p><% } %>
          <p class="muted">
            <% if (designer.era) { %><%= designer.era %><% } %>
            <% if (designer.active_years) { %> · <%= designer.active_years %><% } %>
            <% if (designer.city) { %> · <%= designer.city %><% } %>
          </p>
        </div>
      </header>

      <% if (designer.styles && designer.styles.length) { %>
        <h2>Signature styles</h2>
        <ul class="pills">
          <% designer.styles.forEach(function (s) { %><li><%= s %></li><% }) %>
        </ul>
      <% } %>

      <% if (designer.bio) { %>
        <h2>Profile</h2>
        <p><%= designer.bio %></p>
      <% } %>

      <% if (designer.signature) { %>
        <h2>Visual language</h2>
        <p><%= designer.signature %></p>
      <% } %>

      <% if (designer.preferred_dw && designer.preferred_dw.length) { %>
        <h2>Wallcoverings that match this designer</h2>
        <p class="muted small">Curated by Designer Wallcoverings — collections that map to <%= designer.name.split(' ')[0] %>'s published interiors.</p>
        <div class="pairing-grid">
          <% designer.preferred_dw.forEach(function (p) { %>
            <a class="pairing-card" href="<%= p.url %>" rel="noopener noreferrer" target="_blank">
              <span class="pairing-label"><%= p.label %></span>
              <span class="pairing-arrow" aria-hidden="true">→</span>
            </a>
          <% }); %>
        </div>
      <% } %>

      <% if (typeof designerVideos !== 'undefined' && designerVideos.length) { %>
        <h2>Watch <%= designer.name.split(' ').slice(0, 2).join(' ') %></h2>
        <p class="muted small">Home tours, interviews, and project showcases on YouTube.</p>
        <div class="profile-video-grid">
          <% designerVideos.forEach(function (v) { %>
            <a class="profile-video-card" href="<%= v.url %>" target="_blank" rel="noopener noreferrer" aria-label="<%= v.title %> — opens YouTube in new tab">
              <span class="profile-video-poster" style="background-image:url('<%= v.thumb_url %>')">
                <span class="profile-video-play" aria-hidden="true">▶</span>
              </span>
              <span class="profile-video-meta">
                <strong><%= v.title %></strong>
                <span class="muted small"><%= v.channel %><% if (v.year) { %> · <%= v.year %><% } %></span>
              </span>
            </a>
          <% }); %>
        </div>
      <% } %>

      <% if (designer.firm_url || designer.instagram_url) { %>
        <h2>Find the firm</h2>
        <ul class="links">
          <% if (designer.firm_url) { %><li><a href="<%= designer.firm_url %>" rel="noopener noreferrer" target="_blank">Firm website ↗</a></li><% } %>
          <% if (designer.instagram_url) { %><li><a href="<%= designer.instagram_url %>" rel="noopener noreferrer" target="_blank">Instagram ↗</a></li><% } %>
        </ul>
      <% } %>

      <% if (designer.sources && designer.sources.length) { %>
        <h2>Sources &amp; press</h2>
        <p class="muted small">External editorial coverage we cited when building this profile. Credits in line with fair-use editorial citation.</p>
        <ul class="sources">
          <% designer.sources.forEach(function (s) { %>
            <li>
              <a href="<%= s.url %>" rel="noopener noreferrer" target="_blank"><strong><%= s.outlet %></strong></a><% if (s.title) { %> — <em><%= s.title %></em><% } %><% if (s.year) { %> <span class="muted">(<%= s.year %>)</span><% } %>
            </li>
          <% }); %>
        </ul>
      <% } %>

      <p class="muted small detail-disclaim">
        Stars of Design is an editorial profile directory. We are not affiliated with <%= designer.name %> or
        their studio. Wallcovering pairings are curated based on publicly available work and are sold by
        <a href="https://designerwallcoverings.com" rel="noopener noreferrer">Designer Wallcoverings</a>, the directory's
        publisher. Source citations are provided in line with fair-use editorial practice; visit each outlet
        directly for the full article.
      </p>
    </div>
  </section>
</main>

<%- include('../partials/footer') %>