---
id: seed:archetypes/gallery
title: Gallery / portfolio showcase archetypes
source_type: seed_archetype
priority: warm
---

A gallery section displays visual work in a grid, masonry, or carousel layout. It converts on aesthetics and proof — the work speaks for itself. Use when you have compelling visuals that deserve showcase. Five working archetypes:

---

**Masonry grid (default).** Variable-height cards in a true masonry layout. Hover reveals project title with gradient overlay.

*Animation & Visual Details:*
- *Entrance*: Cards fade in with stagger (0.06s), scale 0.95 → 1
- *Hover*: Overlay slides up from bottom (translateY 100% → 0), image scales to 1.05
- *Gradients*: Overlay uses gradient from transparent to rgba(0,0,0,0.8)
- *Radial circles*: Subtle radial glow on image corners on hover
- *Scroll trigger*: IntersectionObserver at 10% threshold, stagger via CSS animation-delay
- *JS interaction*: Lightbox on click (optional), scroll-reveal

```html
<section data-amb-section="gallery" class="gallery-section">
  <div class="container">
    <div class="gallery-header">
      <h2 class="gallery-title">Our work</h2>
      <p class="gallery-subtitle">Selected projects from the past year</p>
    </div>
    <div class="gallery-masonry">
      <article class="gallery-item">
        <div class="gallery-item-media">
          <img class="gallery-item-image" src="https://placehold.co/600x800" alt="Brand identity project" />
          <div class="gallery-item-overlay">
            <h3 class="gallery-item-title">Brand Identity</h3>
            <p class="gallery-item-category">Branding</p>
          </div>
        </div>
      </article>
      <article class="gallery-item">
        <div class="gallery-item-media">
          <img class="gallery-item-image" src="https://placehold.co/600x400" alt="Website redesign" />
          <div class="gallery-item-overlay">
            <h3 class="gallery-item-title">Website Redesign</h3>
            <p class="gallery-item-category">Web Design</p>
          </div>
        </div>
      </article>
      <article class="gallery-item">
        <div class="gallery-item-media">
          <img class="gallery-item-image" src="https://placehold.co/600x600" alt="App UI design" />
          <div class="gallery-item-overlay">
            <h3 class="gallery-item-title">Mobile App UI</h3>
            <p class="gallery-item-category">UI/UX</p>
          </div>
        </div>
      </article>
      <article class="gallery-item">
        <div class="gallery-item-media">
          <img class="gallery-item-image" src="https://placehold.co/600x500" alt="Marketing campaign" />
          <div class="gallery-item-overlay">
            <h3 class="gallery-item-title">Marketing Campaign</h3>
            <p class="gallery-item-category">Marketing</p>
          </div>
        </div>
      </article>
      <article class="gallery-item">
        <div class="gallery-item-media">
          <img class="gallery-item-image" src="https://placehold.co/600x700" alt="Packaging design" />
          <div class="gallery-item-overlay">
            <h3 class="gallery-item-title">Product Packaging</h3>
            <p class="gallery-item-category">Packaging</p>
          </div>
        </div>
      </article>
      <article class="gallery-item">
        <div class="gallery-item-media">
          <img class="gallery-item-image" src="https://placehold.co/600x450" alt="Illustration work" />
          <div class="gallery-item-overlay">
            <h3 class="gallery-item-title">Illustration Series</h3>
            <p class="gallery-item-category">Illustration</p>
          </div>
        </div>
      </article>
    </div>
  </div>
</section>
```

```css
:root {
  --gallery-enter-duration: 0.5s;
  --gallery-stagger: 0.06s;
}

.gallery-section {
  padding: clamp(64px, 8vw, 100px) 0;
}

.gallery-header {
  text-align: center;
  max-width: 480px;
  margin: 0 auto clamp(40px, 5vw, 60px);
}

.gallery-title {
  font-size: clamp(1.75rem, 3.5vw, 2.5rem);
  font-weight: 700;
  color: var(--color-text, #1e293b);
}

.gallery-subtitle {
  font-size: 1.05rem;
  color: var(--color-text-muted, #64748b);
  margin-top: 12px;
}

.gallery-masonry {
  columns: 3;
  column-gap: 20px;
}

@media (max-width: 900px) {
  .gallery-masonry { columns: 2; }
}

@media (max-width: 600px) {
  .gallery-masonry { columns: 1; }
}

.gallery-item {
  break-inside: avoid;
  margin-bottom: 20px;
  opacity: 0;
  transform: scale(0.95);
  transition: transform 0.3s ease;
}

.gallery-item.is-visible {
  animation: gallery-item-enter var(--gallery-enter-duration) ease-out forwards;
}

.gallery-item:nth-child(1).is-visible { animation-delay: 0s; }
.gallery-item:nth-child(2).is-visible { animation-delay: var(--gallery-stagger); }
.gallery-item:nth-child(3).is-visible { animation-delay: calc(var(--gallery-stagger) * 2); }
.gallery-item:nth-child(4).is-visible { animation-delay: calc(var(--gallery-stagger) * 3); }
.gallery-item:nth-child(5).is-visible { animation-delay: calc(var(--gallery-stagger) * 4); }
.gallery-item:nth-child(6).is-visible { animation-delay: calc(var(--gallery-stagger) * 5); }

@keyframes gallery-item-enter {
  to { opacity: 1; transform: scale(1); }
}

.gallery-item-media {
  position: relative;
  border-radius: 16px;
  overflow: hidden;
  cursor: pointer;
}

.gallery-item-image {
  width: 100%;
  display: block;
  transition: transform 0.5s ease;
}

.gallery-item-media:hover .gallery-item-image {
  transform: scale(1.05);
}

.gallery-item-overlay {
  position: absolute;
  inset: 0;
  background: linear-gradient(to top, rgba(0,0,0,0.85) 0%, rgba(0,0,0,0.2) 50%, transparent 100%);
  display: flex;
  flex-direction: column;
  justify-content: flex-end;
  padding: 24px;
  opacity: 0;
  transform: translateY(20px);
  transition: opacity 0.3s ease, transform 0.3s ease;
}

.gallery-item-media:hover .gallery-item-overlay {
  opacity: 1;
  transform: translateY(0);
}

.gallery-item-title {
  font-size: 1.1rem;
  font-weight: 600;
  color: #fff;
  margin-bottom: 4px;
}

.gallery-item-category {
  font-size: 0.85rem;
  color: rgba(255,255,255,0.8);
}

@media (prefers-reduced-motion: reduce) {
  .gallery-item {
    opacity: 1;
    transform: none;
  }
  .gallery-item-overlay {
    opacity: 1;
    transform: none;
  }
  .gallery-item-image {
    transform: none;
  }
}
```

*JS behavior:*
```javascript
const galleryItems = document.querySelectorAll('.gallery-item');
const observer = new IntersectionObserver((entries) => {
  entries.forEach(entry => {
    if (entry.isIntersecting) {
      entry.target.classList.add('is-visible');
      observer.unobserve(entry.target);
    }
  });
}, { threshold: 0.1 });
galleryItems.forEach(item => observer.observe(item));
```

---

**Hover lightbox grid.** Uniform square grid with overlay reveal and lightbox modal on click.

*Animation & Visual Details:*
- *Entrance*: Grid fades in row by row (each row delays 0.15s), translateX(40px) → 0
- *Hover*: Image desaturates to grayscale, overlay with title scales in from 0.8 → 1
- *Gradients*: Overlay uses gradient from accent to transparent at 70% opacity
- *Radial circles*: Radial pulse on image center when hovering
- *Scroll trigger*: IntersectionObserver at 15% threshold
- *JS interaction*: Lightbox modal opens on item click with image src swap

```html
<section data-amb-section="gallery" class="gallery-section gallery-section--lightbox">
  <div class="container">
    <h2 class="gallery-title">Featured work</h2>
    <div class="gallery-grid">
      <button class="gallery-item gallery-item--square" data-src="https://placehold.co/1200x800" data-title="Brand Identity" aria-label="View Brand Identity project">
        <img class="gallery-item-image" src="https://placehold.co/600x600" alt="Brand Identity" />
        <div class="gallery-item-overlay">
          <h3 class="gallery-item-title">Brand Identity</h3>
          <p class="gallery-item-desc">Visual identity system</p>
        </div>
      </button>
      <button class="gallery-item gallery-item--square" data-src="https://placehold.co/1200x800" data-title="Website Redesign" aria-label="View Website Redesign project">
        <img class="gallery-item-image" src="https://placehold.co/600x600" alt="Website Redesign" />
        <div class="gallery-item-overlay">
          <h3 class="gallery-item-title">Website Redesign</h3>
          <p class="gallery-item-desc">Full site overhaul</p>
        </div>
      </button>
      <button class="gallery-item gallery-item--square" data-src="https://placehold.co/1200x800" data-title="Mobile App UI" aria-label="View Mobile App UI project">
        <img class="gallery-item-image" src="https://placehold.co/600x600" alt="Mobile App UI" />
        <div class="gallery-item-overlay">
          <h3 class="gallery-item-title">Mobile App UI</h3>
          <p class="gallery-item-desc">iOS & Android design</p>
        </div>
      </button>
      <button class="gallery-item gallery-item--square" data-src="https://placehold.co/1200x800" data-title="Marketing Campaign" aria-label="View Marketing Campaign project">
        <img class="gallery-item-image" src="https://placehold.co/600x600" alt="Marketing Campaign" />
        <div class="gallery-item-overlay">
          <h3 class="gallery-item-title">Marketing Campaign</h3>
          <p class="gallery-item-desc">Multi-channel launch</p>
        </div>
      </button>
    </div>
  </div>
</section>

<div class="gallery-lightbox" id="gallery-lightbox" aria-hidden="true">
  <button class="gallery-lightbox-close" aria-label="Close lightbox">
    <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><line x1="18" y1="6" x2="6" y2="18"/><line x1="6" y1="6" x2="18" y2="18"/></svg>
  </button>
  <div class="gallery-lightbox-backdrop"></div>
  <div class="gallery-lightbox-content">
    <img class="gallery-lightbox-image" src="" alt="" />
    <h3 class="gallery-lightbox-title"></h3>
  </div>
</div>
```

```css
.gallery-section--lightbox {
  padding: clamp(64px, 8vw, 100px) 0;
  background: var(--color-bg-alt, #f8fafc);
}

.gallery-grid {
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  gap: 16px;
  max-width: 1000px;
  margin: 0 auto;
}

@media (max-width: 900px) {
  .gallery-grid { grid-template-columns: repeat(2, 1fr); }
}

@media (max-width: 500px) {
  .gallery-grid { grid-template-columns: 1fr; }
}

.gallery-item--square {
  position: relative;
  aspect-ratio: 1;
  border: none;
  padding: 0;
  border-radius: 12px;
  overflow: hidden;
  cursor: pointer;
  background: none;
  opacity: 0;
  transform: translateX(40px);
}

.gallery-item--square.is-visible {
  animation: gallery-row-enter 0.5s ease-out forwards;
}

.gallery-item--square:nth-child(1).is-visible { animation-delay: 0s; }
.gallery-item--square:nth-child(2).is-visible { animation-delay: 0.1s; }
.gallery-item--square:nth-child(3).is-visible { animation-delay: 0.2s; }
.gallery-item--square:nth-child(4).is-visible { animation-delay: 0.3s; }

@keyframes gallery-row-enter {
  to { opacity: 1; transform: translateX(0); }
}

.gallery-item--square .gallery-item-image {
  width: 100%;
  height: 100%;
  object-fit: cover;
  transition: filter 0.4s ease, transform 0.4s ease;
  filter: grayscale(0%);
}

.gallery-item--square:hover .gallery-item-image {
  filter: grayscale(100%);
  transform: scale(1.08);
}

.gallery-item--square .gallery-item-overlay {
  position: absolute;
  inset: 0;
  background: linear-gradient(135deg, rgba(99,102,241,0.85) 0%, rgba(124,58,237,0.7) 100%);
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  opacity: 0;
  transform: scale(0.8);
  transition: opacity 0.3s ease, transform 0.3s ease;
}

.gallery-item--square:hover .gallery-item-overlay {
  opacity: 1;
  transform: scale(1);
}

.gallery-item--square .gallery-item-title {
  font-size: 1.1rem;
  font-weight: 600;
  color: #fff;
  margin-bottom: 4px;
}

.gallery-item--square .gallery-item-desc {
  font-size: 0.85rem;
  color: rgba(255,255,255,0.85);
}

.gallery-lightbox {
  position: fixed;
  inset: 0;
  z-index: 1000;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 24px;
  opacity: 0;
  visibility: hidden;
  transition: opacity 0.3s ease, visibility 0.3s;
}

.gallery-lightbox.is-open {
  opacity: 1;
  visibility: visible;
}

.gallery-lightbox-backdrop {
  position: absolute;
  inset: 0;
  background: radial-gradient(circle at center, rgba(0,0,0,0.7) 0%, rgba(0,0,0,0.9) 100%);
  backdrop-filter: blur(12px);
  -webkit-backdrop-filter: blur(12px);
}

.gallery-lightbox-content {
  position: relative;
  z-index: 1;
  max-width: 90vw;
  max-height: 85vh;
  text-align: center;
  transform: scale(0.9);
  transition: transform 0.3s ease;
}

.gallery-lightbox.is-open .gallery-lightbox-content {
  transform: scale(1);
}

.gallery-lightbox-image {
  max-width: 100%;
  max-height: 75vh;
  border-radius: 12px;
  box-shadow: 0 24px 80px rgba(0,0,0,0.4);
}

.gallery-lightbox-title {
  font-size: 1.25rem;
  font-weight: 600;
  color: #fff;
  margin-top: 20px;
}

.gallery-lightbox-close {
  position: absolute;
  top: 20px;
  right: 20px;
  width: 48px;
  height: 48px;
  border-radius: 50%;
  border: none;
  background: rgba(255,255,255,0.1);
  color: #fff;
  cursor: pointer;
  display: flex;
  align-items: center;
  justify-content: center;
  transition: background 0.2s ease, transform 0.2s ease;
  z-index: 2;
}

.gallery-lightbox-close:hover {
  background: rgba(255,255,255,0.2);
  transform: rotate(90deg);
}

.gallery-lightbox-close svg {
  width: 24px;
  height: 24px;
}

@media (prefers-reduced-motion: reduce) {
  .gallery-item--square {
    opacity: 1;
    transform: none;
  }
  .gallery-item-overlay {
    opacity: 1;
    transform: none;
  }
  .gallery-lightbox-content {
    transform: none;
  }
}
```

*JS behavior:*
```javascript
const galleryItems = document.querySelectorAll('.gallery-item--square');
const lightbox = document.getElementById('gallery-lightbox');
const lightboxImg = lightbox.querySelector('.gallery-lightbox-image');
const lightboxTitle = lightbox.querySelector('.gallery-lightbox-title');
const closeBtn = lightbox.querySelector('.gallery-lightbox-close');

const observer = new IntersectionObserver((entries) => {
  entries.forEach(entry => {
    if (entry.isIntersecting) {
      entry.target.classList.add('is-visible');
      observer.unobserve(entry.target);
    }
  });
}, { threshold: 0.15 });
galleryItems.forEach(item => observer.observe(item));

galleryItems.forEach(item => {
  item.addEventListener('click', () => {
    lightboxImg.src = item.dataset.src;
    lightboxImg.alt = item.dataset.title;
    lightboxTitle.textContent = item.dataset.title;
    lightbox.classList.add('is-open');
    lightbox.setAttribute('aria-hidden', 'false');
    document.body.style.overflow = 'hidden';
  });
});

function closeLightbox() {
  lightbox.classList.remove('is-open');
  lightbox.setAttribute('aria-hidden', 'true');
  document.body.style.overflow = '';
}

closeBtn.addEventListener('click', closeLightbox);
lightbox.querySelector('.gallery-lightbox-backdrop').addEventListener('click', closeLightbox);
document.addEventListener('keydown', (e) => {
  if (e.key === 'Escape' && lightbox.classList.contains('is-open')) closeLightbox();
});
```

---

**Horizontal scroll carousel.** Drag-scrollable row of project cards with momentum. Dot indicators below.

*Animation & Visual Details:*
- *Entrance*: Cards slide in from right with stagger (0.08s per card)
- *Scroll*: CSS scroll-snap for snapping, JS adds momentum physics on release
- *Gradients*: Fade masks on left/right edges indicating more content
- *Radial circles*: Active card has subtle radial glow behind it
- *Scroll trigger*: Not applicable (horizontal scroll)
- *JS interaction*: Drag-to-scroll with momentum, dot indicator updates, scroll snap alignment

```html
<section data-amb-section="gallery" class="gallery-section gallery-section--carousel">
  <div class="container">
    <h2 class="gallery-title">Latest projects</h2>
  </div>
  <div class="gallery-carousel-wrapper">
    <div class="gallery-carousel" id="gallery-carousel">
      <article class="gallery-carousel-item">
        <div class="gallery-carousel-media">
          <img class="gallery-carousel-image" src="https://placehold.co/500x600" alt="Project one" />
        </div>
        <h3 class="gallery-carousel-title">E-commerce Rebrand</h3>
        <p class="gallery-carousel-category">Branding / Web</p>
      </article>
      <article class="gallery-carousel-item">
        <div class="gallery-carousel-media">
          <img class="gallery-carousel-image" src="https://placehold.co/500x600" alt="Project two" />
        </div>
        <h3 class="gallery-carousel-title">SaaS Dashboard</h3>
        <p class="gallery-carousel-category">UI/UX Design</p>
      </article>
      <article class="gallery-carousel-item">
        <div class="gallery-carousel-media">
          <img class="gallery-carousel-image" src="https://placehold.co/500x600" alt="Project three" />
        </div>
        <h3 class="gallery-carousel-title">Mobile Banking App</h3>
        <p class="gallery-carousel-category">App Design</p>
      </article>
      <article class="gallery-carousel-item">
        <div class="gallery-carousel-media">
          <img class="gallery-carousel-image" src="https://placehold.co/500x600" alt="Project four" />
        </div>
        <h3 class="gallery-carousel-title">Tech Conference</h3>
        <p class="gallery-carousel-category">Event Branding</p>
      </article>
      <article class="gallery-carousel-item">
        <div class="gallery-carousel-media">
          <img class="gallery-carousel-image" src="https://placehold.co/500x600" alt="Project five" />
        </div>
        <h3 class="gallery-carousel-title">Fitness Brand</h3>
        <p class="gallery-carousel-category">Identity System</p>
      </article>
    </div>
    <div class="gallery-carousel-fade gallery-carousel-fade--left"></div>
    <div class="gallery-carousel-fade gallery-carousel-fade--right"></div>
  </div>
  <div class="gallery-carousel-dots"></div>
</section>
```

```css
.gallery-section--carousel {
  padding: clamp(64px, 8vw, 100px) 0;
  overflow: hidden;
}

.gallery-section--carousel .gallery-title {
  text-align: center;
  font-size: clamp(1.75rem, 3.5vw, 2.5rem);
  font-weight: 700;
  color: var(--color-text, #1e293b);
  margin-bottom: 40px;
}

.gallery-carousel-wrapper {
  position: relative;
}

.gallery-carousel {
  display: flex;
  gap: 24px;
  overflow-x: auto;
  scroll-snap-type: x mandatory;
  scroll-behavior: smooth;
  padding: 20px clamp(24px, 5vw, 60px);
  -webkit-overflow-scrolling: touch;
  scrollbar-width: none;
}

.gallery-carousel::-webkit-scrollbar {
  display: none;
}

.gallery-carousel-item {
  flex: 0 0 clamp(280px, 40vw, 400px);
  scroll-snap-align: start;
  opacity: 0;
  transform: translateX(60px);
  transition: transform 0.3s ease;
}

.gallery-carousel-item.is-visible {
  animation: carousel-item-enter 0.5s ease-out forwards;
}

.gallery-carousel-item:nth-child(1).is-visible { animation-delay: 0s; }
.gallery-carousel-item:nth-child(2).is-visible { animation-delay: 0.08s; }
.gallery-carousel-item:nth-child(3).is-visible { animation-delay: 0.16s; }
.gallery-carousel-item:nth-child(4).is-visible { animation-delay: 0.24s; }
.gallery-carousel-item:nth-child(5).is-visible { animation-delay: 0.32s; }

@keyframes carousel-item-enter {
  to { opacity: 1; transform: translateX(0); }
}

.gallery-carousel-media {
  position: relative;
  border-radius: 16px;
  overflow: hidden;
  margin-bottom: 16px;
  cursor: pointer;
}

.gallery-carousel-media::after {
  content: '';
  position: absolute;
  inset: 0;
  background: radial-gradient(circle at 50% 50%, rgba(99,102,241,0) 0%, rgba(99,102,241,0.1) 100%);
  opacity: 0;
  transition: opacity 0.3s ease;
}

.gallery-carousel-item.active .gallery-carousel-media::after {
  opacity: 1;
}

.gallery-carousel-image {
  width: 100%;
  aspect-ratio: 5/6;
  object-fit: cover;
  display: block;
  transition: transform 0.4s ease;
}

.gallery-carousel-media:hover .gallery-carousel-image {
  transform: scale(1.04);
}

.gallery-carousel-title {
  font-size: 1.1rem;
  font-weight: 600;
  color: var(--color-text, #1e293b);
  margin-bottom: 4px;
}

.gallery-carousel-category {
  font-size: 0.85rem;
  color: var(--color-text-muted, #64748b);
}

.gallery-carousel-fade {
  position: absolute;
  top: 0;
  bottom: 0;
  width: 80px;
  pointer-events: none;
  z-index: 2;
}

.gallery-carousel-fade--left {
  left: 0;
  background: linear-gradient(to right, var(--color-bg, #ffffff) 0%, transparent 100%);
}

.gallery-carousel-fade--right {
  right: 0;
  background: linear-gradient(to left, var(--color-bg, #ffffff) 0%, transparent 100%);
}

.gallery-carousel-dots {
  display: flex;
  gap: 8px;
  justify-content: center;
  margin-top: 24px;
}

.gallery-carousel-dot {
  width: 8px;
  height: 8px;
  border-radius: 50%;
  background: #e2e8f0;
  border: none;
  cursor: pointer;
  transition: background 0.2s ease, transform 0.2s ease;
}

.gallery-carousel-dot.active {
  background: var(--color-accent, #6366f1);
  transform: scale(1.25);
}

@media (prefers-reduced-motion: reduce) {
  .gallery-carousel-item {
    opacity: 1;
    transform: none;
  }
  .gallery-carousel {
    scroll-behavior: auto;
  }
}
```

*JS behavior:*
```javascript
const carousel = document.getElementById('gallery-carousel');
const carouselItems = carousel.querySelectorAll('.gallery-carousel-item');
const dotsContainer = carousel.parentElement.querySelector('.gallery-carousel-dots');
const totalItems = carouselItems.length;
let isDown = false;
let startX;
let scrollLeft;

carouselItems.forEach((item, i) => {
  const observer = new IntersectionObserver((entries) => {
    entries.forEach(entry => {
      if (entry.isIntersecting) {
        entry.target.classList.add('is-visible');
        observer.unobserve(entry.target);
      }
    });
  }, { threshold: 0.5 });
  observer.observe(item);
});

for (let i = 0; i < totalItems; i++) {
  const dot = document.createElement('button');
  dot.className = 'gallery-carousel-dot' + (i === 0 ? ' active' : '');
  dot.setAttribute('aria-label', `Go to slide ${i + 1}`);
  dotsContainer.appendChild(dot);
}

const dots = dotsContainer.querySelectorAll('.gallery-carousel-dot');

carousel.addEventListener('scroll', () => {
  const scrollPos = carousel.scrollLeft;
  const itemWidth = carouselItems[0].offsetWidth + 24;
  const activeIndex = Math.round(scrollPos / itemWidth);
  dots.forEach((dot, i) => dot.classList.toggle('active', i === activeIndex));
  carouselItems.forEach((item, i) => item.classList.toggle('active', i === activeIndex));
});

dots.forEach((dot, i) => {
  dot.addEventListener('click', () => {
    const itemWidth = carouselItems[0].offsetWidth + 24;
    carousel.scrollTo({ left: i * itemWidth, behavior: 'smooth' });
  });
});

carousel.addEventListener('mousedown', (e) => {
  isDown = true;
  carousel.style.cursor = 'grabbing';
  startX = e.pageX - carousel.offsetLeft;
  scrollLeft = carousel.scrollLeft;
});

carousel.addEventListener('mouseleave', () => { isDown = false; carousel.style.cursor = 'grab'; });
carousel.addEventListener('mouseup', () => { isDown = false; carousel.style.cursor = 'grab'; });

carousel.addEventListener('mousemove', (e) => {
  if (!isDown) return;
  e.preventDefault();
  const x = e.pageX - carousel.offsetLeft;
  const walk = (x - startX) * 1.5;
  carousel.scrollLeft = scrollLeft - walk;
});
```

---

**Parallax depth grid.** Cards at different z-depths with parallax scroll effect. Larger images in front.

*Animation & Visual Details:*
- *Entrance*: Cards scale in from 0.8 → 1 with stagger, larger cards appear closer (faster)
- *Scroll*: Each card layer moves at different speeds (background 0.3x, mid 0.5x, foreground 0.8x)
- *Gradients*: Cards have gradient border (1px accent at 30% opacity)
- *Radial circles*: Radial dot pattern on card backgrounds at low opacity
- *Scroll trigger*: Parallax calculated on scroll event (throttled with rAF)
- *JS interaction*: Parallax depth layers on scroll, card entrance animation

```html
<section data-amb-section="gallery" class="gallery-section gallery-section--parallax">
  <div class="container">
    <h2 class="gallery-title">Selected work</h2>
    <div class="gallery-parallax-scene">
      <div class="gallery-parallax-layer gallery-parallax-layer--back">
        <img class="gallery-parallax-image" src="https://placehold.co/800x600" alt="" />
        <img class="gallery-parallax-image" src="https://placehold.co/700x500" alt="" />
      </div>
      <div class="gallery-parallax-layer gallery-parallax-layer--mid">
        <img class="gallery-parallax-image" src="https://placehold.co/600x500" alt="" />
        <img class="gallery-parallax-image" src="https://placehold.co/650x550" alt="" />
      </div>
      <div class="gallery-parallax-layer gallery-parallax-layer--front">
        <article class="gallery-parallax-card">
          <img class="gallery-parallax-card-image" src="https://placehold.co/500x400" alt="Brand identity" />
          <div class="gallery-parallax-card-content">
            <h3 class="gallery-parallax-card-title">Brand Identity</h3>
            <p class="gallery-parallax-card-category">Visual Identity</p>
          </div>
        </article>
        <article class="gallery-parallax-card">
          <img class="gallery-parallax-card-image" src="https://placehold.co/500x400" alt="Website design" />
          <div class="gallery-parallax-card-content">
            <h3 class="gallery-parallax-card-title">Website Design</h3>
            <p class="gallery-parallax-card-category">Web / UX</p>
          </div>
        </article>
      </div>
    </div>
  </div>
</section>
```

```css
.gallery-section--parallax {
  padding: clamp(64px, 8vw, 100px) 0;
  background: var(--color-bg-alt, #f8fafc);
  overflow: hidden;
}

.gallery-section--parallax .gallery-title {
  text-align: center;
  font-size: clamp(1.75rem, 3.5vw, 2.5rem);
  font-weight: 700;
  color: var(--color-text, #1e293b);
  margin-bottom: 60px;
}

.gallery-parallax-scene {
  position: relative;
  height: 600px;
  max-width: 1200px;
  margin: 0 auto;
}

.gallery-parallax-layer {
  position: absolute;
  inset: 0;
  display: flex;
  gap: 40px;
  justify-content: center;
  pointer-events: none;
}

.gallery-parallax-layer--back {
  z-index: 1;
}

.gallery-parallax-layer--mid {
  z-index: 2;
  padding-top: 60px;
}

.gallery-parallax-layer--front {
  z-index: 3;
  padding-top: 120px;
}

.gallery-parallax-image {
  object-fit: cover;
  border-radius: 20px;
  opacity: 0.4;
  transition: transform 0.1s linear;
}

.gallery-parallax-layer--back .gallery-parallax-image {
  width: 400px;
  height: 300px;
}

.gallery-parallax-layer--mid .gallery-parallax-image {
  width: 350px;
  height: 280px;
}

.gallery-parallax-card {
  display: flex;
  flex-direction: column;
  background: #fff;
  border-radius: 20px;
  overflow: hidden;
  box-shadow: 0 16px 48px rgba(0,0,0,0.1);
  border: 1px solid rgba(99,102,241,0.15);
  opacity: 0;
  transform: scale(0.8);
  width: 340px;
  pointer-events: auto;
  cursor: pointer;
  transition: transform 0.3s ease, box-shadow 0.3s ease;
}

.gallery-parallax-card.is-visible {
  animation: parallax-card-enter 0.6s ease-out forwards;
}

.gallery-parallax-card:nth-child(1).is-visible { animation-delay: 0s; }
.gallery-parallax-card:nth-child(2).is-visible { animation-delay: 0.15s; }

@keyframes parallax-card-enter {
  to { opacity: 1; transform: scale(1); }
}

.gallery-parallax-card:hover {
  transform: translateY(-8px) scale(1.02);
  box-shadow: 0 24px 64px rgba(0,0,0,0.15);
}

.gallery-parallax-card-image {
  width: 100%;
  height: 220px;
  object-fit: cover;
}

.gallery-parallax-card-content {
  padding: 20px;
  background: linear-gradient(135deg, rgba(99,102,241,0.03), transparent);
}

.gallery-parallax-card-title {
  font-size: 1.1rem;
  font-weight: 600;
  color: var(--color-text, #1e293b);
  margin-bottom: 4px;
}

.gallery-parallax-card-category {
  font-size: 0.85rem;
  color: var(--color-text-muted, #64748b);
}

@media (max-width: 768px) {
  .gallery-parallax-scene {
    height: auto;
    display: flex;
    flex-direction: column;
    gap: 40px;
  }
  .gallery-parallax-layer {
    position: static;
    flex-wrap: wrap;
  }
  .gallery-parallax-image {
    display: none;
  }
  .gallery-parallax-card {
    width: 100%;
    max-width: 400px;
    margin: 0 auto;
  }
}

@media (prefers-reduced-motion: reduce) {
  .gallery-parallax-card {
    opacity: 1;
    transform: none;
  }
  .gallery-parallax-image {
    transition: none;
  }
}
```

*JS behavior:*
```javascript
const parallaxCards = document.querySelectorAll('.gallery-parallax-card');
const parallaxImages = document.querySelectorAll('.gallery-parallax-image');
const scene = document.querySelector('.gallery-parallax-scene');

const cardObserver = new IntersectionObserver((entries) => {
  entries.forEach(entry => {
    if (entry.isIntersecting) {
      entry.target.classList.add('is-visible');
      cardObserver.unobserve(entry.target);
    }
  });
}, { threshold: 0.2 });
parallaxCards.forEach(card => cardObserver.observe(card));

let ticking = false;
window.addEventListener('scroll', () => {
  if (!ticking) {
    requestAnimationFrame(() => {
      const scrollY = window.scrollY;
      const sceneRect = scene.getBoundingClientRect();
      const relativeScroll = scrollY - scene.offsetTop;
      const viewportCenter = relativeScroll + window.innerHeight / 2;
      const sceneCenter = sceneRect.height / 2;
      const offset = (viewportCenter - sceneCenter) * 0.3;

      parallaxImages.forEach((img, i) => {
        const speed = i % 2 === 0 ? 0.3 : 0.2;
        img.style.transform = `translateY(${offset * speed}px)`;
      });

      ticking = false;
    });
    ticking = true;
  }
});
```

---

**Animated filter tabs.** Category filter tabs reveal grid with smooth layout animation.

*Animation & Visual Details:*
- *Entrance*: Filter tabs fade in first; grid cards follow with stagger
- *Hover*: Tab has gradient underline on hover; cards have subtle lift
- *Gradients*: Active tab has gradient background; hover on cards has gradient border
- *Radial circles*: Radial glow on card hover behind image
- *Scroll trigger*: Not applicable (filter interaction)
- *JS interaction*: Category filter buttons with CSS display toggle and re-entrance animation

```html
<section data-amb-section="gallery" class="gallery-section gallery-section--filter">
  <div class="container">
    <h2 class="gallery-title">Our portfolio</h2>
    <nav class="gallery-filter-nav" role="tablist">
      <button class="gallery-filter-btn gallery-filter-btn--active" role="tab" data-filter="all">All</button>
      <button class="gallery-filter-btn" role="tab" data-filter="branding">Branding</button>
      <button class="gallery-filter-btn" role="tab" data-filter="web">Web Design</button>
      <button class="gallery-filter-btn" role="tab" data-filter="app">App Design</button>
    </nav>
    <div class="gallery-filter-grid">
      <article class="gallery-card" data-categories="branding">
        <div class="gallery-card-media">
          <div class="gallery-card-glow"></div>
          <img class="gallery-card-image" src="https://placehold.co/600x500" alt="Brand identity project" />
        </div>
        <h3 class="gallery-card-title">Tech Startup Branding</h3>
        <p class="gallery-card-category">Branding</p>
      </article>
      <article class="gallery-card" data-categories="web">
        <div class="gallery-card-media">
          <div class="gallery-card-glow"></div>
          <img class="gallery-card-image" src="https://placehold.co/600x500" alt="Website project" />
        </div>
        <h3 class="gallery-card-title">SaaS Platform Website</h3>
        <p class="gallery-card-category">Web Design</p>
      </article>
      <article class="gallery-card" data-categories="app">
        <div class="gallery-card-media">
          <div class="gallery-card-glow"></div>
          <img class="gallery-card-image" src="https://placehold.co/600x500" alt="App project" />
        </div>
        <h3 class="gallery-card-title">Healthcare App</h3>
        <p class="gallery-card-category">App Design</p>
      </article>
      <article class="gallery-card" data-categories="branding web">
        <div class="gallery-card-media">
          <div class="gallery-card-glow"></div>
          <img class="gallery-card-image" src="https://placehold.co/600x500" alt="Brand and web project" />
        </div>
        <h3 class="gallery-card-title">Restaurant Rebrand</h3>
        <p class="gallery-card-category">Branding / Web</p>
      </article>
    </div>
  </div>
</section>
```

```css
.gallery-section--filter {
  padding: clamp(64px, 8vw, 100px) 0;
}

.gallery-section--filter .gallery-title {
  text-align: center;
  font-size: clamp(1.75rem, 3.5vw, 2.5rem);
  font-weight: 700;
  color: var(--color-text, #1e293b);
  margin-bottom: 32px;
}

.gallery-filter-nav {
  display: flex;
  gap: 12px;
  justify-content: center;
  flex-wrap: wrap;
  margin-bottom: 48px;
}

.gallery-filter-btn {
  padding: 12px 24px;
  border-radius: 30px;
  border: 1px solid #e2e8f0;
  background: #fff;
  font-size: 0.9rem;
  font-weight: 500;
  color: var(--color-text-muted, #64748b);
  cursor: pointer;
  transition: all 0.25s ease;
}

.gallery-filter-btn:hover {
  border-color: var(--color-accent, #6366f1);
  color: var(--color-accent, #6366f1);
}

.gallery-filter-btn--active {
  background: linear-gradient(135deg, rgba(99,102,241,0.1), rgba(124,58,237,0.08));
  border-color: var(--color-accent, #6366f1);
  color: var(--color-accent, #6366f1);
  box-shadow: 0 4px 16px rgba(99,102,241,0.15);
}

.gallery-filter-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(280px, 1fr));
  gap: 24px;
}

.gallery-card {
  background: #fff;
  border-radius: 16px;
  overflow: hidden;
  border: 1px solid #f1f5f9;
  transition: transform 0.3s ease, box-shadow 0.3s ease;
  opacity: 0;
  transform: translateY(20px);
}

.gallery-card.is-visible {
  animation: filter-card-enter 0.4s ease-out forwards;
}

.gallery-card:nth-child(1).is-visible { animation-delay: 0s; }
.gallery-card:nth-child(2).is-visible { animation-delay: 0.06s; }
.gallery-card:nth-child(3).is-visible { animation-delay: 0.12s; }
.gallery-card:nth-child(4).is-visible { animation-delay: 0.18s; }

@keyframes filter-card-enter {
  to { opacity: 1; transform: translateY(0); }
}

.gallery-card:hover {
  transform: translateY(-4px);
  box-shadow: 0 12px 32px rgba(0,0,0,0.08);
}

.gallery-card[data-hidden="true"] {
  display: none;
}

.gallery-card-media {
  position: relative;
  overflow: hidden;
}

.gallery-card-glow {
  position: absolute;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
  width: 100px;
  height: 100px;
  background: radial-gradient(circle, rgba(99,102,241,0.15) 0%, transparent 70%);
  border-radius: 50%;
  opacity: 0;
  transition: opacity 0.3s ease;
  pointer-events: none;
}

.gallery-card:hover .gallery-card-glow {
  opacity: 1;
}

.gallery-card-image {
  width: 100%;
  aspect-ratio: 6/5;
  object-fit: cover;
  display: block;
  transition: transform 0.4s ease;
}

.gallery-card:hover .gallery-card-image {
  transform: scale(1.05);
}

.gallery-card-title {
  font-size: 1rem;
  font-weight: 600;
  color: var(--color-text, #1e293b);
  padding: 16px 16px 4px;
}

.gallery-card-category {
  font-size: 0.85rem;
  color: var(--color-text-muted, #64748b);
  padding: 0 16px 16px;
}

@media (prefers-reduced-motion: reduce) {
  .gallery-card {
    opacity: 1;
    transform: none;
  }
}
```

*JS behavior:*
```javascript
const filterBtns = document.querySelectorAll('.gallery-filter-btn');
const galleryCards = document.querySelectorAll('.gallery-card');

galleryCards.forEach(card => {
  const observer = new IntersectionObserver((entries) => {
    entries.forEach(entry => {
      if (entry.isIntersecting) {
        entry.target.classList.add('is-visible');
        observer.unobserve(entry.target);
      }
    });
  }, { threshold: 0.1 });
  observer.observe(card);
});

filterBtns.forEach(btn => {
  btn.addEventListener('click', () => {
    const filter = btn.dataset.filter;

    filterBtns.forEach(b => b.classList.remove('gallery-filter-btn--active'));
    btn.classList.add('gallery-filter-btn--active');

    galleryCards.forEach(card => {
      const categories = card.dataset.categories;
      const shouldShow = filter === 'all' || categories.includes(filter);
      card.dataset.hidden = shouldShow ? 'false' : 'true';

      if (shouldShow) {
        card.style.animation = 'none';
        card.offsetHeight;
        card.classList.remove('is-visible');
        card.style.animation = '';
        void card.offsetWidth;
        card.classList.add('is-visible');
      }
    });
  });
});
```

---

Copy rules: gallery items need strong titles (not just "Project 1"), category tags help users scan, hover states are mandatory — the work must be clickable and reveal more context. Never show more than 12 items without pagination or "load more."

Layout rules: masonry works best for visual variety; uniform grids feel more curated. Carousels belong in secondary positions (testimonials, press logos) not primary galleries. Keep filter tabs to 5 or fewer categories.

CSS custom property summary:
```css
:root {
  --gallery-enter-duration: 0.5s;
  --gallery-stagger: 0.06s;
  --color-accent: #6366f1;
  --color-text: #1e293b;
  --color-text-muted: #64748b;
}
```