# Black Box: 3D Product Explainer Box for React

> A glossy black box that shows its work. Each face is one part of your product, and they open in turn: the panel lifts off its socket, light leaks out round it, and a hairline draws out to a label saying what that part did. Point at a face, tap it or tab to it to open it yourself. In dark mode the box turns to glass, a wireframe with a small lit core that turns inside.

3D Product Explainer Box for React: a glossy box opens each face to show what it does. Built with Tailwind CSS and Motion; install it with the shadcn CLI. It's a KeyframeUI component: the file lands in your project as source, yours to edit.

- Docs and live preview: https://keyframeui.com/components/black-box
- Tags: 3D, Hero, AI, Diagram
- 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 component. It lands in `components/ui/black-box.tsx`, and the CLI also adds `lib/utils.ts` (the `cn` helper) and installs the npm dependencies:

```bash
npx shadcn@latest add @keyframeui/black-box
```

Or install by URL, without registering the namespace:

```bash
npx shadcn@latest add https://keyframeui.com/r/black-box.json
```

## Usage

The main demo shows typical usage.

### Agent Decision

A support agent's refund, opened up: what it read, how it reasoned and what it did, one face at a time.

Install this demo, with the component included:

```bash
npx shadcn@latest add @keyframeui/black-box-demo
```

```tsx title="components/black-box-demo.tsx"
"use client";

import { BlackBox, type BlackBoxFace } from "@/components/ui/black-box";

// A support agent's decision, opened up: what it read, how it reasoned, what it did.
const FACES: BlackBoxFace[] = [
  {
    id: "read",
    label: "Read",
    detail: "Order #4471, the refund policy and two earlier tickets from the same customer.",
    icon: (
      <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth={1.6} strokeLinecap="round" strokeLinejoin="round">
        <path d="M4 5.5A1.5 1.5 0 0 1 5.5 4H11v16H5.5A1.5 1.5 0 0 1 4 18.5z" />
        <path d="M20 5.5A1.5 1.5 0 0 0 18.5 4H13v16h5.5a1.5 1.5 0 0 0 1.5-1.5z" />
      </svg>
    ),
  },
  {
    id: "reason",
    label: "Reason",
    detail: "Delivered 9 days ago, inside the 14-day window. Item unopened, so no restocking fee.",
    icon: (
      <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth={1.6} strokeLinecap="round" strokeLinejoin="round">
        <circle cx="6" cy="6" r="2" />
        <circle cx="18" cy="12" r="2" />
        <circle cx="6" cy="18" r="2" />
        <path d="M8 6h3a3 3 0 0 1 3 3v0a3 3 0 0 0 3 3M8 18h3a3 3 0 0 0 3-3v0a3 3 0 0 1 3-3" />
      </svg>
    ),
  },
  {
    id: "act",
    label: "Act",
    detail: "Refunded $48.00 to the original card and replied in the customer's language.",
    icon: (
      <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth={1.6} strokeLinecap="round" strokeLinejoin="round">
        <path d="M5 12.5l4.5 4.5L19 7.5" />
      </svg>
    ),
  },
];

export function BlackBoxDemo() {
  return (
    <div className="w-full max-w-3xl">
      <div className="mx-auto flex w-fit items-center gap-2.5 rounded-full bg-white py-1.5 pr-4 pl-1.5 text-[13px] text-zinc-600 shadow-sm ring-1 ring-zinc-950/[0.07] dark:bg-zinc-900 dark:text-zinc-300 dark:ring-white/10">
        <span className="grid size-6 place-items-center rounded-full bg-zinc-950 text-[11px] font-semibold text-white dark:bg-white dark:text-zinc-950">
          M
        </span>
        <span>
          <span className="font-medium text-zinc-950 dark:text-white">Why did order #4471 get a refund?</span>
        </span>
      </div>
      <BlackBox faces={FACES} className="mt-2" aria-label="How the support agent decided" />
    </div>
  );
}
```

## Examples

Finished sections built with Black Box. Each one installs whole, with the component included.

### Stack

A backend platform's three parts as a list beside the box. Point at a row and its face opens; leave the list and the box walks it again.

Install this demo, with the component included:

```bash
npx shadcn@latest add @keyframeui/black-box-stack-demo
```

```tsx title="components/black-box-stack-demo.tsx"
"use client";

import * as React from "react";

import { cn } from "@/lib/utils";
import { BlackBox, type BlackBoxFace } from "@/components/ui/black-box";

// A backend platform's three parts, as a list that opens the box and a box that walks the list.
const LAYERS: (BlackBoxFace & { stat: string })[] = [
  {
    id: "functions",
    label: "Functions",
    detail: "TypeScript or Python, deployed on every push, warm in 38 ms.",
    stat: "38 ms cold start",
    icon: (
      <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth={1.6} strokeLinecap="round" strokeLinejoin="round">
        <path d="M9 7 4 12l5 5M15 7l5 5-5 5" />
      </svg>
    ),
  },
  {
    id: "queues",
    label: "Queues",
    detail: "Retries, delays and fan-out without a broker to babysit.",
    stat: "1.2M jobs a day",
    icon: (
      <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth={1.6} strokeLinecap="round" strokeLinejoin="round">
        <path d="M4 7h16M4 12h11M4 17h6" />
      </svg>
    ),
  },
  {
    id: "postgres",
    label: "Postgres",
    detail: "A branch of your database for every preview, merged with the code.",
    stat: "Branch per preview",
    icon: (
      <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth={1.6} strokeLinecap="round" strokeLinejoin="round">
        <ellipse cx="12" cy="6" rx="7" ry="2.5" />
        <path d="M5 6v6c0 1.4 3.1 2.5 7 2.5s7-1.1 7-2.5V6M5 12v6c0 1.4 3.1 2.5 7 2.5s7-1.1 7-2.5v-6" />
      </svg>
    ),
  },
];

export function BlackBoxStackDemo() {
  const [value, setValue] = React.useState<string | null>(null);
  const [holding, setHolding] = React.useState(false);
  const [picked, setPicked] = React.useState(false);

  return (
    <div className="w-full max-w-4xl @container [--black-box-light:#ff5a1f]">
      <div className="grid items-center gap-x-10 gap-y-2 @[40rem]:grid-cols-[minmax(0,0.9fr)_minmax(0,1.1fr)]">
        <div>
          <p className="text-xs font-medium tracking-[0.08em] text-[#e2470f] uppercase dark:text-[#ff7a45]">Lattice platform</p>
          <h3 className="mt-3 text-3xl font-semibold tracking-[-0.03em] text-balance text-zinc-950 dark:text-white">
            Your whole backend, in one box.
          </h3>
          <ol
            className="mt-7 border-t border-zinc-950/10 dark:border-white/10"
            onPointerLeave={() => setHolding(false)}
            onFocus={() => setHolding(true)}
            onBlur={(event) => {
              if (!event.currentTarget.contains(event.relatedTarget as Node)) setHolding(false);
            }}
          >
            {LAYERS.map((layer, i) => {
              const on = value === layer.id;
              return (
                <li key={layer.id} className="border-b border-zinc-950/10 dark:border-white/10">
                  <button
                    type="button"
                    aria-pressed={on}
                    onPointerEnter={(event) => {
                      if (event.pointerType !== "mouse") return;
                      setHolding(true);
                      setValue(layer.id);
                    }}
                    onFocus={() => setValue(layer.id)}
                    onClick={() => {
                      setPicked(true);
                      setValue(layer.id);
                    }}
                    className="group relative flex w-full gap-4 py-4 text-left outline-none focus-visible:ring-2 focus-visible:ring-[#ff5a1f] focus-visible:ring-offset-2 focus-visible:ring-offset-white dark:focus-visible:ring-offset-zinc-950"
                  >
                    <span
                      aria-hidden
                      className={cn(
                        "absolute top-4 bottom-4 -left-px w-0.5 origin-top rounded-full bg-[#ff5a1f] transition-[opacity,transform] duration-200 ease-[cubic-bezier(0.23,1,0.32,1)]",
                        on ? "scale-y-100 opacity-100" : "scale-y-50 opacity-0",
                      )}
                    />
                    <span className="pl-4 font-mono text-xs leading-6 text-zinc-400 tabular-nums dark:text-zinc-500">
                      0{i + 1}
                    </span>
                    <span className="min-w-0 flex-1">
                      <span className="flex items-baseline justify-between gap-3">
                        <span
                          className={cn(
                            "text-[15px] leading-6 font-medium tracking-[-0.01em] transition-colors duration-200",
                            on ? "text-zinc-950 dark:text-white" : "text-zinc-500 dark:text-zinc-400",
                          )}
                        >
                          {layer.label}
                        </span>
                        <span className="shrink-0 font-mono text-[11px] text-zinc-400 tabular-nums dark:text-zinc-500">
                          {layer.stat}
                        </span>
                      </span>
                      <span className="mt-1 block text-[13px] leading-5 text-pretty text-zinc-500 dark:text-zinc-400">
                        {layer.detail}
                      </span>
                    </span>
                  </button>
                </li>
              );
            })}
          </ol>
        </div>

        <BlackBox
          faces={LAYERS}
          value={value}
          onValueChange={(id) => setValue(id)}
          autoplay={!holding && !picked}
          labels={false}
          aria-label="Lattice's three parts"
        />
      </div>
    </div>
  );
}
```

### X-ray

A secrets vault for AI agents, with a switch that turns the sealed box to glass so you can see the core inside.

Install this demo, with the component included:

```bash
npx shadcn@latest add @keyframeui/black-box-xray-demo
```

```tsx title="components/black-box-xray-demo.tsx"
"use client";

import * as React from "react";

import { cn } from "@/lib/utils";
import { BlackBox, type BlackBoxFace } from "@/components/ui/black-box";

// A secrets vault for AI agents: sealed to them, see-through to you. The switch flips the box
// between solid and glass.
const FACES: BlackBoxFace[] = [
  {
    id: "seal",
    label: "Encrypted",
    detail: "AES-256 per workspace, with the keys in a hardware module you rotate yourself.",
    icon: (
      <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth={1.6} strokeLinecap="round" strokeLinejoin="round">
        <rect x="5" y="10.5" width="14" height="9.5" rx="2" />
        <path d="M8.5 10.5V8a3.5 3.5 0 0 1 7 0v2.5" />
      </svg>
    ),
  },
  {
    id: "scope",
    label: "Scoped",
    detail: "An agent gets a token for one tool, for one run. Then it's gone.",
    icon: (
      <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth={1.6} strokeLinecap="round" strokeLinejoin="round">
        <circle cx="8.5" cy="12" r="3.5" />
        <path d="M12 12h8M17 12v3M20 12v2" />
      </svg>
    ),
  },
  {
    id: "log",
    label: "Logged",
    detail: "Every read is signed and in your SIEM within a second.",
    icon: (
      <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth={1.6} strokeLinecap="round" strokeLinejoin="round">
        <path d="M7 4h7l4 4v12H7z" />
        <path d="M14 4v4h4M10 13h5M10 16.5h3" />
      </svg>
    ),
  },
];

const MODES = [
  { id: "solid", label: "Sealed" },
  { id: "glass", label: "X-ray" },
] as const;

export function BlackBoxXrayDemo() {
  const [mode, setMode] = React.useState<"solid" | "glass">("solid");

  return (
    <div className="w-full max-w-3xl text-center [--black-box-light:#19d48b]">
      <h3 className="text-2xl font-semibold tracking-[-0.03em] text-balance text-zinc-950 dark:text-white">
        Shut to your agents. Glass to you.
      </h3>
      <p className="mx-auto mt-2 max-w-md text-sm leading-6 text-pretty text-zinc-500 dark:text-zinc-400">
        Keel keeps every credential your agents use in one sealed box, and lets you see straight through it.
      </p>

      <div
        role="group"
        aria-label="View"
        className="relative mx-auto mt-6 grid w-fit grid-cols-2 rounded-full bg-zinc-950/[0.05] p-1 text-[13px] font-medium ring-1 ring-zinc-950/[0.06] ring-inset dark:bg-white/[0.06] dark:ring-white/10"
      >
        <span
          aria-hidden
          className={cn(
            "absolute inset-y-1 left-1 w-[calc(50%-4px)] rounded-full bg-white shadow-sm ring-1 ring-zinc-950/[0.06] transition-transform duration-200 ease-[cubic-bezier(0.23,1,0.32,1)] dark:bg-zinc-700 dark:ring-white/10",
            mode === "glass" && "translate-x-full",
          )}
        />
        {MODES.map((option) => (
          <button
            key={option.id}
            type="button"
            aria-pressed={mode === option.id}
            onClick={() => setMode(option.id)}
            className={cn(
              "relative rounded-full px-5 py-1.5 transition-colors duration-200 outline-none focus-visible:ring-2 focus-visible:ring-[#19d48b]",
              mode === option.id ? "text-zinc-950 dark:text-white" : "text-zinc-500 hover:text-zinc-800 dark:text-zinc-400 dark:hover:text-zinc-200",
            )}
          >
            {option.label}
          </button>
        ))}
      </div>

      <BlackBox faces={FACES} material={mode} className="mt-2" aria-label="How Keel keeps credentials" />
    </div>
  );
}
```

## Props

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `faces` (required) | `BlackBoxFace[]` |  | Up to three faces, in the order autoplay opens them: the top, then the left side, then the right. Each is `{ id, label, detail?, icon? }`: `label` titles the label and names the face for screen readers, `detail` is a line or two under it, and `icon` is engraved in the middle of the panel and lights up when it opens. |
| `value` | `string \| null` |  | The open face's id, or `null` for none, when you control it. |
| `defaultValue` | `string \| null` | `null` | The face that's open at first. None by default. |
| `onValueChange` | `(id: string \| null, reason: "autoplay" \| "preview" \| "pick") => void` |  | Called whenever a face opens, or the box shuts (`null`), with the reason: autoplay, a preview (hover or keyboard focus) or a pick (click, tap, Enter). |
| `autoplay` | `boolean \| number` | `true` | Open each face in turn, 3.2s each, with a short rest shut between rounds, until the visitor picks one. A number sets the time per face in ms. |
| `material` | `"auto" \| "solid" \| "glass"` | `"auto"` | What the box is made of: glossy black, a glass wireframe, or `auto`, black in light mode and glass in dark. |
| `labels` | `boolean` | `true` | Show the open face's label beside the box on a leader line, or under it in narrow containers. Turn it off when your own copy says which part is open. |
| `intro` | `boolean` | `true` | Fly the panels in and shut the box the first time it scrolls into view. |
| `lift` | `number` | `0.15` | How far an open panel lifts off its socket, as a share of the box's edge. |
| `liftDuration` | `number` | `0.42` | How long an opening panel takes to lift, in seconds (its spring's visual duration). |
| `liftBounce` | `number` | `0.16` | How much an opening panel overshoots before it settles, from 0 (not at all) to 1. |
| `className` | `string` |  | Extra classes for the root. Set the light's color with `--black-box-light` (default `#4f6bff`), e.g. `[--black-box-light:#ff5a1f]`; glass draws its lines in the text color. |

## What to keep

To adapt it, change its props first. When you do edit `components/ui/black-box.tsx`, keep these, which make it feel and behave right:

- Panels, lights, lines and labels animate only transform and opacity, through `animate()` on the nodes kept in `parts`, each on its own layer, so nothing repaints while a panel flies.
- Requests for a face resolve in one order: hover, then keyboard focus, then a pin, then autoplay. A controlled `value` overrides them all.
- A mouse opens a face only after resting on it for the hover intent, and the face stays open while the pointer is anywhere over the box; leaving waits before it closes.
- Autoplay holds while the pointer is over the box, focus is inside it, the tab is hidden or the box is under 40% in view, then picks up from the face left open. A click or Esc stops it for good.
- Reduced motion has no fly-in, lift, turn or autoplay: every change fades, and the first face rests open once hydrated, since `useReducedMotion` is already true in the hydration render.

Its timing (durations, delays, springs and easings) is all in the `TIMING` block at the top of the file; the timing props default to it.

## Source

```tsx title="components/ui/black-box.tsx"
"use client";

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

import { cn } from "@/lib/utils";

/**
 * Keep:
 * - Panels, lights, lines and labels animate only transform and opacity, through `animate()` on
 *   the nodes kept in `parts`, each on its own layer, so nothing repaints while a panel flies.
 * - Requests for a face resolve in one order: hover, then keyboard focus, then a pin, then
 *   autoplay. A controlled `value` overrides them all.
 * - A mouse opens a face only after resting on it for the hover intent, and the face stays open
 *   while the pointer is anywhere over the box; leaving waits before it closes.
 * - Autoplay holds while the pointer is over the box, focus is inside it, the tab is hidden or
 *   the box is under 40% in view, then picks up from the face left open. A click or Esc stops it
 *   for good.
 * - Reduced motion has no fly-in, lift, turn or autoplay: every change fades, and the first face
 *   rests open once hydrated, since `useReducedMotion` is already true in the hydration render.
 */
const TIMING = {
  /** The panels flying in to shut the box. */
  assemble: { type: "spring", visualDuration: 0.7, bounce: 0.12 },
  /** Between panels flying in, in seconds. */
  stagger: 0.07,
  /** The frame appearing under the panels, and each panel fading in as it flies. */
  reveal: { duration: 0.35, ease: [0.23, 1, 0.32, 1] },
  /** Light through the seams as the box shuts, or as its material changes. */
  flash: { duration: 0.9, ease: "easeOut" },
  /** After the panels start flying in, before the seams flash, in seconds. */
  flashAfter: 0.42,
  /** Between the faces' flashes, in seconds. */
  flashStagger: 0.04,
  /** After the panels start flying in, before the box counts as shut and a face can open, in ms. */
  shut: 950,
  /** A panel lifting off its socket (the `liftDuration` and `liftBounce` props). */
  open: { type: "spring", visualDuration: 0.42, bounce: 0.16 },
  /** A panel settling back in. */
  close: { type: "spring", visualDuration: 0.3, bounce: 0 },
  /** The glass core turning a quarter to follow. */
  turn: { type: "spring", visualDuration: 0.6, bounce: 0.06 },
  /** The highlight crossing an opening panel. */
  sheen: { duration: 0.8, ease: [0.23, 1, 0.32, 1] },
  /** The panel leading its dot, line and caption, in seconds. */
  lead: 0.08,
  /** The label trailing its dot, in seconds. */
  labelLag: 0.06,
  /** The leader line drawing out from the dot. */
  draw: { duration: 0.28, ease: [0.23, 1, 0.32, 1] },
  /** Label, dot and light coming in. */
  show: { duration: 0.22, ease: [0.23, 1, 0.32, 1] },
  /** Light spreading onto the page. */
  spill: { duration: 0.4, ease: [0.23, 1, 0.32, 1] },
  /** Label, line and light going, faster than they came. */
  hide: { duration: 0.15, ease: [0.23, 1, 0.32, 1] },
  /** Reduced motion: every change is a fade. */
  fade: { duration: 0.2, ease: [0.23, 1, 0.32, 1] },
  /** A mouse resting on a face before it opens, in ms. */
  intent: 80,
  /** An open face waiting after the pointer leaves the box, in ms. */
  linger: 250,
  /** After the pointer leaves, before autoplay picks up, in ms. */
  resume: 700,
  /** Each face staying open under `autoplay={true}`, in ms. */
  autoplay: 3200,
  /** The box resting shut between rounds, as a share of the time per face. */
  rest: 0.5,
  /** Before the first face opens once the box has shut, in ms. */
  first: 300,
} as const;

/* ─────────────────────────────────────────────────────────
 * BLACK BOX STORYBOARD
 *
 *  rest      a glossy black cube seen from above one corner, so it
 *            reads as a hexagon: three panels (top, left, right) set
 *            into a darker frame, a soft shadow under it. As glass
 *            (the dark-mode default) it's a wireframe in light lines,
 *            the hidden edges showing through and a small lit core
 *            hanging inside
 *  view      the first time it's 40% in view the panels come in from
 *            55% of an edge out along their faces, 70ms apart, on a
 *            0.7s spring with a little give, and shut. As they land,
 *            light flashes through the seams once (0.9s)
 *  open      a face opens: its panel lifts 15% of an edge off its
 *            socket on a 0.42s spring with a little give, the socket
 *            lights up in the accent, the light spills onto the page
 *            around the box and a sheen crosses the panel. 80ms later
 *            a dot lands on the panel, a hairline draws out to the
 *            label (0.28s) and the label rises 6px into place
 *  close     the panel settles back in 0.3s with no bounce while the
 *            label, line and light go in 0.15s, faster than they came
 *  autoplay  each face opens for 3.2s in turn, the core inside turning
 *            a quarter to follow, then the box rests shut for half
 *            that and starts again. A pointer over the box, focus
 *            inside it, a hidden tab or the box out of view holds it;
 *            leaving picks up 0.7s later from the face left open. A
 *            click stops it for good
 *  point     with a mouse, resting 80ms on a face opens it. It stays
 *            open while the pointer is anywhere over the box and
 *            closes 250ms after it leaves
 *  touch     a tap opens a face and keeps it open; a second tap, Esc
 *            or a tap outside closes it
 *  keys      the faces are buttons behind one tab stop. Arrows move
 *            between them and open each, Enter or Space pins one,
 *            Esc closes
 *  narrow    under 36rem the label sits under the box, one at a time,
 *            with no leader line
 *  smooth    only transform and opacity move, each on its own layer,
 *            so nothing repaints while a panel flies and a busy page
 *            can't stall it
 *  reduced   nothing moves: no fly-in, no lift, no autoplay. An open
 *            face lights its socket and rim and its label fades in
 *            (0.2s); the first face is open at rest
 * ───────────────────────────────────────────────────────── */

const BOX = {
  spread: 0.55, // share of an edge a panel starts out from its socket
  lift: 0.15, // share of an edge an open panel lifts (`lift`)
  inset: 0.075, // share of an edge between a panel and the frame's rim
  core: 0.36, // share of an edge the glass core measures
  rise: 6, // px a label rises into place
  gap: 18, // px between the box and a label beside it
  minLabel: 156, // px; the narrowest a label beside the box gets
} as const;

const LIGHT = "var(--bb-light)";
const WIDE = 576; // px (36rem): from here the labels sit beside the box
const POSE = "rotateX(-35.264deg) rotateY(-45deg)"; // a corner toward you, all three faces equal
const AT_REST = "translateZ(1px)"; // a shut panel, just above its socket

// The same pose as POSE, for working out where things land on screen.
const TILT = -35.264 * (Math.PI / 180);
const TURN = -45 * (Math.PI / 180);

const clamp = (value: number, min: number, max: number) => Math.min(max, Math.max(min, value));

type Vec = [number, number, number];

/** A point on the cube (px from its center, y down), posed and seen through the perspective. */
function project([x, y, z]: Vec, depth: number): [number, number] {
  const x1 = x * Math.cos(TURN) + z * Math.sin(TURN);
  const z1 = -x * Math.sin(TURN) + z * Math.cos(TURN);
  const y2 = y * Math.cos(TILT) - z1 * Math.sin(TILT);
  const z2 = y * Math.sin(TILT) + z1 * Math.cos(TILT);
  const k = depth / (depth - z2);
  return [x1 * k, y2 * k];
}

export type BlackBoxSide = "top" | "left" | "right";
const SIDES: BlackBoxSide[] = ["top", "left", "right"];

// Where each visible face sits on the cube, which way it faces, its fill, and which side of the
// box its label goes.
// `u` and `v` run across the face, for finding its outer edge on screen.
const FACE: Record<BlackBoxSide, { place: string; normal: Vec; u: Vec; v: Vec; fill: string; end: boolean }> = {
  top: { place: "rotateX(90deg)", normal: [0, -1, 0], u: [1, 0, 0], v: [0, 0, 1], fill: "var(--bb-top)", end: false },
  left: { place: "rotateY(0deg)", normal: [0, 0, 1], u: [1, 0, 0], v: [0, 1, 0], fill: "var(--bb-left)", end: false },
  right: { place: "rotateY(90deg)", normal: [1, 0, 0], u: [0, 0, 1], v: [0, 1, 0], fill: "var(--bb-right)", end: true },
};
// The three faces you can't see: only drawn as glass, where they show through.
const HIDDEN = ["rotateY(180deg)", "rotateY(-90deg)", "rotateX(-90deg)"];
const CORE = ["rotateY(0deg)", "rotateY(90deg)", "rotateY(180deg)", "rotateY(-90deg)", "rotateX(90deg)", "rotateX(-90deg)"];

// Each material is a set of variables the faces read. Glass draws in currentColor, so it suits
// light and dark pages alike; `auto` is black in light mode and glass in dark.
const SOLID =
  "[--bb-top:linear-gradient(150deg,#4a4a55_0%,#1b1b21_42%,#0b0b0e_100%)] [--bb-left:linear-gradient(165deg,#222229_0%,#0c0c10_55%,#050506_100%)] [--bb-right:linear-gradient(195deg,#16161b_0%,#070709_60%,#000_100%)] [--bb-body:#030304] [--bb-line:rgb(255_255_255/0.07)] [--bb-rim:rgb(255_255_255/0.1)] [--bb-icon:rgb(255_255_255/0.3)] [--bb-ghost:0] [--bb-shade:1]";
const GLASS =
  "[--bb-top:linear-gradient(150deg,color-mix(in_srgb,currentColor_12%,transparent),color-mix(in_srgb,currentColor_2%,transparent))] [--bb-left:linear-gradient(165deg,color-mix(in_srgb,currentColor_7%,transparent),transparent)] [--bb-right:linear-gradient(195deg,color-mix(in_srgb,currentColor_5%,transparent),transparent)] [--bb-body:transparent] [--bb-line:color-mix(in_srgb,currentColor_22%,transparent)] [--bb-rim:color-mix(in_srgb,currentColor_45%,transparent)] [--bb-icon:color-mix(in_srgb,currentColor_45%,transparent)] [--bb-ghost:1] [--bb-shade:0]";
const AUTO =
  SOLID +
  " dark:[--bb-top:linear-gradient(150deg,color-mix(in_srgb,currentColor_12%,transparent),color-mix(in_srgb,currentColor_2%,transparent))] dark:[--bb-left:linear-gradient(165deg,color-mix(in_srgb,currentColor_7%,transparent),transparent)] dark:[--bb-right:linear-gradient(195deg,color-mix(in_srgb,currentColor_5%,transparent),transparent)] dark:[--bb-body:transparent] dark:[--bb-line:color-mix(in_srgb,currentColor_22%,transparent)] dark:[--bb-rim:color-mix(in_srgb,currentColor_45%,transparent)] dark:[--bb-icon:color-mix(in_srgb,currentColor_45%,transparent)] dark:[--bb-ghost:1] dark:[--bb-shade:0]";
const MATERIAL = { solid: SOLID, glass: GLASS, auto: AUTO } as const;

// Labels draw in the text color around the box, the detail a step quieter, so one `text-*` class
// themes them on any surface.
const MUTED = "text-[color-mix(in_srgb,currentColor_60%,transparent)]";

/** Everything about the box that follows from its width (and how far a panel lifts): sizes, and where each label goes. */
function layout(width: number, liftShare: number = BOX.lift) {
  const wide = width >= WIDE;
  // Mirrors the stage's CSS height (twice the edge), so nothing jumps when this lands.
  const size = clamp(width * (wide ? 0.3 : 0.5), 96, wide ? 300 : 240);
  const half = size / 2;
  const depth = size * 9;
  const lift = size * liftShare;
  const height = size * 2;

  const corners: Vec[] = [];
  for (const x of [-half, half]) for (const y of [-half, half]) for (const z of [-half, half]) corners.push([x, y, z]);
  const lifted: Vec[] = [-half, half].flatMap((x) => [-half, half].map((z): Vec => [x, -half - lift, z]));
  const points = [...corners, ...lifted].map((point) => project(point, depth));
  const ys = points.map(([, y]) => y);
  const reach = Math.max(...points.map(([x]) => Math.abs(x))) + lift * 0.75;

  const cx = width / 2;
  const cy = height / 2 - (Math.min(...ys) + Math.max(...ys)) / 2;
  const bottom = cy + Math.max(...corners.map((point) => project(point, depth)[1]));

  // Each open face's center (where its light spills from), the point on its outer edge where the
  // leader line pins (just inside the panel, nearest the label), and the label's edge.
  const anchors = {} as Record<BlackBoxSide, { x: number; y: number; pin: [number, number]; edge: number }>;
  for (const side of SIDES) {
    const { normal, u, v, end } = FACE[side];
    const d = half + lift;
    const at = (a: number, b: number): Vec => {
      const r = (half - size * BOX.inset) * 0.8;
      return [0, 1, 2].map((i) => normal[i] * d + (u[i] * a + v[i] * b) * r) as Vec;
    };
    const [x, y] = project(at(0, 0), depth);
    let pin: [number, number] = [x, y];
    let best = -Infinity;
    for (const [a, b] of [[-1, -1], [1, -1], [1, 1], [-1, 1], [0, -1], [0, 1], [-1, 0], [1, 0]]) {
      const [px, py] = project(at(a, b), depth);
      // Furthest toward the label, preferring points level with the face's middle.
      const score = (end ? px : -px) - Math.abs(py - y) * 0.2;
      if (score > best) [best, pin] = [score, [px, py]];
    }
    anchors[side] = {
      x: cx + x,
      y: cy + y,
      pin: [cx + pin[0], cy + pin[1]],
      edge: end ? cx + reach + BOX.gap : cx - reach - BOX.gap,
    };
  }
  // A label gets the room beside the box, but never less than `minLabel`: in a tight column it
  // reaches a little past the box's edge (into a gutter) rather than wrap word by word.
  const room = clamp(width / 2 - reach - BOX.gap - 4, BOX.minLabel, 240);
  return { wide, size, half, depth, lift, width, height, cx, cy, bottom, anchors, room };
}

type Layout = ReturnType<typeof layout>;

export interface BlackBoxFace {
  /** A unique id. `value` and `onValueChange` use it. */
  id: string;
  /** What this part of your product is called: the label's title and the face's accessible name. */
  label: string;
  /** A line or two on what this part does, or just did. Shown under the label. */
  detail?: React.ReactNode;
  /** A small mark engraved in the middle of the panel. It lights up when the face opens. */
  icon?: React.ReactNode;
}

/** Why the open face changed: autoplay moved on, the visitor looked (hover, keyboard focus), or picked (click, tap, Enter). */
export type BlackBoxReason = "autoplay" | "preview" | "pick";

export interface BlackBoxProps
  extends Omit<React.HTMLAttributes<HTMLDivElement>, "defaultValue" | "onChange" | "children"> {
  /** Up to three faces, in the order autoplay opens them: the top, then the left side, then the right. */
  faces: BlackBoxFace[];
  /** The open face's id, or `null` for none, when you control it. */
  value?: string | null;
  /** The face that's open at first. None by default. */
  defaultValue?: string | null;
  /** Called whenever a face opens, or the box shuts (`null`), with the reason: autoplay, a preview (hover or keyboard focus) or a pick. */
  onValueChange?: (id: string | null, reason: BlackBoxReason) => void;
  /** Open each face in turn, 3.2s each, with a short rest shut between rounds, until the visitor picks one. A number sets the time per face in ms. */
  autoplay?: boolean | number;
  /** What the box is made of: glossy black, a glass wireframe, or `auto`, black in light mode and glass in dark. */
  material?: "auto" | "solid" | "glass";
  /** Show the open face's label beside the box on a leader line, or under it in narrow containers. Turn it off when your own copy says which part is open. */
  labels?: boolean;
  /** Fly the panels in and shut the box the first time it scrolls into view. */
  intro?: boolean;
  /** How far an open panel lifts off its socket, as a share of the box's edge. */
  lift?: number;
  /** Seconds an opening panel takes to lift (its spring's visual duration). */
  liftDuration?: number;
  /** How much an opening panel overshoots before it settles, from 0 (none) to 1. */
  liftBounce?: number;
}

const subscribeNothing = () => () => {};

/* ── Part refs ─────────────────────────────────────────── */

// Every element a face animates, filled in by callback refs and read only in effects.
type Part = "lifter" | "glow" | "rim" | "sheen" | "lit" | "spill" | "dot" | "line" | "label" | "caption";
type Parts = Map<string, HTMLElement>;
const key = (side: BlackBoxSide, part: Part) => `${side}:${part}`;

// The face ids, joined, stand in for the faces in effects; this finds an id's side among them.
const SEP = "\u0000";
const sideIn = (ids: string, id: string | null) => {
  const index = id === null ? -1 : ids.split(SEP).indexOf(id);
  return index < 0 ? null : SIDES[index];
};

/* ── BlackBox ──────────────────────────────────────────── */

export function BlackBox({
  faces,
  value,
  defaultValue = null,
  onValueChange,
  autoplay = true,
  material = "auto",
  labels = true,
  intro = true,
  lift = BOX.lift,
  liftDuration = TIMING.open.visualDuration,
  liftBounce = TIMING.open.bounce,
  className,
  style,
  onPointerEnter,
  onPointerLeave,
  onFocus,
  onBlur,
  ...props
}: BlackBoxProps) {
  const reduced = useReducedMotion() ?? false;
  const hydrated = React.useSyncExternalStore(subscribeNothing, () => true, () => false);

  const rootRef = React.useRef<HTMLDivElement>(null);
  const stageRef = React.useRef<HTMLDivElement>(null);
  const cubeRef = React.useRef<HTMLDivElement>(null);
  const coreRef = React.useRef<HTMLDivElement>(null);
  const buttons = React.useRef(new Map<string, HTMLButtonElement>());
  const parts = React.useRef<Parts>(new Map());
  const part = React.useCallback(
    (side: BlackBoxSide, name: Part) => (node: HTMLElement | null) => {
      if (node) parts.current.set(key(side, name), node);
      else parts.current.delete(key(side, name));
    },
    [],
  );

  const shown = faces.slice(0, 3).map((face, i) => ({ ...face, side: SIDES[i] }));
  const ids = shown.map((face) => face.id).join(SEP);

  const [width, setWidth] = React.useState(0);
  const liftShare = clamp(lift, 0, 1);
  const geo = React.useMemo(() => (width > 0 ? layout(width, liftShare) : null), [width, liftShare]);

  // Who's asking for which face: a hovering mouse, keyboard focus, a pin (click, tap, Enter) or
  // autoplay. The first one that has an answer wins.
  const [hover, setHover] = React.useState<string | null>(null);
  const [focus, setFocus] = React.useState<string | null>(null);
  const [pinned, setPinned] = React.useState<string | null>(null);
  const [auto, setAuto] = React.useState<string | null>(defaultValue);
  const reason = React.useRef<BlackBoxReason>("autoplay");

  const interval = autoplay === true ? TIMING.autoplay : typeof autoplay === "number" ? autoplay : 0;
  const [stopped, setStopped] = React.useState(false);
  const running = interval > 0 && !stopped && !reduced && shown.length > 0;
  const [pointerIn, setPointerIn] = React.useState(false);
  const [focusIn, setFocusIn] = React.useState(false);
  const [pageHidden, setPageHidden] = React.useState(false);
  const inView = useInView(rootRef, { amount: 0.4 });
  const paused = pointerIn || focusIn || pageHidden || !inView;
  const [shut, setShut] = React.useState(false); // the intro has played
  const ready = !intro || reduced || shut;

  // Under reduced motion nothing cycles, so the first face rests open where autoplay would have
  // shown it. Gated on hydration: useReducedMotion is already true in the hydration render.
  const still = hydrated && reduced && interval > 0 ? (shown[0]?.id ?? null) : null;
  const internal = hover ?? focus ?? pinned ?? auto ?? still;
  const requested = value !== undefined ? value : internal;
  const active = sideIn(ids, requested) ? requested : null;

  const timers = React.useRef({ intent: 0, leave: 0, resume: 0 });
  React.useEffect(() => {
    const current = timers.current;
    return () => {
      window.clearTimeout(current.intent);
      window.clearTimeout(current.leave);
      window.clearTimeout(current.resume);
    };
  }, []);

  // Tell the parent when the face the visitor (or autoplay) asked for changes.
  const onChange = React.useRef(onValueChange);
  React.useEffect(() => {
    onChange.current = onValueChange;
  });
  const reported = React.useRef(internal);
  React.useEffect(() => {
    if (internal === reported.current) return;
    reported.current = internal;
    onChange.current?.(internal, reason.current);
  }, [internal]);

  // Width drives every size; the observer reports once on its own as soon as it's attached.
  React.useEffect(() => {
    const root = rootRef.current;
    if (!root) return;
    const observer = new ResizeObserver(([entry]) => setWidth(Math.round(entry.contentRect.width)));
    observer.observe(root);
    return () => observer.disconnect();
  }, []);

  React.useEffect(() => {
    const update = () => setPageHidden(document.hidden);
    document.addEventListener("visibilitychange", update);
    return () => document.removeEventListener("visibilitychange", update);
  }, []);

  /* ── the box shuts ── */

  const played = React.useRef(false);
  React.useEffect(() => {
    const stage = stageRef.current;
    if (!geo || !stage || played.current) return;
    if (!intro || reduced) {
      played.current = true;
      stage.style.opacity = "1";
      return;
    }
    if (!inView) return;
    played.current = true;
    stage.style.opacity = "1";
    const far = `translateZ(${geo.size * BOX.spread}px)`;
    const frame = cubeRef.current?.querySelectorAll<HTMLElement>("[data-frame]") ?? [];
    frame.forEach((node) => animate(node, { opacity: [0, 1] }, TIMING.reveal));
    SIDES.forEach((side, i) => {
      const lifter = parts.current.get(key(side, "lifter"));
      const glow = parts.current.get(key(side, "glow"));
      if (lifter) {
        const delay = i * TIMING.stagger;
        animate(lifter, { transform: [far, AT_REST] }, { ...TIMING.assemble, delay });
        animate(lifter, { opacity: [0, 1] }, { ...TIMING.reveal, delay });
      }
      if (glow) animate(glow, { opacity: [0, 0.9, 0] }, { ...TIMING.flash, delay: TIMING.flashAfter + i * TIMING.flashStagger });
    });
    const timer = window.setTimeout(() => setShut(true), TIMING.shut);
    return () => window.clearTimeout(timer);
  }, [geo, intro, reduced, inView]);

  /* ── a face opens, another closes ── */

  const openRef = React.useRef<string | null>(null);
  const turns = React.useRef(0);
  const sizeRef = React.useRef("");

  const place = React.useCallback(
    (side: BlackBoxSide, open: boolean, g: Layout, instant: boolean) => {
      const get = (name: Part) => parts.current.get(key(side, name));
      const lifter = get("lifter");
      const quick = instant || reduced;
      const fade = quick ? TIMING.fade : open ? TIMING.show : TIMING.hide;
      const to = (node: HTMLElement | undefined, opacity: number, options: object = fade) => {
        if (node) animate(node, { opacity }, instant ? { duration: 0 } : options);
      };

      if (lifter) {
        const transform = open && !reduced ? `translateZ(${g.lift}px)` : AT_REST;
        const opening = { ...TIMING.open, visualDuration: Math.max(0.01, liftDuration), bounce: clamp(liftBounce, 0, 1) };
        animate(lifter, { transform }, instant ? { duration: 0 } : open ? opening : TIMING.close);
      }
      to(get("glow"), open ? 1 : 0);
      to(get("rim"), open ? 1 : 0);
      to(get("lit"), open ? 1 : 0);
      to(get("spill"), open ? 1 : 0, quick ? TIMING.fade : open ? TIMING.spill : TIMING.hide);

      const dot = get("dot");
      const line = get("line");
      const label = get("label");
      const caption = get("caption");
      if (!open || quick) {
        [dot, line, label, caption].forEach((node) => {
          if (!node) return;
          if (open) node.style.transform = "none";
          to(node, open ? 1 : 0);
        });
        return;
      }
      const sheen = get("sheen");
      if (sheen) animate(sheen, { transform: ["translateX(0%)", "translateX(330%)"] }, TIMING.sheen);
      if (dot) animate(dot, { opacity: [0, 1], transform: ["scale(0.3)", "scale(1)"] }, { ...TIMING.show, delay: TIMING.lead });
      if (line) animate(line, { opacity: [1, 1], transform: ["scaleX(0)", "scaleX(1)"] }, { ...TIMING.draw, delay: TIMING.lead });
      const rise = [`translateY(${BOX.rise}px)`, "translateY(0px)"];
      if (label) animate(label, { opacity: [0, 1], transform: rise }, { ...TIMING.show, delay: TIMING.lead + TIMING.labelLag });
      if (caption) animate(caption, { opacity: [0, 1], transform: rise }, { ...TIMING.show, delay: TIMING.lead });
    },
    [reduced, liftDuration, liftBounce],
  );

  React.useEffect(() => {
    if (!geo || !ready) return;
    // A new size (or lift) re-places whatever is open without animating.
    const shape = `${geo.size}:${geo.lift}`;
    const resized = sizeRef.current !== "" && sizeRef.current !== shape;
    sizeRef.current = shape;
    const before = openRef.current;
    if (before === active && !resized) return;
    openRef.current = active;

    const was = sideIn(ids, before);
    const now = sideIn(ids, active);
    if (resized && was === now) {
      if (now) place(now, true, geo, true);
      return;
    }
    if (was) place(was, false, geo, resized);
    if (now) place(now, true, geo, false);

    // The glass core, a smaller box inside the box, turns a quarter with each face that opens.
    const core = coreRef.current;
    if (now && core && !reduced) {
      turns.current += 1;
      animate(core, { transform: `rotateY(${turns.current * 90}deg)` }, TIMING.turn);
    }
  }, [active, geo, ready, reduced, place, ids]);

  // A new material swaps under a flash of light through the seams, so the change reads as the
  // box lighting up rather than a cut.
  const material0 = React.useRef(material);
  React.useEffect(() => {
    if (material0.current === material) return;
    material0.current = material;
    if (reduced) return;
    SIDES.forEach((side, i) => {
      const glow = parts.current.get(key(side, "glow"));
      if (glow && sideIn(ids, active) !== side) animate(glow, { opacity: [0.9, 0] }, { ...TIMING.flash, delay: i * TIMING.flashStagger });
    });
  }, [material, reduced, active, ids]);

  /* ── autoplay ── */

  // Faces in order, then a rest with the box shut. It moves on from whichever face is showing, so
  // a parent that also sets `value` (from a list beside the box, say) stays in step. The very
  // first face comes up right after the box has shut, so there's something to read at once.
  const started = React.useRef(false);
  React.useEffect(() => {
    if (!running || paused || !ready) return;
    const order: (string | null)[] = [...ids.split(SEP), null];
    const at = order.indexOf(active);
    const delay = active === null ? (started.current ? interval * TIMING.rest : TIMING.first) : interval;
    const timer = window.setTimeout(() => {
      const next = order[(at + 1) % order.length];
      started.current = true;
      reason.current = "autoplay";
      // Reported here rather than from the change, since a controlled box can be asked for the
      // face its own autoplay already holds, and that wouldn't count as a change.
      reported.current = next;
      onChange.current?.(next, "autoplay");
      setAuto(next);
    }, delay);
    return () => window.clearTimeout(timer);
  }, [running, paused, ready, active, interval, ids]);

  /* ── pins close on a tap outside ── */

  React.useEffect(() => {
    if (pinned === null) return;
    const away = (event: PointerEvent) => {
      if (rootRef.current?.contains(event.target as Node)) return;
      reason.current = "pick";
      setPinned(null);
    };
    document.addEventListener("pointerdown", away);
    return () => document.removeEventListener("pointerdown", away);
  }, [pinned]);

  /* ── handlers ── */

  const pick = (id: string) => {
    reason.current = "pick";
    setStopped(true);
    setAuto(null);
    if (pinned === id) {
      setPinned(null);
      setHover(null);
      setFocus(null);
    } else {
      setPinned(id);
    }
  };

  const enterFace = (event: React.PointerEvent, id: string) => {
    if (event.pointerType !== "mouse") return;
    window.clearTimeout(timers.current.intent);
    timers.current.intent = window.setTimeout(() => {
      reason.current = "preview";
      setHover(id);
    }, TIMING.intent);
  };

  const leaveFace = (event: React.PointerEvent) => {
    if (event.pointerType === "mouse") window.clearTimeout(timers.current.intent);
  };

  const enterBox = (event: React.PointerEvent<HTMLDivElement>) => {
    onPointerEnter?.(event);
    if (event.pointerType !== "mouse") return;
    window.clearTimeout(timers.current.leave);
    window.clearTimeout(timers.current.resume);
    setPointerIn(true);
  };

  const leaveBox = (event: React.PointerEvent<HTMLDivElement>) => {
    onPointerLeave?.(event);
    if (event.pointerType !== "mouse") return;
    window.clearTimeout(timers.current.intent);
    if (running) {
      // Autoplay carries on from the face that was left open, so nothing changes as you leave.
      if (hover !== null) setAuto(hover);
      setHover(null);
      timers.current.resume = window.setTimeout(() => setPointerIn(false), TIMING.resume);
      return;
    }
    setPointerIn(false);
    timers.current.leave = window.setTimeout(() => {
      reason.current = "preview";
      setHover(null);
    }, TIMING.linger);
  };

  const onKeyDown = (event: React.KeyboardEvent, index: number) => {
    const list = shown;
    let next = -1;
    if (event.key === "ArrowRight" || event.key === "ArrowDown") next = (index + 1) % list.length;
    else if (event.key === "ArrowLeft" || event.key === "ArrowUp") next = (index - 1 + list.length) % list.length;
    else if (event.key === "Home") next = 0;
    else if (event.key === "End") next = list.length - 1;
    else if (event.key === "Escape") {
      event.preventDefault();
      reason.current = "pick";
      setStopped(true);
      setPinned(null);
      setFocus(null);
      setHover(null);
      setAuto(null);
      return;
    }
    if (next < 0) return;
    event.preventDefault();
    buttons.current.get(list[next].id)?.focus();
  };

  const tabStop = active ?? shown[0]?.id;
  const g = geo;

  return (
    <div
      ref={rootRef}
      role="group"
      className={cn("relative isolate w-full @container/black-box", MATERIAL[material], className)}
      style={{ "--bb-light": "var(--black-box-light, #4f6bff)", ...style } as React.CSSProperties}
      onPointerEnter={enterBox}
      onPointerLeave={leaveBox}
      onFocus={(event) => {
        onFocus?.(event);
        setFocusIn(true);
      }}
      onBlur={(event) => {
        onBlur?.(event);
        if (!event.currentTarget.contains(event.relatedTarget as Node)) setFocusIn(false);
      }}
      {...props}
    >
      <div
        ref={stageRef}
        style={{ opacity: 0 }}
        className="relative h-[calc(2*clamp(96px,50cqw,240px))] @[36rem]/black-box:h-[calc(2*clamp(96px,30cqw,300px))]"
      >
        {g && (
          <>
            {/* Behind the box: its shadow, the glass glow, and the light each open face spills. */}
            <div aria-hidden className="pointer-events-none absolute inset-0">
              <div
                className="absolute rounded-[50%]"
                style={{
                  left: g.cx - g.size * 1.1,
                  top: g.cy - g.size * 0.8,
                  width: g.size * 2.2,
                  height: g.size * 1.9,
                  opacity: "var(--bb-shade)",
                  background: "radial-gradient(closest-side, rgb(0 0 0 / 0.09), transparent)",
                }}
              />
              <div
                className="absolute rounded-[50%]"
                style={{
                  left: g.cx - g.size * 0.62,
                  top: g.bottom - g.size * 0.09,
                  width: g.size * 1.24,
                  height: g.size * 0.24,
                  opacity: "var(--bb-shade)",
                  background: "radial-gradient(closest-side, rgb(0 0 0 / 0.28), transparent)",
                }}
              />
              <div
                className="absolute rounded-[50%]"
                style={{
                  left: g.cx - g.size,
                  top: g.cy - g.size,
                  width: g.size * 2,
                  height: g.size * 2,
                  opacity: "var(--bb-ghost)",
                  background: `radial-gradient(closest-side, color-mix(in srgb, ${LIGHT} 16%, transparent), transparent)`,
                }}
              />
              {shown.map(({ side }) => (
                <div
                  key={side}
                  ref={part(side, "spill")}
                  className="absolute rounded-[50%] will-change-[opacity]"
                  style={{
                    left: g.anchors[side].x - g.size * 0.85,
                    top: g.anchors[side].y - g.size * 0.85,
                    width: g.size * 1.7,
                    height: g.size * 1.7,
                    opacity: 0,
                    background: `radial-gradient(closest-side, color-mix(in srgb, ${LIGHT} 42%, transparent), transparent)`,
                  }}
                />
              ))}
            </div>

            {/* The box: a zero-size point at its center, in perspective, holding the posed cube. */}
            <div
              className="absolute size-0"
              style={{ left: g.cx, top: g.cy, perspective: g.depth }}
            >
              <div ref={cubeRef} className="absolute size-0 transform-3d" style={{ transform: POSE }}>
                {HIDDEN.map((placeFace) => (
                  <div
                    key={placeFace}
                    aria-hidden
                    className="pointer-events-none absolute"
                    style={{
                      left: -g.half,
                      top: -g.half,
                      width: g.size,
                      height: g.size,
                      opacity: "var(--bb-ghost)",
                      boxShadow: "inset 0 0 0 1px var(--bb-line)",
                      transform: `${placeFace} translateZ(${g.half}px)`,
                    }}
                  />
                ))}

                <div
                  ref={coreRef}
                  aria-hidden
                  className="pointer-events-none absolute size-0 transform-3d will-change-transform"
                  style={{ transform: "rotateY(0deg)", opacity: "var(--bb-ghost)" }}
                >
                  {CORE.map((placeFace) => (
                    <div
                      key={placeFace}
                      className="absolute"
                      style={{
                        left: (-g.size * BOX.core) / 2,
                        top: (-g.size * BOX.core) / 2,
                        width: g.size * BOX.core,
                        height: g.size * BOX.core,
                        transform: `${placeFace} translateZ(${(g.size * BOX.core) / 2}px)`,
                        background: `color-mix(in srgb, ${LIGHT} 14%, transparent)`,
                        boxShadow: `inset 0 0 0 1px ${LIGHT}, 0 0 ${g.size * 0.08}px color-mix(in srgb, ${LIGHT} 45%, transparent)`,
                      }}
                    />
                  ))}
                </div>

                {SIDES.map((side, index) => {
                  const face = shown[index];
                  const inset = g.size * BOX.inset;
                  const open = face !== undefined && face.id === active;
                  const panelClass = "absolute overflow-hidden rounded-[6%] will-change-transform";
                  const panelStyle: React.CSSProperties = {
                    left: inset,
                    top: inset,
                    width: g.size - inset * 2,
                    height: g.size - inset * 2,
                    transform: AT_REST,
                    outlineColor: LIGHT,
                  };
                  const inside = (
                    <>
                      <span aria-hidden className="absolute inset-0" style={{ background: FACE[side].fill }} />
                      <span
                        aria-hidden
                        className="absolute inset-0 rounded-[inherit]"
                        style={{ boxShadow: "inset 0 0 0 1px var(--bb-rim), inset 0 1px 0 rgb(255 255 255 / 0.06)" }}
                      />
                      <span
                        ref={part(side, "rim")}
                        aria-hidden
                        className="absolute inset-0 rounded-[inherit] will-change-[opacity]"
                        style={{
                          opacity: 0,
                          boxShadow: `inset 0 0 0 1px ${LIGHT}, inset 0 0 ${g.size * 0.14}px -${g.size * 0.03}px ${LIGHT}`,
                        }}
                      />
                      <span
                        ref={part(side, "sheen")}
                        aria-hidden
                        className="absolute -inset-y-1/4 -left-1/2 w-1/2 -skew-x-12 bg-linear-to-r from-transparent via-white/15 to-transparent will-change-transform"
                      />
                      {face?.icon && (
                        <span
                          aria-hidden
                          className="absolute inset-0 grid place-items-center [&_svg]:size-full"
                        >
                          <span className="relative" style={{ width: g.size * 0.2, height: g.size * 0.2 }}>
                            <span className="absolute inset-0" style={{ color: "var(--bb-icon)" }}>
                              {face.icon}
                            </span>
                            <span
                              ref={part(side, "lit")}
                              className="absolute inset-0 will-change-[opacity]"
                              style={{ color: LIGHT, opacity: 0 }}
                            >
                              {face.icon}
                            </span>
                          </span>
                        </span>
                      )}
                    </>
                  );

                  return (
                    <div
                      key={side}
                      className="absolute transform-3d"
                      style={{
                        left: -g.half,
                        top: -g.half,
                        width: g.size,
                        height: g.size,
                        transform: `${FACE[side].place} translateZ(${g.half}px)`,
                      }}
                    >
                      <div
                        data-frame
                        aria-hidden
                        className="pointer-events-none absolute inset-0"
                        style={{ background: "var(--bb-body)", boxShadow: "inset 0 0 0 1px var(--bb-line)" }}
                      />
                      <div
                        ref={part(side, "glow")}
                        aria-hidden
                        className="pointer-events-none absolute will-change-[opacity]"
                        style={{
                          inset: inset * 0.4,
                          opacity: 0,
                          borderRadius: "6%",
                          background: `radial-gradient(closest-side, color-mix(in srgb, ${LIGHT} 72%, white), color-mix(in srgb, ${LIGHT} 85%, transparent))`,
                        }}
                      />
                      {face ? (
                        <button
                          ref={(node) => {
                            part(side, "lifter")(node);
                            if (node) buttons.current.set(face.id, node);
                            else buttons.current.delete(face.id);
                          }}
                          type="button"
                          aria-pressed={open}
                          tabIndex={face.id === tabStop ? 0 : -1}
                          className={cn(panelClass, "cursor-pointer outline-offset-[-3px] focus-visible:outline-2")}
                          style={panelStyle}
                          onClick={() => pick(face.id)}
                          onPointerEnter={(event) => enterFace(event, face.id)}
                          onPointerLeave={leaveFace}
                          onFocus={(event) => {
                            if (!event.currentTarget.matches(":focus-visible")) return;
                            reason.current = "preview";
                            setFocus(face.id);
                          }}
                          onBlur={() => setFocus(null)}
                          onKeyDown={(event) => onKeyDown(event, index)}
                        >
                          {inside}
                          <span className="sr-only">
                            {face.label}
                            {face.detail ? <>: {face.detail}</> : null}
                          </span>
                        </button>
                      ) : (
                        <div ref={part(side, "lifter")} aria-hidden className={panelClass} style={panelStyle}>
                          {inside}
                        </div>
                      )}
                    </div>
                  );
                })}
              </div>
            </div>

            {/* Leader lines and labels beside the box, from 36rem up. */}
            {labels && g.wide && (
              <div aria-hidden className="pointer-events-none absolute inset-0">
                {shown.map(({ id, side, label, detail }) => {
                  const anchor = g.anchors[side];
                  const [pinX, pinY] = anchor.pin;
                  const end = FACE[side].end;
                  const from = Math.min(pinX, anchor.edge + (end ? -6 : 6));
                  const to = Math.max(pinX, anchor.edge + (end ? -6 : 6));
                  return (
                    <React.Fragment key={id}>
                      <span
                        ref={part(side, "line")}
                        className="absolute h-px will-change-transform"
                        style={{
                          left: from,
                          top: pinY - 0.5,
                          width: to - from,
                          opacity: 0,
                          transformOrigin: end ? "left center" : "right center",
                          background: end
                            ? `linear-gradient(to right, ${LIGHT}, color-mix(in srgb, ${LIGHT} 35%, transparent))`
                            : `linear-gradient(to left, ${LIGHT}, color-mix(in srgb, ${LIGHT} 35%, transparent))`,
                        }}
                      />
                      <span
                        ref={part(side, "dot")}
                        className="absolute size-2 rounded-full will-change-transform"
                        style={{
                          left: pinX - 4,
                          top: pinY - 4,
                          opacity: 0,
                          background: `color-mix(in srgb, ${LIGHT} 70%, white)`,
                          boxShadow: `0 0 0 3px color-mix(in srgb, ${LIGHT} 35%, transparent), 0 0 14px 2px ${LIGHT}`,
                        }}
                      />
                      <div
                        ref={part(side, "label")}
                        className={cn("absolute will-change-transform", end ? "text-left" : "text-right")}
                        style={{
                          top: pinY - 9,
                          maxWidth: g.room,
                          opacity: 0,
                          ...(end ? { left: anchor.edge } : { right: g.width - anchor.edge }),
                        }}
                      >
                        <p className="text-[13px] leading-[18px] font-medium tracking-[-0.01em]">{label}</p>
                        {detail ? <p className={cn("mt-1 text-xs leading-[17px] text-pretty", MUTED)}>{detail}</p> : null}
                      </div>
                    </React.Fragment>
                  );
                })}
              </div>
            )}
          </>
        )}
      </div>

      {/* Under 36rem the label sits under the box instead, one at a time in the same spot. */}
      {labels && (
        <div aria-hidden className="pointer-events-none grid justify-items-center px-4 text-center @[36rem]/black-box:hidden">
          {shown.map(({ id, side, label, detail }) => (
            <div
              key={id}
              ref={part(side, "caption")}
              className="col-start-1 row-start-1 max-w-72 will-change-transform"
              style={{ opacity: 0 }}
            >
              <p className="text-sm font-medium tracking-[-0.01em]">{label}</p>
              {detail ? <p className={cn("mt-1 text-[13px] leading-5 text-pretty", MUTED)}>{detail}</p> : null}
            </div>
          ))}
        </div>
      )}
    </div>
  );
}
```
