Audit

20260712T084733Z-3118

← Back to demoplayandnopecom
Audited URL
https://demo.playandnope.com/
Timestamp
2026-07-12T08:49:04.391Z
Kind
single
Pages
1
Audit summary
https://demo.playandnope.com/
Pagespeed scores
Other checks
LLM Report

Weighted audit summary

78
Overall site quality
Needs Improvementhigh confidence

Mobile performance is strong (90) but LCP (2.9s) falls in the warning zone due to the hero image incorrectly using lazy loading. Security posture is critically weak (20/100) because HTTP traffic does not redirect to HTTPS and HSTS is missing, despite the site being a brochure with no auth/payments. Accessibility is excellent (0 axe violations) but 10 W3C validation errors indicate code quality issues. The site is a pet care community/marketing site, which lowers CSP priority but not basic header requirements.

Audit Report: Tere tulemast Paws N' Play veebilehele! — gotoAndPlay Demo WordPress Base

Website: https://demo.playandnope.com/
Date: 2026-07-12

Overall Score: 78 / 100
Status: 🟡 Needs Improvement
Confidence: high
Audit Coverage: 100% — all sources returned data

Summary

Mobile performance is strong (90) but LCP (2.9s) falls in the warning zone due to the hero image incorrectly using lazy loading. Security posture is critically weak (20/100) because HTTP traffic does not redirect to HTTPS and HSTS is missing, despite the site being a brochure with no auth/payments. Accessibility is excellent (0 axe violations) but 10 W3C validation errors indicate code quality issues. The site is a pet care community/marketing site, which lowers CSP priority but not basic header requirements.

PageSpeed Insights — Mobile vs Desktop

Lower is worse for Performance; higher is worse for LCP and CLS. Worse value is bolded.

Strategy Performance (M / D) LCP (M / D) CLS (M / D)
Mobile vs Desktop 90 / 100 2.86 s / 727 ms 0.000 / 0.003

Optimization Checklist

4 of 6 passing — 4 pass · 1 warn · 1 fail · 1 n/a

Item Status Detail
Page caching plugin / CDN active Pass Caching plugin detected (WP Rocket)
Images lazy-loaded Pass All non-hero raster images use loading="lazy".
Hero image eagerly loaded Fail Hero image has loading="lazy", which delays LCP (inferred from DOM order/size — Lighthouse LCP element unavailable). Use loading="eager" (or omit loading) and add fetchpriority="high".
Hero is a real <img> (not a CSS background-image) N/A No CSS background-images detected on raster-image-eligible elements.
Responsive images (srcset / <picture>) Pass 20/20 raster images use srcset or <picture> (100%).
Reasonable number of image sizes Pass 48 distinct srcset widths.
JS scripts not blocking in <head> Warn 1 render-blocking script in <head>. Move to footer or add defer/async.

Fixes

Priority 1: Critical

Immediate action — impacts user experience, search rankings, or site safety.

1A. Force HTTPS redirect for all HTTP traffic

  • Impact: Security, Data Integrity
  • Problem: http://demo.playandnope.com/ does not redirect to HTTPS, leaving users vulnerable to MITM attacks on unencrypted connections.
  • Solution: Configure the web server (Apache/Nginx) to return a 301 redirect for all HTTP requests to the HTTPS version:
    RewriteEngine On
    RewriteCond %{HTTPS} off
    RewriteRule ^(.*)$ https://%{HTTP_HOST}%{REQUEST_URI} [L,R=301]
    

1B. Remove lazy loading from hero image

  • Impact: LCP, FCP, Performance
  • Problem: Hero image has loading="lazy", which delays rendering and contributes to LCP of 2.9s on mobile.
  • Solution: Remove loading="lazy" from the largest above-the-fold image and add fetchpriority="high":
    <img src="hero.jpg" alt="..." fetchpriority="high" width="..." height="...">
    

Priority 2: Important

Essential for compliance, user reach, and search visibility.

2A. Add HSTS and X-Content-Type-Options headers

  • Impact: Security Headers Grade, Transport Security
  • Problem: HSTS and X-Content-Type-Options are missing; Security Headers grade is 20/100.
  • Solution: Add these headers to the server configuration:
    Header always set Strict-Transport-Security "max-age=63072000; includeSubDomains"
    Header always set X-Content-Type-Options "nosniff"
    

2B. Fix W3C HTML validation errors

  • Impact: Code Quality, Accessibility
  • Problem: 10 errors found including invalid aria-expanded values, target on span, and script type/defer mismatches.
  • Solution:
    • Set aria-expanded="true" or "false" (not empty string) on buttons.
    • Remove target attribute from <span> elements.
    • Change script type="text/rocketlazyloadscript" to type="module" or remove defer if not a valid MIME type.

Priority 3: Best Practice

Recommended for long-term maintainability.

3A. Implement Content-Security-Policy (CSP)

  • Impact: XSS Defense-in-Depth
  • Problem: CSP is missing. Site signals show no auth/payments/UGC, so this is lower priority than headers, but still recommended.
  • Solution: Deploy a strict CSP with nonce/hash approach rather than a flat allowlist:
    Header always set Content-Security-Policy "default-src 'self'; script-src 'nonce-{random}' 'strict-dynamic'; object-src 'none'; base-uri 'none';"
    
▸Raw Markdown sent to the LLM
# Audit — https://demo.playandnope.com/

Run: 2026-07-12T08:47:33.802Z

## Audit Coverage
**100%** of audit sources returned data.

_All sources OK._

## Methodology

Each tool below contributes data to a single overall 0–100 site quality score. Performance dominates the weighting (~40%), followed by accessibility (~30%), image assets (~15%), and SEO/security (~15%). These weights are informative — the score is judged holistically, not from a fixed formula.

Severity scale in `priorities[]`:
- **high** — blocking issue / vulnerability / fail.
- **medium** — significant degradation.
- **low** — minor improvement.

Each tool section below lists: scores → priority fixes → findings → manual checks the tool cannot verify.

## Site Signals (inferred)

Heuristic site-shape signals derived from the audit data. Use these to calibrate the priority of security-header recommendations (see system rubric). Override when the evidence is clearly wrong.

- Auth surface: no
- Payments: no
- User-generated content: no
- E-commerce: no

## PageSpeed Insights
_Captured in 19127 ms (mobile + desktop in parallel)._

**Lighthouse scores (mobile vs desktop; worse value bolded):**
| Category | Mobile | Desktop |
| --- | --- | --- |
| Performance | **90** | 100 |
| Accessibility | 100 | 100 |
| Best Practices | 96 | 96 |
| SEO | 92 | 92 |

**Core Web Vitals — lab (Lighthouse) / field (CrUX p75); worse lab value bolded:**
| Metric | Mobile | Desktop |
| --- | --- | --- |
| LCP | **2.9 s** | 0.7 s |
| CLS | 0.000 | **0.003** |
| TBT | 0 ms | 0 ms |
| FCP | **2.86 s** | 464 ms |
| Speed Index | **3.12 s** | 464 ms |
| TTFB | 4 ms | 4 ms |

### Priority fixes
1. **first-contentful-paint** (medium) — 2.9 s
2. **largest-contentful-paint** (low) — 2.9 s
3. **forced-reflow-insight** (high)
4. **image-delivery-insight** (medium) — Est savings of 100 KiB
5. **network-dependency-tree-insight** (high)

### Findings (mobile)

#### DOM size
- Total nodes: 0

#### Failing modeled audits
- SEO: `robotsTxt`
- SEO: `tapTargets`
- SEO: `structuredData`
- Best Practices: `errorsInConsole`

#### All failing PSI audits (sorted by weight × failure margin)
- `first-contentful-paint` (performance, score 0.54, weight 10) — First Contentful Paint — 2.9 s
- `largest-contentful-paint` (performance, score 0.82, weight 25) — Largest Contentful Paint — 2.9 s
- `forced-reflow-insight` (performance, score 0.00, weight 0) — Forced reflow
- `network-dependency-tree-insight` (performance, score 0.00, weight 0) — Network dependency tree
- `errors-in-console` (best-practices, score 0.00, weight 1) — Browser errors were logged to the console
- `robots-txt` (seo, score 0.00, weight 1) — robots.txt is not valid

### Manual checks
- Real-device behavior on slow 3G / low-tier mobile hardware (Lighthouse is throttled simulation).
- Sustained INP under typical user interaction, not just initial load.
- CrUX data interpretation if site is low-traffic and field data falls back to origin or is missing.

## Security Headers & HTTP
_Captured in 124 ms._

**Transport:**
- Final URL: https://demo.playandnope.com/
- Status: 200
- Redirected: false
- HTTPS redirect: ✗ http://demo.playandnope.com/ does not redirect to HTTPS (target: none)

**Caching:**
- cache-control: `max-age=0`
- etag: n/a
- last-modified: Sat, 27 Jun 2026 12:07:12 GMT
- expires: Sun, 12 Jul 2026 08:47:33 GMT
- pragma: n/a
- vary: Accept-Encoding

**Compression:**
- content-encoding: gzip
- content-length: n/a
- Decoded body: 126.5 KB

### Priority fixes
1. **HTTP does not redirect to HTTPS** (high) — http://demo.playandnope.com/ does not redirect to HTTPS
2. **strict-transport-security missing** (high) — Add HSTS with max-age >= 1 year, includeSubDomains, and preload
3. **content-security-policy weak** (high) — missing default-src; missing object-src 'none'; only frame-ancestors set — no script/resource restrictions
4. **x-content-type-options missing** (medium) — Send X-Content-Type-Options: nosniff
5. **referrer-policy missing** (low) — Set a Referrer-Policy such as strict-origin-when-cross-origin
6. **permissions-policy missing** (low) — Declare Permissions-Policy to disable unused features
7. **cross-origin-opener-policy missing** (low) — Set COOP to same-origin to isolate browsing context
8. **cross-origin-resource-policy missing** (low) — Set CORP to same-origin or same-site to prevent cross-origin reads
9. **x-permitted-cross-domain-policies missing** (low) — Set to none to prevent Flash/PDF cross-domain requests
10. **server header discloses technology** (low) — Server: Apache

### Findings

#### Tracked headers
- **strict-transport-security** (missing, high)
- **content-security-policy** (weak, high) `frame-ancestors 'self';` — missing default-src; missing object-src 'none'; only frame-ancestors set — no script/resource restrictions
- **x-frame-options** (present, medium) `SAMEORIGIN`
- **x-content-type-options** (missing, medium)
- **referrer-policy** (missing, low)
- **permissions-policy** (missing, low)
- **cross-origin-opener-policy** (missing, low)
- **cross-origin-resource-policy** (missing, low)
- **x-permitted-cross-domain-policies** (missing, low)

#### Info disclosure
- Server: `Apache`


#### All response headers
```
cache-control: max-age=0
connection: Upgrade, Keep-Alive
content-encoding: gzip
content-security-policy: frame-ancestors 'self';
content-type: text/html; charset=UTF-8
date: Sun, 12 Jul 2026 08:47:33 GMT
expires: Sun, 12 Jul 2026 08:47:33 GMT
keep-alive: timeout=5, max=100
last-modified: Sat, 27 Jun 2026 12:07:12 GMT
server: Apache
transfer-encoding: chunked
upgrade: h2,h2c
vary: Accept-Encoding
x-frame-options: SAMEORIGIN
```

### Manual checks
- Cookie attributes set via JavaScript (not visible in HTTP response).
- CORS preflight behavior under non-GET methods (only GET response headers checked).
- HSTS preload list inclusion (check hstspreload.org).
- WAF / DDoS posture beyond what static headers reveal.

## W3C HTML Validator
_Captured in 907 ms._

**Scoring:** 10 errors · 9 warnings · 40 cosmetic (suppressed)

### Priority fixes
1. **Bad value “” for attribute “aria-expanded” on element “button”.** (medium) — x4, first at line 1312
2. **A “script” element with a “type” attribute whose value is neither a JavaScript MIME type, “module”, “importmap”, nor “speculationrules” (i.e., a data block) must not have a “defer” attribute.** (medium) — x3, first at line 1595
3. **Attribute “target” not allowed on element “span” at this point.** (medium) — x2, first at line 418
4. **Attribute “area-hidden” not allowed on element “form” at this point.** (medium) — x1, first at line 468

### Issue groups
- (×1) [warning] The “type” attribute is unnecessary for JavaScript resources. — first at line 33 `banner --><script id="cookieyes" type="text/javascript" src="https://cdn-cookiey`
- (×2) [error] Attribute “target” not allowed on element “span” at this point. — first at line 418 `<span class="header-navigation__link" target="_self">Koerad`
- (×1) [error] Attribute “area-hidden” not allowed on element “form” at this point. — first at line 468 `<form id="header-search" class="header__search-form" area-hidden="true" role="se`
- (×4) [warning] Article lacks heading. Consider using “h2”-“h6” elements to add identifying headings to all articles. — first at line 1084 `<article class="card carousel__card">
    <`
- (×4) [error] Bad value “” for attribute “aria-expanded” on element “button”. — first at line 1312 `<button
                    aria-controls="accordion-mis-on-paws-n-play-panel"
 `
- (×4) [warning] Section lacks heading. Consider using “h2”-“h6” elements to add identifying headings to all sections, or else use a “div” element instead for any cases where no heading is needed. — first at line 1325 `<section
                    aria-labelledby="accordion-mis-on-paws-n-play-heade`
- (×3) [error] A “script” element with a “type” attribute whose value is neither a JavaScript MIME type, “module”, “importmap”, nor “speculationrules” (i.e., a data block) must not have a “defer” attribute. — first at line 1595 `</script>
<script type="text/rocketlazyloadscript" data-wp-strategy="defer" defe`

### Manual checks
- Whether each `<section>` / `<article>` wraps semantically meaningful content.
- Language tag accuracy for multi-language pages or quoted content.
- Whether structural choices align with the document outline algorithm in screen readers.

## axe-core (Accessibility)
_Captured in 2285 ms._

**Scoring:** 0 violations · 44 passes · critical 0 · serious 0 · moderate 0 · minor 0

### Incomplete (axe could not determine)
- [ARIA attributes must conform to valid values](https://dequeuniversity.com/rules/axe/4.11/aria-valid-attr-value?application=playwright) — 4 nodes

### Manual checks
- Keyboard-only navigation flow + visible focus indicators on every interactive element.
- Screen reader output (NVDA, VoiceOver) for actual auditory experience.
- Modal focus trapping and restoration on close.
- Touch target sizes (44×44 px minimum per WCAG 2.5.8).
- Color contrast for elements with alpha-transparency or gradients (axe skips these).

## Browser Runtime
_Captured in 2295 ms._

**Capture summary:** 1 console events · 0 mixed-content requests · 32 network requests · 598.6 KB total

**Network bytes by resource type:**
| Type | Count | Bytes |
| --- | --- | --- |
| image | 17 | 461.9 KB |
| font | 2 | 81.5 KB |
| stylesheet | 5 | 31.7 KB |
| script | 5 | 19.4 KB |
| other | 1 | 4.1 KB |
| document | 1 | 0 B |
| xhr | 1 | 0 B |

**Third-party origins (by bytes):**
- https://fonts.gstatic.com — 2 requests, 81.5 KB
- https://cdn.jsdelivr.net — 2 requests, 13.2 KB
- https://cdn-cookieyes.com — 1 request, 0 B
- https://fonts.googleapis.com — 2 requests, 0 B

**Slowest requests (top 5):**
- https://fonts.gstatic.com/s/opensans/v44/memvYaGs126MiZpBA-UvWbX2vVnXBbObj2OVTSGmu1aB.woff2 (font) — 428 ms, 34.3 KB
- https://fonts.gstatic.com/s/opensans/v44/memvYaGs126MiZpBA-UvWbX2vVnXBbObj2OVTS-muw.woff2 (font) — 428 ms, 47.2 KB
- https://demo.playandnope.com/wp-content/uploads/2024/11/img_5959-648x864.jpg (image) — 221 ms, 31.4 KB
- https://demo.playandnope.com/wp-content/uploads/2024/07/41644975_232057294130286_8841412687386948258_n.jpg (image) — 220 ms, 15.7 KB
- https://demo.playandnope.com/wp-content/uploads/2024/07/img_4145-648x864.jpg (image) — 220 ms, 28.6 KB

### Priority fixes
1. **failed request** (medium) — script: https://cdn-cookieyes.com/client_data/29b8e2e5821bed2234ee953c/script.js — net::ERR_ABORTED
2. **console error** (medium) — Failed to load resource: the server responded with a status of 403 ()

### Findings

#### Failed requests
- script: https://cdn-cookieyes.com/client_data/29b8e2e5821bed2234ee953c/script.js — net::ERR_ABORTED

#### Console events
- [error] Failed to load resource: the server responded with a status of 403 () (https://cdn-cookieyes.com/client_data/29b8e2e5821bed2234ee953c/script.js)

### Manual checks
- Console output during user interaction (load-only capture).
- Behavior on slow networks and constrained devices.
- WebGL / canvas FPS profiling via DevTools Layers panel.
- Service worker / cache behavior on repeat visits.

## HTML Inventory
_Captured in 2295 ms._

**Document:**
- Lang: et
- Title: Tere tulemast Paws N' Play veebilehele! — gotoAndPlay Demo WordPress Base
- Canonical: https://demo.playandnope.com/
- Viewport: width=device-width, initial-scale=1
- Charset: UTF-8
- HTML bytes: 124954

**Meta tags:**
- Description: Paws N’ Play on koht, kus loomaarmastajad saavad uurida võimalusi, kuidas lemmikloom saab elada täisväärtuslikku, mängulist ja tervislikku elu.
- Robots: follow, index, max-snippet:-1, max-video-preview:-1, max-image-preview:large
- Theme color: #ffffff
- Open Graph tags: 13 (og:locale, og:type, og:title, og:description, og:url, og:site_name, og:updated_time, og:image, og:image:secure_url, og:image:width, og:image:height, og:image:alt, og:image:type)
- Twitter tags: 8
- hreflang:
  - en → http://demo.playandnope.com/en/
  - et → http://demo.playandnope.com/
  - x-default → http://demo.playandnope.com/
- JSON-LD: 1 blocks (1 valid) — types: Unknown

**Heading outline:**
- Counts: h1 ×1, h2 ×3, h3 ×8, h4 ×0, h5 ×0, h6 ×0
- Sequence (first 20):
  - h1: Tere tulemast Paws N’ Play veebilehele!
  - h2: Loomaomanikule
  - h3: Avastama
  - h3: Koeraomanikule
  - h3: Värskele loomaomanikule
  - h3: Toidu- ja veevajadused
  - h3: Hügieen ja hooldus
  - h3: Tervis
  - h3: Füüsiline ja vaimne stimulatsioon
  - h3: Sotsialiseerumine ja tähelepanu
  - h2: Uudised
  - h2: Korduma kippuvad küsimused

**Landmarks:**
- nav: present
- main: present
- header: present
- footer: present
- Skip-to-content link: present

**Scripts:** 23 total — 3 defer, 1 async, 1 render-blocking. Speculation rules: yes.

External scripts (first 15):
- https://cdn.jsdelivr.net/gh/orestbida/iframemanager@1.3.0/dist/iframemanager.js (defer)
- https://cdn.jsdelivr.net/gh/orestbida/cookieconsent@3.0.1/dist/cookieconsent.umd.js (defer)
- https://cdn-cookieyes.com/client_data/29b8e2e5821bed2234ee953c/script.js
- https://demo.playandnope.com/wp-content/themes/wordpress-base/inc/theme/js/core.370dc6350c7d77f5.js (defer)
- https://demo.playandnope.com/wp-content/plugins/wp-rocket/assets/js/wpr-beacon.min.js (async)

**Stylesheets:** 5 external, 3 inline (9.4 KB)

**Images:** 20 total — **0 without alt**, **0 without width/height**, 0 without loading="lazy"

Image inventory (first 15):
| src | alt | w×h | loading | srcset |
| --- | --- | --- | --- | --- |
| content/uploads/2024/06/6027eb05ed4efb000419981a-155x155.png | _(empty)_ | 155×155 | lazy | ✓ |
| content/uploads/2024/06/6027edfeed4efb0004199826-280x138.png | _(empty)_ | 280×138 | lazy | ✓ |
| content/uploads/2024/06/6027f14eed4efb0004199839-242x155.png | _(empty)_ | 242×155 | lazy | ✓ |
| content/uploads/2024/06/6027f242ed4efb000419983e-280x139.png | _(empty)_ | 280×139 | lazy | ✓ |
| content/uploads/2024/06/6027ecebed4efb0004199821-280x149.png | _(empty)_ | 280×149 | lazy | ✓ |
| 5647e48451dac28def9d4150b4d24-removebg-preview-1-233x155.png | _(empty)_ | 233×155 | lazy | ✓ |
| 5f2a034cbe55634ea49e13518b4-removebg-preview-1-1-233x155.png | _(empty)_ | 233×155 | lazy | ✓ |
| 2024/06/attachment_33573462-removebg-preview-1-1-155x155.png | _(empty)_ | 155×155 | lazy | ✓ |
| -content/uploads/2024/06/6027ed7eed4efb0004199823-280x75.png | _(empty)_ | 280×75 | lazy | ✓ |
| -content/uploads/2024/06/6027efe7ed4efb0004199832-280x61.png | _(empty)_ | 280×61 | lazy | ✓ |
| -content/uploads/2024/06/6027eae1ed4efb0004199819-280x41.png | _(empty)_ | 280×41 | lazy | ✓ |
| /wp-content/uploads/2024/07/royal-canin-logo-svg-280x106.png | _(empty)_ | 280×106 | lazy | ✓ |
| s/2024/07/41644975_232057294130286_8841412687386948258_n.jpg | _(empty)_ | 667×834 | lazy | ✓ |
| ayandnope.com/wp-content/uploads/2024/07/img_4145-scaled.jpg | _(empty)_ | 1920×2560 | lazy | ✓ |
| ayandnope.com/wp-content/uploads/2024/11/img_5959-scaled.jpg | _(empty)_ | 1920×2560 | lazy | ✓ |

**Links:** 47 anchors — 8 external, 1 preconnect, 1 preload.

Vague repeated link text:
- "koerte rühmatreeningud" ×3
- "kasside rühmatreeningud" ×3
- "loe rohkem" ×3
- "paws n' play" ×2
- "teenused" ×2
- "uudised" ×2
- "tiim" ×2
- "kontakt" ×2
- "koerad" ×2
- "kassid" ×2

**Forms:**
Form 1:
- search — labeled

### Priority fixes
1. **Vague link text repeated** (medium) — "loe rohkem" ×3

### Manual checks
- Visual rendering of detected mojibake (browser may auto-correct for display).
- Whether decorative images correctly use empty `alt=""` (vs. content images missing it).
- Whether headings reflect actual document hierarchy semantically.
- Whether vague link text is disambiguated by `aria-label` or surrounding context.

## Optimized-Web Checklist
_Captured in 0 ms._

**Summary:** 4 pass · 1 warn · 1 fail · 1 n/a

**Checklist:**
| Item | Status | Detail |
| --- | --- | --- |
| Page caching plugin / CDN active | ✓ pass | Caching plugin detected (WP Rocket) |
| Images lazy-loaded | ✓ pass | All non-hero raster images use loading="lazy". |
| Hero image eagerly loaded | ✗ fail | Hero image has loading="lazy", which delays LCP (inferred from DOM order/size — Lighthouse LCP element unavailable). Use loading="eager" (or omit loading) and add fetchpriority="high". |
| Hero is a real <img> (not a CSS background-image) | – n/a | No CSS background-images detected on raster-image-eligible elements. |
| Responsive images (srcset / <picture>) | ✓ pass | 20/20 raster images use srcset or <picture> (100%). |
| Reasonable number of image sizes | ✓ pass | 48 distinct srcset widths. |
| JS scripts not blocking in <head> | ! warn | 1 render-blocking script in <head>. Move to footer or add defer/async. |

**Evidence:**
- Page caching plugin / CDN active:
  - `HTML markers: WP Rocket`
  - `generator: WP Rocket 3.22.0.3`
- Hero image eagerly loaded:
  - `hero: …ayandnope.com/wp-content/uploads/2024/06/6027eb05ed4efb000419981a-155x155.png`
  - `loading: lazy`
  - `fetchpriority: (not set)`
- Reasonable number of image sizes:
  - `widths: 100, 116, 124, 150, 155, 200, 225, 233, 240, 242, 248, 280, 300, 310, 324, 400, 410, 483, 500, 510, 512, 560, 600, 626, 648, 667, 700, 768, 800, 840, 959, 972, 1024, 1120, 1152, 1284, 1296, 1400, 1440, 1472, 1536, 1680, 1920, 1944, 1960, 2048, 2240, 2560`
- JS scripts not blocking in <head>:
  - `https://cdn-cookieyes.com/client_data/29b8e2e5821bed2234ee953c/script.js`

### Priority fixes
1. **Hero image eagerly loaded** (high) — Hero image has loading="lazy", which delays LCP (inferred from DOM order/size — Lighthouse LCP element unavailable). Use loading="eager" (or omit loading) and add fetchpriority="high".
2. **JS scripts not blocking in <head>** (medium) — 1 render-blocking script in <head>. Move to footer or add defer/async.

### Manual checks
- Cart / checkout / logged-in pages must bypass the page cache or load dynamic regions via AJAX.
- Verify the cache is warmed for the canonical URL on initial deploy (first visitor should not pay the cold-start cost).
- Inspect "hero" image visually — heuristic above picks the first/largest <img>, which may not match the actual LCP element on JS-heavy pages.
- Confirm scripts marked as defer/async actually tolerate deferred execution (some legacy snippets break when reordered).