---
id: seed:patterns/social-proof
title: Social proof patterns
source_type: seed_pattern
priority: warm
---

Social proof is anything that lets a stranger borrow trust from another stranger. It has multiple working forms, each with a different best-fit placement. Four working variations:

---

**Logo strip with stagger.** Customer logos that animate in on scroll with hover effects.

*Animation & Visual Details:*
- *Entrance*: Logos fade in with stagger (0.08s between), translateY(20px) → 0
- *Hover*: Logo scales to 1.1 with opacity increase
- *Gradients*: Subtle gradient overlay on container edges
- *Radial circles*: Radial glow on active logo
- *Scroll trigger*: IntersectionObserver at 20% threshold
- *JS interaction*: None beyond entrance

```html
<section data-amb-section="social-proof" class="sp-section sp-section--logos">
  <div class="container">
    <p class="sp-label">Trusted by teams at</p>
    <div class="sp-logos">
      <div class="sp-logo">
        <svg viewBox="0 0 100 40" fill="currentColor"><text x="10" y="28" font-size="18" font-weight="bold">Acme</text></svg>
      </div>
      <div class="sp-logo">
        <svg viewBox="0 0 100 40" fill="currentColor"><text x="10" y="28" font-size="18" font-weight="bold">TechCorp</text></svg>
      </div>
      <div class="sp-logo">
        <svg viewBox="0 0 100 40" fill="currentColor"><text x="10" y="28" font-size="18" font-weight="bold">Innovate</text></svg>
      </div>
      <div class="sp-logo">
        <svg viewBox="0 0 100 40" fill="currentColor"><text x="10" y="28" font-size="18" font-weight="bold">StartupX</text></svg>
      </div>
      <div class="sp-logo">
        <svg viewBox="0 0 100 40" fill="currentColor"><text x="10" y="28" font-size="18" font-weight="bold">GlobalCo</text></svg>
      </div>
    </div>
  </div>
</section>
```

```css
:root {
  --sp-enter-duration: 0.5s;
  --sp-stagger: 0.08s;
}

.sp-section {
  padding: clamp(48px, 6vw, 72px) 0;
}

.sp-section--logos {
  background: var(--color-bg-alt, #f8fafc);
  position: relative;
}

.sp-section--logos::before,
.sp-section--logos::after {
  content: '';
  position: absolute;
  top: 0;
  bottom: 0;
  width: 80px;
  pointer-events: none;
}

.sp-section--logos::before {
  left: 0;
  background: linear-gradient(to right, var(--color-bg-alt, #f8fafc), transparent);
}

.sp-section--logos::after {
  right: 0;
  background: linear-gradient(to left, var(--color-bg-alt, #f8fafc), transparent);
}

.sp-label {
  text-align: center;
  font-size: 0.85rem;
  font-weight: 500;
  color: var(--color-text-muted, #64748b);
  text-transform: uppercase;
  letter-spacing: 0.1em;
  margin-bottom: 28px;
  opacity: 0;
  animation: sp-label-enter 0.4s ease-out 0.2s forwards;
}

@keyframes sp-label-enter {
  to { opacity: 1; }
}

.sp-logos {
  display: flex;
  justify-content: center;
  align-items: center;
  flex-wrap: wrap;
  gap: 40px;
  max-width: 900px;
  margin: 0 auto;
}

.sp-logo {
  opacity: 0;
  transform: translateY(20px);
  transition: transform 0.3s ease, opacity 0.3s ease;
}

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

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

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

.sp-logo:hover {
  transform: scale(1.1);
}

.sp-logo svg {
  height: 28px;
  width: auto;
  color: #94a3b8;
  transition: color 0.3s ease;
}

.sp-logo:hover svg {
  color: var(--color-text, #1e293b);
}

@media (prefers-reduced-motion: reduce) {
  .sp-label,
  .sp-logo {
    opacity: 1;
    transform: none;
    animation: none;
  }
}
```

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

---

**Testimonial cards with photo.** Named quotes with attribution, photo, and star rating.

*Animation & Visual Details:*
- *Entrance*: Cards stagger in from translateY(30px), 0.12s between each
- *Hover*: Card lifts with shadow, gradient border appears
- *Gradients*: Quote icon has gradient fill (accent at 15% opacity)
- *Radial circles*: Radial glow behind quote marks
- *Scroll trigger*: IntersectionObserver at 15% threshold
- *JS interaction*: None beyond entrance

```html
<section data-amb-section="social-proof" class="sp-section sp-section--testimonials">
  <div class="container">
    <h2 class="sp-title">What customers say</h2>
    <div class="sp-testimonials">
      <article class="sp-testimonial">
        <div class="sp-testimonial-quote-icon">
          <svg viewBox="0 0 24 24" fill="currentColor"><path d="M14.017 21v-7.391c0-5.704 3.731-9.57 8.983-10.609l.995 2.151c-2.432.917-3.995 3.638-3.995 5.849h4v10h-9.983zm-14.017 0v-7.391c0-5.704 3.748-9.57 9-10.609l.996 2.151c-2.433.917-3.996 3.638-3.996 5.849h3.983v10h-9.983z"/></svg>
        </div>
        <blockquote class="sp-testimonial-text">
          We cut our deployment time from 40 minutes to under 3. The rollback feature alone has saved us from three potential outages this quarter.
        </blockquote>
        <div class="sp-testimonial-rating">
          <svg viewBox="0 0 24 24" fill="currentColor"><polygon points="12 2 15.09 8.26 22 9.27 17 14.14 18.18 21.02 12 17.77 5.82 21.02 7 14.14 2 9.27 8.91 8.26 12 2"/></svg>
          <svg viewBox="0 0 24 24" fill="currentColor"><polygon points="12 2 15.09 8.26 22 9.27 17 14.14 18.18 21.02 12 17.77 5.82 21.02 7 14.14 2 9.27 8.91 8.26 12 2"/></svg>
          <svg viewBox="0 0 24 24" fill="currentColor"><polygon points="12 2 15.09 8.26 22 9.27 17 14.14 18.18 21.02 12 17.77 5.82 21.02 7 14.14 2 9.27 8.91 8.26 12 2"/></svg>
          <svg viewBox="0 0 24 24" fill="currentColor"><polygon points="12 2 15.09 8.26 22 9.27 17 14.14 18.18 21.02 12 17.77 5.82 21.02 7 14.14 2 9.27 8.91 8.26 12 2"/></svg>
          <svg viewBox="0 0 24 24" fill="currentColor"><polygon points="12 2 15.09 8.26 22 9.27 17 14.14 18.18 21.02 12 17.77 5.82 21.02 7 14.14 2 9.27 8.91 8.26 12 2"/></svg>
        </div>
        <div class="sp-testimonial-author">
          <img class="sp-testimonial-photo" src="https://placehold.co/48x48" alt="Sarah Chen" />
          <div class="sp-testimonial-info">
            <span class="sp-testimonial-name">Sarah Chen</span>
            <span class="sp-testimonial-role">VP Engineering, TechCorp</span>
          </div>
        </div>
      </article>
      <article class="sp-testimonial">
        <div class="sp-testimonial-quote-icon">
          <svg viewBox="0 0 24 24" fill="currentColor"><path d="M14.017 21v-7.391c0-5.704 3.731-9.57 8.983-10.609l.995 2.151c-2.432.917-3.995 3.638-3.995 5.849h4v10h-9.983zm-14.017 0v-7.391c0-5.704 3.748-9.57 9-10.609l.996 2.151c-2.433.917-3.996 3.638-3.996 5.849h3.983v10h-9.983z"/></svg>
        </div>
        <blockquote class="sp-testimonial-text">
          Our entire team moved from Jira in one afternoon. The migration was painless, and our developers actually enjoy using it now.
        </blockquote>
        <div class="sp-testimonial-rating">
          <svg viewBox="0 0 24 24" fill="currentColor"><polygon points="12 2 15.09 8.26 22 9.27 17 14.14 18.18 21.02 12 17.77 5.82 21.02 7 14.14 2 9.27 8.91 8.26 12 2"/></svg>
          <svg viewBox="0 0 24 24" fill="currentColor"><polygon points="12 2 15.09 8.26 22 9.27 17 14.14 18.18 21.02 12 17.77 5.82 21.02 7 14.14 2 9.27 8.91 8.26 12 2"/></svg>
          <svg viewBox="0 0 24 24" fill="currentColor"><polygon points="12 2 15.09 8.26 22 9.27 17 14.14 18.18 21.02 12 17.77 5.82 21.02 7 14.14 2 9.27 8.91 8.26 12 2"/></svg>
          <svg viewBox="0 0 24 24" fill="currentColor"><polygon points="12 2 15.09 8.26 22 9.27 17 14.14 18.18 21.02 12 17.77 5.82 21.02 7 14.14 2 9.27 8.91 8.26 12 2"/></svg>
          <svg viewBox="0 0 24 24" fill="currentColor"><polygon points="12 2 15.09 8.26 22 9.27 17 14.14 18.18 21.02 12 17.77 5.82 21.02 7 14.14 2 9.27 8.91 8.26 12 2"/></svg>
        </div>
        <div class="sp-testimonial-author">
          <img class="sp-testimonial-photo" src="https://placehold.co/48x48" alt="Marcus Rodriguez" />
          <div class="sp-testimonial-info">
            <span class="sp-testimonial-name">Marcus Rodriguez</span>
            <span class="sp-testimonial-role">CTO, StartupX</span>
          </div>
        </div>
      </article>
      <article class="sp-testimonial">
        <div class="sp-testimonial-quote-icon">
          <svg viewBox="0 0 24 24" fill="currentColor"><path d="M14.017 21v-7.391c0-5.704 3.731-9.57 8.983-10.609l.995 2.151c-2.432.917-3.995 3.638-3.995 5.849h4v10h-9.983zm-14.017 0v-7.391c0-5.704 3.748-9.57 9-10.609l.996 2.151c-2.433.917-3.996 3.638-3.996 5.849h3.983v10h-9.983z"/></svg>
        </div>
        <blockquote class="sp-testimonial-text">
          We evaluated six platforms before choosing this one. The analytics alone justified the switch — we found bottlenecks we'd never even seen before.
        </blockquote>
        <div class="sp-testimonial-rating">
          <svg viewBox="0 0 24 24" fill="currentColor"><polygon points="12 2 15.09 8.26 22 9.27 17 14.14 18.18 21.02 12 17.77 5.82 21.02 7 14.14 2 9.27 8.91 8.26 12 2"/></svg>
          <svg viewBox="0 0 24 24" fill="currentColor"><polygon points="12 2 15.09 8.26 22 9.27 17 14.14 18.18 21.02 12 17.77 5.82 21.02 7 14.14 2 9.27 8.91 8.26 12 2"/></svg>
          <svg viewBox="0 0 24 24" fill="currentColor"><polygon points="12 2 15.09 8.26 22 9.27 17 14.14 18.18 21.02 12 17.77 5.82 21.02 7 14.14 2 9.27 8.91 8.26 12 2"/></svg>
          <svg viewBox="0 0 24 24" fill="currentColor"><polygon points="12 2 15.09 8.26 22 9.27 17 14.14 18.18 21.02 12 17.77 5.82 21.02 7 14.14 2 9.27 8.91 8.26 12 2"/></svg>
          <svg viewBox="0 0 24 24" fill="currentColor"><polygon points="12 2 15.09 8.26 22 9.27 17 14.14 18.18 21.02 12 17.77 5.82 21.02 7 14.14 2 9.27 8.91 8.26 12 2"/></svg>
        </div>
        <div class="sp-testimonial-author">
          <img class="sp-testimonial-photo" src="https://placehold.co/48x48" alt="Priya Patel" />
          <div class="sp-testimonial-info">
            <span class="sp-testimonial-name">Priya Patel</span>
            <span class="sp-testimonial-role">Head of Product, GlobalCo</span>
          </div>
        </div>
      </article>
    </div>
  </div>
</section>
```

```css
.sp-section--testimonials {
  background: #fff;
}

.sp-title {
  text-align: center;
  font-size: clamp(1.5rem, 3vw, 2rem);
  font-weight: 700;
  color: var(--color-text, #1e293b);
  margin-bottom: 48px;
}

.sp-testimonials {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(300px, 1fr));
  gap: 24px;
  max-width: 1000px;
  margin: 0 auto;
}

.sp-testimonial {
  position: relative;
  background: #f8fafc;
  border-radius: 20px;
  padding: 32px;
  opacity: 0;
  transform: translateY(30px);
  transition: transform 0.3s ease, box-shadow 0.3s ease, border-color 0.3s ease;
  border: 1px solid transparent;
}

.sp-testimonial.is-visible {
  animation: sp-testimonial-enter 0.5s ease-out forwards;
}

.sp-testimonial:nth-child(1).is-visible { animation-delay: 0s; }
.sp-testimonial:nth-child(2).is-visible { animation-delay: 0.12s; }
.sp-testimonial:nth-child(3).is-visible { animation-delay: 0.24s; }

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

.sp-testimonial:hover {
  transform: translateY(-4px);
  box-shadow: 0 12px 40px rgba(0,0,0,0.08);
  border-color: rgba(99,102,241,0.2);
}

.sp-testimonial-quote-icon {
  width: 40px;
  height: 40px;
  margin-bottom: 16px;
  position: relative;
}

.sp-testimonial-quote-icon::before {
  content: '';
  position: absolute;
  top: -4px;
  left: -4px;
  width: 48px;
  height: 48px;
  background: radial-gradient(circle, rgba(99,102,241,0.1) 0%, transparent 70%);
  border-radius: 50%;
}

.sp-testimonial-quote-icon svg {
  width: 100%;
  height: 100%;
  color: var(--color-accent, #6366f1);
  opacity: 0.5;
}

.sp-testimonial-text {
  font-size: 1rem;
  font-style: italic;
  color: var(--color-text, #1e293b);
  line-height: 1.7;
  margin: 0 0 20px;
}

.sp-testimonial-rating {
  display: flex;
  gap: 4px;
  margin-bottom: 20px;
}

.sp-testimonial-rating svg {
  width: 16px;
  height: 16px;
  color: #f59e0b;
}

.sp-testimonial-author {
  display: flex;
  align-items: center;
  gap: 12px;
}

.sp-testimonial-photo {
  width: 48px;
  height: 48px;
  border-radius: 50%;
  object-fit: cover;
}

.sp-testimonial-info {
  display: flex;
  flex-direction: column;
}

.sp-testimonial-name {
  font-size: 0.95rem;
  font-weight: 600;
  color: var(--color-text, #1e293b);
}

.sp-testimonial-role {
  font-size: 0.8rem;
  color: var(--color-text-muted, #64748b);
}

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

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

---

**Stats counter strip.** Animated numbers counting up with context labels.

*Animation & Visual Details:*
- *Entrance*: Stats slide in from bottom with stagger (0.1s per stat)
- *Hover*: Number has gradient text effect intensifies
- *Gradients*: Numbers have gradient fill (accent colors)
- *Radial circles*: Radial glow behind each stat
- *Scroll trigger*: IntersectionObserver at 25% threshold
- *JS interaction*: Count-up animation triggered on viewport entry

```html
<section data-amb-section="social-proof" class="sp-section sp-section--stats">
  <div class="container">
    <div class="sp-stats">
      <div class="sp-stat">
        <div class="sp-stat-glow"></div>
        <span class="sp-stat-value" data-target="14300">0</span>
        <span class="sp-stat-suffix">+</span>
        <span class="sp-stat-label">Teams worldwide</span>
      </div>
      <div class="sp-stat">
        <div class="sp-stat-glow"></div>
        <span class="sp-stat-value" data-target="98">0</span>
        <span class="sp-stat-suffix">%</span>
        <span class="sp-stat-label">Customer satisfaction</span>
      </div>
      <div class="sp-stat">
        <div class="sp-stat-glow"></div>
        <span class="sp-stat-value" data-target="4">0</span>
        <span class="sp-stat-suffix">.9</span>
        <span class="sp-stat-label">Average rating</span>
      </div>
      <div class="sp-stat">
        <div class="sp-stat-glow"></div>
        <span class="sp-stat-value" data-target="50">0</span>
        <span class="sp-stat-suffix">M</span>
        <span class="sp-stat-label">Deployments this month</span>
      </div>
    </div>
  </div>
</section>
```

```css
.sp-section--stats {
  background: linear-gradient(135deg, rgba(99,102,241,0.05), rgba(124,58,237,0.02));
  padding: clamp(64px, 8vw, 100px) 0;
}

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

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

@media (max-width: 480px) {
  .sp-stats { grid-template-columns: 1fr; }
}

.sp-stat {
  text-align: center;
  position: relative;
  opacity: 0;
  transform: translateY(30px);
}

.sp-stat.is-visible {
  animation: sp-stat-enter 0.5s ease-out forwards;
}

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

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

.sp-stat-glow {
  position: absolute;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
  width: 100px;
  height: 100px;
  background: radial-gradient(circle, rgba(99,102,241,0.1) 0%, transparent 70%);
  border-radius: 50%;
  pointer-events: none;
}

.sp-stat-value {
  display: block;
  font-size: clamp(2rem, 4vw, 3rem);
  font-weight: 700;
  background: linear-gradient(135deg, var(--color-accent, #6366f1), #7c3aed);
  -webkit-background-clip: text;
  -webkit-text-fill-color: transparent;
  background-clip: text;
  line-height: 1;
}

.sp-stat.is-counting .sp-stat-value {
  transition: transform 0.2s ease;
}

.sp-stat.is-counting .sp-stat-value::after {
  content: '';
  position: absolute;
  inset: -8px;
  background: radial-gradient(circle, rgba(99,102,241,0.15) 0%, transparent 70%);
  border-radius: 50%;
  z-index: -1;
  animation: stat-pulse 1s ease-in-out infinite;
}

@keyframes stat-pulse {
  0%, 100% { transform: scale(1); opacity: 0.5; }
  50% { transform: scale(1.1); opacity: 1; }
}

.sp-stat-suffix {
  font-size: clamp(1.5rem, 3vw, 2rem);
  font-weight: 700;
  background: linear-gradient(135deg, var(--color-accent, #6366f1), #7c3aed);
  -webkit-background-clip: text;
  -webkit-text-fill-color: transparent;
  background-clip: text;
}

.sp-stat-label {
  display: block;
  font-size: 0.9rem;
  color: var(--color-text-muted, #64748b);
  margin-top: 8px;
}

@media (prefers-reduced-motion: reduce) {
  .sp-stat {
    opacity: 1;
    transform: none;
  }
  .sp-stat.is-counting .sp-stat-value::after {
    animation: none;
  }
}
```

*JS behavior:*
```javascript
const stats = document.querySelectorAll('.sp-stat');
const statValues = document.querySelectorAll('.sp-stat-value');

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

stats.forEach(stat => observer.observe(stat));

const countObserver = new IntersectionObserver((entries) => {
  entries.forEach(entry => {
    if (entry.isIntersecting) {
      const el = entry.target;
      const target = parseInt(el.dataset.target);
      const duration = 2000;
      const start = performance.now();
      const stat = el.closest('.sp-stat');

      stat.classList.add('is-counting');

      const easeOutQuart = t => 1 - Math.pow(1 - t, 4);

      const animate = (now) => {
        const elapsed = now - start;
        const progress = Math.min(elapsed / duration, 1);
        const eased = easeOutQuart(progress);
        el.textContent = Math.round(eased * target);

        if (progress < 1) {
          requestAnimationFrame(animate);
        } else {
          el.textContent = target;
          stat.classList.remove('is-counting');
        }
      };

      requestAnimationFrame(animate);
      countObserver.unobserve(el);
    }
  });
}, { threshold: 0.5 });

statValues.forEach(val => countObserver.observe(val));
```

---

**Press / awards strip.** Media mentions with recognizable logos and rankings.

*Animation & Visual Details:*
- *Entrance*: Logos fade in with stagger (0.06s between)
- *Hover*: Logo has gradient border, badge pulses
- *Gradients*: Featured badge has gradient background (accent at 80%)
- *Radial circles*: Radial ring on #1 badge
- *Scroll trigger*: IntersectionObserver at 20% threshold
- *JS interaction*: None beyond entrance

```html
<section data-amb-section="social-proof" class="sp-section sp-section--press">
  <div class="container">
    <p class="sp-press-label">As featured in</p>
    <div class="sp-press-logos">
      <div class="sp-press-item">
        <div class="sp-press-logo">
          <svg viewBox="0 0 100 40" fill="currentColor"><text x="10" y="28" font-size="16" font-weight="bold">TechCrunch</text></svg>
        </div>
        <span class="sp-press-badge">
          <span class="sp-press-badge-dot"></span>
          #1 Product of the Day
        </span>
      </div>
      <div class="sp-press-item">
        <div class="sp-press-logo">
          <svg viewBox="0 0 100 40" fill="currentColor"><text x="10" y="28" font-size="16" font-weight="bold">Product Hunt</text></svg>
        </div>
        <span class="sp-press-badge sp-press-badge--featured">
          <span class="sp-press-badge-dot"></span>
          Featured
        </span>
      </div>
      <div class="sp-press-item">
        <div class="sp-press-logo">
          <svg viewBox="0 0 100 40" fill="currentColor"><text x="10" y="28" font-size="16" font-weight="bold">Wired</text></svg>
        </div>
      </div>
      <div class="sp-press-item">
        <div class="sp-press-logo">
          <svg viewBox="0 0 100 40" fill="currentColor"><text x="10" y="28" font-size="16" font-weight="bold">Forbes</text></svg>
        </div>
      </div>
    </div>
  </div>
</section>
```

```css
.sp-section--press {
  background: #fff;
  padding: clamp(48px, 6vw, 72px) 0;
}

.sp-press-label {
  text-align: center;
  font-size: 0.8rem;
  font-weight: 500;
  color: var(--color-text-muted, #64748b);
  text-transform: uppercase;
  letter-spacing: 0.1em;
  margin-bottom: 32px;
}

.sp-press-logos {
  display: flex;
  justify-content: center;
  align-items: center;
  flex-wrap: wrap;
  gap: 32px;
  max-width: 900px;
  margin: 0 auto;
}

.sp-press-item {
  position: relative;
  opacity: 0;
  transform: translateY(15px);
}

.sp-press-item.is-visible {
  animation: sp-press-enter 0.4s ease-out forwards;
}

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

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

.sp-press-logo {
  padding: 16px 24px;
  border-radius: 12px;
  border: 1px solid #e2e8f0;
  transition: border-color 0.2s ease, box-shadow 0.2s ease;
}

.sp-press-item:hover .sp-press-logo {
  border-color: rgba(99,102,241,0.3);
  box-shadow: 0 4px 16px rgba(0,0,0,0.04);
}

.sp-press-logo svg {
  height: 24px;
  width: auto;
  color: var(--color-text, #1e293b);
}

.sp-press-badge {
  position: absolute;
  top: -8px;
  right: -8px;
  display: flex;
  align-items: center;
  gap: 4px;
  padding: 4px 10px;
  background: #fff;
  border: 1px solid #e2e8f0;
  border-radius: 12px;
  font-size: 0.65rem;
  font-weight: 600;
  color: var(--color-text-muted, #64748b);
  white-space: nowrap;
}

.sp-press-badge--featured {
  background: linear-gradient(135deg, var(--color-accent, #6366f1), #7c3aed);
  color: #fff;
  border-color: transparent;
}

.sp-press-badge-dot {
  width: 6px;
  height: 6px;
  background: radial-gradient(circle, var(--color-accent, #6366f1) 0%, rgba(99,102,241,0.4) 100%);
  border-radius: 50%;
}

.sp-press-badge--featured .sp-press-badge-dot {
  background: #fff;
  animation: press-pulse 2s ease-in-out infinite;
}

@keyframes press-pulse {
  0%, 100% { transform: scale(1); }
  50% { transform: scale(1.3); }
}

@media (prefers-reduced-motion: reduce) {
  .sp-press-item {
    opacity: 1;
    transform: none;
  }
  .sp-press-badge--featured .sp-press-badge-dot {
    animation: none;
  }
}
```

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

---

Copy rules: use only press sources your target audience will recognize. Name the person, their role, and company in testimonials. Specific outcomes beat generic praise. Compounding rule: strongest pages use 2–3 forms in sequence (logos under hero, named quote after features, star rating near pricing).

Anti-patterns: stock-photo headshots, unattributed quotes ("— Happy customer"), self-referential praise ("our amazing customers"), broken trust signals ("as seen in" logos you never appeared in).

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