← back to Designtradelive

views/index.ejs

217 lines

<!doctype html>
<html lang="en">
<head>
  <title>DesignTradeLive — the <%= new Date().getFullYear() %> design trade-show calendar</title>
  <% const metaDescription = 'The live ' + new Date().getFullYear() + ' design industry calendar: ICFF, NeoCon, KBIS, High Point Market, BDNY, Salone del Mobile, Maison&Objet, Heimtextil, Decorex, Cersaie. Filter by city, category, season.'; %>
  <%- include('partials/head', { GA_ID, metaDescription }) %>
</head>
<body>
  <%- include('partials/header') %>

  <section class="hero">
    <div class="hero-inner">
      <p class="kicker"><span class="kicker-rule"></span>Issue · The <%= new Date().getFullYear() %> Trade Calendar</p>
      <h1>Where the design trade<br><span class="hero-accent">actually shows up.</span></h1>
      <p class="lede">High Point to Salone, ICFF to Heimtextil — every market that moves furniture, textiles, tile, and hospitality design, with the dates, venues, and tier you need to plan the year.</p>
      <form class="search-bar" method="get" action="/">
        <label for="q" class="visually-hidden">Search events</label>
        <input id="q" name="q" type="search" placeholder="Search city, show, country, or category" value="<%= q %>">
        <% if (cat) { %><input type="hidden" name="category" value="<%= cat %>"><% } %>
        <% if (sort && sort !== 'date') { %><input type="hidden" name="sort" value="<%= sort %>"><% } %>
        <button type="submit">Search</button>
      </form>
      <p class="hero-stats">
        <span><strong><%= total %></strong> shows tracked</span>
        <span class="hero-dot" aria-hidden="true">·</span>
        <span><strong><%= categories.length %></strong> categories</span>
        <span class="hero-dot" aria-hidden="true">·</span>
        <span>Updated for <strong><%= new Date().getFullYear() %></strong></span>
      </p>
    </div>
  </section>

  <section class="field-guide" aria-labelledby="field-guide-title">
    <div class="field-guide-inner">
      <p class="kicker"><span class="kicker-rule"></span>Field Guide</p>
      <h2 id="field-guide-title">How to work the year.</h2>
      <div class="field-guide-grid">
        <article class="fg-card">
          <span class="fg-num">01</span>
          <h3>Plan in pairs.</h3>
          <p>Design weeks travel in clusters — NYCxDESIGN wraps ICFF and WantedDesign into one trip; Milan stretches Salone, Fuorisalone, and the Rho satellites across nine days. Budget the corridor, not the show.</p>
        </article>
        <article class="fg-card">
          <span class="fg-num">02</span>
          <h3>Pick your pass.</h3>
          <p>Trade-only shows (NeoCon, BDNY, KBIS) gate the floor with a credential check; consumer-facing markets (Maison&Objet, Heimtextil) sell day passes. Apply early — same-week trade approvals are a coin flip.</p>
        </article>
        <article class="fg-card">
          <span class="fg-num">03</span>
          <h3>Work the rooms, not the booths.</h3>
          <p>The deals happen at the showroom dinners, the rooftop after-parties, and the off-floor previews — not at the 10×10. Calendar the social side first, then fit the floor walk around it.</p>
        </article>
      </div>
    </div>
  </section>

  <main class="container">
    <div class="filters">
      <div class="filter-group">
        <span class="filter-label">Categories:</span>
        <a class="chip <%= !cat ? 'is-active' : '' %>" href="/?<%= q ? 'q=' + encodeURIComponent(q) + '&' : '' %>sort=<%= sort %>">All <em><%= total %></em></a>
        <% categories.forEach(([name, count]) => { %>
          <a class="chip <%= cat === name ? 'is-active' : '' %>" href="/?<%= q ? 'q=' + encodeURIComponent(q) + '&' : '' %>category=<%= encodeURIComponent(name) %>&sort=<%= sort %>"><%= name %> <em><%= count %></em></a>
        <% }) %>
      </div>

      <div class="grid-controls">
        <label class="control">
          <span>Sort</span>
          <select id="sort-select" data-sort>
            <option value="date" <%= sort === 'date' ? 'selected' : '' %>>Soonest</option>
            <option value="name" <%= sort === 'name' ? 'selected' : '' %>>Title A→Z</option>
            <option value="city" <%= sort === 'city' ? 'selected' : '' %>>City A→Z</option>
            <option value="category" <%= sort === 'category' ? 'selected' : '' %>>Category</option>
            <option value="tier" <%= sort === 'tier' ? 'selected' : '' %>>Major first</option>
          </select>
        </label>
        <label class="control">
          <span>Density</span>
          <input id="density-slider" type="range" min="220" max="460" step="10" value="320" data-density>
        </label>
      </div>
    </div>

    <% if (events.length === 0) { %>
      <p class="empty">No events match your search. <a href="/">Clear filters</a>.</p>
    <% } %>

    <ul class="event-grid" data-grid>
      <% events.forEach(e => { %>
        <li class="event-card">
          <a class="card-link" href="/events/<%= e.slug %>">
            <div class="card-date">
              <% const sd = new Date(e.start_date + 'T00:00:00Z'); const ed = new Date(e.end_date + 'T00:00:00Z'); %>
              <span class="card-month"><%= sd.toLocaleString('en-US', { month: 'short', timeZone: 'UTC' }).toUpperCase() %></span>
              <span class="card-day"><%= sd.getUTCDate() %><% if (ed.getUTCDate() !== sd.getUTCDate() || ed.getUTCMonth() !== sd.getUTCMonth()) { %>–<%= ed.getUTCDate() %><% } %></span>
              <span class="card-year"><%= sd.getUTCFullYear() %></span>
            </div>
            <div class="card-body">
              <h2 class="card-name"><%= e.name %></h2>
              <p class="card-loc"><%= e.city %><% if (e.country !== 'USA') { %>, <%= e.country %><% } %></p>
              <p class="card-summary"><%= e.summary %></p>
              <div class="card-meta">
                <span class="tag tag-<%= e.tier %>"><%= e.tier %></span>
                <span class="tag tag-cat"><%= e.category %></span>
              </div>
            </div>
          </a>
        </li>
      <% }) %>
    </ul>
  </main>

  <%- include('partials/footer') %>
<!-- DW-HAMBURGER-NAV v1 (canonical 2026-05-10) -->
<style id="dw-hamburger-styles">
  /* Canonical hamburger nav — always visible top-right, all viewports.
     Standing rule (Steve, 2026-05-08): hamburger is the primary nav. */
  .dw-burger-btn {
    position: fixed; top: 18px; right: 18px; z-index: 9999;
    width: 44px; height: 44px;
    background: rgba(20,20,20,0.78); color: #f5f1e8;
    backdrop-filter: blur(8px) saturate(140%);
    -webkit-backdrop-filter: blur(8px) saturate(140%);
    border: 1px solid rgba(245,241,232,0.18); border-radius: 50%;
    cursor: pointer; padding: 0;
    display: flex; flex-direction: column; align-items: center; justify-content: center;
    gap: 4px;
    box-shadow: 0 6px 20px rgba(0,0,0,0.35);
    transition: background 200ms, transform 200ms;
  }
  .dw-burger-btn:hover { background: rgba(20,20,20,0.92); }
  .dw-burger-btn span {
    display: block; width: 20px; height: 1.5px; background: currentColor;
    transition: transform 220ms, opacity 220ms;
  }
  .dw-burger-btn[aria-expanded="true"] span:nth-child(1) {
    transform: translateY(5.5px) rotate(45deg);
  }
  .dw-burger-btn[aria-expanded="true"] span:nth-child(2) { opacity: 0; }
  .dw-burger-btn[aria-expanded="true"] span:nth-child(3) {
    transform: translateY(-5.5px) rotate(-45deg);
  }
  .dw-burger-panel {
    position: fixed; top: 72px; right: 18px; z-index: 9998;
    min-width: 240px; max-width: 88vw;
    background: rgba(15,15,15,0.96);
    backdrop-filter: blur(14px) saturate(140%);
    -webkit-backdrop-filter: blur(14px) saturate(140%);
    border: 1px solid rgba(245,241,232,0.12);
    border-radius: 14px;
    box-shadow: 0 18px 50px rgba(0,0,0,0.55);
    padding: 10px 0;
    display: none;
    font-family: -apple-system, "SF Pro Text", system-ui, sans-serif;
  }
  .dw-burger-panel.open { display: block; }
  .dw-burger-panel a,
  .dw-burger-panel button {
    display: block; width: 100%;
    padding: 13px 22px;
    color: #f5f1e8 !important;
    background: transparent; border: 0; cursor: pointer;
    text-decoration: none !important;
    font-size: 15px; font-weight: 400;
    text-align: left;
    letter-spacing: 0;
    border-bottom: 1px solid rgba(245,241,232,0.06);
    transition: background 160ms, color 160ms;
    box-sizing: border-box;
  }
  .dw-burger-panel a:last-child,
  .dw-burger-panel button:last-child { border-bottom: 0; }
  .dw-burger-panel a:hover,
  .dw-burger-panel button:hover {
    background: rgba(245,241,232,0.06);
    color: #ffffff !important;
  }
  @media (max-width: 480px) {
    .dw-burger-btn { top: 12px; right: 12px; width: 40px; height: 40px; }
    .dw-burger-panel { top: 62px; right: 12px; }
  }
  /* Avoid double-print on sites already adopting a top-of-page header
     that overlaps z-index. Reserve safe space near top-right. */
</style>
<button type="button" class="dw-burger-btn" id="dwBurgerBtn"
        aria-label="Toggle menu" aria-controls="dwBurgerPanel"
        aria-expanded="false">
  <span></span><span></span><span></span>
</button>
<nav class="dw-burger-panel" id="dwBurgerPanel" aria-label="Primary">
  <a href="/">Home</a>
  <a href="/about">About</a>
  <a href="mailto:info@designtradelive.com">Contact</a>
</nav>
<script>
(function(){
  var btn = document.getElementById('dwBurgerBtn');
  var panel = document.getElementById('dwBurgerPanel');
  if (!btn || !panel) return;
  function close(){ panel.classList.remove('open'); btn.setAttribute('aria-expanded','false'); }
  function toggle(){
    var open = panel.classList.toggle('open');
    btn.setAttribute('aria-expanded', open ? 'true' : 'false');
  }
  btn.addEventListener('click', function(e){ e.stopPropagation(); toggle(); });
  document.addEventListener('click', function(e){
    if (panel.classList.contains('open') && !panel.contains(e.target) && e.target !== btn) close();
  });
  document.addEventListener('keydown', function(e){ if (e.key === 'Escape') close(); });
})();
</script>
<!-- /DW-HAMBURGER-NAV v1 -->

</body>
</html>