{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "flock-form",
  "title": "Flock Form",
  "description": "A flock of birds flies in, forms an arrow, then settles into the R mark.",
  "dependencies": ["remotion"],
  "files": [
    {
      "path": "registry/remocn/flock-form/index.tsx",
      "content": "\"use client\";\n\nimport { useId, useMemo } from \"react\";\nimport {\n  AbsoluteFill,\n  Easing,\n  interpolateColors,\n  useCurrentFrame,\n  useVideoConfig,\n} from \"remotion\";\n\nexport interface FlockFormProps {\n  /** Number of birds. Formations resample to match. */\n  count?: number;\n  accent?: string;\n  speed?: number;\n  theme?: \"dark\" | \"light\";\n}\n\nconst FORM1 = 18;\nconst STAGGER = 18;\nconst FLY_IN = 28;\nconst LAUNCH = 74;\nconst RELEASE = 24;\nconst CRUISE = 20;\nconst FLY_IN2 = 28;\nconst HOLD = 32;\nconst PATH_PX = 1100;\n\nexport const flockFormLength = () =>\n  LAUNCH + RELEASE + 2 + CRUISE + FLY_IN2 + HOLD;\n\nconst INOUT = Easing.bezier(0.65, 0, 0.35, 1);\nconst HEADING = -Math.PI / 4;\n\nfunction hash(n: number): number {\n  const s = Math.sin(n * 127.1 + 311.7) * 43758.5453;\n  return s - Math.floor(s);\n}\n\ninterface Pt {\n  x: number;\n  y: number;\n}\n\n/* The remocn.dev \"R\" mark (viewBox 124.06 × 134.26), flattened once into a\n   polygon so the formation can be sampled without the DOM. */\nconst REMOCN_R_PATH =\n  \"M 0.01 0.81 C 0.01 1.73, 0.36 2.79, 1.09 4.13 C 4.91 11.04, 13.45 17.16, 21.7 18.9 C 22.94 19.16, 23.18 19.16, 51.39 19.27 C 76.07 19.36, 80.02 19.4, 81.32 19.57 C 89.89 20.69, 96.2 24.68, 99.38 31.01 C 103.19 38.56, 102.53 50.1, 97.91 57.07 C 94.66 61.96, 89.68 64.99, 83.26 66 C 82.81 66.07, 70.18 66.83, 55.2 67.69 C 24.82 69.43, 27.03 69.23, 24.18 70.4 C 19.9 72.15, 14.84 75.7, 10.65 79.89 C 4.86 85.68, 1.3 91.91, 0.25 98.13 C 0.12 98.85, 0.08 103.13, 0.04 116.66 L 0 134.26 9.5 134.26 L 19 134.26 19.05 119.41 C 19.1 103.27, 19.08 103.82, 19.82 101.04 C 21.79 93.65, 27.86 88.75, 36.45 87.63 C 37.23 87.53, 39.41 87.5, 43.57 87.53 C 50.12 87.59, 50.75 87.65, 53.22 88.45 C 56.61 89.56, 59.67 91.86, 62.02 95.07 C 62.52 95.76, 69.35 103.85, 77.2 113.07 C 85.04 122.28, 91.63 130.04, 91.85 130.32 C 92.07 130.59, 92.5 131.34, 92.82 131.97 C 93.52 133.35, 94.11 133.93, 95.06 134.13 C 95.5 134.23, 98.97 134.26, 106.36 134.23 L 117.01 134.19 100.82 113.07 C 91.91 101.45, 84.52 91.78, 84.39 91.57 C 83.36 89.89, 83.66 87.53, 85.09 86 C 85.79 85.25, 86.36 84.94, 88.07 84.38 C 96.18 81.72, 104.15 76.62, 109.97 70.36 C 120.59 58.93, 124.06 44.32, 119.44 30.43 C 114.59 15.81, 101.93 4.02, 87.64 0.83 C 83.93 0.01, 88.09 0.08, 41.85 0.04 L 0.01 0 0.01 0.81 Z\";\n\nconst MARK_BOX = { x: 0, y: 0, w: 124.06, h: 134.26 };\n/* Lead birds (the arrow tip) land on the R's leg, then fade to ink. */\nconst LEG = { x: 96, y: 116 };\n\nfunction flattenPath(d: string): Pt[] {\n  const tok = d.replace(/,/g, \" \").split(/\\s+/).filter(Boolean);\n  const out: Pt[] = [];\n  let i = 0;\n  let cmd = \"\";\n  let x = 0;\n  let y = 0;\n  const num = () => Number(tok[i++]);\n  while (i < tok.length) {\n    if (/^[MCLZ]$/.test(tok[i])) cmd = tok[i++];\n    if (cmd === \"Z\") break;\n    if (cmd === \"M\" || cmd === \"L\") {\n      x = num();\n      y = num();\n      out.push({ x, y });\n    } else if (cmd === \"C\") {\n      const x1 = num();\n      const y1 = num();\n      const x2 = num();\n      const y2 = num();\n      const x3 = num();\n      const y3 = num();\n      for (let k = 1; k <= 12; k++) {\n        const t = k / 12;\n        const m = 1 - t;\n        out.push({\n          x:\n            m * m * m * x +\n            3 * m * m * t * x1 +\n            3 * m * t * t * x2 +\n            t * t * t * x3,\n          y:\n            m * m * m * y +\n            3 * m * m * t * y1 +\n            3 * m * t * t * y2 +\n            t * t * t * y3,\n        });\n      }\n      x = x3;\n      y = y3;\n    }\n  }\n  return out;\n}\n\nconst R_POLY = flattenPath(REMOCN_R_PATH);\n\nfunction inPoly(poly: Pt[], x: number, y: number) {\n  let inside = false;\n  for (let i = 0, j = poly.length - 1; i < poly.length; j = i++) {\n    const a = poly[i];\n    const b = poly[j];\n    if (\n      a.y > y !== b.y > y &&\n      x < ((b.x - a.x) * (y - a.y)) / (b.y - a.y) + a.x\n    )\n      inside = !inside;\n  }\n  return inside;\n}\n\nfunction inMark(x: number, y: number): \"ink\" | \"dot\" | null {\n  if (!inPoly(R_POLY, x, y)) return null;\n  return y > 96 && x > 66 ? \"dot\" : \"ink\";\n}\n\nfunction inArrow(x: number, y: number): boolean {\n  // Arrow pointing +x in a unit box: shaft then head.\n  if (x >= 0 && x <= 0.56 && Math.abs(y) <= 0.11) return true;\n  if (x >= 0.5 && x <= 1) {\n    const half = 0.36 * (1 - (x - 0.5) / 0.5);\n    return Math.abs(y) <= half;\n  }\n  return false;\n}\n\nfunction hexSample(\n  test: (x: number, y: number) => boolean,\n  box: { x: number; y: number; w: number; h: number },\n  n: number,\n): { pts: Pt[]; spacing: number } {\n  const grid = (s: number) => {\n    const out: Pt[] = [];\n    const rowH = s * 0.866;\n    let row = 0;\n    for (let y = box.y + rowH / 2; y < box.y + box.h; y += rowH, row++) {\n      const off = row % 2 ? s / 2 : 0;\n      for (let x = box.x + off + s / 2; x < box.x + box.w; x += s) {\n        if (test(x, y)) out.push({ x, y });\n      }\n    }\n    return out;\n  };\n  let lo = 0.001;\n  let hi = Math.max(box.w, box.h);\n  for (let it = 0; it < 28; it++) {\n    const mid = (lo + hi) / 2;\n    if (grid(mid).length >= n) lo = mid;\n    else hi = mid;\n  }\n  const pts = grid(lo);\n  const picked: Pt[] = [];\n  for (let j = 0; j < n; j++) {\n    picked.push(pts[Math.floor(((j + 0.5) * pts.length) / n)] ?? pts[0]);\n  }\n  return { pts: picked, spacing: lo };\n}\n\nfunction hilbert(x: number, y: number, order = 64): number {\n  let d = 0;\n  let px = x;\n  let py = y;\n  for (let s = order / 2; s >= 1; s = Math.floor(s / 2)) {\n    const rx = (px & s) > 0 ? 1 : 0;\n    const ry = (py & s) > 0 ? 1 : 0;\n    d += s * s * ((3 * rx) ^ ry);\n    if (ry === 0) {\n      if (rx === 1) {\n        px = s - 1 - px;\n        py = s - 1 - py;\n      }\n      const t = px;\n      px = py;\n      py = t;\n    }\n  }\n  return d;\n}\n\nfunction hilbertOrder(pts: Pt[], idx: number[]): number[] {\n  let minX = Number.POSITIVE_INFINITY;\n  let minY = Number.POSITIVE_INFINITY;\n  let maxX = Number.NEGATIVE_INFINITY;\n  let maxY = Number.NEGATIVE_INFINITY;\n  for (const i of idx) {\n    minX = Math.min(minX, pts[i].x);\n    minY = Math.min(minY, pts[i].y);\n    maxX = Math.max(maxX, pts[i].x);\n    maxY = Math.max(maxY, pts[i].y);\n  }\n  const span = Math.max(maxX - minX, maxY - minY) || 1;\n  return idx\n    .map((i) => ({\n      i,\n      h: hilbert(\n        Math.min(63, Math.floor(((pts[i].x - minX) / span) * 64)),\n        Math.min(63, Math.floor(((pts[i].y - minY) / span) * 64)),\n      ),\n    }))\n    .sort((a, b) => a.h - b.h)\n    .map((m) => m.i);\n}\n\ninterface Boid {\n  x: number;\n  y: number;\n  vx: number;\n  vy: number;\n}\n\n/** Deterministic boids: returns positions per frame, frames [0, steps]. */\nfunction flock(\n  init: Boid[],\n  steps: number,\n  leader: (i: number, f: number) => Pt,\n  u: number,\n  top = 6.4,\n  held: (i: number, f: number) => boolean = () => false,\n  cohesion = 0.0035,\n): Pt[][] {\n  const n = init.length;\n  const b = init.map((p) => ({ ...p }));\n  const out: Pt[][] = [b.map((p) => ({ x: p.x, y: p.y }))];\n  const rN = 64 * u;\n  const rS = 15 * u;\n  const vMin = 2.4 * u;\n  const vMax = top * u;\n  for (let f = 1; f <= steps; f++) {\n    const ax = new Float64Array(n);\n    const ay = new Float64Array(n);\n    for (let i = 0; i < n; i++) {\n      const p = b[i];\n      if (held(i, f)) continue;\n      const L = leader(i, f);\n      let cnt = 0;\n      let sx = 0;\n      let sy = 0;\n      let svx = 0;\n      let svy = 0;\n      let px = 0;\n      let py = 0;\n      for (let j = 0; j < n; j++) {\n        if (j === i || held(j, f)) continue;\n        const q = b[j];\n        const dx = q.x - p.x;\n        const dy = q.y - p.y;\n        const d2 = dx * dx + dy * dy;\n        if (d2 > rN * rN) continue;\n        cnt++;\n        sx += q.x;\n        sy += q.y;\n        svx += q.vx;\n        svy += q.vy;\n        if (d2 < rS * rS && d2 > 1e-6) {\n          const d = Math.sqrt(d2);\n          const k = (rS - d) / rS / d;\n          px -= dx * k;\n          py -= dy * k;\n        }\n      }\n      if (cnt) {\n        ax[i] += (svx / cnt - p.vx) * 0.07;\n        ay[i] += (svy / cnt - p.vy) * 0.07;\n        ax[i] += (sx / cnt - p.x) * cohesion;\n        ay[i] += (sy / cnt - p.y) * cohesion;\n      }\n      ax[i] += px * 0.9 * u;\n      ay[i] += py * 0.9 * u;\n      const lx = L.x - p.x;\n      const ly = L.y - p.y;\n      const ld = Math.hypot(lx, ly) || 1;\n      ax[i] += ((lx / ld) * vMax - p.vx) * 0.035;\n      ay[i] += ((ly / ld) * vMax - p.vy) * 0.035;\n    }\n    for (let i = 0; i < n; i++) {\n      const p = b[i];\n      if (held(i, f)) continue;\n      p.vx += ax[i];\n      p.vy += ay[i];\n      const sp = Math.hypot(p.vx, p.vy) || 1;\n      const k = Math.min(vMax, Math.max(vMin, sp)) / sp;\n      p.vx *= k;\n      p.vy *= k;\n      p.x += p.vx;\n      p.y += p.vy;\n    }\n    out.push(b.map((p) => ({ x: p.x, y: p.y })));\n  }\n  return out;\n}\n\nfunction lerpAngle(a: number, b: number, t: number) {\n  let d = b - a;\n  while (d > Math.PI) d -= Math.PI * 2;\n  while (d < -Math.PI) d += Math.PI * 2;\n  return a + d * t;\n}\n\nfunction Grain({ opacity, light }: { opacity: number; light: boolean }) {\n  const uid = useId().replace(/[^a-zA-Z0-9_-]/g, \"\");\n  return (\n    <svg\n      width=\"100%\"\n      height=\"100%\"\n      style={{ position: \"absolute\", inset: 0, pointerEvents: \"none\" }}\n      aria-hidden\n    >\n      <filter id={`${uid}-grain`}>\n        <feTurbulence\n          type=\"fractalNoise\"\n          baseFrequency={0.85}\n          numOctaves={2}\n          seed={5}\n        />\n        <feColorMatrix\n          type=\"matrix\"\n          values=\"0 0 0 0 0.5  0 0 0 0 0.5  0 0 0 0 0.5  0 0 0 1 0\"\n        />\n      </filter>\n      <rect\n        width=\"100%\"\n        height=\"100%\"\n        filter={`url(#${uid}-grain)`}\n        opacity={opacity}\n        style={{ mixBlendMode: light ? \"multiply\" : \"screen\" }}\n      />\n    </svg>\n  );\n}\n\n/* gradients.sh \"waves\" palette: a dusk sky behind the flock (dark), or the\n   fill of the birds themselves on the off-white stage (light). */\nconst WAVES = {\n  bg: \"#05070d\",\n  colors: [\"#a9c1df\", \"#5479b0\", \"#24467e\", \"#0f1c38\"],\n} as const;\n\nfunction skyGround(d: number) {\n  const [c0, c1, c2, c3] = WAVES.colors;\n  return `linear-gradient(${(172 - 3 * d).toFixed(2)}deg, ${WAVES.bg} 0%, ${c3} 30%, ${c2} 66%, ${c1} 90%, ${c0} 112%)`;\n}\n\nfunction tri(x: number, y: number, a: number, len: number, wid: number) {\n  const c = Math.cos(a);\n  const s = Math.sin(a);\n  const tx = x + c * len * 0.62;\n  const ty = y + s * len * 0.62;\n  const bx = x - c * len * 0.38;\n  const by = y - s * len * 0.38;\n  const ox = -s * wid * 0.5;\n  const oy = c * wid * 0.5;\n  return `M${tx.toFixed(1)} ${ty.toFixed(1)}L${(bx + ox).toFixed(1)} ${(by + oy).toFixed(1)}L${(bx - ox).toFixed(1)} ${(by - oy).toFixed(1)}Z`;\n}\n\nexport function FlockForm({\n  count = 340,\n  accent = \"#3B5BFD\",\n  speed = 1,\n  theme = \"dark\",\n}: FlockFormProps) {\n  const frame = useCurrentFrame() * speed;\n  const { width: W, height: H } = useVideoConfig();\n  const isLight = theme === \"light\";\n  const bg = isLight ? \"#F5F4F0\" : \"#0A0A0B\";\n  const ink = isLight ? \"#141416\" : \"#EEEEF0\";\n  const n = Math.max(120, Math.round(count));\n  const u = Math.min(W, H) / 720;\n\n  const model = useMemo(() => {\n    const cx = W / 2;\n    const cy = H / 2;\n\n    // Mark targets.\n    const markH = H * 0.54;\n    const ms = markH / MARK_BOX.h;\n    const mark = hexSample((x, y) => inMark(x, y) !== null, MARK_BOX, n);\n    const markPts = mark.pts.map((p) => ({\n      x: cx + (p.x - (MARK_BOX.x + MARK_BOX.w / 2)) * ms,\n      y: cy + (p.y - (MARK_BOX.y + MARK_BOX.h / 2)) * ms,\n      dot: inMark(p.x, p.y) === \"dot\",\n    }));\n    const spacing = mark.spacing * ms;\n    const dotIdx = markPts.flatMap((p, i) => (p.dot ? [i] : []));\n    const inkIdx = markPts.flatMap((p, i) => (p.dot ? [] : [i]));\n    const nDot = dotIdx.length;\n\n    // Arrow targets, rotated to point up-right.\n    const arrowLen = H * 0.62;\n    const arrow = hexSample(inArrow, { x: 0, y: -0.4, w: 1, h: 0.8 }, n);\n    const ca = Math.cos(HEADING);\n    const sa = Math.sin(HEADING);\n    const arrowPts = arrow.pts.map((p) => {\n      const lx = (p.x - 0.5) * arrowLen;\n      const ly = p.y * arrowLen;\n      return {\n        x: cx + lx * ca - ly * sa,\n        y: cy + lx * sa + ly * ca,\n        t: p.x,\n        side: p.y,\n      };\n    });\n\n    // Flight 1: the flock sweeps in from the lower left.\n    const init: Boid[] = [];\n    for (let i = 0; i < n; i++) {\n      const r = Math.sqrt(hash(i * 3.1)) * H * 0.23;\n      const th = hash(i * 7.9) * Math.PI * 2;\n      const front = i < nDot ? H * 0.12 : 0;\n      init.push({\n        x: W * 0.24 + Math.cos(th) * r * 1.6 + front,\n        y: H * 0.66 + Math.sin(th) * r * 0.75 - front * 0.4,\n        vx: (4.2 + hash(i * 1.7)) * u,\n        vy: (-1.6 + hash(i * 2.3) * 0.8) * u,\n      });\n    }\n    const lead1 = (f: number) => {\n      const k = f / (FORM1 + STAGGER + FLY_IN);\n      return {\n        x: W * (0.3 + 0.55 * k),\n        y: H * (0.62 - 0.5 * Math.sin(k * Math.PI * 0.75)),\n      };\n    };\n    const free1 = flock(init, FORM1 + STAGGER + FLY_IN, (_, f) => lead1(f), u);\n\n    // Assign arrow targets: accent birds take the tip, the rest by Hilbert order.\n    const tipOrder = arrowPts\n      .map((p, i) => ({ i, t: p.t }))\n      .sort((a, b) => b.t - a.t)\n      .map((m) => m.i);\n    const arrowOf: number[] = new Array(n);\n    const tip = tipOrder.slice(0, nDot);\n    const restT = tipOrder.slice(nDot);\n    const atForm = free1[FORM1 + 8];\n    tip.forEach((ti, k) => {\n      arrowOf[k] = ti;\n    });\n    const birdsRest = hilbertOrder(\n      atForm,\n      Array.from({ length: n - nDot }, (_, k) => k + nDot),\n    );\n    const targRest = hilbertOrder(arrowPts, restT);\n    birdsRest.forEach((bi, k) => {\n      arrowOf[bi] = targRest[k];\n    });\n\n    // Flight 2: the arrow peels off from the tip as one ribbon, loops right\n    // and streams back into the mark, the accent birds leading into the dot.\n    const release = arrowOf.map(\n      (ti, i) => (1 - arrowPts[ti].t) * RELEASE + hash(i * 5.3) * 2,\n    );\n    const P = [\n      { x: cx + 0.3 * H, y: cy - 0.3 * H },\n      { x: cx + 0.56 * H, y: cy - 0.5 * H },\n      { x: cx + 0.86 * H, y: cy + 0.1 * H },\n      { x: cx + 0.34 * H, y: cy + 0.24 * H },\n    ];\n    const path = (k: number) => {\n      const t = Math.min(1, Math.max(0, k));\n      const m = 1 - t;\n      return {\n        x:\n          m * m * m * P[0].x +\n          3 * m * m * t * P[1].x +\n          3 * m * t * t * P[2].x +\n          t * t * t * P[3].x,\n        y:\n          m * m * m * P[0].y +\n          3 * m * m * t * P[1].y +\n          3 * m * t * t * P[2].y +\n          t * t * t * P[3].y,\n      };\n    };\n    // Mark: birds in release order fill outward from the dot.\n    const dotC = {\n      x: cx + (LEG.x - (MARK_BOX.x + MARK_BOX.w / 2)) * ms,\n      y: cy + (LEG.y - (MARK_BOX.y + MARK_BOX.h / 2)) * ms,\n    };\n    const byDist = (idx: number[]) =>\n      [...idx].sort(\n        (a, b) =>\n          Math.hypot(markPts[a].x - dotC.x, markPts[a].y - dotC.y) -\n          Math.hypot(markPts[b].x - dotC.x, markPts[b].y - dotC.y),\n      );\n    const markOf: number[] = new Array(n);\n    const accBirds = Array.from({ length: nDot }, (_, k) => k).sort(\n      (a, b) => release[a] - release[b],\n    );\n    const dotT = byDist(dotIdx);\n    accBirds.forEach((bi, k) => {\n      markOf[bi] = dotT[k];\n    });\n    const inkBirds = Array.from({ length: n - nDot }, (_, k) => k + nDot).sort(\n      (a, b) => release[a] - release[b],\n    );\n    const inkT = byDist(inkIdx);\n    inkBirds.forEach((bi, k) => {\n      markOf[bi] = inkT[k];\n    });\n\n    const delay1 = arrowOf.map(\n      (ti, i) => (1 - arrowPts[ti].t) * STAGGER * 0.85 + hash(i * 5.3) * 3,\n    );\n    const delay2 = release.map((r) => r + CRUISE);\n\n    return {\n      arrowPts,\n      markPts,\n      arrowOf,\n      markOf,\n      free1,\n      path,\n      release,\n      arrowLen,\n      delay1,\n      delay2,\n      nDot,\n      spacing,\n    };\n  }, [W, H, n, u]);\n\n  const posAt = (i: number, f: number): Pt => {\n    const m = model;\n    if (f < LAUNCH) {\n      const fr = Math.max(0, Math.min(m.free1.length - 1, f));\n      const f0 = Math.floor(fr);\n      const a = m.free1[f0][i];\n      const b = m.free1[Math.min(m.free1.length - 1, f0 + 1)][i];\n      const k = fr - f0;\n      const fx = a.x + (b.x - a.x) * k;\n      const fy = a.y + (b.y - a.y) * k;\n      const w = INOUT(\n        Math.max(0, Math.min(1, (f - FORM1 - m.delay1[i]) / FLY_IN)),\n      );\n      const t = m.arrowPts[m.arrowOf[i]];\n      return { x: fx + (t.x - fx) * w, y: fy + (t.y - fy) * w };\n    }\n    const home = m.arrowPts[m.arrowOf[i]];\n    const tau = f - LAUNCH;\n    const rel = tau - m.release[i];\n    // Before release the whole arrow eases forward along its heading.\n    const drift = (tt: number) => 0.2 * u * Math.max(0, tt) ** 2;\n    const held = drift(Math.min(tau, m.release[i]));\n    const sx = home.x + Math.cos(HEADING) * held;\n    const sy = home.y + Math.sin(HEADING) * held;\n    let fx = sx;\n    let fy = sy;\n    if (rel > 0) {\n      const v0 = 0.4 * u * m.release[i];\n      const k = Math.min(1, (rel * (22 * u + v0 * 0.5)) / (PATH_PX * u));\n      const pa = m.path(k);\n      const pb = m.path(k + 0.01);\n      const tl = Math.hypot(pb.x - pa.x, pb.y - pa.y) || 1;\n      const off = home.side * m.arrowLen * 1.3;\n      const px = pa.x - ((pb.y - pa.y) / tl) * off;\n      const py = pa.y + ((pb.x - pa.x) / tl) * off;\n      const p0 = m.path(0);\n      const q0 = m.path(0.01);\n      const t0 = Math.hypot(q0.x - p0.x, q0.y - p0.y) || 1;\n      const ex = sx - (p0.x - ((q0.y - p0.y) / t0) * off);\n      const ey = sy - (p0.y + ((q0.x - p0.x) / t0) * off);\n      const keep = 1 - INOUT(Math.min(1, rel / 14));\n      fx = px + ex * keep;\n      fy = py + ey * keep;\n    }\n    const w = INOUT(\n      Math.max(0, Math.min(1, (f - LAUNCH - m.delay2[i]) / FLY_IN2)),\n    );\n    const t = m.markPts[m.markOf[i]];\n    return { x: fx + (t.x - fx) * w, y: fy + (t.y - fy) * w };\n  };\n\n  const settle1 = (i: number, f: number) =>\n    f >= LAUNCH\n      ? 0\n      : Math.max(0, Math.min(1, (f - FORM1 - model.delay1[i]) / FLY_IN));\n  const settle2 = (i: number, f: number) =>\n    f < LAUNCH\n      ? 0\n      : Math.max(0, Math.min(1, (f - LAUNCH - model.delay2[i]) / FLY_IN2));\n\n  const len = model.spacing * 0.92;\n  const wid = model.spacing * 0.58;\n  const FADES = 5;\n  const inkD: string[] = [];\n  const accD: string[][] = Array.from({ length: FADES }, () => []);\n  for (let i = 0; i < n; i++) {\n    const p = posAt(i, frame);\n    const q = posAt(i, frame - 1);\n    const vx = p.x - q.x;\n    const vy = p.y - q.y;\n    const moving = Math.hypot(vx, vy) > 0.05 * u;\n    const vel = moving ? Math.atan2(vy, vx) : HEADING;\n    const s = Math.max(settle1(i, frame), settle2(i, frame));\n    const a = lerpAngle(vel, HEADING, s ** 2);\n    const d = tri(p.x, p.y, a, len, wid);\n    if (i < model.nDot) {\n      // Lead birds stay accent in flight and settle into the mark's ink.\n      const k = Math.round(settle2(i, frame) ** 2 * (FADES - 1));\n      if (k >= FADES - 1) inkD.push(d);\n      else accD[k].push(d);\n    } else inkD.push(d);\n  }\n\n  const gid = `ff${useId().replace(/[^a-zA-Z0-9_-]/g, \"\")}`;\n  const drift = Math.min(1, frame / flockFormLength());\n  const birdFill = isLight ? `url(#${gid})` : ink;\n\n  return (\n    <AbsoluteFill\n      style={{\n        background: isLight\n          ? `radial-gradient(80% 75% at 50% 45%, #FBFAF7 0%, ${bg} 72%)`\n          : skyGround(drift),\n        overflow: \"hidden\",\n      }}\n    >\n      <svg\n        width={W}\n        height={H}\n        viewBox={`0 0 ${W} ${H}`}\n        style={{ position: \"absolute\", inset: 0 }}\n        aria-hidden\n      >\n        <defs>\n          <linearGradient\n            id={gid}\n            gradientUnits=\"userSpaceOnUse\"\n            x1={W * (0.46 + 0.04 * drift)}\n            y1={H * 0.1}\n            x2={W * (0.54 - 0.04 * drift)}\n            y2={H * 0.92}\n          >\n            <stop offset=\"0\" stopColor={WAVES.colors[3]} />\n            <stop offset=\"0.45\" stopColor={WAVES.colors[2]} />\n            <stop offset=\"1\" stopColor={WAVES.colors[1]} />\n          </linearGradient>\n        </defs>\n        <path d={inkD.join(\"\")} fill={birdFill} />\n        {accD.map((segs, k) =>\n          segs.length ? (\n            <path\n              key={`a${k.toString()}`}\n              d={segs.join(\"\")}\n              fill={interpolateColors(\n                k / (FADES - 1),\n                [0, 1],\n                [accent, isLight ? WAVES.colors[2] : ink],\n              )}\n            />\n          ) : null,\n        )}\n      </svg>\n      <Grain opacity={isLight ? 0.045 : 0.06} light={isLight} />\n    </AbsoluteFill>\n  );\n}\n",
      "type": "registry:component",
      "target": "components/remocn/flock-form.tsx"
    }
  ],
  "type": "registry:component"
}
