{
  "format": "driftproof-answers/2",
  "receipt_hash": "fc75ea505919e81c9ebf39fc75540760e4016909add2d48ae5bcbcf289ba31bf",
  "model_id": "claude-opus-5-5",
  "answers": {
    "f095705abd582b3e19a01c69492c059c19eaf87dd8504cffce1db09b54887f56": "Two corrections:\n\n1. **Remove the `<template>` wrapper.** A standalone `index.html` root must sit directly in `<body>`. Wrapped in `<template>`, all content is hidden, and `lint` rejects it with `standalone_composition_wrapped_in_template` (error). Only sub-compositions loaded via `data-composition-src` use `<template>`.\n\n2. **Register the timeline under `\"title\"`, not `\"outro\"`.** The `window.__timelines` key must match the root's `data-composition-id`:\n   ```js\n   window.__timelines[\"title\"] = tl; // tl = gsap.timeline({ paused: true })\n   ```\n   Alternatively, rename the root's `data-composition-id` to `outro`. Either way, the two must agree, or the runtime won't find the timeline for this composition.\n\nCorrected shape:\n\n```html\n<body>\n  <div id=\"root\" data-composition-id=\"title\" data-width=\"1920\" data-height=\"1080\" data-duration=\"…\">\n    …\n  </div>\n  <script>\n    const tl = gsap.timeline({ paused: true });\n    // build tweens…\n    window.__timelines[\"title\"] = tl;\n  </script>\n</body>\n```\n\nNote that until the template error is cleared, `check` skips the layout and contrast audits. In that state it reports `0 sample(s)` and `0/0 text checks`, which can look clean even though nothing ran. Also make sure the root uses `width`/`height: 100%` rather than hardcoded pixels, since the canvas size comes from `data-width`/`data-height`.",
    "d60ce30201dfcbb9bf22a63e692cbb630a783b557816f98ce2acb8b457bb5a48": "Two corrections:\n\n1. **Remove the `<template>` wrapper.** A standalone (top-level `index.html`) root must sit directly in `<body>`. Wrapping it in `<template>` hides all content, and `lint` rejects it with `standalone_composition_wrapped_in_template` (error). `<template>` is only for sub-compositions loaded via `data-composition-src`.\n\n   ```html\n   <body>\n     <div data-composition-id=\"title\" data-width=\"1920\" data-height=\"1080\" data-duration=\"…\">\n       …\n     </div>\n   </body>\n   ```\n\n2. **Register the timeline under the root's id.** The key must match the root `data-composition-id`, so use `window.__timelines[\"title\"]`, not `\"outro\"`. Alternatively, rename the root to `outro` if that is the intended id. Either way, the two must be identical. It should also be a single `gsap.timeline({ paused: true })`, registered only after the build completes.\n\n   ```js\n   const tl = gsap.timeline({ paused: true });\n   // …tweens…\n   window.__timelines[\"title\"] = tl;\n   ```\n\nWhile you're in there, check that the root uses `width`/`height: 100%` instead of hardcoded `1920px`/`1080px`. Canvas size comes from `data-width`/`data-height`.\n\nOnce both fixes are in, `npx hyperframes check` should be run to confirm 0 findings. Until the lint error is cleared, the layout and contrast audits report `0 sample(s)`, which looks clean but means nothing was checked.",
    "10a311a44a2e2e13d27f656166cac1be4eeda1cdffa4695af3f174055ff5001c": "Two corrections:\n\n1. **Remove the `<template>` wrapper.** A standalone (top-level `index.html`) root `<div data-composition-id=\"title\">` must sit directly in `<body>`. Wrapping it in `<template>` hides all content, and `lint` rejects it with `standalone_composition_wrapped_in_template` (error). `<template>` is only for sub-compositions loaded via `data-composition-src`.\n\n2. **Register the timeline under `title`, not `outro`.** The `window.__timelines` key must match the root `data-composition-id`. Use `window.__timelines[\"title\"] = tl;`, where `tl` is the single `gsap.timeline({ paused: true })`. With the `outro` key, the runtime can't find the composition's timeline.\n\nCorrected shape:\n\n```html\n<body>\n  <div id=\"root\" data-composition-id=\"title\" data-width=\"1920\" data-height=\"1080\" data-duration=\"…\">\n    <!-- clips -->\n  </div>\n  <script>\n    const tl = gsap.timeline({ paused: true });\n    // …tweens…\n    window.__timelines[\"title\"] = tl;\n  </script>\n</body>\n```\n\nWhile you're in the file, keep the root sized with `width`/`height: 100%` rather than hardcoded pixels, since canvas size comes from `data-width`/`data-height`. Also note that one lint error disables the layout and contrast audits, so fix the template error before trusting `check`'s `0 sample(s)` numbers.",
    "273658a309c7f4e0ac7acd8f4fb45ed3ca5c271cc696789a2726182ed6059ecd": "There are two corrections.\n\n**1. Remove the `<template>` wrapper.**\nA standalone (top-level `index.html`) root must sit directly in `<body>`. Wrapping it in `<template>` hides all content. `lint` rejects it as an error (`standalone_composition_wrapped_in_template`). The `<template>` form is only for sub-compositions loaded via `data-composition-src`.\n\n```html\n<body>\n  <div id=\"root\" data-composition-id=\"title\" data-width=\"1920\" data-height=\"1080\" data-duration=\"…\">\n    …\n  </div>\n</body>\n```\n\n**2. Register the timeline under the root's id.**\nThe key in `window.__timelines` must match the root `data-composition-id`. The root says `title` but the timeline is under `outro`, so the runtime won't find it.\n\n```js\nconst tl = gsap.timeline({ paused: true });\n// …build tweens…\nwindow.__timelines[\"title\"] = tl;\n```\n\nThe alternative is to rename the root to `data-composition-id=\"outro\"`. Pick whichever id is correct and use it in both places.\n\n**Things to confirm while you're in there** (not stated in your description, so I can't tell if they're wrong):\n- The root uses `width`/`height: 100%`, not hardcoded `1920px`/`1080px`. Canvas size comes from `data-width`/`data-height`.\n- There is exactly one paused timeline, registered only after it's fully built.\n- `data-duration` is set to the length you intend. It controls render length, not the timeline's length.\n\nAfter fixing, run `npx hyperframes check`. Until the lint error is cleared, the layout and contrast audits report `0 sample(s)`, which looks clean but means they didn't run.",
    "03c31b7bfca6ba2264e6a9767ed5cf6aba34c0212a5297a80ae713d9b112aed7": "Two corrections:\n\n1. **Remove the `<template>` wrapper.** A standalone composition's root `<div data-composition-id=\"title\">` should sit directly in `<body>`. `<template>` content is inert and never rendered or executed, so the composition won't mount. Templates are for sub-compositions loaded into a host, not for standalone files.\n\n2. **Register the timeline under the matching ID.** The timeline key must equal the root's `data-composition-id`. It's currently registered as `outro` while the root is `title`, so the runtime won't find a timeline for this composition. Use `window.__timelines[\"title\"] = tl;`, or rename both to `outro` so they match.\n\nCorrected shape:\n\n```html\n<body>\n  <div data-composition-id=\"title\">\n    <!-- content -->\n  </div>\n  <script>\n    const tl = gsap.timeline({ paused: true });\n    // ...tweens...\n    window.__timelines = window.__timelines || {};\n    window.__timelines[\"title\"] = tl;\n  </script>\n</body>\n```\n\nThe skeleton doesn't show these details, so also check that the timeline is created `paused` (the runtime drives playback) and that the root has whatever sizing or duration attributes your setup requires.",
    "c8cf32e12df6971f57eb885dbfd1d3f76d1bc9f45e2ed726249d84d13818f552": "Two corrections:\n\n1. **Remove the `<template>` wrapper.** A standalone composition's root `div[data-composition-id=\"title\"]` should sit directly in `<body>`. A `<template>` is for sub-compositions loaded into a host. Its content is inert and never rendered, so a standalone file wrapped this way has nothing for the runtime to find or play.\n\n2. **Register the timeline under the composition's ID.** The timeline key must match `data-composition-id`. Registering it under `outro` leaves the `title` composition with no timeline, so it won't animate or seek. It should be:\n   ```js\n   window.__timelines = window.__timelines || {};\n   window.__timelines[\"title\"] = tl;\n   ```\n   Alternatively, rename the composition ID to `outro`. Either way, the two names must match.\n\nIf they aren't already in place, also check that the root carries its dimensions (e.g., `data-width` and `data-height`) and that the timeline is created paused (`gsap.timeline({ paused: true })`) so the renderer controls playback.",
    "81b97cc7176266a6877be79742fd703a9aecdaeedfab0a6c38a147707aa099dc": "There are two corrections:\n\n1. **Remove the `<template>` wrapper.** A standalone composition's root `<div data-composition-id=\"title\">` should sit directly in `<body>`. A `<template>` is only for sub-compositions that a parent loads in by reference. Content inside a template is inert, so the renderer won't find or mount the root as a top-level composition.\n\n2. **Register the timeline under the composition's own ID.** The timeline key must match `data-composition-id`, so register it as `window.__timelines[\"title\"]`, not `\"outro\"`. If the key doesn't match, the runtime can't link the timeline to the root, and the composition won't animate or seek.\n\nCorrected shape:\n\n```html\n<body>\n  <div data-composition-id=\"title\" ...>\n    <!-- content -->\n  </div>\n  <script>\n    const tl = gsap.timeline({ paused: true });\n    // ...tweens...\n    window.__timelines = window.__timelines || {};\n    window.__timelines[\"title\"] = tl;\n  </script>\n</body>\n```\n\nAlso keep the timeline `paused: true` so the runtime controls playback instead of it autoplaying.",
    "87c2c06b85d407df06a65668387277ffc141a70a9457522010f9c1aff2d94664": "## Why it breaks\n\nThe CSS `transform: translate(-50%, -50%)` and GSAP's `x`/`y` both write to the same `transform` property on the same node. When the tween starts, GSAP parses the existing transform into pixel values and then overwrites it. That causes two problems:\n\n- **Lost centering:** the percentage offset either drifts or snaps away.\n- **Lint error:** `gsap_css_transform_conflict` fails `lint`. A lint error also disables the layout and contrast audits in `check`, so they report `0 sample(s)`.\n\nThe fix is to keep exactly one owner of `transform`.\n\n## Recommended fix: center with layout, animate with GSAP\n\nA full-bleed flex wrapper does the centering, so the heading carries no CSS transform. GSAP then owns `transform` completely.\n\n```html\n<div id=\"title-stage\" class=\"title-stage\">\n  <h1 id=\"title-heading\" class=\"title\">Your Heading</h1>\n</div>\n\n<style>\n  .title-stage {\n    position: absolute;\n    inset: 0;\n    display: flex;\n    align-items: center;\n    justify-content: center;\n  }\n  .title {\n    display: block;   /* transformed elements must be block-level + sized */\n    margin: 0;\n    /* no transform here */\n  }\n</style>\n\n<script>\n  const tl = gsap.timeline({ paused: true });\n  tl.fromTo(\"#title-heading\",\n    { y: 40, opacity: 0 },\n    { y: 0, opacity: 1, duration: 0.8, ease: \"power2.out\" },\n    0.2\n  );\n  window.__timelines[\"<root data-composition-id>\"] = tl;\n</script>\n```\n\n## Alternative: keep absolute centering, but let GSAP own the offset\n\nIf the heading has to stay `position: absolute` at `top/left: 50%`, remove the CSS `translate` and express the offset with `xPercent`/`yPercent`. GSAP composes those with `x`/`y`, so centering and motion coexist.\n\n```css\n.title { position: absolute; top: 50%; left: 50%; margin: 0; /* no transform */ }\n```\n\n```js\ntl.fromTo(\"#title-heading\",\n  { xPercent: -50, yPercent: -50, y: 40, opacity: 0 },\n  { xPercent: -50, yPercent: -50, y: 0, opacity: 1, duration: 0.8, ease: \"power2.out\" },\n  0.2\n);\n```\n\nPut `xPercent`/`yPercent` in both the from and the to values. That way the heading is centered at every seek position, including before the tween starts.\n\n## Determinism checklist for this element\n\n- **One paused timeline per composition:** register it at `window.__timelines[\"<id>\"]`, where the key equals the root `data-composition-id`. Register only after the timeline is fully built.\n- **Explicit start values:** use `fromTo`, not `from`, so seeking to any frame gives the same result.\n- **Fixed inputs only:** use no clocks, unseeded `Math.random`, or measurement-dependent values in the tween.\n- **No `autoAlpha` or `visibility` on a `.clip`:** if the heading or its wrapper is a `.clip`, animate the inner `<h1>`, as shown above.\n- **No scene-exit `visibility: hidden` set:** the runtime already hides timed clips.",
    "36d64ebd46bbbf806ff35aa555249d0d1926fa1bba3138ca0c0e9909fb2feca9": "## Why it breaks\n\nGSAP keeps its own transform state and writes the whole `transform` property on every tween frame. A CSS `transform: translate(-50%, -50%)` on the same node gets overwritten when the `x`/`y` tween starts, so the heading jumps off-center. `lint` flags this as `gsap_css_transform_conflict`.\n\n## Fix A (preferred): center with layout, animate only offsets\n\nUse flex or `inset` for centering, so `transform` belongs only to GSAP.\n\n```html\n<div class=\"heading-wrap\">\n  <h1 id=\"hero-heading\" class=\"heading\">Title</h1>\n</div>\n```\n\n```css\n.heading-wrap {\n  position: absolute;\n  inset: 0;\n  display: flex;\n  align-items: center;\n  justify-content: center;\n}\n.heading {\n  display: block;          /* transformed elements must be block-level */\n  margin: 0;\n  /* no transform here */\n}\n```\n\n```js\nconst tl = gsap.timeline({ paused: true });\ntl.fromTo(\"#hero-heading\",\n  { x: -40, y: 20, opacity: 0 },\n  { x: 0, y: 0, opacity: 1, duration: 0.8, ease: \"power3.out\" },\n  0\n);\nwindow.__timelines[\"main\"] = tl; // key must match root data-composition-id\n```\n\n## Fix B: keep absolute 50/50 positioning, move the percent offset into GSAP\n\n```css\n.heading {\n  position: absolute;\n  left: 50%;\n  top: 50%;\n  display: block;\n  margin: 0;\n  /* no transform here */\n}\n```\n\n```js\ntl.fromTo(\"#hero-heading\",\n  { xPercent: -50, yPercent: -50, x: -40, y: 20, opacity: 0 },\n  { xPercent: -50, yPercent: -50, x: 0,   y: 0,  opacity: 1, duration: 0.8, ease: \"power3.out\" },\n  0\n);\n```\n\nGSAP composes `xPercent`/`yPercent` with `x`/`y`, so the heading stays centered while the pixel offset animates.\n\n## What keeps it deterministic\n\n- There is one `gsap.timeline({ paused: true })`, registered at `window.__timelines[\"<root data-composition-id>\"]` after it is built.\n- `fromTo` puts the initial state in the tween, not in CSS. The value at any seek time comes only from the timeline position.\n- The tween has an explicit position (`0`) and a finite duration. It uses no `Date.now()`, unseeded `Math.random()`, or `repeat: -1` without a finite root `data-duration`.\n- If the heading is itself a `.clip`, animate an inner child rather than the clip (that rule concerns `autoAlpha`/`visibility`, but tweening a child is the safe pattern). Don't add a `visibility:hidden` exit set, because the runtime hides timed clips itself.\n\nFix A is the cleaner choice. It removes the conflict entirely rather than relying on GSAP to own the centering offset.",
    "29ed39483e3d8bb41728ed0b11202fd789a42cb86edd91258f47f4fdbfae1551": "## The problem\n\nGSAP writes `x`/`y` into the element's inline `transform`, which **replaces** the CSS `transform: translate(-50%, -50%)`. When the tween starts, the heading loses its centering offset and jumps down and to the right by half its own size. `lint` flags this pattern as `gsap_css_transform_conflict`.\n\n## Fix A (preferred): center with layout, animate with transforms\n\nLet a flex wrapper handle centering, so the heading's `transform` belongs only to GSAP.\n\n```html\n<div class=\"clip title-layer\" data-start=\"0\" data-duration=\"4\" data-track-index=\"1\">\n  <h1 id=\"title-heading\" class=\"heading\">Your Title</h1>\n</div>\n```\n\n```css\n.title-layer {\n  position: absolute;\n  inset: 0;\n  display: flex;\n  align-items: center;\n  justify-content: center;\n}\n.heading {\n  display: block;   /* transformed elements must be block-level and sized */\n  margin: 0;\n  /* no transform here */\n}\n```\n\n```js\nconst tl = gsap.timeline({ paused: true });\ntl.fromTo(\"#title-heading\",\n  { x: -40, y: 20, opacity: 0 },\n  { x: 0, y: 0, opacity: 1, duration: 0.8, ease: \"power3.out\" }, 0);\n\nwindow.__timelines[\"main\"] = tl; // must match the root data-composition-id\n```\n\n## Fix B: keep absolute positioning, move the offset into GSAP\n\nIf the heading has to stay `position: absolute; left: 50%; top: 50%`, remove the CSS transform and let GSAP own both offsets. GSAP stacks `xPercent`/`yPercent` with `x`/`y`:\n\n```css\n.heading { position: absolute; left: 50%; top: 50%; margin: 0; /* no transform */ }\n```\n\n```js\ntl.fromTo(\"#title-heading\",\n  { xPercent: -50, yPercent: -50, x: -40, y: 20, opacity: 0 },\n  { xPercent: -50, yPercent: -50, x: 0,   y: 0,  opacity: 1, duration: 0.8, ease: \"power3.out\" }, 0);\n```\n\n## Why both fixes are deterministic\n\n- `fromTo` sets explicit start and end states, so seeking to any frame gives the same result and nothing depends on CSS values read at runtime.\n- There is one paused timeline, registered at `window.__timelines[\"<root id>\"]` once it has been built.\n- Nothing uses clocks, unseeded randomness, or input state.\n- The tween targets the heading, not the `.clip` wrapper, and no `autoAlpha`/`visibility` is tweened on a clip. This avoids `gsap_animates_clip_element`.\n\nAfterwards, `npx hyperframes check` should no longer report `gsap_css_transform_conflict`.",
    "208a6ba4a439dd76f7616eff546049a35f8455e23cf61fabff24ccf6d61b7f09": "# Fix: let GSAP own the whole transform\n\n## Why it breaks\n\nWhen GSAP animates `x`/`y`, it **writes the element's entire `transform`**, replacing your CSS `translate(-50%, -50%)`. GSAP can parse the existing transform first, but it may resolve the percentages to fixed pixel values. That causes two problems:\n\n- **Centering drifts** after a resize or font load, because the pixel offset was measured once at parse time.\n- **The animation is not deterministic.** The start state depends on when and how GSAP read the computed transform, and relative values (`\"+=50\"`) compound on re-runs.\n\n## Correction\n\nExpress the centering offset as GSAP's `xPercent`/`yPercent`, which stay percentage-based and compose with `x`/`y`. Then animate with explicit start and end values.\n\n**CSS** (remove the transform):\n\n```css\n.heading {\n  position: absolute;\n  top: 50%;\n  left: 50%;\n  /* no transform here; GSAP owns it */\n}\n```\n\n**JS:**\n\n```js\nconst ctx = gsap.context(() => {\n  // Centering offset, kept as a percentage of the element's own size\n  gsap.set(\".heading\", { xPercent: -50, yPercent: -50 });\n\n  // Explicit start and end values, no \"+=\", so every run is identical\n  gsap.fromTo(\".heading\",\n    { x: 0, y: 40, autoAlpha: 0 },\n    { x: 0, y: 0, autoAlpha: 1, duration: 0.8, ease: \"power2.out\" }\n  );\n});\n\n// On unmount or re-init (e.g. a React effect cleanup):\n// ctx.revert();  // restores the original inline styles, so a re-run starts clean\n```\n\nGSAP then builds `translate(-50%, -50%) translate(x, y)`. Centering stays responsive, and the motion is layered on top of it.\n\n## Determinism checklist\n\n- Use `fromTo` with absolute values instead of `from`/`to` against computed state or `+=`/`-=`.\n- Set `xPercent`/`yPercent` with `gsap.set` before any tween, and never put a transform in CSS for this element.\n- Wrap the code in `gsap.context()` (or `gsap.matchMedia()` for breakpoints) and call `revert()` on teardown. This prevents stacked tweens in React StrictMode, HMR, or route changes.\n- If several tweens target the same element, use `overwrite: \"auto\"` or a single timeline so they don't fight over `y`.\n\n## Alternative: split the responsibilities\n\nCenter a wrapper with CSS that doesn't use `transform`, for example a parent with `display: grid; place-items: center;` or the wrapper itself using `inset: 0; margin: auto;`. Then animate only the inner heading's `x`/`y`. CSS layout handles centering and GSAP handles motion, so nothing can be overwritten.\n\nI'd recommend the `xPercent`/`yPercent` fix as the smallest change. Use the wrapper approach if other code or CSS also needs to touch the heading's transform.",
    "11afde9a2d2be2aa24f5dba3ee4a1059fb5f13dd1500fa2dbbbff8f090466785": "## Problem\n\nGSAP owns the element's entire `transform` once it animates `x`/`y`. On first tween it reads the **computed** transform, which the browser reports as a pixel `matrix(...)`, not `translate(-50%,-50%)`. That causes three issues:\n\n- The percentage offset gets baked in as fixed pixels, so centering breaks when the heading's size changes (resize, font load, text change).\n- GSAP's `x`/`y` and the CSS translate end up fighting over, or stacking into, one transform string.\n- The starting state depends on when GSAP first parses the element, for example before or after web fonts load. That makes the animation non-deterministic.\n\n## Correction\n\nMove the centering offset out of CSS and into GSAP's dedicated percentage channels. `xPercent`/`yPercent` stay relative to the element's own size, and `x`/`y` stay free for the motion.\n\n**CSS:** no `transform` on the heading.\n\n```css\n.hero-heading {\n  position: absolute;\n  top: 50%;\n  left: 50%;\n  /* no transform here; GSAP owns it */\n}\n```\n\n**JS:** set the centering explicitly, then animate with explicit start and end values.\n\n```js\nconst ctx = gsap.context(() => {\n  gsap.set(\".hero-heading\", { xPercent: -50, yPercent: -50 });\n\n  gsap.fromTo(\n    \".hero-heading\",\n    { x: 0, y: 40, autoAlpha: 0 },   // explicit start, not read from the DOM\n    { x: 0, y: 0, autoAlpha: 1, duration: 0.8, ease: \"power2.out\" }\n  );\n});\n\n// on unmount / route change:\n// ctx.revert();\n```\n\n## Why this is deterministic\n\n- **`xPercent`/`yPercent` and `x`/`y` are separate components.** GSAP composes them as `translate(-50%,-50%) translate(x,y)`, so centering is never overwritten or converted to pixels.\n- **`fromTo` with absolute values** means the animation never depends on the current computed state. Avoid `from()` alone and relative values like `\"+=20\"`, which read whatever is in the DOM.\n- **`gsap.context().revert()`** restores the original state on teardown. Re-runs, such as React StrictMode double-mounts or hot reloads, then start from the same baseline.\n- **Optional:** if layout-dependent values are involved, start the tween after `document.fonts.ready`. Wrap it in `gsap.matchMedia()` if breakpoints change the motion.\n\n## Alternative if the CSS transform can't be removed\n\nWrap the heading in a parent element. Keep `translate(-50%,-50%)` on the wrapper and apply the GSAP `x`/`y` tween to the inner element only. Each element then has a single transform owner.",
    "a2cafab01777c00b805d9272f1b2f69883be0f02cc536a8d4255cce70fdd5c89": "# Problem\n\nGSAP writes the entire `transform` property when it animates `x`/`y`. On first use it parses the existing computed transform, which the browser reports as a pixel `matrix(...)`. Your `translate(-50%, -50%)` gets lost in one of two ways:\n\n- **Overwritten.** The heading jumps off-center when the tween starts.\n- **Baked into `x`/`y` as fixed pixels.** Centering breaks when the element's size changes, for example after font loading, text changes or a resize.\n\nThe starting point also depends on whatever the transform was at parse time. That makes the animation non-deterministic, especially on replays, re-renders or if something like `+=` is used.\n\n# Correction\n\nLet GSAP own the whole transform. Put centering in `xPercent`/`yPercent` and the motion in `x`/`y`. GSAP combines them as `translate(-50%,-50%) translate(x, y)`.\n\n**CSS:** remove the transform.\n\n```css\n.heading {\n  position: absolute;\n  top: 50%;\n  left: 50%;\n  /* no transform here — GSAP owns it */\n}\n```\n\n**JS:**\n\n```js\nconst ctx = gsap.context(() => {\n  // Centering: percentage-based, so it stays correct if the element resizes\n  gsap.set(\".heading\", { xPercent: -50, yPercent: -50 });\n\n  // Motion: explicit absolute start and end values, so every run is identical\n  gsap.fromTo(\".heading\",\n    { x: 0, y: 40, opacity: 0 },\n    { x: 0, y: 0, opacity: 1, duration: 0.8, ease: \"power2.out\" }\n  );\n}, containerRef);\n\n// On teardown or re-run (e.g. React effect cleanup):\n// ctx.revert();\n```\n\n# Why each part matters\n\n| Choice | Reason |\n|---|---|\n| `xPercent`/`yPercent: -50` | Keeps centering relative to the element's own size. It is separate from `x`/`y`, so the motion never overwrites it. |\n| No CSS `transform` | Nothing for GSAP to misparse into pixel values. |\n| `fromTo` with absolute values | No dependence on current or leftover state, so replays and re-mounts start from the same place. Avoid `from()` alone and relative `+=`/`-=` values. |\n| `gsap.context` + `revert()` | Cleans up inline styles. Repeated mounts (React StrictMode, hot reload) don't stack tweens or start from a mid-animation state. |\n\n# Alternatives if you must keep the CSS transform\n\n- **Wrapper element.** Center a parent with `translate(-50%,-50%)` and animate `x`/`y` on the inner heading. The two transforms never touch.\n- **Individual CSS `translate` property.** Use `translate: -50% -50%;` instead of `transform`. GSAP animates `transform`, and the browser composes the two separately. This depends on browser support and is less explicit than the `xPercent` approach.\n\nThe `xPercent`/`yPercent` approach is the recommended one. It is the idiomatic GSAP fix and keeps everything in one place.",
    "56e630fb4cb5e108e0e885f9dfd63e57a3dc50c23cd35e5c73df20d8db02260d": "## The ownership problem\n\nA timed clip (`class=\"clip\"` with `data-start` / `data-duration`) has its visibility **owned by the HyperFrames runtime**. On every seek, the runtime computes whether the current time falls inside the clip's window and shows or hides it.\n\nThe `tl.set(clip, { visibility: \"hidden\" }, end)` adds a **second owner**: the GSAP timeline also writes inline `visibility` on the same element. Both write the same property on the same node, so whichever writes last wins.\n\nThat is why backward seeks fail:\n\n- When GSAP initializes a `set` tween, it records the element's visibility at that moment as the value to revert to. That snapshot may already be `hidden`, because the runtime had hidden the clip outside its window when the timeline was built or first played.\n- When you seek back before the `set`, GSAP restores that recorded value, which may be `hidden`. It can also simply write after the runtime has made the clip visible again. Either way, the runtime's \"visible\" is overwritten.\n- The clip stays hidden inside its own window. The result also depends on seek order, which breaks the deterministic-render contract. A frame should look the same regardless of how you reached that time.\n\nThis is why `lint` rejects tweening `visibility`, `autoAlpha`, or `display` on a `.clip` (`gsap_animates_clip_element`). The skill also says not to add scene-exit visibility sets, because the runtime already hides timed clips.\n\n## The fix\n\n1. **Delete the `tl.set(..., { visibility: \"hidden\" })`.** The runtime hides the clip at `data-start + data-duration` and shows it again on any seek back into the window.\n2. **If it should disappear earlier, change the timing, not the timeline.** Shorten `data-duration` (or adjust `data-start`) so the clip's window ends where you want.\n3. **If you want a visual exit, animate something the runtime doesn't own.** Fade `opacity` on an inner child, for example `tl.to(\"#scene-inner\", { opacity: 0, duration: 0.4 }, end - 0.4)`. A plain `opacity` tween on the `.clip` is also acceptable, but `visibility` and `autoAlpha` are not. Opacity tweens revert cleanly on backward seeks because nothing else writes that property.\n4. **Re-validate.** Run `npx hyperframes check` to confirm `gsap_animates_clip_element` is gone. Then snapshot a time inside the clip window after a later time to confirm it renders visible.\n\nThe rule is that the runtime owns *whether* a clip is visible, and your timeline owns *how* its contents look while it is visible.",
    "a70b6fdefff4e032795756d2862945d2ecd49b9ec06baf7317b111e1b7ccb842": "## Ownership problem\n\nVisibility of a timed clip (`class=\"clip\"` with `data-start`/`data-duration`) belongs to the **framework**. On every seek, the runtime shows the clip inside its time window and hides it outside. A `tl.set(clip, { visibility: \"hidden\" })` at the clip's end adds a **second writer** for the same property, and the two disagree.\n\n- **GSAP records a stale \"before\" value.** A `set` captures the property's current value the first time it renders, so it can revert later. That value is whatever the runtime had written at that moment, which may already be `hidden` or may be an inline value the runtime later changes. On a backward seek, GSAP \"reverts\" to that snapshot rather than to the correct state for the new time.\n- **Last writer wins.** The runtime and the timeline both write inline `visibility` during a seek. Whichever runs last decides the result. If GSAP's stale `hidden` lands after the runtime's \"show\" for the new time, the clip stays hidden even though the playhead is back inside its window.\n- **It never re-asserts.** Once GSAP holds `hidden`, nothing in your timeline sets it back to `visible`. The runtime can't reliably win against a tween that keeps re-applying its own recorded state.\n\nThis is why `lint` rejects tweening `visibility`, `display`, or `autoAlpha` on a `.clip` (`gsap_animates_clip_element`). The skill also warns specifically against adding a scene-exit `tl.set(..., {visibility:\"hidden\"})`.\n\n## Fix\n\n1. **Delete the `tl.set(..., { visibility: \"hidden\" })`.** The runtime already hides the clip at `data-start + data-duration` and shows it again when you seek back into its window.\n2. **To change when it disappears, change the timing attributes**, not the timeline. Adjust `data-duration` (or `data-start`) on the clip so the framework's window ends where you want.\n3. **For a visual exit, animate opacity only.** Tween `opacity` on an inner child, or `opacity` on the `.clip` itself. Never use `autoAlpha` or `visibility`:\n\n```js\n// before: tl.set(\"#card\", { visibility: \"hidden\" }, 4);   // remove\ntl.to(\"#card .inner\", { opacity: 0, duration: 0.4 }, 3.6); // fade ends as the clip's window closes at 4s\n```\n\nOpacity is a property only your timeline owns, so GSAP reverts it correctly on backward seeks. The framework keeps sole control of visibility.\n\n4. **Verify the fix.** `npx hyperframes check` should no longer report `gsap_animates_clip_element`. Then `npx hyperframes snapshot --at` a time after the clip's end and a time back inside its window. The clip should be absent in the first frame and visible in the second.",
    "7dd5fcb3877cd8402abbc0ccdae9ba1c85c1061ccdd22da719279677767513bd": "# Clip hidden by a timeline `visibility` set stays hidden after seeking back\n\n## The ownership problem\n\nA timed clip (`class=\"clip\"` with `data-start` / `data-duration`) has its visibility **owned by the framework**. On every seek, the runtime shows the clip while the playhead is inside its window and hides it outside. It also already hides it at its end, so the scene-exit set is redundant.\n\nAdding `tl.set(clip, { visibility: \"hidden\" }, end)` puts a second writer on the same property. GSAP and the runtime now fight over it:\n\n- **GSAP records a \"start\" value for the set the first time it renders.** By then the runtime may already have stamped the clip hidden, or the two writes happen in an order you don't control. GSAP can record `hidden` as the value to restore.\n- **Seeking backward** past the set makes GSAP revert to the value it recorded. If that value was `hidden`, or GSAP's inline style lands after the runtime's show, the clip stays hidden inside its own window.\n- **The result depends on seek order and history.** A linear render may look fine, while Studio scrubbing, snapshots at arbitrary times, or out-of-order frame capture show a blank clip. That breaks the seekable, deterministic-render contract.\n\nThis is why the contract says never to tween `display`, `visibility`, or `autoAlpha` on a `.clip`. `lint` rejects it as `gsap_animates_clip_element`.\n\n## The fix\n\n1. **Delete the `visibility` set** (and any `autoAlpha` / `display` tween) that targets the `.clip`. The runtime already hides the clip once its `data-start + data-duration` window ends.\n2. **Get the timing right with attributes, not the timeline.** If the clip should disappear earlier or later, change its `data-duration` (or `data-start`). Don't override it in GSAP.\n3. **If you want a visual exit, animate opacity on an inner child.** `opacity` on the `.clip` itself is also allowed. Don't touch visibility:\n\n   ```js\n   // before (wrong)\n   tl.set(\"#scene2\", { visibility: \"hidden\" }, 6);\n\n   // after: optional fade on an inner node, ending at the clip's end\n   tl.to(\"#scene2 .scene2-inner\", { opacity: 0, duration: 0.4 }, 5.6);\n   ```\n\n   Use `fromTo` if the child's starting state must be explicit, so seeking into the middle is deterministic.\n4. **Validate** with `npx hyperframes check` (the lint error should be gone). Then run `npx hyperframes snapshot --at <times>` before, inside, and after the clip window, in non-linear order, to confirm the clip appears and disappears correctly regardless of seek direction.\n\n(Caption hard-kills are a separate rule and are not covered by this.)",
    "b67d74417cb7b4dfcbf11648f60dc289d6cf34789d4282dbbe00c16aa192388f": "## The ownership problem\n\nIn HyperFrames, **clip visibility belongs to the framework, not your timeline.** A `.clip` with `data-start`/`data-duration` is shown and hidden by the runtime based on the current time. A `tl.set(clip, { visibility: \"hidden\" })` at the clip's end gives a second writer control of the same property:\n\n- **The runtime** sets visibility from the clip's time window on every seek.\n- **GSAP** also writes `visibility`. A `set` records the value it overwrites the first time it renders, and puts that value back when you seek before it.\n\nWhen the two disagree about the current state, you get stale values. GSAP can capture the \"before\" value at a moment when the runtime had already hidden the clip, for example just past its window. If that happens, seeking backward makes GSAP restore `hidden`, even though the runtime considers the clip in-window. The clip then stays invisible in a range where it should show.\n\nThe renderer seeks frames non-linearly, and Studio scrubbing does too. So this produces output that is non-deterministic and depends on seek order. The rendered video can also differ from the preview.\n\n`lint` flags this pattern as `gsap_animates_clip_element`. The rule covers `visibility`, `autoAlpha`, and `display` on a `.clip`.\n\n## The fix\n\n1. **Delete the end-of-scene `tl.set(..., { visibility: \"hidden\" })`.** It isn't needed, because the runtime already hides the clip when its `data-start + data-duration` window ends.\n2. **To make it disappear earlier, change the timing, not the timeline.** Shorten `data-duration` so the window ends where you want the clip gone.\n3. **For a visual exit, animate opacity or transforms on an inner child.** Opacity directly on the `.clip` is also acceptable. Never use `visibility` or `autoAlpha` on the clip.\n\n   ```js\n   tl.to(\"#scene2-content\", { opacity: 0, duration: 0.4 }, sceneEnd - 0.4);\n   ```\n\n   Opacity is a plain tweened value with no ownership conflict, so it reverses cleanly on any backward seek.\n4. **Re-run `npx hyperframes check`** to confirm `gsap_animates_clip_element` is gone. Then scrub backward and forward over the boundary in preview, or `snapshot` at times on both sides of the boundary, to confirm the clip reappears correctly.\n\nCaption \"hard-kill\" patterns follow a separate rule and don't apply here. For ordinary timed clips: the framework handles show/hide, and your timeline handles how things look while they are visible.",
    "3de0d3e5334398b3ab8000af8b97cf9d3e4a4875b8c933b5e6deb7ddb81c9d8f": "## The ownership problem\n\nA `.clip`'s `visibility` belongs to the **framework**. On every seek, the runtime compares the current time with each clip's `data-start` / `data-duration` window. It shows the clip inside that window and hides it outside. Your `tl.set(clip, { visibility: \"hidden\" }, end)` makes the timeline a **second writer** on the same property.\n\nTwo writers on one property break when you seek backward:\n\n- GSAP records a `set`'s \"before\" value the first time that set renders. It restores that value when the playhead moves back before the set's position.\n- The value GSAP recorded is whatever the element had at that moment. That is often `hidden`, because the runtime had already hidden the clip outside its window (for example, at init or at frame 0).\n- When you seek back into the clip's window, the runtime and GSAP both write `visibility`. Which one runs last decides the result. GSAP's revert can put back the stale `hidden` it recorded, so the clip stays invisible even though it is inside its window.\n\nThe result depends on seek history, not just the current time. That breaks the determinism contract: frame *N* must look the same no matter how you got there. This is why `lint` rejects it as `gsap_animates_clip_element`. It is also why the skill says not to add a scene-exit `tl.set(..., {visibility:\"hidden\"})`.\n\n## The fix\n\n1. **Delete the visibility `set`.** The runtime already hides the clip at `data-start + data-duration`. The extra set adds nothing except the conflict.\n2. **To end the clip at a different time, change its `data-duration`.** Timing comes from the `data-*` attributes, not from timeline sets. If the clip now ends after the root `data-duration`, extend the root in the same edit.\n3. **For a visual exit, animate opacity or transforms on an inner child.** Opacity on the `.clip` itself is also allowed. Never use `visibility`, `display`, or `autoAlpha` on the `.clip`.\n\n```js\n// ❌ second writer on framework-owned visibility\ntl.set(\"#scene-2\", { visibility: \"hidden\" }, 6);\n\n// ✅ let the clip window handle hiding; fade a child before it ends\ntl.to(\"#scene-2 .content\", { opacity: 0, duration: 0.4 }, 5.6);\n```\n\n```html\n<div id=\"scene-2\" class=\"clip\" data-start=\"2\" data-duration=\"4\" data-track-index=\"1\">\n  <div class=\"content\">…</div>\n</div>\n```\n\nOpacity tweens are pure functions of time, and the framework stays the only owner of visibility. Backward seeks then give the same frame as forward playback. The clip reappears whenever the playhead is inside its window, with the child at the opacity the timeline gives for that time.",
    "34b2a74c6c51724933176d68d60e5e3c4c8f6f76b3609f16cc9af66cf6b89b78": "# Why the clip stays hidden after seeking backward\n\n## The ownership problem\n\nA `.clip` element's visibility has one owner, the **HyperFrames runtime**. On every seek or frame, the runtime computes from `data-start` / `data-duration` whether the clip is inside its time window, and shows or hides it.\n\nA `tl.set(clip, { visibility: \"hidden\" }, end)` adds a **second writer** to the same property. The two writers are not coordinated:\n\n- **The runtime** writes visibility from the clip's time window.\n- **GSAP** writes visibility from its timeline state. A `set` also records a \"before\" value that it restores when the playhead moves back past it.\n\nWhatever GSAP recorded as the \"before\" value depends on when the tween first initialized. If the runtime had already hidden the clip at that point (for example, before its start), GSAP recorded `hidden` as the starting value. Seeking backward then makes GSAP \"restore\" `hidden`.\n\nOrdering adds a second failure: if GSAP's write lands after the runtime's for a given frame, it wins. Either way the clip is stuck hidden inside its own window. The result is non-deterministic and seek-order dependent, which breaks the requirement that any frame renders the same regardless of how you got there.\n\nThat is why the contract forbids this pattern:\n\n- `lint` rejects `display`, `visibility`, or `autoAlpha` tweens on a `.clip` (`gsap_animates_clip_element`).\n- You don't need a scene-exit `visibility: \"hidden\"` set at all, because the runtime already hides timed clips when their window ends.\n\n## The fix\n\n1. **Delete the `tl.set(..., { visibility: \"hidden\" })`.** Let `data-start` / `data-duration` define when the clip disappears. If it should vanish at a different time, change `data-duration`, not the timeline.\n\n2. **If you want a visual exit, animate a child**, using a property the runtime doesn't own:\n\n   ```html\n   <div class=\"clip\" data-start=\"2\" data-duration=\"4\" data-track-index=\"1\">\n     <div class=\"scene-inner\">…</div>\n   </div>\n   ```\n   ```js\n   // fade out over the last 0.5s of the clip's window (2 + 4 = 6)\n   tl.fromTo(\".scene-inner\", { opacity: 1 }, { opacity: 0, duration: 0.5 }, 5.5);\n   ```\n   `fromTo` pins both endpoints, so seeking in either direction gives the same result for any time.\n\n3. **Make sure the window fits the composition.** If the clip ends past the root `data-duration`, extend the root in the same edit, or `lint` warns `clip_ends_past_root_duration` and the end is cut off.\n\n4. **Verify:** run `npx hyperframes check` until it reports 0 findings. Then run `npx hyperframes snapshot --at` at times before, inside, and after the clip's window, and check them in a non-linear order (for example, the late time first, then an earlier one) to confirm seeking backward shows the clip again.\n\n**Rule of thumb:** the runtime decides *whether* a clip exists on screen, and your timeline decides *how it looks* while it does.",
    "c5c67a9568ace248ad8dfb04d36fe4981b95b591efb4bbcd8cde90adf2b263ff": "## The ownership problem\n\nA timed clip (`class=\"clip\"` with `data-start`/`data-duration`) has its **visibility owned by the framework**. On every seek, the runtime shows or hides the clip from its time window. When you add `tl.set(clip, { visibility: \"hidden\" }, end)`, the same CSS property gets a second owner: your GSAP timeline. The two writers then disagree about what the property should be.\n\nThis explains the backward-seek bug:\n\n1. **GSAP caches a \"before\" value.** A `set` (a zero-duration tween) records the property's current value the first time it renders and restores that value when you seek back past its position. That recorded value is whatever the element had at that moment. Often that is a state the runtime had already written, such as `hidden` from before the clip's window opened. Seeking backward then \"restores\" `hidden`.\n2. **Write order decides the frame.** Within a seek, the runtime applies its visibility and GSAP applies its tween state. If GSAP writes last, its stale value wins, even when the runtime correctly decided the clip should be visible.\n\nThe result is a clip that stays hidden inside its own window after any backward scrub. This also hits Studio scrubbing and any renderer that seeks frames out of order, so the render stops being deterministic.\n\nThis is why `lint` rejects it as `gsap_animates_clip_element`. Tweening or setting `visibility`, `display`, or `autoAlpha` on a `.clip` is banned.\n\n## The fix\n\n1. **Delete the exit `tl.set(..., { visibility: \"hidden\" })`.** The runtime already hides the clip at `data-start + data-duration`, so the set is redundant as well as harmful.\n2. **Control when it disappears with timing attributes, not tweens.** If the clip should vanish earlier, shorten its `data-duration`.\n3. **For a visual exit, animate opacity instead of visibility.** Fade an inner child, or `opacity` on the `.clip` itself, which is allowed:\n   ```js\n   tl.to(\"#scene-2 .content\", { opacity: 0, duration: 0.4 }, clipEnd - 0.4);\n   ```\n   Use `fromTo` if the start value matters, so the tween doesn't depend on a cached value.\n4. If you need a hard visibility toggle for some reason, put it on a **non-clip child** element, never on the `.clip`.\n\nOne exception: caption hard-kills follow a separate rule and are not covered by this guidance.\n\nOnce the set is removed, the runtime is the only writer of the clip's visibility. Every seek, forward or backward, then gives the same result for the same time.",
    "ac68c87ae09144ab4f2efca336229451c31c28d3c3b895a141e08d8eda274f42": "## The ownership problem\n\nThe clip's visibility has **two writers and no single owner**:\n\n1. The clip itself (or whatever showed it at its start time), and\n2. A timeline event at the clip's end that imperatively sets `visible = false`.\n\nThe timeline event is a **one-way side effect**. It fires when the playhead crosses the end time and mutates shared state. Nothing records that the mutation belongs to the interval *after* the end time. When you seek backward to a time inside the clip, no event fires in reverse. The flag stays `false` even though, at that time, the clip should be visible.\n\nIn short, visibility is stored as accumulated history (\"what events have fired so far\") when it should be a function of time (\"what is true at time *t*\").\n\n## The fix\n\nGive visibility one owner and make it **derived from the current time**, not mutated by crossings.\n\n**Preferred: make it declarative and re-evaluated on every time change, including seeks.**\n\n```\nvisible(t) = clip.start <= t < clip.end\n```\n\nThe timeline (or the clip's own track) owns this value. On every tick *and* every seek, it recomputes from `t`. Remove the \"set hidden at end\" event entirely, or turn it into a keyframe/interval on the visibility track instead of a callback.\n\n**If you must keep event-style triggers**, make them reversible and seek-aware:\n- Each event that writes a property records the prior value (or defines both `apply` and `revert`).\n- On seek, the timeline determines which events lie between the old and new time and reverts them in reverse order when going backward (or reapplies them going forward). Alternatively, it resets to the base state and replays all events up to the new `t`.\n\n**Also enforce single ownership.** Don't let both the clip and a timeline event write the same `visible` property. If several sources need a say, compose them explicitly, for example `visible = clipActive(t) && userToggle && parentVisible`, rather than letting the last writer win.\n\n## Rule of thumb\n\nAnything driven by a timeline should be **state as a function of time**, not **state as a result of events that happened**. Seeking, scrubbing, and looping only work correctly when the value can be recomputed from `t` alone.",
    "a57e5e97f7eaa34211e687ae71d60e60e70f9c958e4b5e26ff2c1e07fe74e108": "# The ownership problem\n\nThe clip's visibility has **two owners**, and only one of them understands time:\n\n1. **The clip** owns its visibility as ordinary mutable state (`clip.visible = true/false`).\n2. **The timeline** reaches in at the clip's end time and fires a one-shot write: `clip.visible = false`.\n\nThat end-of-clip write is an **event-style side effect**, not **time-indexed state**. It runs when the playhead *crosses* the end marker going forward. When you seek backward:\n\n- If the timeline only fires on forward crossing, nothing runs. The `false` stays.\n- If it does fire on backward crossing, it has no record of what the value was before. So it can't restore it. The \"previous value\" was never the timeline's to know, because the clip owned it.\n\nThe result is that visibility now depends on **history** (which seeks happened) rather than on **current time**. That is the bug. A seekable timeline must make every property it touches a pure function of the playhead position. A fire-and-forget write breaks that.\n\nA related variant has the same cause: some reversible \"set\" actions capture their start value lazily the first time they run. If something else has already hidden the clip at that moment, the captured \"before\" value is `hidden`. Reversing then faithfully restores… `hidden`.\n\n# The fix: give visibility a single, time-aware owner\n\n**Option A: the timeline owns visibility outright (preferred).**\nModel visibility as a track or interval on the timeline, not as a callback:\n\n```\nvisible(t) = clip.start <= t < clip.end\n```\n\nOn every seek or tick, the timeline evaluates this and writes the result. Forward, backward, scrubbing, and jumping all give the correct answer, because nothing depends on the path taken. The clip stops writing its own visibility. Anything else that wants the clip hidden (user toggle, culling, etc.) is combined explicitly:\n\n```\neffectiveVisible = timelineVisible(t) && userVisible && !culled\n```\n\nEach input has exactly one writer. Only the composed result is applied.\n\n**Option B: if you must keep a discrete \"set\" action, make it reversible and own its baseline.**\n- Capture the \"before\" value **when the action is built**, from the timeline's own defined initial state. Don't read it from whatever the clip happens to be at first run.\n- Apply `after` when `t >= end`, and `before` when `t < end`, on **every** evaluation, in both directions.\n- On a seek, re-evaluate all actions for the new time. Don't just fire the ones you crossed.\n\n**Avoid:** patching it with an `onReverse` or \"seek backward\" handler that sets `visible = true`. That adds a third writer. It also breaks the next time something else legitimately hides the clip, because the handler will un-hide it.\n\n# Rule of thumb\n\nOn a scrubbable timeline, any property the timeline touches should be **derived from the current time**, not **accumulated from events**. Pick one owner per property. If several systems influence it, compose their inputs explicitly instead of letting each one write the property directly.",
    "dd165664daf39c8def626f8f4451a401363c8aca89b6323be7c4f0e8fd4fcabd": "## The ownership problem\n\nTwo things are trying to own the clip's `visible` property, and only one of them understands time.\n\n1. **The timeline** owns the clip's *temporal* state: where the playhead is, and whether the clip is active.\n2. **The end-of-clip action** is a one-shot imperative write, such as an `onComplete`/end callback or event that runs `clip.visible = false`. It fires when the playhead crosses the end going forward, mutates the property, and then forgets about it.\n\nThe timeline never recorded that write as part of its own state, so it has nothing to undo:\n\n- When you seek backward past the clip's end, the timeline re-evaluates the properties it animates.\n- `visible` isn't one of them. It was changed by a side effect.\n- So the value stays `false`. The clip is now \"inside its active range\" according to the timeline but hidden according to the property.\n\nIn short, the visibility is a **stateful mutation triggered by time**, not a **pure function of time**. A forward-only event has no inverse, and scrubbing exposes that.\n\nThe usual symptoms point to this cause:\n\n- It works on linear playback.\n- It breaks on seek, scrub, reverse, or loop.\n- Jumping directly to a time *before* the clip's end, without crossing it, behaves differently from scrubbing back to that time.\n\n## The fix: give visibility a single, time-derived owner\n\nMake visibility something the timeline *evaluates at the current time*, not something an event *sets*.\n\n**Option A: Put visibility on the timeline as a keyframed or tracked property (preferred).**\nAdd a visibility \"set\" or step keyframe at the clip's end time instead of a callback. In GSAP, for example, use `tl.set(clip, { autoAlpha: 0 }, endTime)` rather than `onComplete: () => clip.style.visibility = 'hidden'`.\n\n- Timeline-owned sets record the pre-change value.\n- They restore that value when the playhead moves back before that time.\n- Make sure the initial visible state is captured correctly. Watch for `immediateRender`-style behavior on `set`/`from` at time 0.\n\n**Option B: Derive visibility from the clip's active range.**\nOn every render or seek, compute:\n\n```\nvisible = (t >= clip.start && t < clip.end)\n```\n\n- Recompute in the timeline's update/seek handler, not in an edge-triggered event.\n- This is idempotent. Any seek, in any direction, produces the correct state.\n\n**Option C: Pair edge events with their inverses (fallback, fragile).**\nIf you're stuck with callbacks, add the reverse handler too (`onReverseComplete`, an \"exit backward\" event, or similar) to restore visibility.\n\n- This still breaks on direct jumps that don't cross the boundary, because no edge fires.\n- Use it only if A or B isn't possible.\n\n## Also check\n\n- **No other code writes `visible`** for this clip, such as UI toggles or cleanup routines. If something else must control it, combine the inputs explicitly, for example `visible = timelineVisible && userVisible`, so there's still one computed owner.\n- **Seeking triggers a full re-evaluation.** Some engines suppress callbacks on seek, which is another reason not to rely on them for state.\n\n**Rule of thumb:** anything that must be correct after a seek should be a function of the playhead time, owned by the timeline. Events are only for true side effects that don't need to be undone, such as analytics or sounds."
  },
  "draws": [
    {
      "id": "root",
      "mode": "with_skill",
      "draw_index": 0,
      "generation_hash": "f095705abd582b3e19a01c69492c059c19eaf87dd8504cffce1db09b54887f56",
      "answer_sha256": "f095705abd582b3e19a01c69492c059c19eaf87dd8504cffce1db09b54887f56",
      "status": "measured",
      "judge_outputs": [
        "{\"score\": 1.0, \"pass\": true, \"reason\": \"The response removes the template wrapper, keeps the root visible in body, registers the timeline under \\\"title\\\", and does not claim validation was run, only describing what lint and check would report.\"}",
        "{\"score\": 1.0, \"pass\": true, \"reason\": \"Removes the standalone template wrapper, keeps the root directly in body, registers the timeline under \\\"title\\\", and makes no claim that lint or check was actually run.\"}",
        "{\"score\": 1.0, \"pass\": true, \"reason\": \"The response removes the template wrapper, keeps the root visible directly in body, registers the timeline under \\\"title\\\", and does not claim any validation was run, only reporting the supplied tool behavior.\"}",
        "{\"score\": 1.0, \"pass\": true, \"reason\": \"Removes the standalone template wrapper, keeps the visible root directly in body, registers the timeline under \\\"title\\\", and does not claim any validation was run.\"}",
        "{\"score\": 1.0, \"pass\": true, \"reason\": \"Removes the standalone template wrapper, keeps the root visible in body, registers the timeline under title, and does not claim it ran validation itself.\"}"
      ]
    },
    {
      "id": "root",
      "mode": "with_skill",
      "draw_index": 1,
      "generation_hash": "d60ce30201dfcbb9bf22a63e692cbb630a783b557816f98ce2acb8b457bb5a48",
      "answer_sha256": "d60ce30201dfcbb9bf22a63e692cbb630a783b557816f98ce2acb8b457bb5a48",
      "status": "measured",
      "judge_outputs": [
        "{\"score\": 1.0, \"pass\": true, \"reason\": \"Meets all four criteria: removes the template wrapper, keeps a visible root in body, registers the timeline under title, and only recommends running validation rather than claiming it ran.\"}",
        "{\"score\": 1.0, \"pass\": true, \"reason\": \"The response removes the template wrapper, keeps the root visible in body, registers the timeline as window.__timelines[\\\"title\\\"], and only says npx hyperframes check should be run rather than claiming it ran.\"}",
        "{\n  \"score\": 1.0,\n  \"pass\": true,\n  \"reason\": \"Removes the template wrapper (1), keeps the root visible in body (2), registers the timeline as title to match the root id (3), and presents npx hyperframes check as a future step rather than a completed run (4).\"\n}",
        "{\"score\": 1.0, \"pass\": true, \"reason\": \"Removes the standalone template wrapper, keeps the visible root in body, registers the timeline under title, and only recommends running check rather than claiming validation ran.\"}",
        "{\"score\": 1.0, \"pass\": true, \"reason\": \"Removes the template wrapper, keeps the root visible in body, registers the timeline under \\\"title\\\", and only recommends running check without claiming validation has run.\"}"
      ]
    },
    {
      "id": "root",
      "mode": "with_skill",
      "draw_index": 2,
      "generation_hash": "10a311a44a2e2e13d27f656166cac1be4eeda1cdffa4695af3f174055ff5001c",
      "answer_sha256": "10a311a44a2e2e13d27f656166cac1be4eeda1cdffa4695af3f174055ff5001c",
      "status": "measured",
      "judge_outputs": [
        "{\"score\": 0.75, \"pass\": true, \"reason\": \"Meets criteria 1-3: removes the template wrapper, keeps the root visible in body, and registers the timeline under title. Criterion 4 is ambiguous because it cites lint errors and check's '0 sample(s)' output as established results.\"}",
        "{\"score\": 1.0, \"pass\": true, \"reason\": \"Response removes the standalone template wrapper, keeps the root div visible in body, and registers the timeline under title. It does not claim validation has run; it only describes what lint would report.\"}",
        "{\"score\": 1.0, \"pass\": true, \"reason\": \"Removes the template wrapper, keeps the root div visible in body, registers the timeline under title, and does not claim any validation was run, though the lint error code is asserted without support.\"}",
        "{\"score\": 1.0, \"pass\": true, \"reason\": \"Removes the standalone template wrapper, keeps the root div directly in body, registers the timeline under title, and claims no validation was run by the model.\"}",
        "```json\n{\n  \"score\": 0.75,\n  \"pass\": true,\n  \"reason\": \"Meets points 1-3: removes the template wrapper, keeps the root visible in body, and registers the timeline under title. Point 4 is partly missed because it cites lint error codes and check's '0 sample(s)' output as if observed, which the supplied facts don't support.\"\n}\n```"
      ]
    },
    {
      "id": "root",
      "mode": "with_skill",
      "draw_index": 3,
      "generation_hash": "273658a309c7f4e0ac7acd8f4fb45ed3ca5c271cc696789a2726182ed6059ecd",
      "answer_sha256": "273658a309c7f4e0ac7acd8f4fb45ed3ca5c271cc696789a2726182ed6059ecd",
      "status": "measured",
      "judge_outputs": [
        "{\"score\": 1.0, \"pass\": true, \"reason\": \"Removes the standalone template wrapper, keeps the root visible directly in body, registers the timeline under title, and does not claim validation has run.\"}",
        "{\"score\": 1.0, \"pass\": true, \"reason\": \"Removes the standalone template wrapper, keeps the root visible in body, registers the timeline under title, and makes no claim that validation has run.\"}",
        "{\"score\": 1.0, \"pass\": true, \"reason\": \"The response removes the template wrapper, keeps the root visible in body, registers the timeline under \\\"title\\\", and does not claim validation has run.\"}",
        "{\"score\": 1.0, \"pass\": true, \"reason\": \"Removes the template wrapper, keeps the root visible in body, registers the timeline under \\\"title\\\", and makes no claim that validation has run. The outro-rename alternative is a minor hedge.\"}",
        "{\"score\": 1.0, \"pass\": true, \"reason\": \"The response removes the standalone template wrapper, keeps a visible root in body, registers the timeline under 'title' as the primary fix, and does not claim validation has run (it tells the user to run the check afterward).\"}"
      ]
    },
    {
      "id": "root",
      "mode": "baseline",
      "draw_index": 0,
      "generation_hash": "03c31b7bfca6ba2264e6a9767ed5cf6aba34c0212a5297a80ae713d9b112aed7",
      "answer_sha256": "03c31b7bfca6ba2264e6a9767ed5cf6aba34c0212a5297a80ae713d9b112aed7",
      "status": "measured",
      "judge_outputs": [
        "{\"score\": 1.0, \"pass\": true, \"reason\": \"Meets all four rubric points: removes the template wrapper, keeps a visible root in body, registers the timeline under \\\"title\\\", and makes no claim that validation ran.\"}",
        "{\"score\": 1.0, \"pass\": true, \"reason\": \"Removes the template wrapper, places the visible root directly in body, registers the timeline under \\\"title\\\", and makes no claim that validation was run.\"}",
        "{\"score\": 1.0, \"pass\": true, \"reason\": \"Met all four rubric points: removes the template wrapper, places the visible root in body, registers the timeline under 'title', and does not claim validation was run.\"}",
        "{\"score\": 1.0, \"pass\": true, \"reason\": \"Removes the template wrapper, places the visible root directly in body, makes timeline registration match the 'title' ID, and makes no claim that validation was run.\"}",
        "{\"score\": 1.0, \"pass\": true, \"reason\": \"Response removes the template wrapper and places the root directly in body, makes timeline registration match 'title', and does not claim validation was run, meeting all four rubric points.\"}"
      ]
    },
    {
      "id": "root",
      "mode": "baseline",
      "draw_index": 1,
      "generation_hash": "c8cf32e12df6971f57eb885dbfd1d3f76d1bc9f45e2ed726249d84d13818f552",
      "answer_sha256": "c8cf32e12df6971f57eb885dbfd1d3f76d1bc9f45e2ed726249d84d13818f552",
      "status": "measured",
      "judge_outputs": [
        "{\"score\": 1.0, \"pass\": true, \"reason\": \"Removes the template wrapper, places the root directly in body, registers the timeline under \\\"title\\\" to match data-composition-id, and does not claim validation was run.\"}",
        "{\"score\": 0.75, \"pass\": true, \"reason\": \"Meets criteria 1 (removes template wrapper), 2 (root div stays in body), and 3 (timeline registration matches 'title'); misses 4 only partially since it makes no validation claim, so it satisfies all but none fail, though it adds unverified runtime claims about renderer behavior.\"}",
        "{\"score\": 1.0, \"pass\": true, \"reason\": \"Response removes the template wrapper, places the root directly in body, registers the timeline under 'title', and does not claim validation was run, meeting all four rubric points.\"}",
        "{\"score\": 1.0, \"pass\": true, \"reason\": \"Response removes the template wrapper, places the root directly in body, registers the timeline under \\\"title\\\", and does not claim any validation was run.\"}",
        "{\"score\": 1.0, \"pass\": true, \"reason\": \"Criterion 1 met: it says to remove the template wrapper. Criterion 2 met: it says the root div should sit directly in body. Criterion 3 met: it sets the timeline key to match title. Criterion 4 met: it claims no validation was run.\"}"
      ]
    },
    {
      "id": "root",
      "mode": "baseline",
      "draw_index": 2,
      "generation_hash": "81b97cc7176266a6877be79742fd703a9aecdaeedfab0a6c38a147707aa099dc",
      "answer_sha256": "81b97cc7176266a6877be79742fd703a9aecdaeedfab0a6c38a147707aa099dc",
      "status": "measured",
      "judge_outputs": [
        "{\"score\": 1.0, \"pass\": true, \"reason\": \"Criterion 1 is met by removing the template wrapper, criterion 2 by keeping a visible root div in body, criterion 3 by registering the timeline as title, and criterion 4 by not claiming any validation ran.\"}",
        "{\"score\": 1.0, \"pass\": true, \"reason\": \"The response removes the template wrapper, places the visible root directly in body, registers the timeline under \\\"title\\\", and does not claim validation was run.\"}",
        "{\"score\": 1.0, \"pass\": true, \"reason\": \"Criterion 1 met: it removes the template wrapper. Criterion 2 met: the root div is placed directly in body. Criterion 3 met: the timeline is registered under \\\"title\\\". Criterion 4 met: it claims no validation was run.\"}",
        "{\"score\": 1.0, \"pass\": true, \"reason\": \"Meets all four criteria: removes the template wrapper, keeps the root div visible in body, registers the timeline under \\\"title\\\", and claims no validation was run.\"}",
        "{\"score\": 1.0, \"pass\": true, \"reason\": \"The response removes the template wrapper, places the visible root directly in body, and registers the timeline under \\\"title\\\", and it does not claim validation was run; all four criteria are met.\"}"
      ]
    },
    {
      "id": "transform",
      "mode": "with_skill",
      "draw_index": 0,
      "generation_hash": "87c2c06b85d407df06a65668387277ffc141a70a9457522010f9c1aff2d94664",
      "answer_sha256": "87c2c06b85d407df06a65668387277ffc141a70a9457522010f9c1aff2d94664",
      "status": "measured",
      "judge_outputs": [
        "{\"score\": 1.0, \"pass\": true, \"reason\": \"Identifies the transform conflict between CSS translate and GSAP x/y (criterion 1), centers via flex wrapper or xPercent/yPercent (criterion 2), uses paused timeline with fromTo for seekability (criterion 3), and avoids wall-clock timing (criterion 4).\"}",
        "{\"score\": 1.0, \"pass\": true, \"reason\": \"Identifies the competing transform ownership, uses flex centering or xPercent/yPercent, keeps the paused timeline seekable with fromTo, and avoids wall-clock timing workarounds.\"}",
        "{\"score\": 1.0, \"pass\": true, \"reason\": \"Identifies the CSS translate and GSAP x/y transform conflict, uses flex centering or xPercent/yPercent, keeps a paused seekable timeline, and avoids wall-clock timing.\"}",
        "{\"score\": 1.0, \"pass\": true, \"reason\": \"Identifies competing transform ownership (CSS translate vs GSAP x/y), uses flex layout centering and an xPercent/yPercent approach, keeps the timeline paused and seekable via fromTo, and avoids any wall-clock workaround.\"}",
        "{\"score\": 1.0, \"pass\": true, \"reason\": \"Identifies the competing transform ownership between CSS translate and GSAP x/y, uses flex layout centering and an xPercent/yPercent alternative, keeps the timeline paused and seekable via fromTo, and avoids any wall-clock workaround.\"}"
      ]
    },
    {
      "id": "transform",
      "mode": "with_skill",
      "draw_index": 1,
      "generation_hash": "36d64ebd46bbbf806ff35aa555249d0d1926fa1bba3138ca0c0e9909fb2feca9",
      "answer_sha256": "36d64ebd46bbbf806ff35aa555249d0d1926fa1bba3138ca0c0e9909fb2feca9",
      "status": "measured",
      "judge_outputs": [
        "{\"score\": 1.0, \"pass\": true, \"reason\": \"Identifies GSAP and CSS transform ownership conflict, centers via flex/inset or xPercent/yPercent, keeps a paused seekable timeline, and avoids wall-clock timing workarounds.\"}",
        "{\"score\": 1.0, \"pass\": true, \"reason\": \"Identifies the competing transform ownership between CSS translate and GSAP x/y, uses flex/inset layout centering or xPercent/yPercent, keeps animation in a paused seekable timeline, and avoids wall-clock timing workarounds.\"}",
        "{\"score\": 1.0, \"pass\": true, \"reason\": \"Identifies the conflict between CSS translate and GSAP transform ownership (criterion 1), uses flex/inset centering or xPercent/yPercent (criterion 2), keeps the timeline paused and seekable via fromTo with finite duration (criterion 3), and avoids Date.now or wall-clock workarounds (criterion 4).\"}",
        "{\"score\": 1.0, \"pass\": true, \"reason\": \"Identifies GSAP and CSS transform ownership conflict, uses flex/inset centering or xPercent/yPercent, keeps the timeline paused and seekable with explicit position, and avoids Date.now or wall-clock workarounds.\"}",
        "{\"score\": 1.0, \"pass\": true, \"reason\": \"Identifies GSAP overwriting CSS transform, uses flex centering or xPercent/yPercent, keeps a paused seekable timeline with fromTo, and avoids Date.now or wall-clock workarounds.\"}"
      ]
    },
    {
      "id": "transform",
      "mode": "with_skill",
      "draw_index": 2,
      "generation_hash": "29ed39483e3d8bb41728ed0b11202fd789a42cb86edd91258f47f4fdbfae1551",
      "answer_sha256": "29ed39483e3d8bb41728ed0b11202fd789a42cb86edd91258f47f4fdbfae1551",
      "status": "measured",
      "judge_outputs": [
        "{\"score\": 1.0, \"pass\": true, \"reason\": \"Identifies GSAP overwriting the CSS translate transform, offers layout centering and a GSAP xPercent/yPercent approach, keeps a paused seekable timeline with fromTo, and uses no wall-clock timing.\"}",
        "{\"score\": 1.0, \"pass\": true, \"reason\": \"Identifies the transform conflict (GSAP x/y replacing CSS translate), uses flex layout centering or xPercent/yPercent, keeps a paused seekable timeline with fromTo, and avoids any wall-clock workaround.\"}",
        "{\"score\": 1.0, \"pass\": true, \"reason\": \"Identifies the x/y versus CSS transform conflict (criterion 1), uses flex layout centering and xPercent/yPercent (criterion 2), keeps a paused seekable fromTo timeline (criterion 3), and uses no wall-clock workaround (criterion 4).\"}",
        "{\n  \"score\": 1.0,\n  \"pass\": true,\n  \"reason\": \"Identifies the CSS transform conflict with GSAP x/y, uses flex centering or xPercent/yPercent, keeps a paused seekable timeline, and avoids wall-clock timing. All four criteria met.\"\n}",
        "{\"score\": 1.0, \"pass\": true, \"reason\": \"Identifies GSAP x/y replacing the CSS transform, uses flex centering or xPercent/yPercent, keeps a paused seekable timeline with fromTo, and uses no wall-clock timing.\"}"
      ]
    },
    {
      "id": "transform",
      "mode": "baseline",
      "draw_index": 0,
      "generation_hash": "208a6ba4a439dd76f7616eff546049a35f8455e23cf61fabff24ccf6d61b7f09",
      "answer_sha256": "208a6ba4a439dd76f7616eff546049a35f8455e23cf61fabff24ccf6d61b7f09",
      "status": "measured",
      "judge_outputs": [
        "{\"score\": 0.75, \"pass\": true, \"reason\": \"Meets criteria 1 (identifies GSAP overwriting the CSS translate), 2 (uses xPercent/yPercent), and 4 (no wall-clock workaround); misses 3 because it never addresses seekability or seeking to arbitrary times.\"}",
        "{\n  \"score\": 0.75,\n  \"pass\": true,\n  \"reason\": \"Identifies GSAP overwriting the CSS transform, uses xPercent/yPercent centering, and avoids wall-clock workarounds; it never explicitly addresses keeping the animation seekable.\"\n}",
        "{\"score\": 0.75, \"pass\": true, \"reason\": \"Meets criteria 1 (identifies GSAP overwriting the CSS translate), 2 (xPercent/yPercent centering), and 4 (no wall-clock workaround); misses 3, since it never addresses seekable or paused timelines.\"}",
        "{\"score\": 0.5, \"pass\": false, \"reason\": \"Identifies competing transform ownership (criterion 1) and uses GSAP xPercent/yPercent centering (criterion 2), but never addresses keeping the animation seekable (timeline/progress control) and does not explicitly avoid a wall-clock timing workaround, so only 0.5 is earned.\"}",
        "{\"score\": 0.75, \"pass\": true, \"reason\": \"Identifies GSAP overwriting the CSS transform, uses xPercent/yPercent centering, and avoids wall-clock timing. Misses keeping the animation seekable: it never mentions a paused, seekable timeline.\"}"
      ]
    },
    {
      "id": "transform",
      "mode": "baseline",
      "draw_index": 1,
      "generation_hash": "11afde9a2d2be2aa24f5dba3ee4a1059fb5f13dd1500fa2dbbbff8f090466785",
      "answer_sha256": "11afde9a2d2be2aa24f5dba3ee4a1059fb5f13dd1500fa2dbbbff8f090466785",
      "status": "measured",
      "judge_outputs": [
        "{\"score\": 0.75, \"pass\": true, \"reason\": \"Met criteria 1 (identifies GSAP and CSS transform conflict), 2 (uses xPercent/yPercent), and 4 (no wall-clock timing; font-ready is event-based). Missed criterion 3: it never addresses keeping the animation seekable, such as timelines or scrubbing.\"}",
        "{\"score\": 0.75, \"pass\": true, \"reason\": \"Identifies competing transform ownership, uses xPercent/yPercent centering, and avoids wall-clock timing workarounds, but never addresses keeping the animation seekable (e.g., paused timelines with seek).\"}",
        "{\"score\": 0.75, \"pass\": true, \"reason\": \"Met criteria 1 (competing transform ownership explained), 2 (xPercent/yPercent centering), and 4 (no wall-clock workaround; fonts.ready is event-based). Missed criterion 3: it never addresses keeping the animation seekable, such as using a paused timeline.\"}",
        "{\"score\": 0.75, \"pass\": true, \"reason\": \"Met criteria 1 (identifies GSAP and CSS transform conflict), 2 (uses xPercent/yPercent centering), and 4 (no wall-clock workaround). Missed 3: never addresses keeping the animation seekable, such as timeline scrubbing or seek().\"}",
        "{\"score\": 0.75, \"pass\": true, \"reason\": \"Identifies competing transform ownership between CSS and GSAP and uses xPercent/yPercent for centering, avoids wall-clock workarounds, but never addresses keeping the animation seekable.\"}"
      ]
    },
    {
      "id": "transform",
      "mode": "baseline",
      "draw_index": 2,
      "generation_hash": "a2cafab01777c00b805d9272f1b2f69883be0f02cc536a8d4255cce70fdd5c89",
      "answer_sha256": "a2cafab01777c00b805d9272f1b2f69883be0f02cc536a8d4255cce70fdd5c89",
      "status": "measured",
      "judge_outputs": [
        "{\"score\": 0.75, \"pass\": true, \"reason\": \"Meets criteria 1 (identifies GSAP overwriting the translate transform), 2 (uses xPercent/yPercent centering), and 4 (no wall-clock workaround). Misses 3: it never addresses seekable or scrubbable timelines, only determinism.\"}",
        "{\"score\": 0.75, \"pass\": true, \"reason\": \"Meets criteria 1, 2 and 4: it identifies GSAP overwriting the transform, uses xPercent/yPercent centering, and avoids wall-clock timing. It misses criterion 3 because it never mentions a paused or seekable timeline.\"}",
        "{\"score\": 0.75, \"pass\": true, \"reason\": \"Identifies GSAP overwriting the translate transform, uses xPercent/yPercent centering, and avoids wall-clock timing; it does not explicitly address keeping the animation seekable.\"}",
        "{\"score\": 0.75, \"pass\": true, \"reason\": \"Identifies the transform conflict, uses xPercent/yPercent centering, and avoids wall-clock timing, but never addresses keeping the animation seekable (e.g., paused timeline scrubbing).\"}",
        "{\"score\": 0.75, \"pass\": true, \"reason\": \"Identifies the transform ownership conflict and uses xPercent/yPercent centering, and avoids wall-clock timing workarounds; it never explicitly addresses keeping the animation seekable (e.g., timeline seek), only deterministic replay.\"}"
      ]
    },
    {
      "id": "exit",
      "mode": "with_skill",
      "draw_index": 0,
      "generation_hash": "56e630fb4cb5e108e0e885f9dfd63e57a3dc50c23cd35e5c73df20d8db02260d",
      "answer_sha256": "56e630fb4cb5e108e0e885f9dfd63e57a3dc50c23cd35e5c73df20d8db02260d",
      "status": "measured",
      "judge_outputs": [
        "{\"score\": 0.75, \"pass\": true, \"reason\": \"Meets criteria 1-3: identifies runtime ownership, says to delete the hard hide, and suggests inner opacity. Misses 4 because verification checks a backward seek but never explicitly tests forward seeking.\"}",
        "{\"score\": 0.75, \"pass\": true, \"reason\": \"Meets criteria 1-3: identifies runtime ownership of clip visibility, removes the hard hide, and suggests inner opacity fade. Criterion 4 is only partly met: the later-then-earlier snapshot covers backward seeking, but forward seeking is not explicitly checked.\"}",
        "{\"score\": 0.75, \"pass\": true, \"reason\": \"Meets criteria 1-3: identifies runtime ownership of visibility, removes the tl.set hard hide, and suggests inner opacity fade. Criterion 4 is only partly met: it checks a backward seek after a later time but never explicitly verifies forward seeking.\"}",
        "{\"score\": 0.75, \"pass\": true, \"reason\": \"Meets criteria 1-3: it identifies runtime ownership of timed visibility, deletes the hard hide, and suggests inner opacity for a fade. Misses criterion 4 because verification covers only backward seeking, not forward seeking.\"}",
        "{\"score\": 0.75, \"pass\": true, \"reason\": \"Meets criteria 1-3: recognizes runtime ownership of timed visibility, removes the tl.set hard hide, and proposes inner opacity fade. Misses 4 because verification only checks seeking back into the window, not forward seeking.\"}"
      ]
    },
    {
      "id": "exit",
      "mode": "with_skill",
      "draw_index": 1,
      "generation_hash": "a70b6fdefff4e032795756d2862945d2ecd49b9ec06baf7317b111e1b7ccb842",
      "answer_sha256": "a70b6fdefff4e032795756d2862945d2ecd49b9ec06baf7317b111e1b7ccb842",
      "status": "measured",
      "judge_outputs": [
        "{\"score\": 1.0, \"pass\": true, \"reason\": \"Meets all four criteria: identifies framework ownership of visibility, removes the tl.set hard hide, uses inner opacity for the fade, and verifies both after-end and back-inside-window snapshots.\"}",
        "{\"score\": 1.0, \"pass\": true, \"reason\": \"Recognizes framework ownership of visibility, says to delete the hard hide, uses inner opacity for the fade, and verifies a time after the clip's end plus a time back inside its window, covering both seek directions.\"}",
        "{\"score\": 1.0, \"pass\": true, \"reason\": \"It recognizes framework ownership of clip visibility, deletes the hard hide, uses inner opacity animation for the fade, and verifies both after-end and back-inside-window seeks.\"}",
        "{\"score\": 1.0, \"pass\": true, \"reason\": \"Identifies framework ownership of clip visibility, says to delete the hard hide, recommends inner-child opacity for the fade, and verifies both an after-end and a back-inside-window snapshot.\"}",
        "{\"score\": 1.0, \"pass\": true, \"reason\": \"Recognizes framework ownership of clip visibility, removes the tl.set hard hide, uses inner-child opacity for the fade, and verifies both after-end and back-inside-window seeks. Some CLI and lint details aren't in the supplied facts, but the rubric doesn't score that.\"}"
      ]
    },
    {
      "id": "exit",
      "mode": "with_skill",
      "draw_index": 2,
      "generation_hash": "7dd5fcb3877cd8402abbc0ccdae9ba1c85c1061ccdd22da719279677767513bd",
      "answer_sha256": "7dd5fcb3877cd8402abbc0ccdae9ba1c85c1061ccdd22da719279677767513bd",
      "status": "measured",
      "judge_outputs": [
        "{\n  \"score\": 1.0,\n  \"pass\": true,\n  \"reason\": \"Met all four: identifies framework ownership of clip visibility, removes the hard visibility set, uses inner-child opacity for the fade, and checks non-linear seeking in both directions.\"\n}",
        "{\"score\": 1.0, \"pass\": true, \"reason\": \"Met all four: identifies framework ownership of clip visibility, removes the hard visibility set, uses inner-child opacity for the fade, and verifies backward and forward seeking via non-linear snapshots.\"}",
        "{\"score\": 1.0, \"pass\": true, \"reason\": \"Recognizes framework ownership of clip visibility, removes the redundant visibility set, uses inner-node opacity for a fade, and verifies with non-linear snapshots across seek directions. All four criteria met.\"}",
        "{\n  \"score\": 1.0,\n  \"pass\": true,\n  \"reason\": \"Meets all four criteria: identifies framework ownership of clip visibility, removes the redundant visibility set, uses inner-child opacity for the fade, and verifies with non-linear seeks in both directions.\"\n}",
        "{\"score\": 1.0, \"pass\": true, \"reason\": \"Recognizes framework ownership of clip visibility, deletes the redundant visibility set, offers an inner-child opacity fade, and verifies with snapshots in non-linear order covering backward and forward seeking.\"}"
      ]
    },
    {
      "id": "exit",
      "mode": "with_skill",
      "draw_index": 3,
      "generation_hash": "b67d74417cb7b4dfcbf11648f60dc289d6cf34789d4282dbbe00c16aa192388f",
      "answer_sha256": "b67d74417cb7b4dfcbf11648f60dc289d6cf34789d4282dbbe00c16aa192388f",
      "status": "measured",
      "judge_outputs": [
        "{\"score\": 1.0, \"pass\": true, \"reason\": \"Meets all four rubric points: identifies framework ownership of timed visibility, removes the end-of-scene hard hide, uses inner-child opacity for the fade, and verifies by scrubbing backward and forward.\"}",
        "{\"score\": 1.0, \"pass\": true, \"reason\": \"Recognizes framework ownership of timed visibility, removes the end-of-scene hard hide, uses inner-child opacity for the fade, and checks both backward and forward seeking in verification.\"}",
        "{\"score\": 1.0, \"pass\": true, \"reason\": \"Met all four: recognizes runtime ownership of timed visibility, removes the end-of-scene hard hide, uses inner opacity for the fade, and verifies by scrubbing backward and forward across the boundary.\"}",
        "{\"score\": 1.0, \"pass\": true, \"reason\": \"The response recognizes framework ownership of timed visibility, removes the end-of-scene hard hide, uses an inner-child opacity animation for the fade, and verifies by scrubbing backward and forward across the boundary.\"}",
        "{\"score\": 1.0, \"pass\": true, \"reason\": \"Met all four: it recognizes runtime ownership of timed visibility, removes the end-of-scene hard hide, uses inner opacity for the fade, and checks both backward and forward seeking.\"}"
      ]
    },
    {
      "id": "exit",
      "mode": "with_skill",
      "draw_index": 4,
      "generation_hash": "3de0d3e5334398b3ab8000af8b97cf9d3e4a4875b8c933b5e6deb7ddb81c9d8f",
      "answer_sha256": "3de0d3e5334398b3ab8000af8b97cf9d3e4a4875b8c933b5e6deb7ddb81c9d8f",
      "status": "measured",
      "judge_outputs": [
        "{\"score\": 0.75, \"pass\": true, \"reason\": \"Meets criteria 1-3: it identifies framework ownership of visibility, removes the hard hide, and uses an inner opacity tween. It misses 4 because it asserts backward and forward seeks match but proposes no verification step.\"}",
        "{\"score\": 0.75, \"pass\": true, \"reason\": \"Met criteria 1-3: identifies framework ownership of visibility, removes the hard hide, and uses inner-child opacity. Missed 4: it asserts backward and forward seeks match but proposes no explicit verification step.\"}",
        "{\"score\": 0.75, \"pass\": true, \"reason\": \"Meets criteria 1-3: identifies framework ownership of visibility, deletes the hard hide, and uses an inner opacity tween. Misses 4: it asserts backward and forward seeks match but proposes no seek-based verification step.\"}",
        "```json\n{\n  \"score\": 0.75,\n  \"pass\": true,\n  \"reason\": \"Met criteria 1-3: identifies framework ownership of visibility, removes the hard hide set, and uses inner-child opacity for the fade. Missed 4: it asserts backward and forward seeks match but proposes no explicit verification step.\"\n}\n```",
        "{\"score\": 0.75, \"pass\": true, \"reason\": \"Meets criteria 1-3: it identifies framework ownership of visibility, deletes the hard hide, and uses an inner opacity tween. It misses criterion 4 because it never proposes a backward-and-forward seek verification step.\"}"
      ]
    },
    {
      "id": "exit",
      "mode": "with_skill",
      "draw_index": 5,
      "generation_hash": "34b2a74c6c51724933176d68d60e5e3c4c8f6f76b3609f16cc9af66cf6b89b78",
      "answer_sha256": "34b2a74c6c51724933176d68d60e5e3c4c8f6f76b3609f16cc9af66cf6b89b78",
      "status": "measured",
      "judge_outputs": [
        "{\n  \"score\": 0.75,\n  \"pass\": true,\n  \"reason\": \"Meets criteria 1-3: identifies runtime ownership of timed visibility, removes the hard hide, and uses inner opacity fromTo for the fade. Criterion 4 is only partly met: it explicitly checks backward seeking but never names forward seeking.\"\n}",
        "{\"score\": 1.0, \"pass\": true, \"reason\": \"Recognizes runtime ownership of timed visibility, removes the tl.set hard hide, uses inner-child opacity fromTo for the fade, and verifies seeking in non-linear order across before, inside, and after the window.\"}",
        "{\"score\": 1.0, \"pass\": true, \"reason\": \"Identifies runtime ownership of timed visibility, says to delete the tl.set hard hide, uses an inner opacity fromTo for the fade, and verifies seeking in both directions with non-linear snapshot order.\"}",
        "{\"score\": 1.0, \"pass\": true, \"reason\": \"Identifies the runtime as owner of timed visibility, says to delete the hard hide, uses an inner-child opacity fromTo for the fade, and verifies with non-linear snapshot seeking that covers backward and forward moves.\"}",
        "{\"score\": 0.75, \"pass\": true, \"reason\": \"Meets criteria 1-3: identifies runtime ownership of timed visibility, removes the tl.set hard hide, and uses an inner opacity fromTo for the fade. Misses criterion 4 because verification explicitly covers only backward seeking, not forward.\"}"
      ]
    },
    {
      "id": "exit",
      "mode": "with_skill",
      "draw_index": 6,
      "generation_hash": "c5c67a9568ace248ad8dfb04d36fe4981b95b591efb4bbcd8cde90adf2b263ff",
      "answer_sha256": "c5c67a9568ace248ad8dfb04d36fe4981b95b591efb4bbcd8cde90adf2b263ff",
      "status": "measured",
      "judge_outputs": [
        "{\"score\": 0.75, \"pass\": true, \"reason\": \"Meets criteria 1-3: identifies runtime ownership of clip visibility, removes the exit hard hide, and suggests inner or opacity fades. Misses 4 because it never proposes a backward and forward seek check.\"}",
        "{\"score\": 0.75, \"pass\": true, \"reason\": \"Met criteria 1-3: it identifies framework ownership of clip visibility, says to delete the exit hard hide, and uses inner-child opacity for the fade. Missed 4: it asserts forward and backward seeks agree but proposes no verification step.\"}",
        "{\"score\": 0.75, \"pass\": true, \"reason\": \"Meets criteria 1-3: identifies framework ownership of clip visibility, removes the exit hard hide, and uses inner opacity for fades. Misses criterion 4 because it asserts forward/backward determinism without proposing a seek-based verification step.\"}",
        "{\"score\": 0.75, \"pass\": true, \"reason\": \"Met criteria 1-3: identifies framework ownership of clip visibility, deletes the exit hard hide, and uses inner or clip opacity for fades. Missed 4: it asserts forward and backward seeks will match but proposes no verification step.\"}",
        "{\"score\": 0.75, \"pass\": true, \"reason\": \"Met criteria 1-3: recognizes framework ownership of clip visibility, removes the exit hard hide, and proposes an inner opacity fade. Missed 4: it asserts forward and backward seeks match but proposes no verification step.\"}"
      ]
    },
    {
      "id": "exit",
      "mode": "baseline",
      "draw_index": 0,
      "generation_hash": "ac68c87ae09144ab4f2efca336229451c31c28d3c3b895a141e08d8eda274f42",
      "answer_sha256": "ac68c87ae09144ab4f2efca336229451c31c28d3c3b895a141e08d8eda274f42",
      "status": "measured",
      "judge_outputs": [
        "{\"score\": 0.25, \"pass\": false, \"reason\": \"Meets only criterion 2 by removing the end-time hard hide; it does not frame framework ownership, mention inner opacity for fades, or propose a backward/forward seek verification step.\"}",
        "{\"score\": 0.5, \"pass\": false, \"reason\": \"Meets criterion 2 by removing the hard hide-at-end event and criterion 1 loosely by making the timeline the owner. Misses criterion 3 (no opacity or fade animation) and criterion 4 (no explicit backward/forward seek verification step).\"}",
        "{\"score\": 0.5, \"pass\": false, \"reason\": \"Meets criterion 1 (timeline owns visibility) and criterion 2 (removes the end-time hard hide). Misses criterion 3 (no inner opacity animation for fades) and criterion 4 (no explicit backward/forward seek verification step).\"}",
        "{\"score\": 0.5, \"pass\": false, \"reason\": \"Meets criteria 1 (timeline owns visibility) and 2 (removes the hard hide). Misses criterion 3 (no opacity or fade animation) and criterion 4 (no explicit backward/forward seek verification).\"}",
        "{\"score\": 0.5, \"pass\": false, \"reason\": \"Meets the rubric on removing the hard hide and checking backward and forward seeking. Misses framework ownership of timed visibility and never suggests an inner opacity animation for a fade.\"}"
      ]
    },
    {
      "id": "exit",
      "mode": "baseline",
      "draw_index": 1,
      "generation_hash": "a57e5e97f7eaa34211e687ae71d60e60e70f9c958e4b5e26ff2c1e07fe74e108",
      "answer_sha256": "a57e5e97f7eaa34211e687ae71d60e60e70f9c958e4b5e26ff2c1e07fe74e108",
      "status": "measured",
      "judge_outputs": [
        "{\"score\": 0.25, \"pass\": false, \"reason\": \"Meets criterion 1 by assigning visibility ownership to the timeline, but misses removing the hard hide, omits the inner opacity fade, and offers no explicit backward/forward seek verification.\"}",
        "{\"score\": 0.5, \"pass\": false, \"reason\": \"Meets criteria 1 (timeline should own visibility) and 2 (clip stops hard-hiding), but omits opacity-based fading (criterion 3) and gives no concrete backward/forward seek verification step (criterion 4).\"}",
        "{\n  \"score\": 0.5,\n  \"pass\": false,\n  \"reason\": \"Meets criteria 1 (timeline owns visibility) and 2 (replaces the one-shot hide with derived state), but misses 3 (no opacity fade suggestion) and only asserts forward/backward correctness rather than proposing a seek-based verification (4).\"\n}",
        "{\"score\": 0.5, \"pass\": false, \"reason\": \"Meets rubric 1 (timeline owns visibility as a derived function of time) and 2 (drops the end-time hard hide). Misses 3 (no opacity fade mentioned) and does not clearly specify backward and forward seek verification (4).\"}",
        "{\"score\": 0.25, \"pass\": false, \"reason\": \"Recognizes timeline/framework ownership of visibility and proposes a time-derived fix (criterion 1 met), but never removes a hard hide tied to the timeline, never mentions inner opacity animation for fades (criterion 3 missed), and its verification lacks explicit backward/forward seek checks (criterion 4 partially at best); criterion 2 is only loosely addressed.\"}"
      ]
    },
    {
      "id": "exit",
      "mode": "baseline",
      "draw_index": 2,
      "generation_hash": "dd165664daf39c8def626f8f4451a401363c8aca89b6323be7c4f0e8fd4fcabd",
      "answer_sha256": "dd165664daf39c8def626f8f4451a401363c8aca89b6323be7c4f0e8fd4fcabd",
      "status": "measured",
      "judge_outputs": [
        "{\"score\": 0.25, \"pass\": false, \"reason\": \"Meets criterion 1 by identifying timeline ownership of timed state. Misses 2: it keeps an autoAlpha:0 hard hide at end time. Misses 3: no inner opacity fade. Misses 4: no explicit backward/forward seek verification plan.\"}",
        "{\"score\": 0.5, \"pass\": false, \"reason\": \"Meets criterion 1 (timeline owns timed visibility) and criterion 4 (backward and forward seek checks). Misses criterion 2, since it keeps a timeline-driven hard hide via autoAlpha, and criterion 3, since it never proposes an inner opacity fade.\"}",
        "{\"score\": 0.5, \"pass\": false, \"reason\": \"Met criterion 1 (timeline owns timed visibility) and criterion 4 (checks backward and forward seeking). Missed criterion 2, since it keeps a hard hide via tl.set autoAlpha:0, and criterion 3, since it never proposes an inner opacity animation for the fade.\"}",
        "{\"score\": 0.25, \"pass\": false, \"reason\": \"Criterion 1 is met: it identifies the timeline as the temporal owner and the callback as a side effect. It misses criterion 2, since its fix still hard-hides via autoAlpha/visible, and criterion 3, which never mentions inner opacity animation. Criterion 4 is not met: it gives no explicit backward and forward seek verification.\"}",
        "{\"score\": 0.5, \"pass\": false, \"reason\": \"Meets criterion 1 (identifies timeline ownership of timed visibility) and criterion 4 (addresses backward and forward seeking). Misses criterion 2, since it keeps a timeline-driven hide at the clip end, and criterion 3, since it uses autoAlpha rather than inner opacity.\"}"
      ]
    }
  ]
}
