<3/products/personal-site/remotion/scenes/

CapTableRow.tsx

import type { CSSProperties } from "react";
import { Img, staticFile } from "remotion";
import { CAP_COLORS as C } from "./CapTable.data";

type CapTableRowProps = {
  name: string;
  src: string;
  pct: number;
  /** 0 → hidden below its final position; 1 → fully present. */
  progress: number;
  rowHeight: number;
  marginTop: number;
  monoFontFamily: string;
};

/**
 * Shared holder row for every cap-table composition. Its layout is always at
 * final size; entrance motion is only a short slide upward plus a fade, so
 * adding a holder never grows or reflows an existing row.
 */
export function CapTableRow({
  name,
  src,
  pct,
  progress,
  rowHeight,
  marginTop,
  monoFontFamily,
}: CapTableRowProps) {
  return (
    <div
      style={{
        display: "flex",
        alignItems: "center",
        gap: 28,
        height: rowHeight,
        marginTop,
        opacity: progress,
        transform: `translateY(${(1 - progress) * 16}px)`,
      }}
    >
      <Avatar src={src} size={60} />
      <span style={{ flex: "none", fontWeight: 500, fontSize: 40, color: C.inkStrong }}>{name}</span>
      <span style={{ flex: "1 1 auto", minWidth: 24, height: 0, borderBottom: `2px solid ${C.hairline}` }} aria-hidden />
      <span style={{ flex: "none", fontFamily: monoFontFamily, fontWeight: 500, fontSize: 40, fontVariantNumeric: "tabular-nums", color: C.ink }}>
        {pct}%
      </span>
    </div>
  );
}

function Avatar({ src, size }: { src: string; size: number }) {
  const style: CSSProperties = {
    flex: "none",
    width: size,
    height: size,
    maxWidth: "none",
    display: "block",
    borderRadius: "50%",
    objectFit: "cover",
    background: "#ececea",
  };
  return <Img src={staticFile(src)} alt="" style={style} />;
}