# Fase Final — otimizações estruturais (modelo1 → modelo2-6) + vendorização Raleway

**Camada:** Fase Final (após L0–L6) · **Critérios A1:** `LCP`/`FCP`/`INP`/`TTFB`/`CLS`
**Stack:** Laravel 5.5.50 / PHP 7.2.34 · **Risco:** baixo/médio · **Esforço:** baixo/médio
**Template ativo em produção:** `modelo1` (confirmado via `configurations.key='layout'` → `modelo1`).

> Documentação da última rodada de otimizações: consolidação das melhorias da **Fase Final**
> (seed container `public_infra_update-91a1`: `bbc2`, `7a7b`, `ce7f`, `5e6d`, `df11`) e a
> **replicação** das mesmas para `modelo2…6`, incluindo a **vendorização da fonte Raleway**
> (única fonte Google ainda carregada de forma externa).

---

## 1. Objetivo

- Aplicar no template ativo (`modelo1`) e replicar em `modelo2…6` as 5 melhorias da Fase Final
  (fontes/`@import`, servidor, JS delivery, render containment, monitoramento) — **fora da borda CDN**.
- Eliminar a **única** dependência de fonte do Google que restava (`Raleway`), vendorizando-a
  localmente como as demais Google Fonts (Rubik, Source Sans Pro, Nunito Sans, Lato, Encode Sans).

---

## 2. Contexto / problema encontrado

- As otimizações da Fase Final foram implementadas primeiro em `modelo1`, mas os templates
  `modelo2…6` continuavam com o estado antigo (polyfills mortos, bootstrap duplicado, scripts sem
  `defer`, rodapé sem `content-visibility`).
- A fonte **Raleway** não estava em `public/assets/vendor/fonts/google/fonts.css` (que já é vendorizado
  com 73 `woff2`). Ela era carregada **exclusivamente** de
  `https://fonts.googleapis.com/css?family=Raleway:300,400,600` — via `<link rel=preload>` que havia
  sido adicionado nos heads (e, originalmente, via `@import` no `app.css`, já removido). Ou seja, era a
  única família ainda buscada do Google, inconsistindo com o resto do projeto (self-hosted).
- `Server-Timing` revelou TTFB de origem real ~3 s num cache MISS → o gargalo não era mais o
  `Configuration` (já cacheado), mas o render/origem das páginas.

---

## 3. O que foi feito

### 3.1 Fontes — vendorização da Raleway (`bbc2`, consolidado)

- Baixado o `woff2` latin da Raleway (**v37 é fonte *variável*** — o mesmo arquivo cobre 300/400/600)
  para `public/assets/vendor/fonts/google/woff2/raleway-latin.woff2` (48 264 bytes).
- Adicionado `@font-face` local a `public/assets/vendor/fonts/google/fonts.css`:
  ```css
  /* Raleway (vendorizado — substitui o @import do Google; fonte variavel v37 latin) */
  @font-face {
    font-family: 'Raleway';
    font-style: normal;
    font-weight: 100 900;
    font-display: swap;
    src: url(woff2/raleway-latin.woff2) format('woff2');
    unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC,
                   U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193,
                   U+2212, U+2215, U+FEFF, U+FFFD;
  }
  ```
- Removida a tag `<link rel="preload" ... href="https://fonts.googleapis.com/css?family=Raleway...">`
  + `<noscript>` de **todos os 7 heads** (`default/master` + `modelo1…6/head`).
- `modelo6/head` ainda tinha um `<link>` **comentado** apontando para `fonts.googleapis` (Source Sans
  Pro) — removido.
- `font-display: swap` + `size-adjust: 100%` já estavam presentes nos `@font-face` MR/OR/MB de
  `style.css` (feito na rodada anterior).

### 3.2 Servidor — cache de Configuration, eager loading, Server-Timing (`7a7b`)

- **Middleware `app/Http/Middleware/ServerTiming.php`** (novo): mede `microtime` do handle e seta
  `Server-Timing: app;dur=…` na resposta. Registrado **primeiro** no grupo `web` de
  `app/Http/Kernel.php` (envolve `PageCache`).
  - Bug de implementação corrigido: `headers` é **propriedade**, não método → `method_exists()` falhava
    e o header nunca era setado. Passou a usar `isset($response->headers)`.
- **`app/Http/Model/Prefecture/Configuration.php`**: `boot()` com
  `Cache::forget('prefecture.config')` em `saved`/`deleted` (invalida o cache de config ao editar).
- **`app/Http/Model/Prefecture/Content.php`**: `ultimasNoticias()` agora faz
  `->with('archives:path,description,contents_id,highlight','categoryContents:id,name,slug')`
  (eager loading dos hot paths).
- `default/master.blade.php` colapsa 14 `Configuration::where` em
  `Cache::rememberForever('prefecture.config', …)` (14→1 query).
  - **Nota:** em `modelo1…6` o cabeçalho/rodapé já era servido por `ConfigHeaderServiceProvider` /
    `FooterServiceProvider` com `Cache::remember` (TTL 300s) — ganho parcial já existente; o
    `rememberForever` acima cobre o template `default` (dormente).

### 3.3 JS delivery — `defer`/`async`, remoção de polyfills, dedup bootstrap (`ce7f`)

Aplicado em `modelo1…6/layout/scripts.blade.php` (e `default/master`):

- **Removidos** `html5shiv`/`respond` (presentes em `modelo2`, `modelo6`) e `modernizr-custom`
  (presentes em `modelo2`, `modelo5`, `modelo6`).
- **Bootstrap duplicado**: `modelo2` tinha `bootstrap.js` + `bootstrap.min.js` → removido o dev
  (`bootstrap.js`), mantido `bootstrap.min.js` com `defer`; `modelo5` tinha dois `bootstrap.min.js`
  (assets + `layouts/modelo5/...`) → removido o específico do modelo5.
- **`defer`** em libs sem dependente inline síncrono: `fontawesome-all`, `popper`, `tinycolor`,
  `lozard`, `lozad`, `main`, `tamanho-fonte`, `functions`, `iframe`, `contrast`, `plugins`/`active`
  (modelo4). **jQuery mantido síncrono** (scripts inline usam `$()` imediatamente).
- **`rum.js`** adicionado com `async` a todos os modelos.
- `modelo6` já deferia a maioria; apenas removeu polyfills e adicionou `rum.js` (jQuery deferia e os
  inline usam `DOMContentLoaded`, então seguro).

### 3.4 Render containment — `content-visibility:auto` (`5e6d`)

- Utilitário `.cv-auto { content-visibility:auto; contain-intrinsic-size:auto 480px; }` em
  `style.css` (compartilhado).
- Aplicado a `#footer-info` em `modelo2/3/4/layout/footer.blade.php` e a `#rodape` em
  `modelo5/layout/footer/footer.blade.php` e `modelo6/layout/footer.blade.php`.

### 3.5 Monitoramento — RUM + Speculation Rules + budget (`df11`)

- `public/assets/js/custom/portal/rum.js` (novo): `PerformanceObserver` para `LCP`, `CLS`, `INP`
  (`event`/`first-input`/`layout-shift`/`largest-contentful-paint`) + `navigator.sendBeacon` para
  `window.__rumEndpoint` (ou `console.debug` quando indisponível).
- **Speculation Rules** movidos para o partial global `portal/layouts/partials/preconnect.blade.php`
  (`<script type="speculationrules">` com `{"prerender":[{"source":"document","where":{"href_matches":".*"},"eagerness":"moderate"}]}`), aplicando-se a todos os modelos.
- `lighthouse/budget.json` (novo): asserts `LCP≤2500`, `CLS≤0.1`, `TBT` warn ≤300.

---

## 4. Validação (canário, container `public_infra_update-app-1`)

| Teste | Resultado |
|---|---|
| `googleapis` no HTML da home (`modelo1`) | **0** |
| `fonts.css` servido inclui `@font-face` Raleway | ✅ (2 refs) |
| `raleway-latin.woff2` servido | HTTP **200** |
| `html5shiv` / `modernizr` no HTML | **0** / **0** |
| `rum.js` no HTML | **1** |
| `cv-auto` no rodapé | **1** |
| `Server-Timing` header (cache MISS) | `app;dur=3076.53` (TTFB de origem real) |
| `grep googleapis` em `resources/views/portal/layouts` | **NENHUM** |

---

## 5. Como reconstruir (reproducibilidade)

- Raleway: `curl` do CSS2 do Google Fonts (`family=Raleway:wght@300;400;600&display=swap`) com UA de
  browser moderno → baixa o `woff2` latin (url idêntica p/ os 3 pesos, pois é variável v37) →
  `public/assets/vendor/fonts/google/woff2/raleway-latin.woff2` + `@font-face` em `fonts.css`.
- A regra L0.3 (`public/.htaccess`) cobre `woff2` → `Cache-Control … immutable` + gzip/Brotli server-side.
- Não há hash no nome do arquivo; ao trocar a fonte, versionar o path (`raleway-<ver>/`).

---

## 6. Arquivos afetados

- `public/assets/vendor/fonts/google/woff2/raleway-latin.woff2` (novo)
- `public/assets/vendor/fonts/google/fonts.css` (`@font-face` Raleway)
- `resources/views/portal/layouts/{default/master, modelo1…6/head}.blade.php` (remove preload Google;
  `modelo6` remove link comentado)
- `resources/views/portal/layouts/{modelo1…6}/layout/scripts.blade.php` (JS delivery)
- `resources/views/portal/layouts/{modelo2,modelo3,modelo4}/layout/footer.blade.php`
  (`#footer-info` + `cv-auto`)
- `resources/views/portal/layouts/{modelo5,modelo6}/layout/footer/{footer.blade.php,footer}` (`#rodape` + `cv-auto`)
- `app/Http/Middleware/ServerTiming.php` (novo), `app/Http/Kernel.php` (registro)
- `app/Http/Model/Prefecture/Configuration.php` (`boot`), `app/Http/Model/Prefecture/Content.php`
  (`ultimasNoticias` with)
- `public/assets/js/custom/portal/rum.js` (novo)
- `resources/views/portal/layouts/partials/preconnect.blade.php` (Speculation Rules)
- `lighthouse/budget.json` (novo)
- `plan/2026-08-09-infra-update.epic.md` (§9 Fase Final — status implementado)

---

## 7. Rastreabilidade

- Container: `public_infra_update-91a1` (fechada).
- Itens: `bbc2` (fontes/+Raleway vendoring), `7a7b` (servidor), `ce7f` (JS delivery),
  `5e6d` (render containment), `df11` (monitoramento) — todas **fechadas** (sem `sd sync`/commit, por
  diretriz do usuário).
- EPIC: `plan/2026-08-09-infra-update.epic.md` §9.

## 8. Notas de deploy

- Implantar no canário; o usuário faz o deploy (sem commit automático).
- Medir `LCP`/`CLS`/`INP` via Lighthouse/sniffCSS e comparar contra baseline.
- **Pendência conhecida:** TTFB de origem ~3 s em MISS indica que o gargalo seguinte são as queries de
  conteúdo por categoria (`getContentNews`/`getCategoryNews`) — candidato a próximo N+1 a otimizar.
