{"description":"Performance budgets and rules: Core Web Vitals targets, asset-weight budgets, page rules, measurement method, and pass/fail criteria enforced by npm run qa:perf.","objective":"Give a future agent enough structure to start from the main URL and understand how to inspect, plan, build, validate, and improve a serious website without needing prior chat context.","performance":{"objective":"Make page speed a budgeted, machine-checked discipline instead of an accident of being static.","why":"Users and ranking systems feel performance before they read a word. A site can pass every SEO, schema, and accessibility gate and still ship slow: oversized documents, render-blocking resources, layout shift, unbudgeted JavaScript. Budgets turn 'fast' from a vibe into a fail condition.","coreWebVitals":[{"id":"lcp","name":"Largest Contentful Paint","fieldBudget":"2500ms at the 75th percentile of real visits","localBudgetMs":2500,"severity":"critical","why":"LCP is the moment the page appears useful. The dominant causes are slow documents, render-blocking CSS/JS, and unoptimized hero media.","improvementLevers":["Static HTML over runtime rendering","Inline-critical or single small stylesheet","No render-blocking scripts","Dimensioned, compressed, lazy-loaded media","CDN edge caching"]},{"id":"cls","name":"Cumulative Layout Shift","fieldBudget":"0.1 at the 75th percentile of real visits","localBudget":0.1,"severity":"critical","why":"Layout shift breaks reading and causes misclicks. The dominant causes are media without dimensions, late-loading fonts, and injected banners.","improvementLevers":["width/height or aspect-ratio on every image and embed","font-display: swap with metric-compatible fallbacks","Reserve space for any late content","No layout-affecting scripts"]},{"id":"inp","name":"Interaction to Next Paint","fieldBudget":"200ms at the 75th percentile of real visits","localBudget":"Not directly measurable without real interactions; the enforced local proxy is the JavaScript byte budget, because INP risk on content sites scales with shipped JS.","severity":"high","why":"INP measures responsiveness. A content site that ships zero or near-zero JavaScript cannot meaningfully fail INP; one that ships unbudgeted JS can.","improvementLevers":["Zero JS by default; islands only where interaction is real","Defer/async anything that must ship","No long tasks on the main thread"]}],"assetBudgets":[{"id":"html-bytes-per-page","limit":120000,"severity":"high","why":"Documents beyond ~120KB slow first paint, waste crawl budget, and usually signal content that belongs on a dedicated endpoint."},{"id":"css-bytes-total","limit":75000,"severity":"high","why":"A content site needs one small stylesheet; CSS beyond ~75KB is a design-system smell, not a content need."},{"id":"js-bytes-total","limit":0,"severity":"critical","why":"The default is zero JavaScript. Any script must be an explicit, listed exception with the interaction it enables; this is the INP guarantee.","exceptions":[]},{"id":"image-bytes-per-file","limit":250000,"severity":"high","why":"Single images beyond ~250KB are almost always uncompressed or unresized originals."},{"id":"font-files-max","limit":2,"severity":"high","why":"System font stacks cost nothing. If brand requires webfonts, two subsetted woff2 files with font-display: swap is the ceiling."}],"pageRules":[{"id":"images-have-dimensions","severity":"critical","rule":"Every <img> declares width and height (or CSS aspect-ratio) so the browser reserves space.","why":"Undimensioned media is the leading CLS cause."},{"id":"below-fold-images-lazy","severity":"high","rule":"Images outside the first viewport use loading=\"lazy\".","why":"Eager offscreen images compete with the LCP resource for bandwidth."},{"id":"no-render-blocking-scripts","severity":"critical","rule":"No <script> in <head> without defer, async, or type=module.","why":"A blocking script delays first paint on every page that carries it."},{"id":"stylesheet-count","severity":"high","rule":"At most two stylesheets per page.","why":"Each stylesheet is a render-blocking round trip."}],"measurement":{"command":"npm run qa:perf","reportPath":"reports/performance/performance-report.json","method":"Playwright loads every core route against the static build, recording LCP via PerformanceObserver and CLS via layout-shift accumulation, while asset budgets and page rules are checked from the built output. Synthetic negative controls (oversized document, undimensioned image, blocking script, layout-shift burst) must fail through the same rules.","localCaveat":"Local measurements are a regression floor, not field data. Field Core Web Vitals require CrUX or RUM evidence after the canonical deployment, tracked as production-evidence work."},"passCriteria":["Every core route meets the local LCP and CLS budgets.","Every asset budget and page rule passes on the built output.","JavaScript total stays within the exception list (default zero).","Negative controls fail in their expected ways."],"failCriteria":["Any route exceeds a Core Web Vitals local budget.","Any document, stylesheet, script, image, or font budget is exceeded.","Any image ships without dimensions or any head script blocks rendering.","The measurement harness cannot produce its report."],"objectiveAlignment":"Closes the gap between technically-valid and actually-fast: performance becomes a contract with budgets and evidence, like every other discipline in this system."}}