# How we tuned apogee.lu for a 95 mobile PageSpeed score

Our corporate site at [apogee.lu](https://apogee.lu/) is a static marketing front door: two HTML pages, a hero photograph, Victor Mono typography, and no application server in the path. It should be an easy Core Web Vitals win. Earlier this month, PageSpeed Insights on mobile still reported First Contentful Paint and Largest Contentful Paint above 2.5 seconds, with render-blocking CSS and a filmstrip that stayed blank until late in the load. We build [Apogee Watcher](https://apogeewatcher.com/?utm_source=hashnode&utm_medium=referral&utm_campaign=hashnode-tuning-apogee-lu-pagespeed-mobile) to monitor PageSpeed for client estates; tuning our own homepage was overdue, and we used the new domain-level report Summary to keep the work honest across every URL we ship under that host.

Below is the sequence we ran: domain triage, diagnosis, failed experiments, and what landed us at **95 mobile** and **100 desktop** in lab tests, with **Cumulative Layout Shift at 0**.

## What PageSpeed Insights showed before we changed anything

Mobile lab runs told a consistent story. First Contentful Paint and Largest Contentful Paint were nearly equal, often around 2.6 to 3.0 seconds. The filmstrip showed no painted pixels for most of the timeline, then the full hero appeared in one step. Total Blocking Time stayed at zero; the bottleneck was not JavaScript on the main thread. Lighthouse flagged render-blocking `index.css` from our asset CDN. Speed Index lagged because the visible hero arrived only after CSS allowed layout.

That single-URL picture was useful, but incomplete. apogee.lu also ships `mailops.html` and will keep growing small product pages. A green homepage with a neglected second URL is how agencies ship “we fixed PageSpeed” stories that fall apart on the next client call.

## How we used Watcher domain reports before touching CSS

Before we edited templates, we generated a [domain report](https://apogeewatcher.com/check?utm_source=hashnode&utm_medium=referral&utm_campaign=hashnode-tuning-apogee-lu-pagespeed-mobile) for the host and opened it on the **Summary** tab. Shared domain reports now default to Summary: an [attention-free score](https://apogeewatcher.com/blog/attention-free-score-domain-report-summary?utm_source=hashnode&utm_medium=referral&utm_campaign=hashnode-tuning-apogee-lu-pagespeed-mobile) (the share of completed mobile and desktop tests that pass Performance, category, and Core Web Vitals gates), a short list of metrics that still need attention, and the top pages that fail those gates. For apogee.lu that view made three decisions cheap: the homepage on **mobile** was the first attention sink (LCP and render-blocking CSS), not a desktop-only quirk; `mailops.html` either shared the same head and hero pattern or needed its own pass; and a shareable `#summary` link kept the before state visible while we experimented locally. Single-URL PageSpeed Insights remains the right drill-down once Summary names the URL and strategy. Domain Summary is what stops you from optimising the wrong page first.

## How we found the real Largest Contentful Paint bottleneck

Lighthouse named the hero photograph as the Largest Contentful Paint element, but the image was not the first gate. Our 500w WebP hero is about 15 KB and often finished downloading before layout. The gate was **synchronous CSS**: the browser waited for stylesheet parse before painting.

On production we also found Cloudflare injecting a `cdn-cgi/challenge-platform` script into HTML. That script added main-thread work and pushed Largest Contentful Paint above 2.9 seconds. Setting `Cache-Control: no-transform` on HTML from Apache stopped rewrite; injected scripts left view-source and lab Largest Contentful Paint dropped by roughly a second.

## Moving assets to our CDN and reordering the document head

We moved images, CSS, JavaScript, and fonts to `https://cdn.apogee.lu/assets/` with `preconnect` in the head. Critical links now run before title and social meta so discovery order matches what lab filmstrips punish: CDN first, then paint-critical assets, then the rest of the document chrome.

1. `preconnect` to the CDN
2. Hero image `preload` (500w on mobile via `<picture>`)
3. Font preloads and `victor-mono.css`
4. Main CSS `preload` and synchronous `stylesheet`

The hero download starts in the head even though paint still waits on CSS. That parallel fetch is why preload still helps Largest Contentful Paint when the stylesheet remains synchronous.

## Hero images and fixed heights per breakpoint

We ship WebP at 500w, 780w, 1200w, and full width. `<picture>` serves 500w on viewports up to 980px so mobile does not pull a 780w file on a 2x screen. The preload uses `media="(max-width: 980px)"` so lab mobile requests the smaller asset first.

Fluid height caused Cumulative Layout Shift when we tried async CSS. `min-height: clamp(420px, 62vh, 720px)` gave **577px** on an iPhone 15 Pro Max (62% of 932px viewport height). Late full CSS shifted `.hero-stage` and Lighthouse scored **0.197** Cumulative Layout Shift.

We replaced fluid height with fixed values from the same formula at canonical breakpoints:


| Breakpoint | Reference viewport | Fixed height |
| ---------- | ------------------ | ------------ |
| ≤980px     | 390×844            | 524px        |
| 981-1360px | 1280×900           | 558px        |
| ≥1361px    | design cap         | 720px        |


Mobile lab Cumulative Layout Shift returned to **0** after deploy. That is the same “reserve space” idea you see in CLS how-tos, applied to a hero **stage** whose height was CSS-driven rather than an `<img>` without dimensions. Width and height on the image alone would not have stopped the stage from resizing when late CSS replaced the fluid `min-height`.

## CSS and fonts: what we tried and what we kept

Extracting inline CSS into `index.css` shrank HTML but did not remove render-blocking.

**Inline critical CSS plus async full stylesheet** cut First Contentful Paint to about 1.0 seconds locally and cleared the render-blocking warning. It also broke typography (`font-display: optional` skipped Victor Mono on slow links) and pushed Cumulative Layout Shift back up when below-the-fold rules arrived late. We kept **synchronous main CSS** and **self-hosted Victor Mono** on the CDN instead: four latin WOFF2 files and a small `@font-face` sheet, with preloads for normal and italic latin, and `font-display: swap` so the correct family appears after files load. The Lighthouse render-blocking warning can remain while Performance still lands in the mid-90s; we preferred stable layout and brand fonts over chasing a green Opportunities row.

## Scores after deploy and what Summary showed next

![PageSpeed Insights for apogee.lu after deploy: mobile Performance 95 and desktop Performance 100 side by side](https://apogeewatcher.com/blog-images/44-apogee-lu-pagespeed-mobile-desktop.png)

Mobile PageSpeed Insights after CDN alignment (captured **30 Aug 2026**):

- **Performance 95**  
- **First Contentful Paint ~1.7s**, **Largest Contentful Paint ~2.4s**  
- **Cumulative Layout Shift 0**, **Total Blocking Time 0ms**

Desktop lab reached **100** with sub-second paint metrics (FCP **0.5s**, LCP **0.6s**, Speed Index **0.7s**). Speed Index on mobile can stay above 4 seconds because the hero image paints after text; we accept that on a rarely changed marketing page rather than reopening the async-CSS experiment.

We regenerated the domain report and opened Summary again. All **4** completed page tests passed the CWV and category gates, so the attention-free score sat at **100%**, with average Lighthouse Performance **97** on mobile and **100** on desktop.

![Apogee Watcher domain report Summary for apogee.lu: site status PASS, attention-free score 100%, four completed page tests within gates](https://apogeewatcher.com/blog-images/44-apogee-lu-domain-report-summary.png)

That before/after pair is what we keep when someone asks whether the ship actually moved the portfolio, not only one PageSpeed Insights screenshot.

## What we monitor now

We keep [apogee.lu](https://apogee.lu/) on scheduled PageSpeed runs in Watcher and re-check after HTML header, Cloudflare, or CDN cache changes. Lab green after deploy proves the fix shipped. Search Console field data still moves on its [28-day CrUX window](https://apogeewatcher.com/blog/why-core-web-vitals-fix-not-in-crux-yet-28-day-window?utm_source=hashnode&utm_medium=referral&utm_campaign=hashnode-tuning-apogee-lu-pagespeed-mobile), so we do not treat one green lab week as field proof.

Manual PageSpeed Insights before every deploy does not scale across client sites. [Apogee Watcher](https://apogeewatcher.com/sign-up?utm_source=hashnode&utm_medium=referral&utm_campaign=hashnode-tuning-apogee-lu-pagespeed-mobile) automates runs, stores history, and alerts on regression. Domain reports with Summary give you the same triage shape we used on our own host. For pipeline versus scheduled monitoring, see [Lighthouse CI vs managed monitoring](https://apogeewatcher.com/blog/lighthouse-ci-vs-managed-monitoring-build-vs-buy-agencies?utm_source=hashnode&utm_medium=referral&utm_campaign=hashnode-tuning-apogee-lu-pagespeed-mobile).

## FAQ

**Does CDN CSS stop render-blocking?**

No. Synchronous `stylesheet` blocks first paint until parsed, even for a small file. Preload helps discovery; async or inlined critical CSS removes the gate. We used sync CSS plus fixed hero height to hold Cumulative Layout Shift at zero.

**Why preload the hero if CSS blocks paint?**

The image can download in parallel with the stylesheet. When paint is allowed, Largest Contentful Paint is shorter if those bytes are already cached instead of starting only after CSS finishes.

**Why open a domain report instead of only PageSpeed Insights?**

PageSpeed Insights answers one URL. Domain Summary answers which URLs and strategies still need attention under one host, with an attention-free pass rate you can share. Drill into Opportunities after Summary names the page.

---

**Next step:** Run PageSpeed Insights on your homepage. If First Contentful Paint equals Largest Contentful Paint in the filmstrip, check render-blocking CSS and injected scripts before resizing images. Generate a [free domain scan](https://apogeewatcher.com/check?utm_source=hashnode&utm_medium=referral&utm_campaign=hashnode-tuning-apogee-lu-pagespeed-mobile), open `#summary`, then [start a Watcher trial](https://apogeewatcher.com/sign-up?utm_source=hashnode&utm_medium=referral&utm_campaign=hashnode-tuning-apogee-lu-pagespeed-mobile) to keep the same URLs on a schedule.

## References

- [PageSpeed Insights](https://pagespeed.web.dev/) (Google)
- [Optimize Largest Contentful Paint](https://web.dev/articles/optimize-lcp) (web.dev)
- [Render-blocking resources](https://developer.chrome.com/docs/lighthouse/performance/render-blocking-resources) (Chrome Developers)
- [Optimize CSS Delivery](https://developers.google.com/speed/docs/insights/OptimizeCSSDelivery) (Google)
- [Attention-Free Score: How Domain Reports Show Which Pages Need Work](https://apogeewatcher.com/blog/attention-free-score-domain-report-summary) (Apogee Watcher)
- [Why a Core Web Vitals fix may not show in CrUX yet](https://apogeewatcher.com/blog/why-core-web-vitals-fix-not-in-crux-yet-28-day-window) (Apogee Watcher)
- [Lighthouse CI vs managed monitoring for agencies](https://apogeewatcher.com/blog/lighthouse-ci-vs-managed-monitoring-build-vs-buy-agencies) (Apogee Watcher)
- [Apogee Information Systems](https://apogee.lu/) (corporate site)


