# Agent Requests Feature

> A feature section where four example requests drive one Agent Session. The picked request plays in a terminal beside the screen it changes: an alert rule and its Slack message, a retention table filling cohort by cohort, revenue split by plan, events tagged one by one. A line under the row fills as each change lands, and the next request plays on its own until the visitor picks one.

A Feature Section block from KeyframeUI: a whole landing-page section built around the Agent Session component, free to install as source with the shadcn CLI. Built with React, Tailwind CSS and Motion.

- Live preview: https://keyframeui.com/blocks/feature-sections/agent-requests-feature
- Category: Feature Sections
- Built with: [Agent Session](https://keyframeui.com/components/agent-session.md)
- npm dependencies: motion, clsx, tailwind-merge

## Installation

Register the @keyframeui registry in `components.json` once per project:

```json
{
  "registries": {
    "@keyframeui": "https://keyframeui.com/r/{name}.json"
  }
}
```

Then add the block. It lands in `components/agent-requests-feature.tsx`, and the CLI also adds the components it's built with (`components/ui/agent-session.tsx`) and `lib/utils.ts`, and installs the npm dependencies:

```bash
npx shadcn@latest add @keyframeui/agent-requests-feature
```

Or install by URL, without registering the namespace:

```bash
npx shadcn@latest add https://keyframeui.com/r/agent-requests-feature.json
```

## Usage

Put it where the section goes. The copy, links and data sit in constants at the top of the file; edit them there.

```tsx
import { AgentRequestsFeature } from "@/components/agent-requests-feature";

export default function Page() {
  return <AgentRequestsFeature />;
}
```

## Source

```tsx title="components/agent-requests-feature.tsx"
"use client";

import * as React from "react";
import { motion, useReducedMotion } from "motion/react";

import { cn } from "@/lib/utils";
import {
  AgentSession,
  AgentSessionTarget,
  useAgentSessionEffect,
  type AgentSessionStep,
} from "@/components/ui/agent-session";

/* ── Content ─────────────────────────────────────────────────
 * Everything you'd change to make the section yours: the copy,
 * the requests a visitor can pick, and the script the agent runs
 * for each. The app the agent changes is plain markup further
 * down; its numbers are here.
 * ─────────────────────────────────────────────────────────── */

/** The one accent: progress lines, the glow and rings in the session, and the app's highlights. */
const ACCENT = { light: "#ea580c", dark: "#fb923c" };

const BRAND = "Tern";
const HEADLINE = "Ask for what you need.";
const HEADLINE_QUIET = "Tern does the wiring.";
const SUBLINE =
  "Describe the change in one sentence. Your coding agent finds the events, writes the tracking code and puts the chart where you asked, as plain files in your repo.";
const FOOTNOTE = "Every change is a file you read in review and undo with git.";
const TERMINAL_TITLE = "web — agent";

/** How long a finished request rests before the next one plays, until the visitor picks one. */
const ADVANCE_MS = 2600;

type Screen = "alerts" | "retention" | "revenue" | "events";
type Icon = "bell" | "hash" | "grid" | "bars" | "tag" | "clock";

const REQUESTS: { id: Screen; label: string; icons: Icon[]; url: string; script: AgentSessionStep[] }[] = [
  {
    id: "alerts",
    label: "Ping #payments when checkout errors spike",
    icons: ["bell", "hash"],
    url: "localhost:3000/alerts",
    script: [
      { prompt: "Ping #payments in Slack when checkout errors spike" },
      {
        run: "npx tern events list --match checkout",
        ms: 800,
        output: ["checkout_started    checkout_failed    checkout_completed"],
      },
      {
        run: 'npx tern alert add checkout_failed --above 2% --window 10m --slack "#payments"',
        ms: 1200,
        pace: 380,
        output: [
          { text: "+ tern/alerts/checkout-failed.yml   2% over 10 min", tone: "added", effect: "rule" },
          { text: "✓ Slack connected · #payments", tone: "success", effect: "slack" },
        ],
      },
      {
        run: "npx tern alert test checkout-failed",
        ms: 900,
        output: [{ text: "✓ Test alert sent to #payments", tone: "success", effect: "toast" }],
      },
      { say: "Done. If more than 2% of checkouts fail within ten minutes, #payments hears about it." },
    ],
  },
  {
    id: "retention",
    label: "Add a weekly retention table",
    icons: ["grid", "clock"],
    url: "localhost:3000/retention",
    script: [
      { prompt: "Add a weekly retention table for the last four cohorts" },
      {
        run: "npx tern add retention --cohort weekly --weeks 4",
        ms: 1000,
        output: [
          { text: "+ app/retention/cohort-table.tsx   CohortTable", tone: "added", effect: "table" },
          "~ app/retention/page.tsx           +1 section",
        ],
      },
      {
        run: "npx tern backfill retention --since 2026-09-28",
        ms: 1300,
        pace: 340,
        output: [
          { text: "Week of Sep 28   1,912 people", effect: "w0" },
          { text: "Week of Oct 5    2,104 people", effect: "w1" },
          { text: "Week of Oct 12   2,388 people", effect: "w2" },
          { text: "Week of Oct 19   2,241 people", effect: "w3" },
        ],
      },
      { say: "Retention is climbing: 41% of the Oct 12 cohort came back in week one, up from 38% in September." },
    ],
  },
  {
    id: "revenue",
    label: "Break revenue down by plan",
    icons: ["bars"],
    url: "localhost:3000/revenue",
    script: [
      { prompt: "Break revenue down by plan, week by week" },
      {
        run: 'npx tern query "sum(amount) by plan, week" --last 8w',
        ms: 1100,
        output: [
          { text: "plan       this week   vs 8w ago", tone: "muted" },
          "Pro        $18,420     +31%",
          "Team       $9,870      +9%",
          "Starter    $2,310      −4%",
        ],
      },
      {
        run: "npx tern add chart revenue-by-plan --type stacked",
        ms: 1000,
        output: [{ text: "+ app/revenue/by-plan.tsx   RevenueByPlan", tone: "added", effect: "chart" }],
      },
      { say: "Pro is 60% of revenue now, up from 52% eight weeks ago. Starter is flat." },
    ],
  },
  {
    id: "events",
    label: "Tag every event with the customer's plan",
    icons: ["tag"],
    url: "localhost:3000/events",
    script: [
      { prompt: "Tag every event with the customer's plan" },
      {
        run: 'npx tern properties add plan --from "user.subscription.plan"',
        ms: 900,
        output: [{ text: "+ tern/properties/plan.ts   reads user.subscription.plan", tone: "added" }],
      },
      {
        run: "npx tern backfill --property plan --last 24h",
        ms: 1200,
        pace: 320,
        output: [
          { text: "signup_completed    412 events", effect: "e0" },
          { text: "checkout_started    1,036 events", effect: "e1" },
          { text: "page_viewed         18,920 events", effect: "e2" },
          { text: "invite_sent         96 events", effect: "e3" },
        ],
      },
      { say: "Every event carries plan now, the last 24 hours included, so any chart can split by it." },
    ],
  },
];

/* The app the agent changes. */

const NAV: { label: string; screen?: Screen }[] = [
  { label: "Overview" },
  { label: "Events", screen: "events" },
  { label: "Retention", screen: "retention" },
  { label: "Revenue", screen: "revenue" },
  { label: "Alerts", screen: "alerts" },
];

const COHORTS = [
  { effect: "w0", week: "Sep 28", people: "1,912", rates: [100, 38, 31, 27] },
  { effect: "w1", week: "Oct 5", people: "2,104", rates: [100, 40, 33] },
  { effect: "w2", week: "Oct 12", people: "2,388", rates: [100, 41] },
  { effect: "w3", week: "Oct 19", people: "2,241", rates: [100] },
];

/** Revenue per week in $k: Pro, Team, Starter. */
const REVENUE = [
  [10.2, 7.9, 2.4],
  [11.0, 8.1, 2.5],
  [11.8, 8.4, 2.3],
  [12.9, 8.6, 2.4],
  [14.1, 9.0, 2.4],
  [15.6, 9.2, 2.3],
  [16.9, 9.5, 2.3],
  [18.4, 9.9, 2.3],
];

const EVENTS = [
  { effect: "e0", name: "signup_completed", who: "maya@kiln.studio", ago: "2s", plan: "Pro" },
  { effect: "e1", name: "checkout_started", who: "theo@ferngrove.co", ago: "6s", plan: "Team" },
  { effect: "e2", name: "page_viewed", who: "/pricing", ago: "9s", plan: "Starter" },
  { effect: "e3", name: "invite_sent", who: "ana@lumenhaus.io", ago: "14s", plan: "Pro" },
];

/* ── Section ─────────────────────────────────────────────── */

const focusRing =
  "outline-none focus-visible:ring-2 focus-visible:ring-(--accent) focus-visible:ring-offset-2 focus-visible:ring-offset-white dark:focus-visible:ring-offset-zinc-950";

const effectsOf = (script: AgentSessionStep[]) => {
  const names = new Set<string>();
  for (const step of script) {
    if ("say" in step && step.effect) names.add(step.effect);
    if ("output" in step)
      for (const line of step.output ?? []) if (typeof line !== "string" && line.effect) names.add(line.effect);
  }
  return names.size;
};

export function AgentRequestsFeature() {
  const reduced = useReducedMotion();
  const [active, setActive] = React.useState(0);
  const [landed, setLanded] = React.useState(0);
  const [done, setDone] = React.useState(false);
  // Auto-advance until the visitor picks a request themselves.
  const [touring, setTouring] = React.useState(true);
  const timer = React.useRef<number>(0);
  const tabs = React.useRef<(HTMLButtonElement | null)[]>([]);
  const id = React.useId();

  React.useEffect(() => () => window.clearTimeout(timer.current), []);

  const show = (index: number) => {
    window.clearTimeout(timer.current);
    setActive(index);
    setLanded(0);
    setDone(false);
  };

  const pick = (index: number) => {
    setTouring(false);
    show(index);
  };

  const onKeyDown = (event: React.KeyboardEvent) => {
    const last = REQUESTS.length - 1;
    const next =
      event.key === "ArrowRight" || event.key === "ArrowDown"
        ? active === last ? 0 : active + 1
        : event.key === "ArrowLeft" || event.key === "ArrowUp"
          ? active === 0 ? last : active - 1
          : event.key === "Home"
            ? 0
            : event.key === "End"
              ? last
              : null;
    if (next == null) return;
    event.preventDefault();
    pick(next);
    tabs.current[next]?.focus();
  };

  const request = REQUESTS[active];
  const changes = effectsOf(request.script);

  return (
    <section
      style={{ "--accent-light": ACCENT.light, "--accent-dark": ACCENT.dark } as React.CSSProperties}
      className="bg-white text-zinc-950 [--accent:var(--accent-light)] dark:bg-zinc-950 dark:text-white dark:[--accent:var(--accent-dark)]"
    >
      <div className="mx-auto max-w-6xl px-6 py-24 sm:py-32">
        <div className="grid gap-x-10 gap-y-5 lg:grid-cols-12 lg:items-end">
          <h2 className="text-4xl/[1.05] font-semibold tracking-[-0.035em] text-balance sm:text-5xl/[1.02] lg:col-span-7">
            {HEADLINE} <span className="block text-zinc-400 dark:text-zinc-500">{HEADLINE_QUIET}</span>
          </h2>
          <p className="max-w-md text-base/7 text-pretty text-zinc-600 lg:col-span-5 lg:justify-self-end dark:text-zinc-400">
            {SUBLINE}
          </p>
        </div>

        <div
          role="tablist"
          aria-label={`Requests to ${BRAND}`}
          onKeyDown={onKeyDown}
          className="mt-12 grid border-t border-zinc-950/10 sm:grid-cols-2 sm:gap-x-10 dark:border-white/10"
        >
          {REQUESTS.map((item, index) => {
            const selected = index === active;
            return (
              <button
                key={item.id}
                ref={(el) => {
                  tabs.current[index] = el;
                }}
                type="button"
                role="tab"
                id={`${id}-tab-${index}`}
                aria-selected={selected}
                aria-controls={`${id}-panel`}
                tabIndex={selected ? 0 : -1}
                onClick={() => pick(index)}
                className={cn(
                  "group relative flex h-14 min-w-0 items-center justify-between gap-4 border-b border-zinc-950/10 text-left text-sm transition-colors dark:border-white/10",
                  focusRing,
                  selected
                    ? "text-zinc-950 dark:text-white"
                    : "text-zinc-500 hover:text-zinc-950 dark:text-zinc-400 dark:hover:text-white",
                )}
              >
                <span className="min-w-0 truncate font-medium">{item.label}</span>
                <span className="flex shrink-0 items-center gap-2 text-zinc-400 dark:text-zinc-500">
                  {selected && (
                    <span className="text-xs text-zinc-500 tabular-nums dark:text-zinc-400">
                      {done ? <CheckIcon className="size-3.5 text-(--accent)" /> : `${landed}/${changes}`}
                    </span>
                  )}
                  {item.icons.map((icon) => (
                    <RowIcon key={icon} icon={icon} className="size-3.5" />
                  ))}
                </span>
                {/* Fills as each of this request's changes lands in the app. */}
                {selected && (
                  <motion.span
                    aria-hidden
                    initial={{ transform: "scaleX(0)" }}
                    animate={{ transform: `scaleX(${done ? 1 : landed / Math.max(1, changes)})` }}
                    transition={reduced ? { duration: 0 } : { type: "spring", visualDuration: 0.45, bounce: 0 }}
                    className="absolute inset-x-0 -bottom-px h-px origin-left bg-(--accent)"
                  />
                )}
              </button>
            );
          })}
        </div>

        <div id={`${id}-panel`} role="tabpanel" aria-labelledby={`${id}-tab-${active}`} className="mt-8">
          <AgentSession
            script={request.script}
            title={TERMINAL_TITLE}
            url={request.url}
            onEffect={() => setLanded((count) => count + 1)}
            onComplete={() => {
              setDone(true);
              if (!touring) return;
              window.clearTimeout(timer.current);
              timer.current = window.setTimeout(() => show((active + 1) % REQUESTS.length), ADVANCE_MS);
            }}
            className="[--agent-session-accent:var(--accent)] [--agent-session-height:21rem] [--agent-session-preview-height:19rem] sm:[--agent-session-height:23rem] dark:[--agent-session-accent:var(--accent)]"
          >
            <TernApp screen={request.id} />
          </AgentSession>
          <p className="mt-4 text-center text-sm text-zinc-500">{FOOTNOTE}</p>
        </div>
      </div>
    </section>
  );
}

/* ── The app ─────────────────────────────────────────────── */

function TernApp({ screen }: { screen: Screen }) {
  return (
    <div className="@container/app flex h-full text-zinc-900 dark:text-zinc-100">
      <aside className="hidden w-32 shrink-0 flex-col gap-0.5 border-r border-zinc-950/[0.06] bg-zinc-50/70 p-2.5 @[26rem]/app:flex dark:border-white/[0.06] dark:bg-white/[0.02]">
        <div className="mb-3 flex items-center gap-1.5 px-1.5 pt-1 text-[13px] font-semibold tracking-tight">
          <TernMark className="size-3.5 text-(--accent)" />
          {BRAND}
        </div>
        {NAV.map((item) => (
          <div
            key={item.label}
            className={cn(
              "rounded-md px-2 py-1.5 text-xs",
              item.screen === screen
                ? "bg-white font-medium shadow-sm ring-1 ring-zinc-950/5 dark:bg-white/10 dark:ring-white/5"
                : "text-zinc-500 dark:text-zinc-400",
            )}
          >
            {item.label}
          </div>
        ))}
      </aside>
      <div className="relative min-w-0 flex-1 p-4">
        {screen === "alerts" && <AlertsScreen />}
        {screen === "retention" && <RetentionScreen />}
        {screen === "revenue" && <RevenueScreen />}
        {screen === "events" && <EventsScreen />}
      </div>
    </div>
  );
}

function ScreenTitle({ title, aside }: { title: string; aside?: React.ReactNode }) {
  return (
    <div className="mb-3 flex items-baseline justify-between gap-3">
      <h3 className="text-sm font-semibold tracking-tight">{title}</h3>
      {aside && <span className="truncate text-[11px] text-zinc-400">{aside}</span>}
    </div>
  );
}

const card =
  "rounded-xl bg-white ring-1 ring-zinc-950/[0.08] shadow-sm dark:bg-white/[0.04] dark:ring-white/10";

function AlertsScreen() {
  return (
    <>
      <ScreenTitle title="Alerts" aside="2 rules" />
      <div className={cn(card, "divide-y divide-zinc-950/[0.06] dark:divide-white/[0.06]")}>
        <AlertRow name="Signup drop" rule="−30% in 1 hour" to="Email" />
        <AgentSessionTarget
          effect="rule"
          className="[--agent-session-ring-radius:10px]"
          fallback={<p className="px-3 py-2.5 text-[11px] text-zinc-400">Nothing watches checkout yet</p>}
        >
          <AlertRow name="Checkout failures" rule="> 2% in 10 min" to="#payments" fresh />
        </AgentSessionTarget>
      </div>

      <div className="mt-3 flex items-center justify-between rounded-xl px-3 py-2.5 ring-1 ring-zinc-950/[0.08] dark:ring-white/10">
        <span className="text-xs font-medium">Slack</span>
        <AgentSessionTarget
          effect="slack"
          className="justify-items-end [--agent-session-ring-radius:999px]"
          fallback={<span className="text-[11px] text-zinc-400">Not connected</span>}
        >
          <span className="flex items-center gap-1.5 text-[11px] text-zinc-600 dark:text-zinc-300">
            <span className="size-1.5 rounded-full bg-emerald-500" />
            #payments
          </span>
        </AgentSessionTarget>
      </div>

      {/* The test alert, as it arrives in Slack. */}
      <AgentSessionTarget
        effect="toast"
        className="absolute right-4 bottom-4 left-4 [--agent-session-ring-radius:14px] @[26rem]/app:left-auto @[26rem]/app:w-64"
      >
        <div className={cn(card, "flex gap-2.5 p-3")}>
          <span className="grid size-7 shrink-0 place-items-center rounded-lg bg-zinc-950 text-white dark:bg-white dark:text-zinc-950">
            <TernMark className="size-3.5" />
          </span>
          <div className="min-w-0">
            <p className="text-[11px] font-semibold">
              {BRAND} <span className="font-normal text-zinc-400">in #payments</span>
            </p>
            <p className="mt-0.5 text-[11px]/4 text-zinc-600 dark:text-zinc-300">
              Test: checkout_failed at 2.4% over the last 10 minutes.
            </p>
          </div>
        </div>
      </AgentSessionTarget>
    </>
  );
}

function AlertRow({ name, rule, to, fresh = false }: { name: string; rule: string; to: string; fresh?: boolean }) {
  return (
    <div className="flex items-center gap-2.5 px-3 py-2.5">
      <span className={cn("size-1.5 shrink-0 rounded-full", fresh ? "bg-(--accent)" : "bg-zinc-300 dark:bg-zinc-600")} />
      <span className="min-w-0 flex-1 truncate text-xs font-medium">{name}</span>
      <span className="text-[11px] text-zinc-500 tabular-nums dark:text-zinc-400">{rule}</span>
      <span className="hidden rounded-md bg-zinc-100 px-1.5 py-0.5 text-[10px] text-zinc-600 @[22rem]/app:inline dark:bg-white/[0.06] dark:text-zinc-300">
        {to}
      </span>
    </div>
  );
}

function RetentionScreen() {
  return (
    <>
      <ScreenTitle title="Retention" aside="Weekly cohorts" />
      <AgentSessionTarget
        effect="table"
        className="[--agent-session-ring-radius:14px]"
        fallback={
          <div className="flex h-full min-h-40 flex-col items-center justify-center gap-1 rounded-xl border border-dashed border-zinc-950/15 text-center dark:border-white/15">
            <p className="text-xs font-medium">No retention view yet</p>
            <p className="text-[11px] text-zinc-400">Cohorts appear once events are tracked.</p>
          </div>
        }
      >
        <div className={cn(card, "p-3")}>
          <div className="grid grid-cols-[minmax(0,1fr)_repeat(4,2.25rem)] gap-1 text-[10px] text-zinc-400">
            <span>Cohort</span>
            {["W0", "W1", "W2", "W3"].map((week) => (
              <span key={week} className="text-center">
                {week}
              </span>
            ))}
          </div>
          <div className="mt-1.5 flex flex-col gap-1">
            {COHORTS.map((cohort) => (
              <CohortRow key={cohort.effect} {...cohort} />
            ))}
          </div>
        </div>
      </AgentSessionTarget>
    </>
  );
}

function CohortRow({ effect, week, people, rates }: (typeof COHORTS)[number]) {
  const landed = useAgentSessionEffect(effect);
  const reduced = useReducedMotion();
  return (
    <AgentSessionTarget effect={effect} enter="none" className="[--agent-session-ring-radius:8px]">
      <div className="grid grid-cols-[minmax(0,1fr)_repeat(4,2.25rem)] items-center gap-1">
        <span className="min-w-0 truncate text-[11px]">
          {week} <span className="text-zinc-400 tabular-nums">{people}</span>
        </span>
        {[0, 1, 2, 3].map((i) => {
          const rate = rates[i];
          return (
            <span
              key={i}
              className="relative grid h-6 place-items-center overflow-hidden rounded-md bg-zinc-100 text-[10px] tabular-nums dark:bg-white/[0.05]"
            >
              {rate != null && (
                <>
                  <motion.span
                    aria-hidden
                    initial={false}
                    animate={{ opacity: landed ? 0.15 + (rate / 100) * 0.85 : 0 }}
                    transition={reduced ? { duration: 0 } : { duration: 0.35, delay: landed ? i * 0.06 : 0 }}
                    className="absolute inset-0 bg-(--accent)"
                  />
                  <motion.span
                    initial={false}
                    animate={{ opacity: landed ? 1 : 0 }}
                    transition={reduced ? { duration: 0 } : { duration: 0.25, delay: landed ? i * 0.06 : 0 }}
                    className={cn("relative font-medium", rate > 60 ? "text-white dark:text-zinc-950" : "")}
                  >
                    {rate}%
                  </motion.span>
                </>
              )}
            </span>
          );
        })}
      </div>
    </AgentSessionTarget>
  );
}

function RevenueScreen() {
  const max = Math.max(...REVENUE.map((week) => week.reduce((a, b) => a + b, 0)));
  return (
    <>
      <ScreenTitle title="Revenue" aside="$30,600 this week" />
      <div className="mb-3 grid grid-cols-2 gap-2">
        <Stat label="This week" value="$30.6k" delta="+8.4%" />
        <Stat label="Paying teams" value="1,148" delta="+3.0%" />
      </div>
      <AgentSessionTarget
        effect="chart"
        className="[--agent-session-ring-radius:14px]"
        fallback={
          <div className={cn(card, "flex h-full min-h-36 items-center justify-center text-[11px] text-zinc-400")}>
            Revenue isn&apos;t split by plan yet
          </div>
        }
      >
        {(landed) => (
          <div className={cn(card, "p-3")}>
            <div className="flex h-24 items-end gap-1.5">
              {REVENUE.map((week, i) => (
                <StackedBar key={i} week={week} max={max} grow={landed} index={i} />
              ))}
            </div>
            <div className="mt-2.5 flex gap-3 text-[10px] text-zinc-500 dark:text-zinc-400">
              <Legend className="bg-(--accent)" label="Pro" />
              <Legend className="bg-zinc-800 dark:bg-zinc-300" label="Team" />
              <Legend className="bg-zinc-300 dark:bg-zinc-600" label="Starter" />
            </div>
          </div>
        )}
      </AgentSessionTarget>
    </>
  );
}

function StackedBar({ week, max, grow, index }: { week: number[]; max: number; grow: boolean; index: number }) {
  const reduced = useReducedMotion();
  const total = week.reduce((a, b) => a + b, 0);
  const tones = ["bg-(--accent)", "bg-zinc-800 dark:bg-zinc-300", "bg-zinc-300 dark:bg-zinc-600"];
  return (
    <motion.div
      initial={false}
      animate={{ transform: `scaleY(${grow ? 1 : 0.04})` }}
      transition={reduced ? { duration: 0 } : { type: "spring", visualDuration: 0.5, bounce: 0.08, delay: grow ? index * 0.04 : 0 }}
      className="flex min-w-0 flex-1 origin-bottom flex-col-reverse overflow-hidden rounded-[3px]"
      style={{ height: `${(total / max) * 100}%` }}
    >
      {week.map((value, i) => (
        <span key={i} className={tones[i]} style={{ height: `${(value / total) * 100}%` }} />
      ))}
    </motion.div>
  );
}

function EventsScreen() {
  return (
    <>
      <ScreenTitle
        title="Live events"
        aside={
          <span className="flex items-center gap-1.5">
            <span className="size-1.5 rounded-full bg-emerald-500" />
            Streaming
          </span>
        }
      />
      <div className={cn(card, "divide-y divide-zinc-950/[0.06] dark:divide-white/[0.06]")}>
        {EVENTS.map((event) => (
          <div key={event.effect} className="flex items-center gap-2.5 px-3 py-2.5">
            <span className="min-w-0 flex-1">
              <span className="block truncate font-mono text-[11px] font-medium">{event.name}</span>
              <span className="block truncate text-[10px] text-zinc-400">
                {event.who} · {event.ago} ago
              </span>
            </span>
            <AgentSessionTarget
              effect={event.effect}
              className="justify-items-end [--agent-session-ring-radius:999px]"
              fallback={<span className="text-[10px] text-zinc-300 dark:text-zinc-600">no plan</span>}
            >
              <span className="rounded-full bg-zinc-100 px-2 py-0.5 text-[10px] font-medium text-zinc-700 ring-1 ring-zinc-950/5 dark:bg-white/[0.06] dark:text-zinc-200 dark:ring-white/10">
                plan: <span className="text-(--accent)">{event.plan}</span>
              </span>
            </AgentSessionTarget>
          </div>
        ))}
      </div>
    </>
  );
}

function Stat({ label, value, delta }: { label: string; value: string; delta: string }) {
  return (
    <div className={cn(card, "p-2.5 shadow-none")}>
      <p className="text-[11px] text-zinc-500 dark:text-zinc-400">{label}</p>
      <p className="mt-0.5 flex items-baseline gap-1.5">
        <span className="text-base font-semibold tracking-tight tabular-nums">{value}</span>
        <span className="text-[10px] font-medium text-emerald-600 dark:text-emerald-400">{delta}</span>
      </p>
    </div>
  );
}

function Legend({ className, label }: { className: string; label: string }) {
  return (
    <span className="flex items-center gap-1.5">
      <span className={cn("size-2 rounded-[2px]", className)} />
      {label}
    </span>
  );
}

/* ── Icons ───────────────────────────────────────────────── */

function TernMark({ className }: { className?: string }) {
  return (
    <svg viewBox="0 0 24 24" className={className} aria-hidden>
      <path d="M1 7.5c4.2-.9 8 1.2 11 5 3-3.8 6.8-5.9 11-5-4.4 1.4-8.1 5-11 10.5C9.1 12.5 5.4 8.9 1 7.5Z" fill="currentColor" />
    </svg>
  );
}

function RowIcon({ icon, className }: { icon: Icon; className?: string }) {
  const paths: Record<Icon, React.ReactNode> = {
    bell: <path d="M4 11.5h8l-1-1.5V7a3 3 0 0 0-6 0v3l-1 1.5ZM6.8 13.5h2.4" />,
    hash: <path d="M6 2.5 5 13.5M11 2.5l-1 11M2.5 6h11M2 10h11" />,
    grid: <path d="M2.5 2.5h11v11h-11zM2.5 6.2h11M2.5 9.8h11M6.2 2.5v11M9.8 2.5v11" />,
    clock: <path d="M8 2.5a5.5 5.5 0 1 0 0 11 5.5 5.5 0 0 0 0-11ZM8 5v3l2 1.5" />,
    bars: <path d="M3 13.5v-4M6.3 13.5V6M9.7 13.5V8M13 13.5V3" />,
    tag: <path d="M2.5 2.5h5l6 6-5 5-6-6v-5ZM5.3 5.3h.01" />,
  };
  return (
    <svg
      viewBox="0 0 16 16"
      className={className}
      fill="none"
      stroke="currentColor"
      strokeWidth={1.4}
      strokeLinecap="round"
      strokeLinejoin="round"
      aria-hidden
    >
      {paths[icon]}
    </svg>
  );
}

function CheckIcon({ className }: { className?: string }) {
  return (
    <svg viewBox="0 0 16 16" className={className} fill="none" stroke="currentColor" strokeWidth={1.8} aria-hidden>
      <path d="m3.5 8.5 3 3 6-7" strokeLinecap="round" strokeLinejoin="round" />
    </svg>
  );
}
```
