Tawny

Dashboard

Issue list

A 4px grid, 16px icons on the text line, labels and ids on the left, numbers and dates on the right.

components/issue-list
Issues11

Source

Copy the files below into your project.

apps/web/components/writeups/linear-ux-demo.tsx
"use client";

import { useRef, useState, type ReactNode } from "react";
import {
  Bell,
  CircleUser,
  FolderKanban,
  Inbox,
  LayoutGrid,
  Repeat,
  Search,
} from "lucide-react";

import { BoardDashboard } from "@/components/board/dashboard"

/**
 * Cumulative replica of the five decisions in Design Motion's
 * "Reverse-Engineered Linear" walkthrough.
 *
 * Loose rows are 56px, dense rows are 32px. The list viewport is 14 × 32 = 448,
 * which is also 8 × 56, so the first decision reveals six more issues in place.
 */

type Status = "todo" | "progress" | "done";
type Priority = "low" | "medium" | "high";
type Label = "Bug" | "Infra" | "Design" | "Feature";

type Issue = {
  id: string;
  title: string;
  status: Status;
  priority: Priority;
  label: Label;
  assignee: string;
  date: string;
};

const ISSUES: Issue[] = [
  { id: "NOR-140", title: "Fix focus ring on command palette", status: "progress", priority: "high", label: "Bug", assignee: "SC", date: "Sep 1" },
  { id: "NOR-141", title: "Migrate billing webhooks to v2", status: "progress", priority: "high", label: "Infra", assignee: "MK", date: "Sep 1" },
  { id: "NOR-142", title: "Empty state for archived projects", status: "todo", priority: "medium", label: "Design", assignee: "JR", date: "Sep 2" },
  { id: "NOR-143", title: "Keyboard shortcut cheat sheet", status: "todo", priority: "low", label: "Feature", assignee: "AL", date: "Sep 2" },
  { id: "NOR-144", title: "Onboarding checklist drop-off", status: "progress", priority: "medium", label: "Design", assignee: "TP", date: "Sep 3" },
  { id: "NOR-145", title: "Rate limit on search endpoint", status: "todo", priority: "high", label: "Infra", assignee: "MK", date: "Sep 3" },
  { id: "NOR-146", title: "Dark mode contrast on badges", status: "done", priority: "medium", label: "Bug", assignee: "SC", date: "Sep 4" },
  { id: "NOR-147", title: "Bulk edit for labels", status: "todo", priority: "low", label: "Feature", assignee: "AL", date: "Sep 4" },
  { id: "NOR-148", title: "Sidebar collapse animation jank", status: "progress", priority: "medium", label: "Bug", assignee: "JR", date: "Sep 5" },
  { id: "NOR-149", title: "Export issues to CSV", status: "todo", priority: "low", label: "Feature", assignee: "TP", date: "Sep 5" },
  { id: "NOR-150", title: "Retry failed webhook deliveries", status: "done", priority: "high", label: "Infra", assignee: "MK", date: "Sep 6" },
  { id: "NOR-151", title: "Mobile row height too tall", status: "todo", priority: "medium", label: "Bug", assignee: "SC", date: "Sep 6" },
  { id: "NOR-152", title: "Snooze notifications", status: "todo", priority: "low", label: "Feature", assignee: "AL", date: "Sep 7" },
  { id: "NOR-153", title: "Cycle burndown rounding bug", status: "done", priority: "medium", label: "Bug", assignee: "JR", date: "Sep 7" },
];

const DENSE_ROW = 32;
const LOOSE_ROW = 56;
const VIEWPORT = DENSE_ROW * ISSUES.length;

const CANVAS = "var(--background)";
const SURFACE = "var(--card)";
const HOVER = "var(--accent)";
const ACCENT = "var(--tawny)";
const HAIRLINE = "var(--border)";
const SNAP = "cubic-bezier(.25,.46,.45,.94)";
const BOUNCE = "cubic-bezier(.34,1.56,.64,1)";

const STATUS_COLOR: Record<Status, string> = {
  todo: "#3B82F6",
  progress: "#EAB308",
  done: "#22C55E",
};

const STATUS_LABEL: Record<Status, string> = {
  todo: "Todo",
  progress: "In progress",
  done: "Done",
};

const PRIORITY_COLOR: Record<Priority, string> = {
  high: "#EF4444",
  medium: "#F97316",
  low: "#94A3B8",
};

const LABEL_COLOR: Record<Label, string> = {
  Bug: "#A855F7",
  Infra: "#2563EB",
  Design: "#F97316",
  Feature: "#22C55E",
};

const STEPS = [
  {
    id: 0,
    label: "Before",
    kicker: "BEFORE",
    title: "The same list, before the decisions.",
    chip: "56 px rows · seven colors · drop shadow",
  },
  {
    id: 1,
    label: "Density",
    kicker: "DECISION 01 · DENSITY",
    title: "Density reads as competence.",
    chip: "13 px · 32 px rows · -0.01em",
  },
  {
    id: 2,
    label: "Borders",
    kicker: "DECISION 02 · BORDERS",
    title: "Borders, not shadows.",
    chip: "box-shadow: none · 1 px at 8%",
  },
  {
    id: 3,
    label: "One color",
    kicker: "DECISION 03 · ONE COLOR",
    title: "One color. Everything else grey.",
    chip: "1 brand color · icons for status",
  },
  {
    id: 4,
    label: "Keyboard",
    kicker: "DECISION 04 · KEYBOARD",
    title: "The UI answers first.",
    chip: "80 ms hover · 150 ms max · 0 overshoot",
  },
  {
    id: 5,
    label: "Alignment",
    kicker: "DECISION 05 · ALIGNMENT",
    title: "Invisible when right.",
    chip: "4 px grid · 16 px icons · numbers right",
  },
] as const;

const COMMANDS = [
  { name: "Set priority", shortcut: "P" },
  { name: "Create issue", shortcut: "C" },
  { name: "Assign to me", shortcut: "I" },
  { name: "Search", shortcut: "/" },
  { name: "Toggle sidebar", shortcut: "⌘B" },
];

const COLLAPSED_HUES = ["#3B82F6", "#EAB308", "#22C55E", "#EF4444", "#F97316", "#A855F7", "#2563EB"];

function Kbd({ children }: { children: string }) {
  return (
    <kbd className="ml-1 inline-flex h-4 min-w-4 items-center justify-center rounded border border-border bg-foreground/5 px-1 font-sans text-[10px] leading-none text-muted-foreground">
      {children}
    </kbd>
  );
}

function StatusIcon({ status, size }: { status: Status; size: number }) {
  const color = "var(--muted-foreground)";
  if (status === "done") {
    return (
      <svg width={size} height={size} viewBox="0 0 16 16" aria-hidden="true">
        <circle cx="8" cy="8" r="6" fill="none" stroke={color} strokeWidth="1.5" />
        <path d="M5 8.2 7.1 10.2 11 6.1" fill="none" stroke={color} strokeWidth="1.5" strokeLinecap="round" strokeLinejoin="round" />
      </svg>
    );
  }
  if (status === "progress") {
    return (
      <svg width={size} height={size} viewBox="0 0 16 16" aria-hidden="true">
        <circle cx="8" cy="8" r="6" fill="none" stroke={color} strokeWidth="1.5" />
        <path d="M8 2.2a5.8 5.8 0 0 1 0 11.6Z" fill={color} />
      </svg>
    );
  }
  return (
    <svg width={size} height={size} viewBox="0 0 16 16" aria-hidden="true">
      <circle cx="8" cy="8" r="6" fill="none" stroke={color} strokeWidth="1.5" />
    </svg>
  );
}

function Pill({ color, dense, children }: { color: string; dense: boolean; children: string }) {
  return (
    <span
      className="inline-flex shrink-0 items-center rounded-full font-medium leading-none"
      style={{
        background: `${color}26`,
        color,
        fontSize: dense ? 10 : 11,
        padding: dense ? "3px 6px" : "4px 8px",
      }}
    >
      {children}
    </span>
  );
}

function Chip({ children }: { children: string }) {
  return (
    <div className="mx-auto mt-3 w-fit rounded-full border border-tawny/35 bg-tawny/12 px-3 py-1 text-center text-[11px] font-medium text-tawny">
      {children}
    </div>
  );
}

const STAGE_INDEX = {
  before: 0,
  density: 1,
  borders: 2,
  "one-color": 3,
  keyboard: 4,
  alignment: 5,
} as const;

type StageName = keyof typeof STAGE_INDEX;

export function LinearUxStage({
  stage,
  presentation = "lesson",
}: {
  stage: StageName;
  /** `product` is the clickable Board app. Lesson stages stay the writeup. */
  presentation?: "lesson" | "product";
}) {
  if (presentation === "product") return <BoardDashboard />
  return <LinearUxLesson stage={stage} />
}

function LinearUxLesson({ stage }: { stage: StageName }) {
  const product = false
  const step = STAGE_INDEX[stage]
  const [selectedId, setSelectedId] = useState("NOR-142");
  const [hoveredId, setHoveredId] = useState<string | null>(null);
  const [paletteOpen, setPaletteOpen] = useState(step === 4);
  const [query, setQuery] = useState("");
  const inputRef = useRef<HTMLInputElement>(null);

  const dense = step >= 1;
  const flat = step >= 2;
  const mono = step >= 3;
  const keys = step >= 4;
  const aligned = step >= 5;
  const meta = STEPS[step] ?? STEPS[0];
  const hoverMs = keys ? 80 : 300;
  const moveMs = keys ? 150 : 300;

  const filtered = COMMANDS.filter((command) =>
    command.name.toLowerCase().includes(query.trim().toLowerCase()),
  );

  function rowBackground(id: string) {
    const selected = id === selectedId;
    const hot = id === hoveredId && !selected;
    if (selected && mono) return "color-mix(in srgb, var(--tawny) 28%, transparent)";
    if (selected) return "color-mix(in srgb, #3B82F6 22%, transparent)";
    if (hot && flat) return HOVER;
    if (hot) return "color-mix(in srgb, var(--foreground) 6%, transparent)";
    return "transparent";
  }

  return (
    <div
      data-testid="linear-ux-demo"
      data-writeup-stage={product ? undefined : ""}
      data-step={step}
      className={
        product
          ? "flex h-full min-h-0 w-full flex-col overflow-hidden bg-background font-sans text-foreground antialiased"
          : "my-8 w-full overflow-hidden rounded-xl border border-border bg-background font-sans text-foreground antialiased"
      }
      style={{
        fontFamily: "Inter, ui-sans-serif, system-ui, sans-serif",
        lineHeight: 1.35,
        backgroundImage: product
          ? undefined
          : "linear-gradient(color-mix(in srgb, var(--foreground) 6%, transparent) 1px, transparent 1px), linear-gradient(90deg, color-mix(in srgb, var(--foreground) 6%, transparent) 1px, transparent 1px)",
        backgroundSize: product ? undefined : "22px 22px",
      }}
      onKeyDown={(event) => {
        if (!keys) return;
        if ((event.metaKey || event.ctrlKey) && event.key.toLowerCase() === "k") {
          event.preventDefault();
          setPaletteOpen((open) => !open);
        }
        if (event.key === "Escape") setPaletteOpen(false);
      }}
    >
      <div className={product ? "flex h-full min-h-0 flex-col" : "px-3 py-4 sm:px-4"}>
        {product ? null : (
          <div className="mb-3 text-center">
            <div className="mx-auto mb-2 w-fit rounded-full border border-tawny/35 bg-tawny/12 px-2.5 py-1 text-[10px] font-semibold tracking-[0.14em] text-tawny">
              {meta.kicker}
            </div>
            <div className="text-[20px] font-semibold tracking-[-0.03em] text-foreground sm:text-[22px]" aria-live="polite">
              {meta.title}
            </div>
          </div>
        )}

        <div
          className={
            product
              ? "flex min-h-0 flex-1 overflow-hidden"
              : "overflow-hidden rounded-xl motion-reduce:transition-none"
          }
          style={{
            background: flat ? CANVAS : SURFACE,
            border: product ? undefined : `1px solid ${HAIRLINE}`,
            boxShadow: product || flat ? "none" : "0 22px 48px color-mix(in srgb, var(--foreground) 16%, transparent)",
            transition: `background-color ${moveMs}ms ${SNAP}, box-shadow ${moveMs}ms ${SNAP}, border-color ${moveMs}ms ${SNAP}`,
          }}
        >
          <div className="flex min-h-0 flex-1 items-stretch">
            <aside
              className={
                product
                  ? "flex w-[180px] shrink-0 flex-col"
                  : "hidden w-[118px] shrink-0 flex-col sm:flex"
              }
              style={{ borderRight: `1px solid ${HAIRLINE}` }}
            >
              <div className="flex items-center gap-2 px-2.5 py-2.5">
                <span className="flex size-5 items-center justify-center rounded bg-foreground/8 text-[10px] font-semibold text-foreground">
                  N
                </span>
                <span className="truncate text-[12px] text-foreground">Northstar</span>
              </div>
              <div className="flex flex-col gap-0.5 px-1.5 text-[12px] text-muted-foreground">
                <SideLink icon={<Inbox size={13} />} label="Inbox" count="3" />
                <SideLink icon={<CircleUser size={13} />} label="My issues" />
                <SideLink icon={<LayoutGrid size={13} />} label="Views" />
                <SideLink icon={<FolderKanban size={13} />} label="Projects" />
                <SideLink icon={<Repeat size={13} />} label="Cycles" />
              </div>
              <div className="mt-3 px-2.5 text-[10px] font-medium tracking-[0.12em] text-muted-foreground">TEAMS</div>
              <div className="mt-1 flex flex-col gap-0.5 px-1.5 pb-2 text-[12px]">
                <div className="px-1.5 py-1 text-muted-foreground">Frontend</div>
                <div className="rounded-md bg-accent px-1.5 py-1 text-foreground">
                  Issues
                </div>
                <div className="px-1.5 py-1 text-muted-foreground">Backlog</div>
              </div>
              <div className="mt-auto flex items-center gap-2 px-2.5 py-2.5 text-[11px] text-muted-foreground">
                <span className="flex size-5 items-center justify-center rounded-full bg-foreground/8 text-[9px] text-foreground">SC</span>
                <span className="truncate">Sarah Chen</span>
              </div>
            </aside>

            <div className="relative min-w-0 flex-1" style={{ background: flat ? SURFACE : "transparent" }}>
              <div
                className="flex items-center justify-between gap-2 px-2.5 py-2"
                style={{ borderBottom: `1px solid ${HAIRLINE}` }}
              >
                <div className="flex items-center gap-2 text-[13px] text-foreground">
                  Active issues
                  <span className="rounded bg-foreground/8 px-1.5 text-[11px] text-muted-foreground">{ISSUES.length}</span>
                </div>
                <div className="flex items-center gap-1.5 text-muted-foreground">
                  {keys ? (
                    <button
                      type="button"
                      aria-expanded={paletteOpen}
                      aria-label="Search commands"
                      onClick={() => {
                        setQuery("");
                        setPaletteOpen((open) => !open);
                      }}
                      className="inline-flex items-center rounded-md px-1.5 py-1 text-[11px] text-foreground/80"
                      style={{ border: `1px solid ${HAIRLINE}` }}
                    >
                      <Search size={12} />
                      <Kbd>⌘K</Kbd>
                    </button>
                  ) : null}
                  <LayoutGrid size={14} />
                  <Bell size={14} />
                </div>
              </div>

              <div className="flex flex-wrap items-center justify-between gap-2 px-2.5 py-2">
                <div className="flex items-center gap-1.5">
                  <span
                    className="inline-flex items-center rounded-md px-2 py-1 text-[12px] text-foreground/80"
                    style={{ border: `1px solid ${HAIRLINE}` }}
                  >
                    Filter
                    {keys ? <Kbd>F</Kbd> : null}
                  </span>
                  <span
                    className="inline-flex items-center rounded-md px-2 py-1 text-[12px] text-foreground/80"
                    style={{ border: `1px solid ${HAIRLINE}` }}
                  >
                    Display
                    {keys ? <Kbd>D</Kbd> : null}
                  </span>
                </div>
                <button
                  type="button"
                  onClick={() => {
                    if (!keys) return;
                    setQuery("Create");
                    setPaletteOpen(true);
                  }}
                  className="inline-flex items-center rounded-md px-2.5 py-1 text-[12px] font-medium motion-reduce:transition-none motion-reduce:hover:transform-none"
                  style={{
                    background: mono ? ACCENT : "#3B82F6",
                    color: mono ? "var(--tawny-foreground)" : "#fff",
                    boxShadow: flat ? "none" : "0 8px 18px color-mix(in srgb, #3B82F6 40%, transparent)",
                    transition: keys
                      ? `background-color ${hoverMs}ms ${SNAP}, box-shadow ${moveMs}ms ${SNAP}, transform ${moveMs}ms ${SNAP}`
                      : `background-color 300ms ${BOUNCE}, box-shadow 300ms ease, transform 300ms ${BOUNCE}`,
                  }}
                  onMouseEnter={(event) => {
                    if (!keys) event.currentTarget.style.transform = "scale(1.06)";
                  }}
                  onMouseLeave={(event) => {
                    event.currentTarget.style.transform = "scale(1)";
                  }}
                >
                  + New issue
                  {keys ? <Kbd>C</Kbd> : null}
                </button>
              </div>

              <div className="relative overflow-hidden" style={{ height: VIEWPORT }} data-viewport={VIEWPORT}>
                <div role="listbox" aria-label="Active issues">
                  {ISSUES.map((issue) => {
                    const selected = issue.id === selectedId;
                    return (
                      <button
                        key={issue.id}
                        type="button"
                        role="option"
                        aria-selected={selected}
                        title={issue.title}
                        onClick={() => setSelectedId(issue.id)}
                        onMouseEnter={() => setHoveredId(issue.id)}
                        onMouseLeave={() => setHoveredId((current) => (current === issue.id ? null : current))}
                        className="flex w-full min-w-0 text-left motion-reduce:transition-none"
                        style={{
                          height: dense ? DENSE_ROW : LOOSE_ROW,
                          fontSize: dense ? 13 : 15,
                          letterSpacing: dense ? "-0.01em" : "0em",
                          background: rowBackground(issue.id),
                          boxShadow: selected && mono ? `inset 2px 0 0 ${ACCENT}` : undefined,
                          color: "var(--foreground)",
                          gap: aligned ? 8 : 6,
                          padding: aligned ? "0 8px" : "0 13px",
                          alignItems: "center",
                          transition: `height ${moveMs}ms ${SNAP}, background-color ${hoverMs}ms ${SNAP}, font-size ${moveMs}ms ${SNAP}, letter-spacing ${moveMs}ms ${SNAP}`,
                        }}
                      >
                        <span
                          className="inline-flex shrink-0 items-center justify-center"
                          style={{
                            width: mono ? (aligned ? 16 : 14) : undefined,
                            height: mono ? (aligned ? 16 : 14) : undefined,
                            transform: aligned ? undefined : "translateY(2px)",
                          }}
                        >
                          {mono ? (
                            <StatusIcon status={issue.status} size={aligned ? 16 : 14} />
                          ) : (
                            <Pill color={STATUS_COLOR[issue.status]} dense={dense}>
                              {STATUS_LABEL[issue.status]}
                            </Pill>
                          )}
                        </span>
                        {mono ? null : (
                          <Pill color={PRIORITY_COLOR[issue.priority]} dense={dense}>
                            {issue.priority[0].toUpperCase() + issue.priority.slice(1)}
                          </Pill>
                        )}
                        <span className={aligned ? "w-14 shrink-0 text-muted-foreground" : "shrink-0 text-muted-foreground"}>{issue.id}</span>
                        <span className={aligned ? "min-w-0 flex-1 truncate" : "min-w-0 max-w-[24%] truncate sm:max-w-[32%]"}>
                          {issue.title}
                        </span>
                        {mono ? (
                          <span className={aligned ? "w-14 shrink-0 text-right text-muted-foreground" : "shrink-0 text-muted-foreground"}>{issue.label}</span>
                        ) : (
                          <Pill color={LABEL_COLOR[issue.label]} dense={dense}>
                            {issue.label}
                          </Pill>
                        )}
                        <span className={aligned ? "w-6 shrink-0 text-right text-muted-foreground" : "shrink-0 text-muted-foreground"}>
                          {issue.assignee}
                        </span>
                        <span className={aligned ? "w-10 shrink-0 text-right text-muted-foreground tabular-nums" : "shrink-0 text-muted-foreground"}>
                          {issue.date}
                        </span>
                      </button>
                    );
                  })}
                </div>
              </div>

              {paletteOpen && keys ? (
                <div
                  data-testid="command-palette"
                  className="absolute inset-x-2 top-12 z-10 overflow-hidden rounded-lg"
                  style={{ background: "var(--popover)", border: `1px solid ${HAIRLINE}` }}
                >
                  <div className="flex items-center gap-2 px-3" style={{ borderBottom: `1px solid ${HAIRLINE}` }}>
                    <Search size={14} className="text-muted-foreground" />
                    <input
                      ref={inputRef}
                      value={query}
                      onChange={(event) => setQuery(event.target.value)}
                      placeholder="Type a command"
                      aria-label="Command palette"
                      className="h-9 w-full bg-transparent text-[13px] text-foreground outline-none placeholder:text-muted-foreground"
                    />
                  </div>
                  <ul className="py-1">
                    {filtered.length === 0 ? (
                      <li className="px-3 py-2 text-[13px] text-muted-foreground">No matches</li>
                    ) : (
                      filtered.map((command, index) => (
                        <li key={command.name}>
                          <div
                            className="flex items-center justify-between px-3 py-1.5 text-[13px]"
                            style={{ background: index === 0 ? "var(--accent)" : "transparent" }}
                          >
                            <span>{command.name}</span>
                            <Kbd>{command.shortcut}</Kbd>
                          </div>
                        </li>
                      ))
                    )}
                  </ul>
                </div>
              ) : null}
            </div>
          </div>
        </div>

        {product ? null : (
          <>
            <Annotation step={step} dense={dense} />
            <Chip>{meta.chip}</Chip>
          </>
        )}
      </div>
    </div>
  );
}

function SideLink({ icon, label, count }: { icon: ReactNode; label: string; count?: string }) {
  return (
    <div className="flex items-center gap-1.5 rounded-md px-1.5 py-1">
      <span className="text-muted-foreground">{icon}</span>
      <span className="truncate">{label}</span>
      {count ? <span className="ml-auto text-[10px] text-muted-foreground">{count}</span> : null}
    </div>
  );
}

function Annotation({ step, dense }: { step: number; dense: boolean }) {
  if (step === 0) {
    return (
      <p className="mt-3 text-center text-[12px] text-muted-foreground">
        Eight of fourteen rows fit. The other six sit below this viewport.
      </p>
    );
  }

  if (step === 1) {
    return (
      <div className="mt-3">
        <Chip>14 rows, same height</Chip>
        <div
          className="mt-2 flex items-center justify-between rounded-lg px-3 py-2"
          style={{ background: SURFACE, border: `1px solid ${HAIRLINE}` }}
        >
          <div>
            <div className="text-[10px] tracking-[0.12em] text-muted-foreground">LETTER SPACING</div>
            <div className="text-[14px] text-foreground" style={{ letterSpacing: dense ? "-0.01em" : "0em" }}>
              Migrate billing webhooks to v2
            </div>
          </div>
          <span className="text-[12px] text-muted-foreground">-0.01em</span>
        </div>
      </div>
    );
  }

  if (step === 2) {
    const rows = [
      ["canvas · background", CANVAS, "background"],
      ["list · card", SURFACE, "card"],
      ["hover · accent", HOVER, "accent"],
    ] as const;
    return (
      <div className="mt-3 space-y-1.5">
        {rows.map(([label, value, token]) => (
          <div
            key={label}
            className="flex items-center justify-between rounded-md px-3 py-1.5 text-[12px]"
            style={{ background: value, border: `1px solid ${HAIRLINE}` }}
          >
            <span className="text-muted-foreground">{label}</span>
            <span className="font-mono text-[11px] text-foreground">{token}</span>
          </div>
        ))}
      </div>
    );
  }

  if (step === 3) {
    return (
      <div className="mt-3">
        <div className="grid grid-cols-8 gap-1.5">
          {COLLAPSED_HUES.map((hue) => (
            <div key={hue} className="text-center">
              <div className="mx-auto size-5 rounded-md bg-muted" title={hue} />
              <div className="mt-1 text-[9px] text-muted-foreground">grey</div>
            </div>
          ))}
          <div className="text-center">
            <div className="mx-auto size-5 rounded-md bg-tawny" />
            <div className="mt-1 text-[9px] text-foreground">tawny</div>
          </div>
        </div>
      </div>
    );
  }

  if (step === 4) {
    return (
      <div className="mt-3 space-y-2 rounded-lg px-3 py-2.5" style={{ background: SURFACE, border: `1px solid ${HAIRLINE}` }}>
        <Meter label="hover" ms={80} max={300} />
        <Meter label="transition" ms={150} max={300} />
        <Meter label="generic" ms={300} max={300} muted />
        <div className="flex justify-end gap-3 pt-1 text-[11px]">
          <span className="text-destructive">bouncy</span>
          <span className="text-tawny">snappy</span>
        </div>
      </div>
    );
  }

  return (
    <div className="mt-3 rounded-lg px-3 py-2.5" style={{ background: SURFACE, border: `1px solid ${HAIRLINE}` }}>
      <div className="mb-2 flex items-center justify-between text-[10px] tracking-[0.12em] text-tawny">
        <span>×2 · ONE ROW</span>
        <span>aligned</span>
      </div>
      <div className="flex items-center gap-2 text-[13px] text-foreground">
        <StatusIcon status="todo" size={16} />
        <span className="text-muted-foreground">NOR-147</span>
        <span className="min-w-0 flex-1 truncate">Bulk edit for labels</span>
        <span className="text-muted-foreground">Feature</span>
        <span className="text-muted-foreground">AL</span>
        <span className="text-muted-foreground">Sep 4</span>
      </div>
      <div className="mt-2 flex justify-between text-[10px] text-tawny">
        <span>16 px · centered</span>
        <span>left</span>
        <span>right</span>
      </div>
    </div>
  );
}

function Meter({ label, ms, max, muted }: { label: string; ms: number; max: number; muted?: boolean }) {
  return (
    <div className="grid grid-cols-[76px_1fr_48px] items-center gap-2 text-[11px]">
      <span className="text-muted-foreground">{label}</span>
      <span className="h-1.5 overflow-hidden rounded-full bg-foreground/10">
        <span
          className="block h-full rounded-full"
          style={{ width: `${(ms / max) * 100}%`, background: muted ? "var(--destructive)" : ACCENT }}
        />
      </span>
      <span className={muted ? "text-right text-destructive line-through" : "text-right text-muted-foreground"}>{ms} ms</span>
    </div>
  );
}