← back to Stars of Design

views/public/firms.ejs

117 lines

<%- include('../partials/head', { title: 'Top US Design Firms · Stars of Design' }) %>
<%- include('../partials/header') %>

<main class="home">
  <section class="hero-gucci" aria-labelledby="hero-wordmark" style="min-height:42vh">
    <div class="hero-gucci-bg" role="img" aria-label="Editorial firm placeholder"></div>
    <div class="hero-gucci-stack">
      <h1 id="hero-wordmark" class="hero-gucci-wordmark" style="font-size:clamp(40px,5vw,72px)">Top US Firms</h1>
      <p class="hero-gucci-tagline"><em>Architecture + interior design — the public-knowledge canon, seeded.</em></p>
      <div class="hero-gucci-cta">
        <a class="btn ghost" href="/clients">DW client designers →</a>
        <a class="btn ghost" href="/designers" style="margin-left:8px">Editorial directory →</a>
      </div>
    </div>
  </section>

  <section class="section" id="firms">
    <div class="wrap">
      <div class="section-head">
        <h2>Architecture + interior design firms</h2>
        <span class="muted"><%= firms.length %> of <%= total %> · public-knowledge seed + sig-extract</span>
      </div>

      <form class="grid-controls" method="get" action="/firms" data-grid-controls>
        <div class="grid-controls-left">
          <input type="search" name="q" placeholder="Search firm, city, state" value="<%= (filter.q||'').replace(/"/g,'&quot;') %>" style="background:transparent;border:1px solid var(--line,#2a2620);color:inherit;padding:7px 12px;border-radius:6px;font:inherit;min-width:240px">
          <button type="submit" class="btn ghost" style="margin-left:8px">Search</button>
        </div>
        <div class="grid-controls-right">
          <label class="control">
            <span>Sort</span>
            <select id="sort-select" name="sort" data-sort onchange="this.form.submit()">
              <option value="claimed"     <%= sortKey==='claimed'     ? 'selected' : '' %>>Claimed first</option>
              <option value="name_az"     <%= sortKey==='name_az'     ? 'selected' : '' %>>Name A→Z</option>
              <option value="name_za"     <%= sortKey==='name_za'     ? 'selected' : '' %>>Name Z→A</option>
              <option value="founded_new" <%= sortKey==='founded_new' ? 'selected' : '' %>>Founded — newest</option>
              <option value="founded_old" <%= sortKey==='founded_old' ? 'selected' : '' %>>Founded — oldest</option>
              <option value="city"        <%= sortKey==='city'        ? 'selected' : '' %>>By city</option>
              <option value="size"        <%= sortKey==='size'        ? 'selected' : '' %>>By size</option>
            </select>
          </label>
          <label class="control">
            <span>Density</span>
            <input id="density-slider" type="range" min="220" max="440" step="20" value="320" data-density>
          </label>
        </div>
      </form>

      <% if (!firms.length) { %>
        <p class="muted" style="padding:60px 0;text-align:center;font-style:italic">No firms match that filter yet.</p>
      <% } %>

      <div class="designer-grid" data-designer-grid>
        <% firms.forEach(function(f) { %>
          <article class="designer-card">
            <header>
              <span class="card-mark" aria-hidden="true"><%= (f.name||'?').split(/[\s&+]/).filter(Boolean).map(s=>s[0]).join('').slice(0,2).toUpperCase() %></span>
              <div>
                <h3><a href="/firms/<%= f.slug %>"><%= f.name %></a><% if (f.claimed) { %> <span class="chip gold" style="margin-left:6px">CLAIMED</span><% } %></h3>
                <p class="muted">
                  <% if (f.city || f.state) { %><%= [f.city, f.state, f.country && f.country !== 'United States' && f.country !== 'USA' ? f.country : null].filter(Boolean).join(', ') %><% } %>
                  <% if (f.founded_year) { %> · est. <%= f.founded_year %><% } %>
                  <% if (f.size_band) { %> · <%= f.size_band %><% } %>
                  <% if (f.designer_count > 0) { %> · <%= f.designer_count %> principal<%= f.designer_count === 1 ? '' : 's' %><% } %>
                </p>
              </div>
            </header>

            <% if (f.styles && f.styles.length) { %>
              <ul class="tags">
                <% f.styles.slice(0,6).forEach(function(s) { %><li><%= s %></li><% }); %>
              </ul>
            <% } %>

            <% if (f.links && (f.links.linkedin || f.links['firm-site'])) { %>
              <div class="card-links" style="display:flex;gap:10px;margin-top:10px;flex-wrap:wrap">
                <% (f.links['firm-site']||[]).slice(0,1).forEach(function(u) { %>
                  <a href="<%= u %>" rel="nofollow noopener noreferrer" target="_blank" class="muted" style="font-size:12px">Website ↗</a>
                <% }); %>
                <% (f.links.linkedin||[]).slice(0,1).forEach(function(u) { %>
                  <a href="<%= u %>" rel="nofollow noopener noreferrer" target="_blank" class="muted" style="font-size:12px">LinkedIn ↗</a>
                <% }); %>
                <% (f.links.instagram||[]).slice(0,1).forEach(function(u) { %>
                  <a href="<%= u %>" rel="nofollow noopener noreferrer" target="_blank" class="muted" style="font-size:12px">Instagram ↗</a>
                <% }); %>
              </div>
            <% } %>
          </article>
        <% }); %>
      </div>
    </div>
  </section>

  <section class="section alt">
    <div class="wrap">
      <h2>How this list is built</h2>
      <p class="prose">
        The firm canon is seeded from public knowledge — company names, founding
        years, headquarters cities, and LinkedIn company slugs. Logos are
        placeholder <em>made-with-ai</em> marks until the firm claims and
        uploads a real one. <strong>Never extracted or scraped from
        LinkedIn</strong> — every datum here is on the firm's own About page
        or in public press.
      </p>
      <p class="prose">
        Firms can <a href="/submit">claim and edit their profile</a> at any
        time. Claimed profiles get a Verified badge, real logo upload, full
        portfolio gallery, and the ability to list current principals.
      </p>
    </div>
  </section>
</main>

<%- include('../partials/footer') %>
<script src="/js/grid-controls.js" defer></script>
<script src="/js/hamburger.js" defer></script>