+ {tags.map((tag, i) => {
+ const ang = (i / tags.length) * Math.PI * 2 + t * 0.22;
+ const x = 260 + Math.cos(ang) * 170;
+ const y = 130 + Math.sin(ang) * 75;
+ return (
+
+ {tag}
+
+ );
+ })}
+ {/* center dot */}
+
+
+ );
+};
+
+/** Card fan — three tapering cards fanning out from a pivot (stacked deck). */
+export const CardFan: React.FC<{ delay?: number }> = ({ delay = 0 }) => {
+ const frame = useCurrentFrame();
+ const { fps } = useVideoConfig();
+ const p = spring({ frame: Math.max(0, frame - delay), fps, config: { damping: 16, stiffness: 70 } });
+ const cards = [
+ { rot: -24, x: -120, txt: "IDEA" },
+ { rot: 0, x: 0, txt: "BUILD" },
+ { rot: 24, x: 120, txt: "SHIP" },
+ ];
+ return (
+
+ {[
+ `M ${gap} ${gap + c} L ${gap} ${gap} L ${gap + c} ${gap}`,
+ `M ${560 - gap - c} ${gap} L ${560 - gap} ${gap} L ${560 - gap} ${gap + c}`,
+ `M ${gap} ${340 - gap - c} L ${gap} ${340 - gap} L ${gap + c} ${340 - gap}`,
+ `M ${560 - gap - c} ${340 - gap} L ${560 - gap} ${340 - gap} L ${560 - gap} ${340 - gap - c}`,
+ ].map((d, i) => (
+
+ ))}
+
+ {/* rule-of-thirds grid */}
+
+ {[0, 1].map((i) =>
)}
+
+
+ );
+};
+
+/** World grid clock — 4 tiles with city:time, one highlighted cycling. */
+export const WorldClock: React.FC<{ delay?: number }> = ({ delay = 0 }) => {
+ const frame = useCurrentFrame();
+ const t = Math.max(0, (frame - delay) / 60);
+ const active = Math.floor(t) % 4;
+ const cities = [
+ { city: "JKT", off: 7 },
+ { city: "NYC", off: -4 },
+ { city: "LON", off: 1 },
+ { city: "TYO", off: 9 },
+ ];
+ const hh = (off: number) => ((9 + off + 24) % 24);
+ return (
+
+
+ {/* ripple rings */}
+ {[0, 1].map((i) => {
+ const r = 40 + (((t + i * 1) % 2)) * 46;
+ const o = Math.max(0, 1 - ((t + i * 1) % 2)) * 0.7;
+ return (
+
+ );
+ })}
+ {/* lock body */}
+
+ {/* shackle */}
+
+
+
+ {closed ? "LOCKED" : "OPEN"}
+
+
+ );
};
\ No newline at end of file