Velmora UI Template - Let's templates
Velmora preview 1
Free
0 5

Velmora

By Avery Cole

Velmora is a calm interior design studio template with an organic photo hero, scrolling room gallery, service icons, project spotlights, a process map, material swatches and testimonials.

Live Demo
velmora-tailwind.html
<%# letstemplates: access=free category=agency %>
<!DOCTYPE html>
<html lang="en">
<head>
  <script>
    window.FontAwesomeConfig = { autoReplaceSvg: 'nest' };
  </script>
  <script src="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/js/all.min.js" crossorigin="anonymous" referrerpolicy="no-referrer"></script>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Velmora | Tailwind CSS Agency Template</title>
  <meta name="description" content="Velmora is a calm Tailwind CSS interior design studio template with an organic photo hero, scrolling room gallery, service icons, project spotlights, a process map, material swatches and testimonials.">
  <meta name="keywords" content="Velmora, Tailwind CSS template, HTML website template, interior design template, architecture studio, design agency, portfolio, case studies">
  <meta name="author" content="Let's templates">
  <link rel="canonical" href="">

  <meta property="og:type" content="product">
  <meta property="og:site_name" content="Let's templates">
  <meta property="og:locale" content="en_US">
  <meta property="og:url" content="">
  <meta property="og:title" content="Velmora | Tailwind CSS Agency Template">
  <meta property="og:description" content="A calm Tailwind CSS interior design studio template with an organic photo hero, scrolling room gallery, project spotlights, a process map and material swatches.">
  <meta property="og:image" content="">
  <meta property="og:image:alt" content="Velmora template preview">

  <meta name="twitter:card" content="summary_large_image">
  <meta name="twitter:url" content="">
  <meta name="twitter:title" content="Velmora | Tailwind CSS Agency Template">
  <meta name="twitter:description" content="A calm Tailwind CSS interior design studio template with an organic photo hero, scrolling room gallery, project spotlights, a process map and material swatches.">
  <meta name="twitter:image" content="">
  <meta name="twitter:image:alt" content="Velmora template preview">
  <script src="https://cdn.tailwindcss.com/3.4.17"></script>
  <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
  <link rel="preconnect" href="https://fonts.googleapis.com">
  <link href="https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600&display=swap" rel="stylesheet">
  <script>
    tailwind.config = {
      theme: {
        extend: {
          colors: {
            ink: '#121212',
            stone: '#f4f4f4',
            ember: '#e5483d'
          },
          fontFamily: {
            sans: ['Inter', 'ui-sans-serif', 'system-ui', 'sans-serif']
          }
        }
      }
    };
  </script>
  <style>
    @view-transition { navigation: auto; }
    ::view-transition-old(root) { animation: 180ms ease-out both page-fade-out; }
    ::view-transition-new(root) { animation: 220ms ease-in both page-fade-in; }
    @keyframes page-fade-out { to { opacity: 0; } }
    @keyframes page-fade-in { from { opacity: 0; } }
    @keyframes marquee { to { transform: translateX(-50%); } }
    html { scroll-behavior: smooth; }
    [hidden] { display: none !important; }
    .sketch { filter: grayscale(1) contrast(1.6) brightness(1.35); opacity: .22; mask-image: radial-gradient(ellipse at center, #000 35%, transparent 75%); }
    .blob { border-radius: 58% 42% 55% 45% / 48% 58% 42% 52%; mask-image: radial-gradient(closest-side, #000 86%, transparent 100%); }
    .blob-small { border-radius: 45% 55% 40% 60% / 55% 45% 60% 40%; mask-image: radial-gradient(closest-side, #000 82%, transparent 100%); }
    .marquee-track { animation: marquee 48s linear infinite; }
    .marquee:hover .marquee-track { animation-play-state: paused; }
    .swatch { clip-path: polygon(0 0, 62% 0, 100% 22%, 100% 100%, 0 100%); }
    .swatch-wood { background: repeating-linear-gradient(92deg, #6b4428 0 3px, #7a4f30 3px 7px, #5d3a22 7px 9px, #8a5a36 9px 14px); }
    .swatch-brick { background-color: #9c4a36; background-image: linear-gradient(#d9d3cc 2px, transparent 2px), linear-gradient(90deg, #d9d3cc 2px, transparent 2px); background-size: 34px 16px, 34px 32px; background-position: 0 0, 17px 16px; }
    .swatch-concrete { background-color: #6f716f; background-image: radial-gradient(#5a5c5b 1px, transparent 1.5px), radial-gradient(#8a8c8a 1px, transparent 1.5px); background-size: 9px 9px, 13px 13px; background-position: 0 0, 4px 6px; }
    .swatch-glass { background: linear-gradient(135deg, #2c3640 0%, #5b6b78 30%, #1f262c 46%, #6e7f8c 62%, #2a3138 100%); }
    .reveal-ready [data-reveal] { opacity: 0; transform: translateY(24px); transition: opacity .6s ease, transform .6s ease; transition-delay: var(--reveal-delay, 0ms); }
    .reveal-ready [data-reveal].is-visible { opacity: 1; transform: none; }
    @media (prefers-reduced-motion: reduce) {
      html { scroll-behavior: auto; }
      .marquee-track { animation: none; }
      ::view-transition-old(root), ::view-transition-new(root) { animation: none; }
    }
  </style>
</head>
<body class="bg-white font-sans text-ink antialiased">
  <% active_page = "index" %>
  <!-- block:start navbar -->
  <header class="relative z-30">
    <div class="mx-auto flex max-w-[1680px] items-start justify-between px-6 py-6 lg:px-8">
      <a href="index.html" class="text-2xl tracking-[0.18em]" aria-label="Velmora home">VELM<span class="relative">O<span class="absolute -top-1.5 left-0 right-0 h-px bg-current"></span></span>RA</a>
      <nav class="hidden gap-24 text-lg md:flex" aria-label="Main">
        <% [{ "resources" => "Resources", "enterprise" => "Enterprise" }, { "work" => "Work", "services" => "Services", "process" => "Process" }].each do |column| %>
          <ul class="space-y-1.5">
            <% column.each do |slug, label| %>
              <% active = active_page == slug %>
              <li><a href="<%= slug %>.html" <%= 'aria-current="page"'.html_safe if active %> class="transition hover:text-ember <%= 'text-ember' if active %>"><%= label %></a></li>
            <% end %>
          </ul>
        <% end %>
      </nav>
      <button type="button" data-menu-toggle aria-controls="mobile-menu" aria-expanded="false" aria-label="Open menu" class="flex h-10 w-10 items-center justify-center rounded-full border border-ink/20 md:hidden">
        <i class="fa-solid fa-bars" aria-hidden="true"></i>
      </button>
    </div>
    <nav id="mobile-menu" class="absolute inset-x-4 top-full hidden rounded-2xl bg-white p-3 shadow-2xl md:hidden" aria-label="Mobile">
      <ul class="flex flex-col">
        <% { "index" => "Home", "work" => "Work", "services" => "Services", "process" => "Process", "resources" => "Resources", "enterprise" => "Enterprise", "contact" => "Start a space" }.each do |slug, label| %>
          <% active = active_page == slug %>
          <li><a href="<%= slug == "index" ? "index.html" : "#{slug}.html" %>" <%= 'aria-current="page"'.html_safe if active %> class="block rounded-xl px-4 py-3 text-lg <%= active ? 'bg-stone text-ember' : 'hover:bg-stone' %>"><%= label %></a></li>
        <% end %>
      </ul>
    </nav>
    <a href="contact.html" class="fixed left-0 top-1/2 z-40 hidden -translate-y-1/2 rounded-r-lg bg-ink px-3 py-6 text-sm text-white transition hover:bg-ember md:block" style="writing-mode: vertical-rl">Start a space</a>
  </header>
  <!-- block:end navbar -->

  <!-- block:start hero -->
  <section id="intro" class="relative -mt-24 overflow-hidden px-6 pb-24 pt-40 md:pt-48">
    <img src="https://images.unsplash.com/photo-1618221195710-dd6b41faaea6?auto=format&fit=crop&w=1800&q=70" alt="" aria-hidden="true" class="sketch pointer-events-none absolute inset-0 h-full w-full object-cover">
    <div class="relative mx-auto max-w-6xl text-center">
      <h1 data-reveal class="text-5xl font-medium leading-[1.05] tracking-tight md:text-7xl lg:text-[6.5rem]">Shaping how<br>a room feels.</h1>
      <div class="relative mx-auto mt-4 max-w-4xl">
        <div data-reveal class="blob mx-auto aspect-[4/3] w-full max-w-2xl overflow-hidden">
          <img src="https://images.unsplash.com/photo-1600607687939-ce8a6c25118c?auto=format&fit=crop&w=1400&q=80" alt="Open-plan living room with a timber feature wall and soft grey sofa" class="h-full w-full object-cover">
        </div>
        <div data-reveal style="--reveal-delay: 200ms" class="blob-small absolute -right-2 bottom-6 hidden aspect-square w-48 overflow-hidden md:block lg:-right-16">
          <img src="https://images.unsplash.com/photo-1567016432779-094069958ea5?auto=format&fit=crop&w=500&q=80" alt="Close up of a burnt orange sofa with a pink cushion" class="h-full w-full object-cover">
        </div>
      </div>
      <p data-reveal class="mx-auto mt-10 max-w-xs text-lg leading-snug">We turn light, volume and quiet into rooms you actually want to live in.</p>
    </div>
  </section>
  <!-- block:end hero -->

  <!-- block:start marquee -->
  <section class="marquee overflow-hidden" aria-label="Recent rooms">
    <% rooms = [
      ["1505693416388-ac5ce068fe85", "Bedroom with an upholstered headboard and soft linen"],
      ["1493663284031-b7e3aefcae8e", "Grey sofa with teal cushions under a bright window"],
      ["1586023492125-27b2c045efd7", "Yellow armchair beside a floor lamp and framed print"],
      ["1615873968403-89e068629265", "Living room with a deep green wall and leather sofa"],
      ["1556228453-efd6c1ff04f6", "Pastel lounge with blush armchairs and a woven wall hanging"],
      ["1617104678098-de229db51175", "Dark bedroom with a garden view"]
    ] %>
    <ul class="marquee-track flex w-max gap-3">
      <% (rooms * 2).each_with_index do |(image, alt), index| %>
        <li class="h-72 w-64 shrink-0 overflow-hidden rounded-2xl md:h-[26rem] md:w-[28rem]" <%= 'aria-hidden="true"'.html_safe if index >= rooms.size %>>
          <img src="https://images.unsplash.com/photo-<%= image %>?auto=format&fit=crop&w=900&q=80" alt="<%= index >= rooms.size ? '' : alt %>" class="h-full w-full object-cover" loading="lazy">
        </li>
      <% end %>
    </ul>
  </section>
  <!-- block:end marquee -->

  <!-- block:start about -->
  <section id="philosophy" class="mx-auto max-w-[1680px] px-6 py-24 lg:px-14">
    <div class="grid gap-12 lg:grid-cols-[minmax(0,1fr)_340px]">
      <div>
        <p data-reveal class="text-sm uppercase tracking-wide">[Philosophy]</p>
        <h2 data-reveal class="mt-4 max-w-3xl text-4xl font-medium leading-tight tracking-tight md:text-6xl">We design how you move, rest, gather and slip away.</h2>
        <div class="mt-16 grid gap-8 md:ml-[34%] md:grid-cols-2">
          <p data-reveal><span class="text-ink/50">Mission:</span> To create rooms that quietly guide behaviour, where light, proportion and material work as one.</p>
          <p data-reveal style="--reveal-delay: 90ms"><span class="text-ink/50">Vision:</span> To treat interiors as applied psychology, shaping how people feel, move and connect at home and at work.</p>
        </div>
      </div>
      <ul class="relative mx-auto h-80 w-80" aria-hidden="true">
        <% [
          ["1502672260266-1c1ef2d93688", "left-[18%] top-[6%]"], ["1501045661006-fcebe0257c3f", "left-[50%] top-0"],
          ["1594026112284-02bb6f3352fe", "left-[6%] top-[30%]"], ["1567225557594-88d73e55f2cb", "right-[2%] top-[30%]"],
          ["1554118811-1e0d58224f24", "left-[38%] top-[44%]"], ["1484154218962-a197022b5858", "right-[12%] top-[55%]"],
          ["1595526114035-0d45ed16cfbf", "left-[16%] top-[62%]"], ["1560448204-e02f11c3d0e2", "left-[46%] top-[80%]"]
        ].each_with_index do |(image, place), index| %>
          <li data-reveal style="--reveal-delay: <%= index * 60 %>ms" class="absolute h-16 w-16 overflow-hidden rounded-full <%= place %>"><img src="https://images.unsplash.com/photo-<%= image %>?auto=format&fit=crop&w=200&h=200&q=70" alt="" class="h-full w-full object-cover"></li>
        <% end %>
      </ul>
    </div>
  </section>
  <figure class="mx-auto max-w-[1680px]">
    <img src="https://images.unsplash.com/photo-1502672260266-1c1ef2d93688?auto=format&fit=crop&w=2000&q=80" alt="Sunlit living room with plants, open shelving and a pink lounge chair" class="aspect-[16/9] w-full object-cover md:aspect-[21/10]">
  </figure>
  <!-- block:end about -->

  <!-- block:start features -->
  <section id="services" class="mx-auto max-w-[1680px] px-6 py-28 text-center lg:px-14">
    <p data-reveal class="text-sm uppercase tracking-wide">[Services]</p>
    <h2 data-reveal class="mt-4 text-4xl font-medium tracking-tight md:text-5xl">Shaping behaviour through space.</h2>
    <div class="mt-20 grid gap-16 sm:grid-cols-2 lg:grid-cols-4">
      <% [
        ["Spatial Planning", "Flow before finishes", "#1f6fb2", "M100 8 L190 190 L12 150 Z", "M86 70 C92 52 108 52 114 70 L124 84 C132 92 128 100 118 104 L124 122 C126 132 118 136 100 136 C82 136 74 132 76 122 L82 104 C72 100 68 92 76 84 Z"],
        ["Lighting Design", "Shadows on purpose", "#d9443a", "M40 10 L120 28 L122 70 L176 96 L140 190 L50 182 L18 120 Z", "M84 66 C96 56 110 66 112 80 C126 86 122 104 116 116 L104 136 C98 140 92 136 82 124 C76 112 78 96 80 84 Z"],
        ["Custom Furniture", "Pieces that belong", "#2fa53a", "M48 8 L96 60 L110 46 L172 58 L172 182 L118 156 L58 190 L20 104 Z", "M70 112 L86 74 C92 66 98 70 98 78 L104 92 L112 70 C116 62 126 64 126 72 L128 126 C128 132 120 134 116 128 L104 108 L90 128 C84 134 74 130 72 122 Z"],
        ["Material Direction", "Texture you can feel", "#c58a10", "M54 8 L96 70 L180 14 L160 116 L120 186 L36 156 L12 96 Z", "M66 88 L90 76 L108 88 L130 80 L156 88 C160 92 158 98 152 102 L108 136 C102 140 96 138 92 132 Z"]
      ].each_with_index do |(title, line, color, outline, mark), index| %>
        <article data-reveal style="--reveal-delay: <%= index * 90 %>ms" class="flex flex-col items-center">
          <div class="relative h-52 w-52">
            <span class="absolute inset-6 rounded-xl bg-stone"></span>
            <svg viewBox="0 0 200 200" class="absolute inset-0 h-full w-full" aria-hidden="true">
              <path d="<%= outline %>" fill="none" stroke="<%= color %>" stroke-width="1.6" stroke-linejoin="round"></path>
              <path d="<%= mark %>" fill="#232323"></path>
            </svg>
          </div>
          <h3 class="mt-6 text-2xl font-medium"><%= title %></h3>
          <p class="mt-2 text-ink/70"><%= line %></p>
        </article>
      <% end %>
    </div>
  </section>
  <!-- block:end features -->

  <!-- block:start gallery -->
  <section id="spaces" class="mx-auto max-w-[1680px] px-6 pb-28 lg:px-14">
    <div class="text-center">
      <p data-reveal class="text-sm uppercase tracking-wide">[Selected spaces]</p>
      <h2 data-reveal class="mx-auto mt-4 max-w-3xl text-4xl font-medium tracking-tight md:text-5xl">Made for lazy Sundays and second cups of coffee.</h2>
    </div>
    <div class="mt-16 grid gap-3 lg:grid-cols-2">
      <% [
        ["sunday-loft", "Sunday Loft", "Lisbon, 2025", "1600210492486-724fe5c67fb0", "Bright living room with a leather sofa and gallery wall", [["Type", "Residential"], ["Size", "96 m²"], ["Scope", "Interior Design"]], "bg-neutral-600/40"],
        ["ember-cafe", "Ember Corner Café", "Porto, 2026", "1554118811-1e0d58224f24", "Café with plants, rattan chairs and a chalkboard menu", [["Type", "Hospitality"], ["Size", "150 m²"], ["Scope", "Material Direction"]], "bg-black/40"]
      ].each_with_index do |(slug, title, place, image, alt, facts, glass), index| %>
        <a href="<%= slug %>.html" data-reveal style="--reveal-delay: <%= index * 100 %>ms" class="group relative block aspect-[4/3] overflow-hidden rounded-2xl">
          <img src="https://images.unsplash.com/photo-<%= image %>?auto=format&fit=crop&w=1400&q=80" alt="<%= alt %>" class="h-full w-full object-cover transition duration-700 group-hover:scale-105">
          <div class="absolute right-4 top-4 w-[19rem] max-w-[calc(100%-2rem)] rounded-xl <%= glass %> p-6 text-white backdrop-blur-xl">
            <div class="flex items-start justify-between gap-4">
              <h3 class="text-3xl font-medium leading-tight"><%= title %></h3>
              <i class="fa-solid fa-arrow-up rotate-45 transition group-hover:-translate-y-0.5" aria-hidden="true"></i>
            </div>
            <p class="mt-2 text-sm text-white/80"><%= place %></p>
            <dl class="mt-8 divide-y divide-white/25 border-t border-white/25 text-sm">
              <% facts.each do |label, value| %>
                <div class="flex justify-between py-2.5"><dt><%= label %>:</dt><dd class="text-white/80"><%= value %></dd></div>
              <% end %>
            </dl>
          </div>
        </a>
      <% end %>
    </div>
  </section>
  <!-- block:end gallery -->

  <!-- block:start process -->
  <section id="process" class="mx-auto max-w-[1680px] px-6 pb-28 lg:px-14">
    <div class="text-center">
      <p data-reveal class="text-sm uppercase tracking-wide">[Process]</p>
      <h2 data-reveal class="mt-4 text-4xl font-medium tracking-tight md:text-5xl">Teaching rooms to feel something.</h2>
    </div>
    <% steps = [["Listen", "We watch how you live"], ["Interpret", "Habits become geometry"], ["Compose", "The space finds its rhythm"], ["Build", "Makers follow the story"], ["Live", "The room grows with you"]] %>
    <ol class="mt-14 space-y-6 md:hidden">
      <% steps.each_with_index do |(title, copy), index| %>
        <li class="flex items-center gap-5"><span class="flex h-14 w-14 shrink-0 items-center justify-center rounded-full bg-stone text-lg"><%= format("%02d", index + 1) %></span><span><span class="block text-2xl font-medium"><%= title %></span><span class="text-ink/70"><%= copy %></span></span></li>
      <% end %>
    </ol>
    <div class="relative mx-auto mt-16 hidden h-[760px] max-w-6xl md:block" aria-hidden="false">
      <svg viewBox="0 0 1000 760" preserveAspectRatio="none" class="absolute inset-0 h-full w-full" aria-hidden="true">
        <g stroke="#e5483d" stroke-width="1.2" fill="none">
          <line x1="40" y1="110" x2="320" y2="640"></line>
          <line x1="280" y1="250" x2="320" y2="640"></line>
          <line x1="700" y1="190" x2="320" y2="640"></line>
          <line x1="560" y1="470" x2="320" y2="640"></line>
          <line x1="860" y1="600" x2="320" y2="640"></line>
        </g>
      </svg>
      <span class="absolute left-[32%] top-[84.2%] flex h-9 w-9 -translate-x-1/2 -translate-y-1/2 items-center justify-center rounded-full bg-ember text-white" aria-hidden="true"><i class="fa-solid fa-cube text-xs"></i></span>
      <% [["left-[0%] top-[4%]"], ["left-[24%] top-[25%]"], ["left-[66%] top-[16%]"], ["left-[52%] top-[56%]"], ["left-[80%] top-[72%]"]].each_with_index do |(place), index| %>
        <% title, copy = steps[index] %>
        <div data-reveal style="--reveal-delay: <%= index * 90 %>ms" class="absolute flex items-start gap-5 bg-white py-1 pr-2 <%= place %>">
          <span class="flex h-16 w-16 shrink-0 items-center justify-center rounded-full bg-stone text-xl"><%= format("%02d", index + 1) %></span>
          <span><span class="block text-3xl font-medium"><%= title %></span><span class="mt-1 block text-lg"><%= copy %></span></span>
        </div>
      <% end %>
      <% [["1594026112284-02bb6f3352fe", "left-[28%] top-[60%] w-52"], ["1567225557594-88d73e55f2cb", "left-[56%] top-[36%] w-44"], ["1556228453-efd6c1ff04f6", "left-[64%] top-[78%] w-44"]].each do |image, place| %>
        <img src="https://images.unsplash.com/photo-<%= image %>?auto=format&fit=crop&w=500&q=75" alt="" class="absolute aspect-[4/3] rounded-xl object-cover shadow-lg <%= place %>">
      <% end %>
    </div>
  </section>
  <!-- block:end process -->

  <!-- block:start products -->
  <section id="materials" class="mx-auto max-w-[1680px] px-6 pb-28 lg:px-14">
    <div class="text-center">
      <p data-reveal class="text-sm uppercase tracking-wide">[Materials &amp; objects]</p>
      <h2 data-reveal class="mx-auto mt-4 max-w-4xl text-4xl font-medium leading-tight tracking-tight md:text-5xl">Beyond the floor plan, surface, weight and warmth decide how comfortable a room feels.</h2>
    </div>
    <div class="mx-auto mt-16 grid max-w-6xl gap-10 sm:grid-cols-2 lg:grid-cols-4">
      <% [
        ["Oak", "Warm grain for built-in joinery and wall panelling.", "swatch-wood"],
        ["Brick", "Reclaimed texture for feature walls and fireplaces.", "swatch-brick"],
        ["Concrete", "Honest, cool surfaces for floors and counters.", "swatch-concrete"],
        ["Glass", "Fluted screens that share light but keep privacy.", "swatch-glass"]
      ].each_with_index do |(title, copy, swatch), index| %>
        <article data-reveal style="--reveal-delay: <%= index * 80 %>ms" class="grid h-72 grid-cols-[1fr_80px] border-t-2 border-ink">
          <div class="pr-6 pt-6">
            <h3 class="text-2xl font-medium"><%= title %></h3>
            <p class="mt-3 text-ink/60"><%= copy %></p>
          </div>
          <div class="swatch <%= swatch %> border-l-2 border-ink" aria-hidden="true"></div>
        </article>
      <% end %>
    </div>
  </section>
  <!-- block:end products -->

  <!-- block:start reviews -->
  <section id="testimonials" class="mx-auto max-w-[1680px] px-6 pb-28 lg:px-14">
    <p data-reveal class="text-center text-sm uppercase tracking-wide">[Testimonials]</p>
    <% quotes = [
      ["1534528741775-53994a69daeb", "Homeowner", "Lisbon · 2025", "Before Velmora, our flat felt chopped up and smaller than it really was. We didn't want a big renovation, just something subtle, thoughtful and easy to live with."],
      ["1494790108377-be9c29b29330", "Café owner", "Porto · 2026", "Guests stay longer and come back more often. The room does half the hospitality for us, and we never had to explain what we wanted twice."],
      ["1438761681033-6461ffad8d80", "Studio founder", "Madrid · 2025", "They listened for two full days before drawing anything. Every corner of our new office now has a reason to exist."]
    ] %>
    <div data-tabs class="mx-auto mt-14 grid max-w-5xl gap-8 md:grid-cols-[80px_1fr]">
      <span class="text-8xl font-semibold leading-none" aria-hidden="true">&ldquo;</span>
      <div>
        <% quotes.each_with_index do |(image, role, meta, quote), index| %>
          <blockquote id="quote-<%= index %>" role="tabpanel" <%= "hidden" unless index.zero? %> class="text-2xl leading-snug md:text-4xl"><%= quote %></blockquote>
        <% end %>
        <div class="mt-8 flex flex-wrap items-center gap-3" role="tablist" aria-label="Choose a testimonial">
          <% quotes.each_with_index do |(image, role, meta, _quote), index| %>
            <button type="button" role="tab" aria-selected="<%= index.zero? %>" aria-controls="quote-<%= index %>" class="group flex items-center gap-3 rounded-full bg-stone p-1.5 transition aria-selected:pr-6">
              <img src="https://images.unsplash.com/photo-<%= image %>?auto=format&fit=crop&w=120&h=120&q=70" alt="<%= role %>" class="h-11 w-11 rounded-full object-cover">
              <span class="hidden text-left text-sm leading-tight group-aria-selected:block"><span class="block"><%= role %></span><span class="font-semibold"><%= meta %></span></span>
            </button>
          <% end %>
        </div>
      </div>
    </div>
  </section>
  <!-- block:end reviews -->

  <!-- block:start footer -->
  <footer class="bg-ink text-white">
    <div class="mx-auto max-w-[1680px] px-6 py-20 lg:px-14">
      <div class="flex flex-col gap-6 border-b border-white/15 pb-12 md:flex-row md:items-center md:justify-between">
        <a href="index.html" class="text-4xl tracking-[0.18em]">VELMORA</a>
        <p class="text-xl">Blending light, proportion and material into calm environments.</p>
      </div>
      <div class="grid gap-12 py-14 sm:grid-cols-2 lg:grid-cols-[1fr_1.2fr_1fr_2fr]">
        <div>
          <h2 class="text-xl text-white/50">#Explore</h2>
          <ul class="mt-4 space-y-2.5">
            <% { "work" => "Work", "services" => "Services", "process" => "Process", "resources" => "Resources", "enterprise" => "Enterprise" }.each do |slug, label| %>
              <li><a href="<%= slug %>.html" class="hover:text-ember"><%= label %></a></li>
            <% end %>
          </ul>
        </div>
        <div>
          <h2 class="text-xl text-white/50">#Projects</h2>
          <ul class="mt-4 space-y-2.5">
            <li><a href="sunday-loft.html" class="hover:text-ember">Sunday Loft</a></li>
            <li><a href="ember-cafe.html" class="hover:text-ember">Ember Corner Café</a></li>
            <li><a href="work.html" class="hover:text-ember">Slim House</a></li>
            <li><a href="work.html" class="hover:text-ember">Saffron Kitchen</a></li>
          </ul>
        </div>
        <div>
          <h2 class="text-xl text-white/50">#Connect</h2>
          <ul class="mt-4 space-y-2.5">
            <% [["Instagram", "https://instagram.com"], ["Pinterest", "https://pinterest.com"], ["LinkedIn", "https://linkedin.com"], ["Behance", "https://behance.net"]].each do |label, href| %>
              <li><a href="<%= href %>" class="hover:text-ember"><%= label %></a></li>
            <% end %>
          </ul>
        </div>
        <div>
          <h2 class="text-xl text-white/50">#Newsletter</h2>
          <p class="mt-4">Design notes and spatial thinking, once a month.</p>
          <form data-newsletter class="mt-4 flex max-w-md flex-wrap gap-2" novalidate>
            <label for="newsletter-email" class="sr-only">Email address</label>
            <input id="newsletter-email" type="email" required placeholder="Email address" class="min-w-0 flex-1 rounded-lg bg-white/10 px-4 py-2.5 text-white placeholder:text-white/40 focus:outline-none focus:ring-2 focus:ring-white/40">
            <button type="submit" class="rounded-lg bg-white px-5 py-2.5 font-medium text-ink transition hover:bg-ember hover:text-white">Subscribe</button>
            <p data-form-status class="w-full text-sm text-white/70" role="status" aria-live="polite"></p>
          </form>
        </div>
      </div>
      <div class="flex flex-col gap-3 border-t border-white/15 pt-8 text-white/60 md:flex-row md:justify-between">
        <p>&copy; <span data-current-year>2026</span> Velmora Studio</p>
        <a href="mailto:hello@velmora.studio" class="hover:text-white">hello@velmora.studio</a>
      </div>
    </div>
  </footer>
  <!-- block:end footer -->

  <script>
    document.querySelectorAll('[data-menu-toggle]').forEach((button) => {
      const menu = document.getElementById(button.getAttribute('aria-controls'));
      if (!menu) return;
      button.addEventListener('click', () => {
        const open = button.getAttribute('aria-expanded') === 'true';
        button.setAttribute('aria-expanded', String(!open));
        menu.classList.toggle('hidden', open);
      });
    });

    document.querySelectorAll('[data-tabs]').forEach((tabs) => {
      const buttons = tabs.querySelectorAll('[role="tab"]');
      buttons.forEach((button) => {
        button.addEventListener('click', () => {
          buttons.forEach((other) => {
            const active = other === button;
            other.setAttribute('aria-selected', String(active));
            document.getElementById(other.getAttribute('aria-controls')).hidden = !active;
          });
        });
      });
    });

    document.querySelectorAll('[data-accordion]').forEach((accordion) => {
      const buttons = accordion.querySelectorAll('[data-accordion-button]');
      buttons.forEach((button) => {
        button.addEventListener('click', () => {
          const open = button.getAttribute('aria-expanded') === 'true';
          buttons.forEach((other) => {
            other.setAttribute('aria-expanded', 'false');
            document.getElementById(other.getAttribute('aria-controls')).hidden = true;
          });
          button.setAttribute('aria-expanded', String(!open));
          document.getElementById(button.getAttribute('aria-controls')).hidden = open;
        });
      });
    });

    document.querySelectorAll('[data-filter-group]').forEach((group) => {
      const chips = group.querySelectorAll('[data-filter]');
      chips.forEach((chip) => {
        chip.addEventListener('click', () => {
          chips.forEach((other) => {
            const active = other === chip;
            other.setAttribute('aria-pressed', String(active));
            other.classList.toggle('bg-ink', active);
            other.classList.toggle('text-white', active);
          });
          document.querySelectorAll('[data-filter-item]').forEach((item) => {
            item.hidden = chip.dataset.filter !== 'all' && item.dataset.category !== chip.dataset.filter;
          });
        });
      });
    });

    document.querySelectorAll('[data-newsletter], [data-contact-form]').forEach((form) => {
      form.addEventListener('submit', (event) => {
        event.preventDefault();
        const status = form.querySelector('[data-form-status]');
        if (!form.checkValidity()) {
          status.textContent = form.dataset.error || 'Please enter a valid email address.';
          return;
        }
        status.textContent = form.dataset.success || 'Thank you! Your first letter is on its way.';
        form.reset();
      });
    });

    document.querySelectorAll('[data-current-year]').forEach((el) => { el.textContent = new Date().getFullYear(); });

    (() => {
      const items = document.querySelectorAll('[data-reveal]');
      const skip = navigator.webdriver || !('IntersectionObserver' in window) || matchMedia('(prefers-reduced-motion: reduce)').matches;
      if (!items.length || skip) return;
      document.documentElement.classList.add('reveal-ready');
      const observer = new IntersectionObserver((entries) => {
        entries.forEach((entry) => {
          if (!entry.isIntersecting) return;
          entry.target.classList.add('is-visible');
          observer.unobserve(entry.target);
        });
      }, { rootMargin: '0px 0px -10% 0px' });
      items.forEach((item) => observer.observe(item));
    })();
  </script>
</body>
</html>

Unlimited access to all templates

Unlock our entire library of premium UI kits, dashboards, and landing pages. Renews automatically, cancel anytime.

$20 / 3 months or $60 / year
Get Unlimited Access

More Agency Templates