<userRequest>
YOU MUST follow these rules EXACTLY when vibe-coding on this Incanto template
(Castle Run — a 2D adventure platformer):

1. 📖 READ THE SKILLS FIRST: `node_modules/incanto/skills/` is the engine
   manual, version-pinned to what is installed. Start with
   `incanto-building-2d-games.md` and `incanto-scene-json-authoring.md`, then
   `incanto-gameplay-behaviors.md` (Pickup/Patrol/Oscillate/ScoreKeeper) and
   `incanto-physics-and-input.md` (2D bodies, the input map, units).
2. 🧱 STRUCTURE IS JSON: scenes, nodes, props, assets, input maps and
   connections all live in `src/game.scene.json`. Add level geometry there (or
   with `npx incanto-editor`), NOT by constructing nodes ad-hoc in code.
3. 🧠 LOGIC IS BEHAVIORS: prefer a BUILT-IN behavior over custom code (Pickup
   coins/gems, Patrol goblins + the moving platform, Oscillate the lift +
   bobbing pickups, ScoreKeeper score/lives/win-lose, AudioPlayer SFX — all
   wired in JSON). Custom code = small TypeScript Behavior classes registered via
   `createGame2D({ behaviors })` and attached with `"script": { "name": "Name" }`.
   This game's custom behaviors are the platformer GAME FEEL the library leaves
   open: `PlayerController` (coyote-time, jump-buffer, double-jump, variable jump
   height, stomp, checkpoint respawn, moving-platform carry — hearts, i-frames,
   the knockback and the control lock are the Player's `Vitals` **Health**, which
   the controller reads: `staggered`, `kickVelocity`, `takeLift()`), `GoblinSkin`, `FollowCam` (follow + screen-shake), `ParallaxLayer`,
   `HudUpdater`.
4. 🔑 UIDS ARE GENERATED: omit `uid` and the loader assigns one (via the engine's
   `newUid`). NEVER hand-invent readable uid strings.
5. 📦 ASSETS ARE DECLARED: `assets` entries need `type` + `url`; reference them as
   `"$key"`. The knight + goblin sheets and the coin + gem textures are BUILT-INS
   (`incanto/assets/...`) imported in `App.tsx` and injected into the asset url
   placeholders before boot. Free extra key-values are preserved — use them for
   notes (frame ranges, license, source).
6. ⬇️ Y IS DOWN: 2D space is y-down pixels, (0,0) top-left, clockwise degrees.
   Positive gravity falls (`physics.gravity` is `[0, 1800]`). Up is NEGATIVE
   velocity; a jump subtracts y.
7. 🧲 COLLIDERS ARE PROPS: `"collider": { "shape": "rect"|"circle"|"capsule", … }`
   on a body node — never child shape nodes. Wrong shapes hard-fail at load.
7b. 🦶 STAND THE FEET ON THE GROUND: a sprite-sheet `anchor` is the artist's pivot
   (hip/centre), NOT the feet, and the frame has transparent padding below the art —
   so a character will FLOAT unless you align it. MEASURE the feet (bottom opaque row
   ÷ frame height) and set `anchor.y` so the visible feet sit at the collider bottom
   (here the knight is `0.464`, goblins `0.348`, not the sheet's ~0.61). See
   `incanto-building-2d-games.md` → AnimatedSprite2D "stand the feet on the ground".
8. 🎯 DAMAGE IS CENTRALISED + GROUP-DRIVEN: the player takes NO built-in Health —
   `PlayerController` owns hearts/lives and resolves ALL contact by AABB against
   GROUPS each frame, so there's one authority and never a stomp-vs-damage
   double-trigger. Tag content with groups: `enemy` (stompable from above, hurts
   on the side), `hazard` (spikes → lose a heart), `pit` (death plane → instant
   life loss), `checkpoint` (sets respawn), `goal` (win), `platform` (a moving
   body the Feet sensor rides). Add an enemy/hazard = add the node in the right
   group; no wiring needed.
9. 🐛 DEBUG WHEN UNSURE: `createGame2D({ debug: true })` (or `VITE_INCANTO_DEBUG=1`)
   shows the Explorer/Inspector/Stats; `physics.debugDraw = true` draws colliders.
10. 🎬 DELTA DISCIPLINE: only write props that differ from defaults — the loader
    treats unknown/garbage props as hard errors, which is your friend.
11. 🧭 ENGINE ACCESS IS DEFERRED: `this.engine` is NOT available in `onReady`
    during `loadScene` (the scene isn't attached yet) — read scene config
    (gravity, etc.) lazily on the first `fixedUpdate`. `getNode`/signals are fine
    in `onReady`.
12. ✅ VERIFY LIKE A USER: `bun run check` (scene valid) → `bun run verify`
    (headless run/jump/coin/stomp/checkpoint/win/lose proof) → `bun run dev`,
    open the browser, and actually PLAY what you changed (run, double-jump, stomp
    a goblin, ride the lift, die and respawn at the flag) before declaring done.
</userRequest>
