# Fontes Web — Estratégia de Carregamento para PageSpeed

---

## Regra de Ouro

**Nenhuma fonte deve bloquear o render.** O browser precisa pintar o primeiro pixel antes de esperar qualquer fonte externa carregar.

---

## ✅ MÉTODO DEFINITIVO: Self-hosting no Cloudinary (VALIDADO — mobile 70 → 85)

**Resultado validado em produção no GHL**: CLS caiu de 0.227 → 0.078, TBT de 450ms → 230ms, score 70 → **85 mobile**.

### Por que funciona melhor que Google Fonts

- Fontes e imagens vêm do **mesmo domínio** (`res.cloudinary.com`) — HTTP/2 multiplexing, sem cross-origin penalty
- Preload funciona de verdade — o browser não descarta o hint por ser cross-origin
- Zero dependência de `fonts.gstatic.com` — elimina um round-trip inteiro

### Como fazer

**1. Baixar o subset Latin de cada fonte (woff2 apenas)**

Baixar de [fontsource.org](https://fontsource.org) ou [fonts.google.com/specimen/Oswald](https://fonts.google.com/specimen/Oswald) (Download All → extrair o `-latin-400-normal.woff2`).

Fontes do projeto `operacao-aprova-petro` já hospedadas:
```
https://res.cloudinary.com/dozxsfkmm/raw/upload/operacao-aprova-petro/fonts/oswald-latin.woff2
https://res.cloudinary.com/dozxsfkmm/raw/upload/operacao-aprova-petro/fonts/montserrat-latin.woff2
https://res.cloudinary.com/dozxsfkmm/raw/upload/operacao-aprova-petro/fonts/firacode-latin.woff2
```

**2. Upload para Cloudinary com `resource_type=raw`**

```bash
# IMPORTANTE: resource_type NÃO entra na string de assinatura (bug do Cloudinary)
# Usar a API de upload autenticado com resource_type=raw
curl -X POST https://api.cloudinary.com/v1_1/CLOUD_NAME/raw/upload \
  -F "file=@oswald-latin.woff2" \
  -F "folder=meu-projeto/fonts" \
  -F "public_id=oswald-latin" \
  -F "api_key=API_KEY" \
  -F "timestamp=TIMESTAMP" \
  -F "signature=ASSINATURA"
```

**3. HTML: preconnect + preload no topo do arquivo**

```html
<!-- Preconnect: abre conexão antes de qualquer request -->
<link rel="preconnect" href="https://res.cloudinary.com" crossorigin>
<!-- Preload das fontes com alta prioridade -->
<link rel="preload" as="font" type="font/woff2" crossorigin
      href="https://res.cloudinary.com/CLOUD/raw/upload/PASTA/oswald-latin.woff2">
<link rel="preload" as="font" type="font/woff2" crossorigin
      href="https://res.cloudinary.com/CLOUD/raw/upload/PASTA/montserrat-latin.woff2">
```

**4. CSS: @font-face inline no início do `<style>`**

```css
/* ── Fontes auto-hospedadas (Cloudinary) — sem cross-origin, sem CLS ── */
@font-face {
    font-family: 'Oswald';
    font-style: normal;
    font-weight: 400 700;
    font-display: swap;
    src: url('https://res.cloudinary.com/CLOUD/raw/upload/PASTA/oswald-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;
}
@font-face {
    font-family: 'Montserrat';
    font-style: normal;
    font-weight: 400 700;
    font-display: swap;
    src: url('https://res.cloudinary.com/CLOUD/raw/upload/PASTA/montserrat-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;
}
```

**5. REMOVER** qualquer JS que carregue Google Fonts dinamicamente — substituir por comentário.

### O que NÃO fazer com este método

```html
<!-- ❌ NUNCA preload de fontes de domínio diferente (fonts.gstatic.com) -->
<!-- Resultado: LCP subiu de 2.9s → 10.4s, score caiu de 70 → 42 -->
<!-- Causa: fontes cross-origin competem com a imagem hero no bandwidth 4G simulado -->
<link rel="preload" as="font" type="font/woff2" crossorigin
      href="https://fonts.gstatic.com/s/oswald/v53/TK3_WkUHHAIjg75cFRf3bXL8LICs169vsUZiZQ.woff2">
```

---

## Método 2: `media="print"` + `onload` (recomendado para GHL/Custom Code)

```html
<!-- Preconnect: abre conexão TCP/TLS antes da requisição -->
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>

<!-- Body font: swap — pinta com fallback imediatamente, troca quando carregar -->
<link rel="stylesheet"
      media="print"
      onload="this.media='all';this.onload=null;"
      href="https://fonts.googleapis.com/css2?family=Montserrat:wght@400;500;600;700&display=swap">

<!-- Display/heading font: optional — não troca se não carregar rápido (elimina CLS) -->
<link rel="stylesheet"
      media="print"
      onload="this.media='all';this.onload=null;"
      href="https://fonts.googleapis.com/css2?family=Oswald:wght@700;800&display=optional">

<!-- Fallback para usuários sem JS -->
<noscript>
  <link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=Montserrat:wght@400;500;600;700&family=Oswald:wght@700;800&display=swap">
</noscript>
```

**Como funciona**: `media="print"` faz o browser carregar o CSS com baixa prioridade (não bloqueia render). O `onload` troca para `media="all"` assim que carrega, aplicando as fontes.

---

## Método 2: `rel="preload"` (alternativa para frameworks com `<head>` controlável)

```html
<link rel="preload" as="style"
      href="https://fonts.googleapis.com/css2?family=Montserrat:wght@400;700&display=swap"
      onload="this.rel='stylesheet'">
```

Não funciona em GHL Custom Code pois o `<head>` é controlado pelo GHL.

---

## Método 3: Nunca fazer (causa bloqueio de render)

```css
/* ❌ NUNCA — bloqueia render completamente */
@import url('https://fonts.googleapis.com/css2?family=Montserrat');
```

```html
<!-- ❌ NUNCA — bloqueia render (sem media trick) -->
<link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=Montserrat">
```

---

## Estratégia por tipo de fonte

| Tipo | `font-display` | Justificativa |
|------|---------------|---------------|
| Body text (parágrafos) | `swap` | Conteúdo precisa ser legível; CLS controlado por fallback calibrado |
| Headings (h1, h2) | `optional` | Visual — preferível não mostrar que trocar e causar shift |
| Monospace (código, badges) | `optional` | Decorativo — fallback monospace do sistema funciona bem |
| CTA buttons | `optional` | Texto curto — fallback não quebra layout |

---

## Fallback `@font-face` com métricas calibradas

Cada fonte crítica deve ter um `@font-face` local com `size-adjust` para que o fallback ocupe o mesmo espaço que a fonte web, eliminando qualquer CLS na troca.

```css
/* Fallback para Oswald (heading) */
@font-face {
    font-family: 'Oswald Fallback';
    src: local('Arial Narrow Bold'), local('Impact'), local('Arial');
    font-weight: 700 800;
    size-adjust: 90%;
    ascent-override: 105%;
    descent-override: 20%;
    line-gap-override: 0%;
}

/* Fallback para Montserrat (body) */
@font-face {
    font-family: 'Montserrat Fallback';
    src: local('Trebuchet MS'), local('Gill Sans'), local('Arial');
    size-adjust: 95%;
    ascent-override: 96%;
    descent-override: 22%;
    line-gap-override: 0%;
}

/* Fallback para Fira Code (monospace) */
@font-face {
    font-family: 'Fira Code Fallback';
    src: local('Courier New'), local('Menlo'), local('Consolas');
    size-adjust: 98%;
    ascent-override: 90%;
    descent-override: 22%;
    line-gap-override: 0%;
}
```

**Uso na stack de fontes:**
```css
.heading { font-family: 'Oswald', 'Oswald Fallback', sans-serif; }
body      { font-family: 'Montserrat', 'Montserrat Fallback', sans-serif; }
code      { font-family: 'Fira Code', 'Fira Code Fallback', monospace; }
```

---

## Economizar requests: agrupar no mesmo link

```html
<!-- ✅ Uma request só para fontes com mesmo display -->
<link rel="stylesheet"
      media="print" onload="this.media='all';this.onload=null;"
      href="https://fonts.googleapis.com/css2?family=Fira+Code:wght@400;600&family=Oswald:wght@700;800&display=optional">
```

Separar apenas quando os `display` forem diferentes (swap vs optional).

---

## Checklist de fontes

### Via Cloudinary (método preferido para GHL)
- [ ] Fontes woff2 (latin subset) hospedadas no Cloudinary com `resource_type=raw`
- [ ] `<link rel="preconnect" href="https://res.cloudinary.com" crossorigin>` no topo
- [ ] `<link rel="preload" as="font" type="font/woff2" crossorigin href="...">` para cada fonte
- [ ] `@font-face` inline no início do `<style>` com `font-display: swap`
- [ ] Nenhum JS de carregamento de Google Fonts

### Via Google Fonts (fallback quando Cloudinary não é opção)
- [ ] Zero `@import url(...)` dentro de `<style>`
- [ ] Preconnect para `fonts.googleapis.com` e `fonts.gstatic.com`
- [ ] Todos os `<link>` de fontes com `media="print" onload`
- [ ] `<noscript>` com fallback para usuários sem JS
- [ ] Body font com `display=swap`
- [ ] Heading/display fonts com `display=optional`
- [ ] `@font-face` fallback com `size-adjust` para cada fonte usada em texto visível
- [ ] Stack de fontes no CSS sempre inclui o fallback local: `'Fonte', 'Fonte Fallback', generic`
