<template>
  <div class="trust-flow">
    <div class="endpoint">
      <svg viewBox="0 0 40 40" class="silhouette" aria-hidden="true">
        <circle cx="20" cy="14" r="6" />
        <path d="M 6 40 Q 6 25, 20 25 Q 34 25, 34 40 Z" />
      </svg>
    </div>

    <div class="stream">
      <div
        v-for="p in particles"
        :key="p.id"
        class="particle"
        :class="[p.kind, { in: p.inPlatform }]"
        :style="{ left: `${p.x}%`, top: `${p.y}%` }"
      />
      <div class="platform">
        <div class="platform-label">PLATFORM</div>
        <div class="platform-counter">USERS &middot; {{ userCount.toLocaleString() }} &uarr;</div>
      </div>
    </div>

    <div class="endpoint">
      <svg viewBox="0 0 40 40" class="silhouette" aria-hidden="true">
        <circle cx="20" cy="14" r="6" />
        <path d="M 6 40 Q 6 25, 20 25 Q 34 25, 34 40 Z" />
      </svg>
    </div>
  </div>
</template>

<script setup lang="ts">
import { ref, watch, onBeforeUnmount } from 'vue'
import { useIsSlideActive } from '@slidev/client'

interface Particle {
  id: number
  x: number
  y: number
  speed: number
  kind: 'unverified' | 'human' | 'bot'
  inPlatform: boolean
}

const particles = ref<Particle[]>([])
const userCount = ref(458_231_000)

let nextId = 0
let raf: number | null = null
let lastSpawn = 0
let lastTime = 0

const PLATFORM_LEFT = 38
const PLATFORM_RIGHT = 62

function spawn() {
  particles.value.push({
    id: nextId++,
    x: -3,
    y: 30 + Math.random() * 40,
    speed: 0.28 + Math.random() * 0.18,
    kind: 'unverified',
    inPlatform: false,
  })
}

function tick(now: number) {
  const dt = lastTime ? Math.min((now - lastTime) / 16.6, 2.5) : 1
  lastTime = now

  const list = particles.value
  for (let i = list.length - 1; i >= 0; i--) {
    const p = list[i]
    const wasIn = p.inPlatform
    p.x += p.speed * dt
    p.inPlatform = p.x >= PLATFORM_LEFT && p.x <= PLATFORM_RIGHT
    if (wasIn && !p.inPlatform && p.x > PLATFORM_RIGHT) {
      // Platform assigns a label on the way out — but it's arbitrary,
      // it never actually verified anything.
      p.kind = Math.random() < 0.6 ? 'human' : 'bot'
      userCount.value += 1
    }
    if (p.x > 105) list.splice(i, 1)
  }

  if (now - lastSpawn > 320) {
    spawn()
    lastSpawn = now
  }

  raf = requestAnimationFrame(tick)
}

function teardown() {
  if (raf !== null) { cancelAnimationFrame(raf); raf = null }
  particles.value = []
  userCount.value = 458_231_000
  lastSpawn = 0
  lastTime = 0
  nextId = 0
}

function kickoff() {
  teardown()
  // Pre-seed so the slide doesn't enter empty.
  for (let i = 0; i < 4; i++) {
    spawn()
    particles.value[i].x = i * 22 - 4
  }
  raf = requestAnimationFrame(tick)
}

const isActive = useIsSlideActive()
watch(isActive, (active) => {
  if (active) kickoff()
  else teardown()
}, { immediate: true })

onBeforeUnmount(teardown)
</script>

<style scoped>
.trust-flow {
  display: flex;
  align-items: center;
  gap: 1.4rem;
  margin: 0 auto;
  max-width: 46rem;
  width: 100%;
}
.endpoint {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 3rem;
  flex-shrink: 0;
}
.endpoint .silhouette {
  width: 2.6rem;
  height: 2.6rem;
  fill: rgba(255, 255, 255, 0.42);
}

.stream {
  position: relative;
  flex: 1;
  height: 5.5rem;
  border-top: 1px solid rgba(255, 255, 255, 0.08);
  border-bottom: 1px solid rgba(255, 255, 255, 0.08);
}

.particle {
  position: absolute;
  width: 7px;
  height: 7px;
  border-radius: 50%;
  transform: translate(-50%, -50%);
  transition:
    opacity 0.18s ease,
    background 0.35s ease,
    box-shadow 0.35s ease;
  pointer-events: none;
}
.particle.unverified {
  background: rgba(200, 200, 210, 0.65);
  box-shadow: 0 0 4px rgba(200, 200, 210, 0.3);
}
.particle.human {
  background: #ffffff;
  box-shadow: 0 0 8px rgba(255, 255, 255, 0.65);
}
.particle.bot {
  background: #ff5a5a;
  box-shadow: 0 0 7px rgba(255, 90, 90, 0.6);
}
.particle.in { opacity: 0.22; }

.platform {
  position: absolute;
  left: 38%;
  top: 50%;
  width: 24%;
  min-width: 9rem;
  transform: translate(0, -50%);
  border: 1px solid rgba(255, 255, 255, 0.45);
  background: rgba(255, 255, 255, 0.04);
  border-radius: 4px;
  padding: 0.55rem 0.4rem;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 0.35rem;
  box-shadow: 0 0 18px rgba(255, 255, 255, 0.06);
}
.platform-label {
  font-family: 'Inter', system-ui, sans-serif;
  font-size: 0.55rem;
  font-weight: 500;
  letter-spacing: 0.42em;
  text-transform: uppercase;
  color: rgba(255, 255, 255, 0.88);
}
.platform-counter {
  font-family: 'JetBrains Mono', ui-monospace, monospace;
  font-size: 0.52rem;
  color: rgba(255, 255, 255, 0.55);
  font-variant-numeric: tabular-nums;
  letter-spacing: 0.02em;
}
</style>
