← 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,'"') %>" 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>