{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "max-q",
  "title": "Max Q",
  "description": "Speed and altitude figures climb together beside a rising trajectory curve.",
  "dependencies": ["remotion", "@remotion/google-fonts"],
  "files": [
    {
      "path": "registry/remocn/max-q/index.tsx",
      "content": "\"use client\";\n\nimport { loadFont } from \"@remotion/google-fonts/BarlowCondensed\";\nimport type React from \"react\";\nimport {\n  AbsoluteFill,\n  Easing,\n  interpolate,\n  useCurrentFrame,\n  useVideoConfig,\n} from \"remotion\";\n\nconst { fontFamily: condensed } = loadFont(\"normal\", {\n  weights: [\"400\", \"500\"],\n  subsets: [\"latin\"],\n});\n\nexport interface MaxQProps {\n  /** Left number at the end of the climb. */\n  velocity?: number;\n  /** Right number at the end of the climb, one decimal. */\n  altitude?: number;\n  speedLabel?: string;\n  speedUnit?: string;\n  altitudeLabel?: string;\n  altitudeUnit?: string;\n  /** The event banner that flashes across at the peak. */\n  event?: string;\n  /** Small line inside the banner. Pass \"\" to hide it. */\n  eventDetail?: string;\n  /** The status that settles at the end. */\n  status?: string;\n  /** Vehicle dot on the trajectory and small marks. */\n  accent?: string;\n  theme?: \"dark\" | \"light\";\n  /** Playback multiplier. */\n  speed?: number;\n}\n\n// Beat sheet (30fps):\n//   0–8     labels and both numbers rise in, already at zero and climbing\n//   0–112   SPEED and ALTITUDE race up in tabular digits (slow start, fast\n//           middle, settle); the trajectory draws between them, the vehicle\n//           dot riding its head; the mission clock runs up top\n//   60      MAX Q: a \"beam\" gradient band wipes across the frame, the marker\n//           drops on the trajectory; the band holds, then wipes out (88)\n//   92–104  NOMINAL settles under the numbers; values freeze at 112\n//   112–    poster hold\nconst RACE_END = 112;\nconst MAXQ_AT = 60;\nconst BAND_IN = 6;\nconst BAND_HOLD = 22;\nconst BAND_OUT = 7;\n\nconst OUT = Easing.bezier(0.22, 1, 0.36, 1);\nconst MOVE = Easing.bezier(0.7, 0, 0.2, 1);\nconst CLIMB = Easing.bezier(0.5, 0, 0.3, 1);\nconst CLAMP = {\n  extrapolateLeft: \"clamp\",\n  extrapolateRight: \"clamp\",\n} as const;\n\n// gradients.sh \"beam\": bg #02030a, #f4f7ff #8fb4ff #3d5bff #1a1660.\nconst BEAM = [\"#F4F7FF\", \"#8FB4FF\", \"#3D5BFF\", \"#1A1660\"] as const;\n\nconst THEMES = {\n  dark: {\n    stage: \"#04050B\",\n    ink: \"#F2F3F7\",\n    dim: \"#7A7C88\",\n    faint: \"#24263A\",\n    bandInk: \"#05061A\",\n    groundOp: 0.85,\n  },\n  light: {\n    stage: \"#F5F4F0\",\n    ink: \"#111216\",\n    dim: \"#7F7E79\",\n    faint: \"#D9D7D0\",\n    bandInk: \"#05061A\",\n    groundOp: 0.5,\n  },\n} as const;\n\nfunction ramp(t: number, from: number, len: number) {\n  return interpolate(t, [from, from + len], [0, 1], CLAMP);\n}\n\nfunction kick(d: number, len: number) {\n  if (d < 0 || d > len + 1) return 0;\n  if (d < 1) return d;\n  const k = 1 - (d - 1) / len;\n  return k * k;\n}\n\nfunction pad(n: number) {\n  return String(n).padStart(2, \"0\");\n}\n\n/** Point on a cubic bezier. */\nfunction bez(\n  p: number,\n  a: [number, number],\n  b: [number, number],\n  c: [number, number],\n  d: [number, number],\n): [number, number] {\n  const q = 1 - p;\n  const w0 = q * q * q;\n  const w1 = 3 * q * q * p;\n  const w2 = 3 * q * p * p;\n  const w3 = p * p * p;\n  return [\n    w0 * a[0] + w1 * b[0] + w2 * c[0] + w3 * d[0],\n    w0 * a[1] + w1 * b[1] + w2 * c[1] + w3 * d[1],\n  ];\n}\n\nexport function MaxQ({\n  velocity = 1880,\n  altitude = 14.2,\n  speedLabel = \"Speed\",\n  speedUnit = \"km/h\",\n  altitudeLabel = \"Altitude\",\n  altitudeUnit = \"km\",\n  event = \"Max Q\",\n  eventDetail = \"Maximum dynamic pressure\",\n  status = \"Nominal\",\n  accent = \"#3B5BFD\",\n  theme = \"dark\",\n  speed = 1,\n}: MaxQProps) {\n  const t = useCurrentFrame() * speed;\n  const { width: W, height: H } = useVideoConfig();\n  const c = THEMES[theme];\n  const u = Math.min(W / 1280, H / 720);\n\n  // ── Values ────────────────────────────────────────────────────────────────\n  const race = CLIMB(ramp(t, 0, RACE_END));\n  const altRace = Easing.bezier(0.6, 0, 0.3, 1)(ramp(t, 0, RACE_END));\n  const spd = Math.round(velocity * race);\n  const alt = altitude * altRace;\n  const spdText = spd.toLocaleString(\"en-US\");\n  const altText = alt.toFixed(1);\n  const clockS = 38 + Math.floor((t / 30) * 1);\n  const clock = `T+00:${pad(Math.floor(clockS / 60))}:${pad(clockS % 60)}`;\n\n  // ── Layout ────────────────────────────────────────────────────────────────\n  const margin = 64 * u;\n  const numFs = 292 * u;\n  const numCY = H / 2 + 62 * u;\n  const labelY = numCY - numFs * 0.5 - 12 * u;\n  const intro = OUT(ramp(t, -3, 10));\n\n  // Trajectory: a gravity turn from the bottom centre to the top right.\n  const P0: [number, number] = [W * 0.505, H + 10 * u];\n  const P1: [number, number] = [W * 0.515, H * 0.52];\n  const P2: [number, number] = [W * 0.6, H * 0.2];\n  const P3: [number, number] = [W + 20 * u, H * 0.1];\n  const pathD = `M ${P0[0]} ${P0[1]} C ${P1[0]} ${P1[1]}, ${P2[0]} ${P2[1]}, ${P3[0]} ${P3[1]}`;\n  const drawP = interpolate(altRace, [0, 1], [0.02, 0.86]);\n  const head = bez(drawP, P0, P1, P2, P3);\n  const qP = interpolate(\n    Easing.bezier(0.6, 0, 0.3, 1)(ramp(MAXQ_AT, 0, RACE_END)),\n    [0, 1],\n    [0.02, 0.86],\n  );\n  const qPt = bez(qP, P0, P1, P2, P3);\n\n  // ── Event band ────────────────────────────────────────────────────────────\n  const bandIn = OUT(ramp(t, MAXQ_AT, BAND_IN));\n  const bandOut = MOVE(ramp(t, MAXQ_AT + BAND_IN + BAND_HOLD, BAND_OUT));\n  const bandOn = t >= MAXQ_AT && bandOut < 1;\n  const bandH = 262 * u;\n  const bandY = numCY + numFs * 0.04 - bandH / 2;\n  const bandHit = kick(t - MAXQ_AT - 2, 8);\n  const textIn = OUT(ramp(t, MAXQ_AT + 2, 8));\n  const markerIn = OUT(ramp(t, MAXQ_AT, 10));\n\n  const statusAt = MAXQ_AT + BAND_IN + BAND_HOLD + BAND_OUT - 1;\n  const statusIn = OUT(ramp(t, statusAt, 12));\n  const settleHit = kick(t - RACE_END, 8);\n\n  const drift = ramp(t, 0, 150);\n  const ground = [\n    `radial-gradient(60% 55% at ${70 + drift * 6}% 0%, color-mix(in oklab, ${BEAM[2]} 45%, transparent) 0%, transparent 70%)`,\n    `radial-gradient(40% 40% at 50% 115%, color-mix(in oklab, ${theme === \"dark\" ? BEAM[3] : BEAM[1]} 70%, transparent) 0%, transparent 75%)`,\n  ].join(\", \");\n\n  const label: React.CSSProperties = {\n    fontFamily: condensed,\n    fontWeight: 500,\n    fontSize: 24 * u,\n    letterSpacing: \"0.16em\",\n    textTransform: \"uppercase\",\n    color: c.dim,\n    whiteSpace: \"nowrap\",\n    lineHeight: 1,\n  };\n  const number: React.CSSProperties = {\n    fontFamily: condensed,\n    fontWeight: 500,\n    fontSize: numFs,\n    lineHeight: 1,\n    letterSpacing: \"-0.02em\",\n    fontVariantNumeric: \"tabular-nums\",\n    color: c.ink,\n    whiteSpace: \"nowrap\",\n  };\n\n  return (\n    <AbsoluteFill style={{ background: c.stage, overflow: \"hidden\" }}>\n      <div\n        style={{\n          position: \"absolute\",\n          inset: 0,\n          background: ground,\n          opacity: c.groundOp * (0.55 + 0.45 * ramp(t, MAXQ_AT, 20)),\n        }}\n      />\n\n      {/* Trajectory. */}\n      <svg\n        width={W}\n        height={H}\n        style={{ position: \"absolute\", inset: 0 }}\n        aria-hidden\n      >\n        <path\n          d={pathD}\n          fill=\"none\"\n          stroke={c.faint}\n          strokeWidth={2 * u}\n          strokeDasharray={`${2 * u} ${8 * u}`}\n          opacity={intro}\n        />\n        <path\n          d={pathD}\n          fill=\"none\"\n          stroke={c.ink}\n          strokeWidth={2.5 * u}\n          pathLength={1}\n          strokeDasharray={`${drawP} 1`}\n          opacity={0.9 * intro}\n        />\n        {/* Max Q marker. */}\n        <g opacity={markerIn}>\n          <line\n            x1={qPt[0] - 16 * u}\n            y1={qPt[1]}\n            x2={qPt[0] + 16 * u}\n            y2={qPt[1]}\n            stroke={c.ink}\n            strokeWidth={2 * u}\n          />\n          <text\n            x={qPt[0] - 26 * u}\n            y={qPt[1] + 7 * u}\n            textAnchor=\"end\"\n            fontFamily={condensed}\n            fontWeight={500}\n            fontSize={20 * u}\n            letterSpacing=\"0.16em\"\n            fill={c.dim}\n          >\n            {event.toUpperCase()}\n          </text>\n        </g>\n        <circle\n          cx={head[0]}\n          cy={head[1]}\n          r={14 * u}\n          fill=\"none\"\n          stroke={accent}\n          strokeWidth={2 * u}\n          opacity={0.45 * intro}\n        />\n        <circle cx={head[0]} cy={head[1]} r={6 * u} fill={accent} />\n      </svg>\n\n      {/* Mission clock. */}\n      <div\n        style={{\n          ...label,\n          position: \"absolute\",\n          left: 0,\n          right: 0,\n          top: margin - 10 * u,\n          textAlign: \"center\",\n          fontVariantNumeric: \"tabular-nums\",\n          color: c.ink,\n          opacity: intro,\n        }}\n      >\n        {clock}\n      </div>\n\n      {/* SPEED. */}\n      <div\n        style={{\n          position: \"absolute\",\n          left: margin,\n          top: 0,\n          opacity: intro,\n          transform: `translateY(${(1 - intro) * 30 * u}px)`,\n        }}\n      >\n        <div\n          style={{\n            ...label,\n            position: \"absolute\",\n            left: 4 * u,\n            top: labelY - 24 * u,\n          }}\n        >\n          {speedLabel}\n          <span style={{ color: c.ink, marginLeft: 14 * u }}>{speedUnit}</span>\n        </div>\n        <div\n          style={{\n            ...number,\n            position: \"absolute\",\n            left: 0,\n            top: numCY - numFs / 2,\n            transform: `scale(${1 + 0.015 * settleHit})`,\n            transformOrigin: \"left center\",\n          }}\n        >\n          {spdText}\n        </div>\n      </div>\n\n      {/* ALTITUDE. */}\n      <div\n        style={{\n          position: \"absolute\",\n          right: margin,\n          top: 0,\n          opacity: intro,\n          transform: `translateY(${(1 - intro) * 30 * u}px)`,\n        }}\n      >\n        <div\n          style={{\n            ...label,\n            position: \"absolute\",\n            right: 4 * u,\n            top: labelY - 24 * u,\n            textAlign: \"right\",\n          }}\n        >\n          {altitudeLabel}\n          <span style={{ color: c.ink, marginLeft: 14 * u }}>\n            {altitudeUnit}\n          </span>\n        </div>\n        <div\n          style={{\n            ...number,\n            position: \"absolute\",\n            right: 0,\n            top: numCY - numFs / 2,\n            transform: `scale(${1 + 0.015 * settleHit})`,\n            transformOrigin: \"right center\",\n          }}\n        >\n          {altText}\n        </div>\n      </div>\n\n      {/* Status. */}\n      <div\n        style={{\n          position: \"absolute\",\n          left: 0,\n          right: 0,\n          top: margin + 52 * u,\n          display: \"flex\",\n          justifyContent: \"center\",\n          alignItems: \"center\",\n          gap: 16 * u,\n          opacity: statusIn,\n          transform: `translateY(${(1 - statusIn) * -22 * u}px)`,\n        }}\n      >\n        <div\n          style={{\n            width: 12 * u,\n            height: 12 * u,\n            borderRadius: 99,\n            background: `linear-gradient(135deg, ${BEAM[1]}, ${BEAM[2]})`,\n          }}\n        />\n        <div\n          style={{\n            fontFamily: condensed,\n            fontWeight: 500,\n            fontSize: 54 * u,\n            letterSpacing: \"0.14em\",\n            lineHeight: 1,\n            textTransform: \"uppercase\",\n            backgroundImage: `linear-gradient(90deg, ${theme === \"dark\" ? BEAM[0] : BEAM[3]} 0%, ${theme === \"dark\" ? BEAM[1] : BEAM[2]} 100%)`,\n            WebkitBackgroundClip: \"text\",\n            backgroundClip: \"text\",\n            color: \"transparent\",\n          }}\n        >\n          {status}\n        </div>\n      </div>\n\n      {/* MAX Q band. */}\n      {bandOn ? (\n        <div\n          style={{\n            position: \"absolute\",\n            left: 0,\n            top: bandY,\n            width: W,\n            height: bandH,\n            clipPath: `inset(0 ${(1 - bandIn) * 100}% 0 ${bandOut * 100}%)`,\n            background: `linear-gradient(90deg, ${BEAM[3]} 0%, ${BEAM[2]} 22%, ${BEAM[1]} 40%, ${BEAM[0]} 52%, ${BEAM[1]} 66%, ${BEAM[2]} 84%, ${BEAM[3]} 100%)`,\n            display: \"flex\",\n            alignItems: \"center\",\n            justifyContent: \"center\",\n            gap: 40 * u,\n          }}\n        >\n          <div\n            style={{\n              position: \"absolute\",\n              inset: 0,\n              background: BEAM[0],\n              opacity: bandHit * 0.8,\n            }}\n          />\n          <div\n            style={{\n              fontFamily: condensed,\n              fontWeight: 500,\n              fontSize: 168 * u,\n              lineHeight: 1,\n              letterSpacing: \"-0.01em\",\n              textTransform: \"uppercase\",\n              color: c.bandInk,\n              whiteSpace: \"nowrap\",\n              transform: `translateX(${(1 - textIn) * -60 * u}px)`,\n              opacity: textIn,\n            }}\n          >\n            {event}\n          </div>\n          {eventDetail ? (\n            <div\n              style={{\n                fontFamily: condensed,\n                fontWeight: 500,\n                fontSize: 24 * u,\n                letterSpacing: \"0.16em\",\n                lineHeight: 1.2,\n                textTransform: \"uppercase\",\n                color: c.bandInk,\n                width: 200 * u,\n                opacity: textIn,\n                transform: `translateX(${(1 - textIn) * -40 * u}px)`,\n              }}\n            >\n              {eventDetail}\n            </div>\n          ) : null}\n        </div>\n      ) : null}\n    </AbsoluteFill>\n  );\n}\n",
      "type": "registry:component",
      "target": "components/remocn/max-q.tsx"
    }
  ],
  "type": "registry:component"
}
