{
  "version": 1,
  "site": "https://motionprompts.dev",
  "prose": "https://motionprompts.dev/COMPOSITION_RULES.md",
  "budget": {
    "js_gzip": 460800,
    "assets_bytes": 6291456,
    "pin_vh": 2400,
    "scrub_pinned_max": 3,
    "webgl_full_viewport_max": 1
  },
  "exclusive_capabilities": {
    "document-height": 17,
    "window-onload": 3,
    "page-scroll-owner": 105,
    "first-canvas": 5,
    "page-scroll-lock": 31,
    "entry-veil": 22
  },
  "non_repeatable_categories": [
    "preloader",
    "transition",
    "menu",
    "footer"
  ],
  "library_weight_gzip": {
    "CustomEase": 3375,
    "Draggable": 11588,
    "Flip": 7630,
    "GLTFLoader": 13494,
    "OrbitControls": 4454,
    "ScrollTrigger": 18045,
    "SplitText": 3340,
    "UnrealBloomPass": 3906,
    "split_type": 4483,
    "gsap_core": 27454,
    "lenis": 5338,
    "lottie": 78557,
    "gsap_matrix": 2127,
    "matter": 26624,
    "three": 151261
  },
  "hard_rules": [
    {
      "id": "R1",
      "rule": "un solo page-scroll-owner",
      "holders": 105
    },
    {
      "id": "R2",
      "rule": "un solo entry-veil",
      "holders": 22
    },
    {
      "id": "R3",
      "rule": "el velo de entrada precede a todo; nada más depende de DOMContentLoaded"
    },
    {
      "id": "R4",
      "rule": "máximo 1 WebGL a viewport completo",
      "full_viewport": 28
    },
    {
      "id": "R5",
      "rule": "máximo 3 pinned con scrub y 2400vh de pasillo; el pasillo suma TODO lo que declare viewport_heights, no solo los pins",
      "unpinned_corridors": 26
    },
    {
      "id": "R5b",
      "rule": "cero titulares de document-height en una página compuesta: uno solo ya recorta el documento entero",
      "holders": 17
    },
    {
      "id": "R6",
      "rule": "categorías no repetibles: preloader, transition, menu, footer"
    },
    {
      "id": "R8",
      "rule": "cero page-scroll-lock si hay más de un componente; cero first-canvas si hay más de un canvas; un solo window-onload"
    },
    {
      "id": "R13",
      "rule": "una sola barra fija (nav/header/footer a top:0 o bottom:0) por página",
      "holders": 44
    },
    {
      "id": "R14",
      "rule": "ninguna capa fija cubre el viewport salvo el velo de entrada; la reparación es sticky + margen negativo, NO absolute",
      "declared_full": 47,
      "unsized": 4
    },
    {
      "id": "R15",
      "rule": "el CSS de cada componente se acota a su raíz antes de pegarlo (scripts/scope-css.mjs); 0 de 219 usan @layer/@scope/contain",
      "measured_drift_without_it": "78 de 203 propiedades computadas (38%)"
    },
    {
      "id": "R16",
      "rule": "todo bucle rAF que pinta se gatea con IntersectionObserver, sea WebGL o canvas 2D; NO se gatea el rAF que alimenta a Lenis",
      "loops": 42,
      "non_webgl": 14
    },
    {
      "id": "R17",
      "rule": "position:relative en la raíz de cada sección Y en cada <section> interior; si no, los absolute se resuelven contra el documento entero",
      "components": 221,
      "rules": 843
    },
    {
      "id": "R9",
      "rule": "ease-name compartida exige fijar un motion system"
    },
    {
      "id": "R10",
      "rule": "ningún kills-all junto a otro consumidor de scroll",
      "killers": [
        "kaitonote-3d-gallery-showcase-scroll-animation",
        "madeinuxstudio-page-transition",
        "nakedcityfilms-scroll-animation"
      ]
    }
  ],
  "soft_rules": [
    {
      "id": "R7",
      "rule": "presupuesto de bundle y assets"
    },
    {
      "id": "R11",
      "rule": "coherencia de motion system"
    },
    {
      "id": "R12",
      "rule": "solo_on_page avisa"
    }
  ],
  "not_covered": [
    "qué parte del marcado de un componente ES el componente: plan_page devuelve slugs y nadie marca qué nodos son la pieza y cuáles eran relleno de su demo",
    "re-alojar lo que scope-css.mjs retira de html/body: la herramienta informa, decidir es manual",
    "assets rotos fuera de este dominio (194/219 usan rutas /c/ absolutas)",
    "montaje en framework (128/219 atados a DOMContentLoaded)",
    "dirección de arte entre secciones — medida, no resuelta: ver art_direction_gap",
    "coste de arranque: R3 al pie de la letra retrasa la carga hasta el fin del velo (medido: listo a 9.4s con un velo de 8s)"
  ],
  "art_direction_gap": {
    "measured_on": "examples/editorial-reveal-on-enter (deriva estructural 2/203)",
    "variable_collisions": 3,
    "polarity_seams_between_components": 1,
    "polarity_changes_within_a_component": 3,
    "low_contrast_probes": "8 de 29 — las ocho idénticas al componente aislado; componer no introduce ninguna nueva",
    "unbounded": "el cromo que sobrevive a R13 cruza todas las secciones y nadie comprueba que se lea sobre ellas"
  },
  "verified_builds": [
    {
      "what": "brief de portfolio · scrub-lagged",
      "where": "examples/portfolio-scrub-lagged/",
      "report": "generated/motion/portfolio-scrub-lagged-report.json",
      "result": "20/20 comprobaciones"
    },
    {
      "what": "brief editorial · reveal-on-enter · construido primero al pie de la letra del contrato",
      "where": "examples/editorial-reveal-on-enter/",
      "report": "generated/motion/editorial-reveal-on-enter-report.json",
      "result": "fase 1 (solo lo que pedía el contrato): 13/15 y 78/203 de deriva de tema · fase 2 (con R14-R17): 18/18 y 2/203"
    }
  ]
}