Nothing broke in the render — first pass clean. Pre-render fixes: footer raised from 18px to the 19px type floor. The checker flags the closing line's 1.36s hold as under its reading-speed floor; accepted deliberately — the end state itself (all five events) is what holds, and it stays fully readable for the final 1.8s.
Banned list —Beat grid —Build method —Self-review —
<direction>
Build a 1280x720, 9-second timeline animation in HyperFrames + GSAP: a horizontal line, a travelling progress dot, and five dated events that rise in sequence, alternating above and below the line. House style: background #0a0a0a, white type, ONE orange accent #ff5c1a, Arial/Helvetica bold, NO webfonts, no gradients, no glows, no 3D. All dates and events are fictional sample content: the title carries "SAMPLE TIMELINE" and the footer reads "ILLUSTRATIVE SAMPLE TIMELINE — FICTIONAL DATES" for the whole film.
Events, in order (year | label): 2019 | First sketch; 2021 | First customer; 2023 | Team of five; 2025 | 10,000 users; 2026 | This film
Banned: vertical timelines, extra colours, photos or icons, any real company history presented as fact.
</direction>
<structure>
0.0-0.9s: orange kicker "TIMELINE ANIMATION — SAMPLE TIMELINE" and the title "HOW WE GOT HERE — SAMPLE TIMELINE" settle at top-left; footer fades in.
0.9-2.1s: the line sweeps in across the middle of the frame (scaleX from the left) over a dark track.
2.0-7.2s: an orange progress dot travels the line at constant speed. Five markers pop as the dot reaches them (about 2.3s, 3.4s, 4.5s, 5.6s, 6.7s), alternating above/below: year in 44px/900, label small under it, a thin connector tick joining marker to text block. First and last markers and years are the orange accent.
7.2-9.0s: end state — full line lit white, all five events readable, closing line "FIVE MOMENTS. ONE LINE." fades in low; hold.
</structure>
<build>
One paused GSAP timeline registered as window.__timelines["main"]. Line at vertical centre (top 357px), from x=90 to x=1190; markers at x=150, 385, 620, 855, 1090. Move the dot with a single linear x tween and derive every marker time from (markerX - lineStart) / lineLength x the dot's travel duration, so the pops can never drift from the dot. Above events: text block bottom sits 20px above the tick top; below events mirrored. Each event block is 230px wide, centred on its marker — check the first block's left edge and the last block's right edge stay inside the frame.
</build>
<gotchas>
- Above/below labels are the collision risk: above blocks must clear the title zone, below blocks must clear the closing line and footer — screenshot at the end state and read every label.
- The dot must travel at constant speed (linear ease); easing makes later markers pop before the dot arrives.
- Accent only the first and last events — accenting all five destroys the hierarchy.
- The connector tick belongs to the marker, not the text: it should appear with the pop, not before.
</gotchas>
Before you finish: step through the timeline at five points and fix anything that overlaps, clips the frame, or drifts off the beat grid.
Marker timings are not hand-tuned: each pop time is the dot's linear travel duration multiplied by the marker's fraction along the line, so adding or moving an event keeps the sync exact. The alternating above/below layout was still-checked at the end state — no label touches the title, the closing line, or the frame edges.