# SLR Viewfinder Photo Gallery with a Split-Image Focusing Circle You Line Up Before the Shutter Fires

> Viewfinder gallery: each photo arrives blurred, you turn the lens ring until the split circle lines up, then the shutter fires. Free React and HTML code.

A shadcn/ui block from the [@weinc](https://we.inc/components?utm_source=shadcn.exchange) registry, listed on [shadcn.exchange](https://shadcn.exchange/r/weinc/slr-viewfinder-photo-gallery-with-a-split-image-focusing-circle-you-line-up-before-the-shutter-fires).

- Page, with a live preview: https://shadcn.exchange/r/weinc/slr-viewfinder-photo-gallery-with-a-split-image-focusing-circle-you-line-up-before-the-shutter-fires
- Screenshot: https://shadcn.exchange/shot/weinc/slr-viewfinder-photo-gallery-with-a-split-image-focusing-circle-you-line-up-before-the-shutter-fires/light.png
- Registry item: https://we.inc/r/slr-viewfinder-photo-gallery-with-a-split-image-focusing-circle-you-line-up-before-the-shutter-fires.json
- Listed: 2026-10-28, source last changed 2026-10-28

## Install

```bash
npx shadcn@latest add https://shadcn.exchange/r/weinc/slr-viewfinder-photo-gallery-with-a-split-image-focusing-circle-you-line-up-before-the-shutter-fires.json
```

Run it in a project that has shadcn/ui set up. It adds the files below and installs what they depend on.

## Files

### `components/we-inc/slr-viewfinder-photo-gallery-with-a-split-image-focusing-circle-you-line-up-before-the-shutter-fires.tsx`

```tsx
import { useEffect, useRef, useState } from "react";

// The frames on the roll. `focus` is where on the ring (0 near, 1 far) this picture is sharp.
const shots = [
  { photo: "https://images.pexels.com/photos/3765140/pexels-photo-3765140.jpeg?auto=compress&cs=tinysrgb&fit=crop&w=1200&h=800", alt: "Young woman in oversized pale blue sunglasses and a mustard roll-neck against a turquoise wall", tone: "#3aa9c4", focus: 0.58, speed: "250", stop: "2.8" },
  { photo: "https://images.pexels.com/photos/14793008/pexels-photo-14793008.jpeg?auto=compress&cs=tinysrgb&fit=crop&w=1200&h=800", alt: "Woman with long hair lit deep blue in front of a yellow backdrop", tone: "#8a7a10", focus: 0.3, speed: "125", stop: "2" },
  { photo: "https://images.pexels.com/photos/9769861/pexels-photo-9769861.jpeg?auto=compress&cs=tinysrgb&fit=crop&w=1200&h=800", alt: "Woman with braided hair lifting green oval sunglasses, wearing a pale green jacket", tone: "#8f9499", focus: 0.76, speed: "500", stop: "4" },
  { photo: "https://images.pexels.com/photos/20829098/pexels-photo-20829098.jpeg?auto=compress&cs=tinysrgb&fit=crop&w=1200&h=800", alt: "Woman with bright red hair falling across half her face in low evening sun", tone: "#6b5a4c", focus: 0.44, speed: "1000", stop: "1.4" },
];
const distances = ["0.45", "0.5", "0.6", "0.7", "0.85", "1", "1.2", "1.5", "2", "3", "5", "10", "∞"]; // metres, engraved round the ring
const SPAN = 2.7; // radians the ring turns from nearest focus to infinity
const SHARP = 0.012; // how close the ring must be to count as in focus
const INDEX = "#ff8a3c"; // the painted index line under the ring

const GRAIN = `url("data:image/svg+xml,${encodeURIComponent('<svg xmlns="http://www.w3.org/2000/svg" width="160" height="160"><filter id="n"><feTurbulence type="fractalNoise" baseFrequency="0.9" numOctaves="2" stitchTiles="stitch"/><feColorMatrix values="0 0 0 0 0.5  0 0 0 0 0.5  0 0 0 0 0.5  0 0 0 0.9 0"/></filter><rect width="160" height="160" filter="url(#n)"/></svg>')}")`;
const PRISM = "repeating-conic-gradient(#000 0 25%, transparent 0 50%)";

export default function SlrViewfinderSplitImageGallery() {
  const view = useRef<HTMLDivElement>(null);
  const glass = useRef<HTMLDivElement>(null);
  const ringBox = useRef<HTMLDivElement>(null);
  const canvas = useRef<HTMLCanvasElement>(null);
  const [index, setIndex] = useState(0);
  const [sharp, setSharp] = useState(false);
  const [ringValue, setRingValue] = useState(0.2);
  const [firing, setFiring] = useState(false);
  const api = useRef<{ fire: (fromUser: boolean) => void; turnTo: (v: number) => void } | null>(null);

  useEffect(() => {
    const root = view.current, pane = glass.current, box = ringBox.current, cv = canvas.current;
    if (!root || !pane || !box || !cv) return;
    const ctx = cv.getContext("2d");
    if (!ctx) return;
    const reduce = window.matchMedia("(prefers-reduced-motion: reduce)").matches;
    const dpr = Math.min(2, window.devicePixelRatio || 1);
    const s = { v: reduce ? shots[0].focus : shots[0].focus - 0.3, vel: 0, target: null as number | null, drag: false, idx: 0, sharp: false, busy: false, W: 300, H: 78 };
    const timers: number[] = [];
    const later = (fn: () => void, ms: number) => { timers.push(window.setTimeout(fn, ms)); };

    const size = () => {
      const g = pane.getBoundingClientRect();
      root.style.setProperty("--slrvf-w", `${g.width}px`); root.style.setProperty("--slrvf-h", `${g.height}px`);
      s.W = box.clientWidth; s.H = box.clientHeight;
      cv.width = Math.round(s.W * dpr); cv.height = Math.round(s.H * dpr);
    };

    // The focus ring, seen from above: a cylinder, so everything bunches up towards the two edges.
    const drawRing = () => {
      const { W, H } = s, cx = W / 2, R = W / 2 - 1, GRIP = Math.round(H * 0.56), TL = -0.5;
      ctx.setTransform(dpr, 0, 0, dpr, 0, 0);
      ctx.clearRect(0, 0, W, H);
      ctx.fillStyle = "#070708"; ctx.fillRect(cx - R, 0, R * 2, H);
      const phi = s.v * SPAN, step = 0.074;
      const k0 = Math.floor((phi - Math.PI / 2) / step);
      for (let k = k0; k <= k0 + Math.ceil(Math.PI / step) + 1; k++) {
        const th = k * step - phi;
        if (th < -Math.PI / 2 || th > Math.PI / 2) continue;
        const x0 = cx + R * Math.sin(th), x1 = cx + R * Math.sin(Math.min(Math.PI / 2, th + step * 0.56));
        const light = Math.pow(Math.max(0, Math.cos(th - TL)), 4), body = Math.max(0, Math.cos(th));
        const v = 17 + 64 * light + 16 * body;
        const g = ctx.createLinearGradient(0, 2, 0, GRIP - 2);
        g.addColorStop(0, `rgb(${v * 0.45},${v * 0.45},${v * 0.48})`); g.addColorStop(0.12, `rgb(${v + 14},${v + 14},${v + 17})`); g.addColorStop(0.22, `rgb(${v},${v},${v + 3})`); g.addColorStop(0.85, `rgb(${v * 0.9},${v * 0.9},${v * 0.93})`); g.addColorStop(1, `rgb(${v * 0.35},${v * 0.35},${v * 0.38})`);
        ctx.fillStyle = g; ctx.fillRect(x0, 2, Math.max(0.6, x1 - x0), GRIP - 4);
        ctx.fillStyle = `rgba(255,255,255,${0.05 + 0.3 * light})`; ctx.fillRect(x0, 2, 0.7, GRIP - 4);
      }
      // bright metal band under the rubber
      const chrome = ctx.createLinearGradient(cx - R, 0, cx + R, 0);
      ([[0, 12], [0.1, 60], [0.2, 120], [0.265, 250], [0.31, 150], [0.45, 72], [0.62, 44], [0.8, 86], [0.93, 40], [1, 8]] as [number, number][]).forEach(([p, v]) => chrome.addColorStop(p, `rgb(${v},${v},${Math.min(255, v + 6)})`));
      ctx.fillStyle = chrome; ctx.fillRect(cx - R, GRIP, R * 2, 4);
      ctx.fillStyle = "rgba(0,0,0,0.6)"; ctx.fillRect(cx - R, GRIP + 4, R * 2, 1);
      // engraved distance scale
      ctx.fillStyle = "#121214"; ctx.fillRect(cx - R, GRIP + 5, R * 2, H - GRIP - 5);
      ctx.textAlign = "center"; ctx.textBaseline = "middle";
      const n = distances.length, mid = GRIP + 5 + (H - GRIP - 5) * 0.56;
      for (let i = 0; i < n * 4 - 3; i++) {
        const th = (i / (n * 4 - 4) - s.v) * SPAN;
        if (Math.abs(th) > 1.5) continue;
        const x = cx + R * Math.sin(th), c = Math.cos(th), major = i % 4 === 0;
        ctx.fillStyle = `rgba(238,240,243,${(major ? 0.85 : 0.4) * (0.25 + 0.75 * c)})`;
        ctx.fillRect(x - 0.5, GRIP + 6, 1, major ? 5 : 3);
        if (!major) continue;
        ctx.save(); ctx.translate(x, mid + 1.5); ctx.scale(Math.max(0.05, c), 1);
        ctx.font = `600 ${distances[i / 4] === "∞" ? 15 : 11}px ui-sans-serif, system-ui, -apple-system, Helvetica, Arial, sans-serif`;
        ctx.fillStyle = `rgba(0,0,0,0.8)`; ctx.fillText(distances[i / 4], 0, 0.8);
        ctx.fillStyle = `rgba(238,240,243,${0.2 + 0.75 * c})`; ctx.fillText(distances[i / 4], 0, 0);
        ctx.restore();
      }
      // roundness: dark limbs and one long highlight where the lamp hits the barrel
      const limb = ctx.createLinearGradient(cx - R, 0, cx + R, 0);
      limb.addColorStop(0, "rgba(0,0,0,0.94)"); limb.addColorStop(0.16, "rgba(0,0,0,0.3)"); limb.addColorStop(0.255, "rgba(255,255,255,0.1)"); limb.addColorStop(0.3, "rgba(255,255,255,0.02)"); limb.addColorStop(0.6, "rgba(0,0,0,0.08)"); limb.addColorStop(0.86, "rgba(0,0,0,0.5)"); limb.addColorStop(1, "rgba(0,0,0,0.96)");
      ctx.fillStyle = limb; ctx.fillRect(cx - R, 0, R * 2, H);
    };

    const apply = () => {
      const want = shots[s.idx].focus;
      const e = Math.max(-1, Math.min(1, (s.v - want) * 3.2));
      root.style.setProperty("--slrvf-e", e.toFixed(4)); root.style.setProperty("--slrvf-a", Math.abs(e).toFixed(4));
      const ok = Math.abs(s.v - want) < SHARP;
      root.dataset.focus = ok ? "ok" : e < 0 ? "near" : "far";
      if (ok !== s.sharp) { s.sharp = ok; setSharp(ok); }
      drawRing();
    };

    let raf = 0, last = 0, seen = true, touched = -1e9, heldSince = 0, startAt = performance.now() + 350, hold = 5200;
    const fire = (fromUser: boolean) => {
      if (fromUser) touched = performance.now();
      if (s.busy) return;
      s.busy = true;
      const next = () => { s.idx = (s.idx + 1) % shots.length; setIndex(s.idx); heldSince = 0; startAt = performance.now() + 900; s.target = null; apply(); };
      if (reduce) { next(); s.busy = false; return; }
      setFiring(true);
      later(next, 150);
      later(() => { setFiring(false); s.busy = false; wake(); }, 460);
    };
    const frame = (now: number) => {
      raf = 0;
      const dt = Math.min(0.033, (now - last) / 1000); last = now;
      const idle = now - touched > 8000;
      if (idle && !s.drag && !s.busy) {
        if (s.target === null && now > startAt) s.target = shots[s.idx].focus;
        if (s.sharp) { if (!heldSince) heldSince = now; else if (now - heldSince > hold) { hold = 2600; fire(false); } } else heldSince = 0;
      }
      if (s.target !== null && !s.drag) {
        // hunt a little past the mark and come back, the way a hand does
        s.vel += (62 * (s.target - s.v) - 9.5 * s.vel) * dt; s.v += s.vel * dt;
        if (Math.abs(s.target - s.v) < 0.0006 && Math.abs(s.vel) < 0.004) { s.v = s.target; s.vel = 0; s.target = null; setRingValue(s.v); }
      }
      apply();
      if (seen && !document.hidden) raf = requestAnimationFrame(frame);
    };
    const wake = () => { if (!raf && !reduce) { last = performance.now(); raf = requestAnimationFrame(frame); } };
    api.current = {
      fire,
      turnTo: (v) => { touched = performance.now(); s.target = null; s.vel = 0; s.v = v; setRingValue(v); if (reduce) apply(); else wake(); },
    };

    // Dragging the ring: the surface follows the finger, and it drops into focus when let go close enough.
    let lastX = 0;
    const onDown = (e: PointerEvent) => { s.drag = true; s.target = null; s.vel = 0; lastX = e.clientX; touched = performance.now(); box.setPointerCapture(e.pointerId); e.preventDefault(); wake(); };
    const onMove = (e: PointerEvent) => {
      if (!s.drag) return;
      touched = performance.now();
      s.v = Math.max(0, Math.min(1, s.v - (e.clientX - lastX) / ((s.W / 2) * SPAN))); lastX = e.clientX;
      if (reduce) apply();
    };
    const onUp = () => {
      if (!s.drag) return;
      s.drag = false; touched = performance.now();
      const want = shots[s.idx].focus;
      if (Math.abs(s.v - want) < 0.05) { if (reduce) { s.v = want; apply(); } else s.target = want; }
      setRingValue(s.v);
    };
    box.addEventListener("pointerdown", onDown); box.addEventListener("pointermove", onMove);
    box.addEventListener("pointerup", onUp); box.addEventListener("pointercancel", onUp);
    const ro = new ResizeObserver(() => { size(); apply(); });
    ro.observe(pane); ro.observe(box);
    size(); apply(); wake();
    const io = new IntersectionObserver(([entry]) => { seen = entry.isIntersecting; if (seen) wake(); });
    io.observe(root);
    const onVis = () => { if (!document.hidden) wake(); };
    document.addEventListener("visibilitychange", onVis);
    return () => {
      cancelAnimationFrame(raf); timers.forEach((t) => window.clearTimeout(t)); api.current = null;
      box.removeEventListener("pointerdown", onDown); box.removeEventListener("pointermove", onMove);
      box.removeEventListener("pointerup", onUp); box.removeEventListener("pointercancel", onUp);
      ro.disconnect(); io.disconnect(); document.removeEventListener("visibilitychange", onVis);
    };
  }, []);

  const shot = shots[index];
  // One copy of the picture, sized and placed to sit exactly over the ground glass wherever it is nested.
  const copy = (extra = "") => (
    <img src={shot.photo} alt="" width={1200} height={800} draggable={false} className={`absolute max-w-none object-cover ${extra}`} style={{ width: "var(--slrvf-w)", height: "var(--slrvf-h)", left: "calc(50% - var(--slrvf-w) / 2)", top: "calc(50% - var(--slrvf-h) / 2)" }} />
  );
  const prism = (shift: string, offset: string) => (
    <div className="absolute inset-0" style={{ WebkitMaskImage: PRISM, maskImage: PRISM, WebkitMaskSize: "7px 7px", maskSize: "7px 7px", WebkitMaskPosition: offset, maskPosition: offset }}>
      <div className="absolute inset-0 will-change-transform" style={{ transform: `translate3d(calc(var(--slrvf-e) * ${shift}), 0, 0)` }}>{copy()}</div>
    </div>
  );

  return (
    <div ref={view} data-focus="near" className="slrvf flex min-h-[560px] w-full flex-col items-center justify-center overflow-hidden bg-[#050506] px-3 py-8 sm:min-h-[780px] sm:px-6" style={{ ["--slrvf-e" as string]: "-0.96", ["--slrvf-a" as string]: "0.96", ["--slrvf-w" as string]: "300px", ["--slrvf-h" as string]: "200px" }}>
      <style>{`
        @keyframes slrvf-slap { 0% { transform: translateY(-102%); } 22% { transform: translateY(0); } 62% { transform: translateY(0); } 100% { transform: translateY(-102%); } }
        .slrvf .slrvf-mirror { transform: translateY(-102%); }
        .slrvf .slrvf-mirror[data-on="true"] { animation: slrvf-slap 460ms cubic-bezier(0.5, 0, 0.3, 1) both; }
        .slrvf .slrvf-led { color: #ff4a36; text-shadow: 0 0 6px rgba(255, 60, 40, 0.85), 0 0 1px #ff9a8a; }
        .slrvf .slrvf-arrow, .slrvf .slrvf-dot { opacity: 0.16; transition: opacity 120ms linear; }
        .slrvf[data-focus="near"] .slrvf-arrow-r, .slrvf[data-focus="far"] .slrvf-arrow-l { opacity: 1; }
        .slrvf[data-focus="ok"] .slrvf-dot { opacity: 1; }
        .slrvf .slrvf-dot { color: #5dff8c; text-shadow: 0 0 7px rgba(70, 255, 130, 0.9); }
        @media (prefers-reduced-motion: reduce) { .slrvf .slrvf-mirror[data-on="true"] { animation: none; } .slrvf .slrvf-arrow, .slrvf .slrvf-dot { transition: none; } }
      `}</style>

      {/* The view through the eyepiece: nothing but black round the screen. */}
      <div className="w-full max-w-[820px]">
        <div className="overflow-hidden rounded-[6px] bg-black sm:rounded-[10px]">
          {/* The ground glass. */}
          <div ref={glass} role="img" aria-label={shot.alt} className="relative aspect-[3/2] w-full overflow-hidden" style={{ backgroundColor: shot.tone }}>
            <img src={shot.photo} alt="" width={1200} height={800} draggable={false} className="absolute inset-0 h-full w-full object-cover" />
            <div className="absolute inset-0" style={{ opacity: "calc(var(--slrvf-a) * 2.6)" }}>
              <img src={shot.photo} alt="" width={1200} height={800} draggable={false} className="absolute inset-0 h-full w-full scale-[1.02] object-cover blur-[3px] sm:blur-[5px]" />
            </div>
            <div className="absolute inset-0" style={{ opacity: "calc((var(--slrvf-a) - 0.3) * 2.2)" }}>
              <img src={shot.photo} alt="" width={1200} height={800} draggable={false} className="absolute inset-0 h-full w-full scale-[1.06] object-cover blur-[9px] sm:blur-[18px]" />
            </div>
            {/* fresnel rings and the grain of the screen */}
            <div aria-hidden="true" className="absolute inset-0 opacity-[0.5] mix-blend-overlay" style={{ backgroundImage: GRAIN }} />
            <div aria-hidden="true" className="absolute inset-0 bg-[repeating-radial-gradient(circle_at_50%_50%,rgba(255,255,255,0.035)_0_1px,rgba(0,0,0,0)_1px_4px)]" />

            {/* microprism collar: two copies seen through opposite squares of a fine check, pulled apart when out of focus */}
            <div aria-hidden="true" className="absolute left-1/2 top-1/2 aspect-square h-[40%] -translate-x-1/2 -translate-y-1/2 overflow-hidden rounded-full shadow-[0_0_0_1px_rgba(0,0,0,0.42),0_0_0_2px_rgba(255,255,255,0.14)]">
              {prism("5.5%", "0 0")}
              {prism("-5.5%", "3.5px 0")}
              <div className="absolute inset-0 bg-black" style={{ opacity: "calc(var(--slrvf-a) * 0.22)" }} />
              <div className="absolute inset-0 opacity-[0.07]" style={{ backgroundImage: PRISM, backgroundSize: "7px 7px" }} />
            </div>
            {/* split image: the top half slides one way and the bottom half the other until the edges meet */}
            <div aria-hidden="true" className="absolute left-1/2 top-1/2 aspect-square h-[23%] -translate-x-1/2 -translate-y-1/2 overflow-hidden rounded-full shadow-[0_0_0_1px_rgba(0,0,0,0.5),0_0_0_2px_rgba(255,255,255,0.18)]">
              <div className="absolute inset-x-0 top-0 h-1/2 overflow-hidden">
                <div className="absolute left-0 top-0 h-[200%] w-full will-change-transform" style={{ transform: "translate3d(calc(var(--slrvf-e) * 17%), 0, 0)" }}>{copy()}</div>
              </div>
              <div className="absolute inset-x-0 top-1/2 h-1/2 overflow-hidden">
                <div className="absolute left-0 top-[-100%] h-[200%] w-full will-change-transform" style={{ transform: "translate3d(calc(var(--slrvf-e) * -17%), 0, 0)" }}>{copy()}</div>
              </div>
              <div className="absolute inset-x-0 top-1/2 h-px -translate-y-1/2 bg-black/45" />
              <div className="absolute inset-0 rounded-full bg-[linear-gradient(180deg,rgba(255,255,255,0.1),rgba(255,255,255,0)_45%,rgba(0,0,0,0.08))]" />
            </div>

            {/* the edge of the eyepiece and the mirror that swings up when the shutter fires */}
            <div aria-hidden="true" className="absolute inset-0 shadow-[inset_0_0_90px_10px_rgba(0,0,0,0.55),inset_0_0_10px_1px_rgba(0,0,0,0.7)]" />
            <div aria-hidden="true" data-on={firing} className="slrvf-mirror absolute inset-0 bg-black" />
          </div>

          {/* readout along the bottom of the finder */}
          <div className="flex h-[26px] items-center justify-between bg-black px-3 font-mono text-[11px] font-bold tracking-[0.12em] sm:h-[38px] sm:px-6 sm:text-[15px]">
            <span className="slrvf-led">{shot.speed}<span className="mx-2 opacity-60 sm:mx-3">F{shot.stop}</span></span>
            <span aria-hidden="true" className="flex items-center gap-2 sm:gap-3">
              <span className="slrvf-led slrvf-arrow slrvf-arrow-r">{"▶"}</span>
              <span className="slrvf-dot text-[13px] sm:text-[17px]">{"●"}</span>
              <span className="slrvf-led slrvf-arrow slrvf-arrow-l">{"◀"}</span>
            </span>
            <span className="slrvf-led">{String(index + 1).padStart(2, "0")}<span className="opacity-60">/{String(shots.length).padStart(2, "0")}</span></span>
          </div>
        </div>
      </div>

      {/* The lens barrel from above, and the shutter release. */}
      <div className="mt-6 flex w-full max-w-[620px] items-center gap-4 sm:mt-8 sm:gap-7">
        <div className="relative min-w-0 flex-1">
          <div ref={ringBox} className="relative h-[66px] w-full cursor-ew-resize touch-none overflow-hidden rounded-[5px] shadow-[0_16px_26px_-10px_rgba(0,0,0,1),0_0_0_1px_rgba(0,0,0,1)] sm:h-[82px]">
            <canvas ref={canvas} aria-hidden="true" className="absolute inset-0 h-full w-full" />
          </div>
          <span aria-hidden="true" className="absolute left-1/2 top-full mt-[5px] h-[9px] w-[2px] -translate-x-1/2 rounded-full" style={{ backgroundColor: INDEX }} />
          <label htmlFor="slrvf-ring" className="sr-only">Focus ring</label>
          <input
            id="slrvf-ring"
            type="range"
            min={0}
            max={1}
            step={0.01}
            value={Math.round(ringValue * 100) / 100}
            onChange={(e) => api.current?.turnTo(Number(e.target.value))}
            aria-valuetext={sharp ? "In focus" : "Out of focus"}
            className="peer pointer-events-none absolute inset-0 h-full w-full opacity-0"
          />
          <span aria-hidden="true" className="pointer-events-none absolute -inset-[5px] rounded-[9px] ring-2 ring-[#ff8a3c] opacity-0 peer-focus-visible:opacity-100" />
        </div>
        <button
          type="button"
          onClick={() => api.current?.fire(true)}
          aria-label="Release the shutter and wind on to the next frame"
          className="group relative h-[58px] w-[58px] shrink-0 rounded-full bg-[conic-gradient(from_210deg,#f4f5f7,#6d7178_14%,#d9dbdf_28%,#3d4046_44%,#babdc3_58%,#55585e_72%,#eceef0_86%,#f4f5f7)] shadow-[0_14px_22px_-8px_rgba(0,0,0,1),0_2px_3px_rgba(0,0,0,0.9),inset_0_0_0_1px_rgba(0,0,0,0.45)] outline-none transition-transform duration-100 focus-visible:ring-2 focus-visible:ring-[#ff8a3c] focus-visible:ring-offset-4 focus-visible:ring-offset-[#050506] active:translate-y-[1px] active:scale-[0.97] motion-reduce:transition-none sm:h-[72px] sm:w-[72px]"
        >
          <span aria-hidden="true" className="absolute inset-[7px] rounded-full bg-[conic-gradient(from_30deg,#2a2c30,#8d9097_18%,#e9eaed_30%,#7a7d84_46%,#33353a_62%,#a9acb2_80%,#2a2c30)] shadow-[inset_0_0_0_1px_rgba(0,0,0,0.55)]" />
          <span aria-hidden="true" className="absolute inset-[11px] rounded-full bg-[radial-gradient(circle_at_50%_70%,#f3f4f6_0%,#b3b6bc_38%,#5c5f66_78%,#2c2e32_100%)] shadow-[inset_0_3px_5px_rgba(0,0,0,0.55),inset_0_-1px_1px_rgba(255,255,255,0.7)] transition-shadow group-active:shadow-[inset_0_5px_8px_rgba(0,0,0,0.75)]" />
          <span aria-hidden="true" className="absolute left-1/2 top-1/2 h-[10px] w-[10px] -translate-x-1/2 -translate-y-1/2 rounded-full bg-[radial-gradient(circle,#050506_45%,#3a3c41_60%,#0c0c0d_100%)] shadow-[0_1px_0_rgba(255,255,255,0.55)] sm:h-[12px] sm:w-[12px]" />
        </button>
      </div>
      <p aria-live="polite" className="mt-6 min-h-[20px] text-center font-mono text-[11px] uppercase leading-5 tracking-[0.16em] text-zinc-400 sm:mt-7">
        <span className="text-zinc-100">{sharp ? "In focus" : "Turn the ring"}</span>
        <span className="mx-2 text-zinc-600">/</span>
        {sharp ? "press the release" : "line up the two halves"}
      </p>
    </div>
  );
}
```
