Browse Source

snapshot: pre-destructive 20260512-183821

Alfredo 4 hours ago
parent
commit
a6df8e26a1
45 changed files with 1386 additions and 780 deletions
  1. 60 0
      CLAUDE.md
  2. 0 12
      blog/2019-05-28-first-blog-post.mdx
  3. 0 44
      blog/2019-05-29-long-blog-post.mdx
  4. 0 24
      blog/2021-08-01-mdx-blog-post.mdx
  5. BIN
      blog/2021-08-26-welcome/docusaurus-plushie-banner.jpeg
  6. 0 29
      blog/2021-08-26-welcome/index.mdx
  7. 0 25
      blog/authors.yml
  8. 0 19
      blog/tags.yml
  9. 11 0
      docs/intro.md
  10. 0 47
      docs/intro.mdx
  11. 0 8
      docs/tutorial-basics/_category_.json
  12. 0 23
      docs/tutorial-basics/congratulations.mdx
  13. 0 34
      docs/tutorial-basics/create-a-blog-post.mdx
  14. 0 57
      docs/tutorial-basics/create-a-document.mdx
  15. 0 43
      docs/tutorial-basics/create-a-page.mdx
  16. 0 31
      docs/tutorial-basics/deploy-your-site.mdx
  17. 0 168
      docs/tutorial-basics/markdown-features.mdx
  18. 0 7
      docs/tutorial-extras/_category_.json
  19. BIN
      docs/tutorial-extras/img/docsVersionDropdown.png
  20. BIN
      docs/tutorial-extras/img/localeDropdown.png
  21. 0 55
      docs/tutorial-extras/manage-docs-versions.mdx
  22. 0 88
      docs/tutorial-extras/translate-your-site.mdx
  23. 52 0
      documentation/add-article.md
  24. 55 0
      documentation/add-guide.md
  25. 68 0
      documentation/add-section.md
  26. 49 0
      documentation/brand-tokens.md
  27. 64 0
      documentation/cloudflare-deploy.md
  28. 35 0
      documentation/dev-build-deploy.md
  29. 56 0
      documentation/posthog-events.md
  30. 63 0
      documentation/seo-and-llms.md
  31. 76 20
      docusaurus.config.ts
  32. 421 0
      package-lock.json
  33. 1 0
      package.json
  34. 39 0
      plugins/copy-md/index.ts
  35. 26 0
      src/pages/404.tsx
  36. 130 15
      src/pages/index.module.css
  37. 53 31
      src/pages/index.tsx
  38. 69 0
      src/posthog/init.ts
  39. BIN
      static/img/icons/apple-touch-icon.png
  40. BIN
      static/img/icons/icon-192.png
  41. BIN
      static/img/icons/icon-512.png
  42. 21 0
      static/img/icons/site.webmanifest
  43. BIN
      static/img/og-default.png
  44. 18 0
      static/llms.txt
  45. 19 0
      static/robots.txt

+ 60 - 0
CLAUDE.md

@@ -0,0 +1,60 @@
+# gazperi.com — Docusaurus project
+
+<scope>
+Always-load companion when working anywhere under `SITE/site-com/`. Routes Claude to the right operational procedure in `documentation/` per task. Project history and rationale live in spec 0211.
+</scope>
+
+## Project shape
+
+<conventions>
+- Stack: Docusaurus v3 (TypeScript), Infima theming, custom React landing.
+- Hosting: Cloudflare Pages (`gazperi-com` project) on `gazperi.com`. Registrar: Hostinger; nameservers point at Cloudflare.
+- Analytics: PostHog EU (`https://eu.i.posthog.com`).
+- Git: local-only; no remote.
+- Public content lives under `docs/` (route `/educacao/*`). Internal operational procedures live under `documentation/` (this folder, not part of the site build).
+</conventions>
+
+## Routing — operational procedures
+
+<instructions>
+<step n="1">
+**Adding a guide.** Read `documentation/add-guide.md`.
+</step>
+
+<step n="2">
+**Adding an article.** Read `documentation/add-article.md`.
+</step>
+
+<step n="3">
+**Adding a top-level section.** Read `documentation/add-section.md`.
+</step>
+
+<step n="4">
+**Adjusting brand tokens (colors, fonts).** Read `documentation/brand-tokens.md`. Brand source of truth lives in `/Users/gazperi/Developer/ASSETS/brand/CLAUDE.md` — do not invent tokens.
+</step>
+
+<step n="5">
+**Running dev / build / deploy locally.** Read `documentation/dev-build-deploy.md`.
+</step>
+
+<step n="6">
+**Working with PostHog (events, env vars, rotation).** Read `documentation/posthog-events.md`.
+</step>
+
+<step n="7">
+**Updating SEO surfaces or LLM-discoverability artifacts.** Read `documentation/seo-and-llms.md`.
+</step>
+
+<step n="8">
+**Deploying to Cloudflare or doing a DNS / domain change.** Read `documentation/cloudflare-deploy.md`.
+</step>
+</instructions>
+
+## Boundaries
+
+<rules>
+- `documentation/` is internal — never published to the site. Public content lives in `docs/`.
+- Voice: published copy in `docs/` and `src/pages/` follows `DOCS/brand-voice/` rules (PT-BR). `documentation/` (internal procedures) is English by default.
+- Brand tokens come from `ASSETS/brand/`. Do not duplicate values here.
+- No direct edits to `build/` — regenerated on every build.
+</rules>

+ 0 - 12
blog/2019-05-28-first-blog-post.mdx

@@ -1,12 +0,0 @@
----
-slug: first-blog-post
-title: First Blog Post
-authors: [slorber, yangshun]
-tags: [hola, docusaurus]
----
-
-Lorem ipsum dolor sit amet...
-
-{/* truncate */}
-
-...consectetur adipiscing elit. Pellentesque elementum dignissim ultricies. Fusce rhoncus ipsum tempor eros aliquam consequat. Lorem ipsum dolor sit amet

+ 0 - 44
blog/2019-05-29-long-blog-post.mdx

@@ -1,44 +0,0 @@
----
-slug: long-blog-post
-title: Long Blog Post
-authors: yangshun
-tags: [hello, docusaurus]
----
-
-This is the summary of a very long blog post,
-
-Use a `{/*` `truncate` `*/}` comment to limit blog post size in the list view.
-
-{/* truncate */}
-
-Lorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque elementum dignissim ultricies. Fusce rhoncus ipsum tempor eros aliquam consequat. Lorem ipsum dolor sit amet
-
-Lorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque elementum dignissim ultricies. Fusce rhoncus ipsum tempor eros aliquam consequat. Lorem ipsum dolor sit amet
-
-Lorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque elementum dignissim ultricies. Fusce rhoncus ipsum tempor eros aliquam consequat. Lorem ipsum dolor sit amet
-
-Lorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque elementum dignissim ultricies. Fusce rhoncus ipsum tempor eros aliquam consequat. Lorem ipsum dolor sit amet
-
-Lorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque elementum dignissim ultricies. Fusce rhoncus ipsum tempor eros aliquam consequat. Lorem ipsum dolor sit amet
-
-Lorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque elementum dignissim ultricies. Fusce rhoncus ipsum tempor eros aliquam consequat. Lorem ipsum dolor sit amet
-
-Lorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque elementum dignissim ultricies. Fusce rhoncus ipsum tempor eros aliquam consequat. Lorem ipsum dolor sit amet
-
-Lorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque elementum dignissim ultricies. Fusce rhoncus ipsum tempor eros aliquam consequat. Lorem ipsum dolor sit amet
-
-Lorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque elementum dignissim ultricies. Fusce rhoncus ipsum tempor eros aliquam consequat. Lorem ipsum dolor sit amet
-
-Lorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque elementum dignissim ultricies. Fusce rhoncus ipsum tempor eros aliquam consequat. Lorem ipsum dolor sit amet
-
-Lorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque elementum dignissim ultricies. Fusce rhoncus ipsum tempor eros aliquam consequat. Lorem ipsum dolor sit amet
-
-Lorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque elementum dignissim ultricies. Fusce rhoncus ipsum tempor eros aliquam consequat. Lorem ipsum dolor sit amet
-
-Lorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque elementum dignissim ultricies. Fusce rhoncus ipsum tempor eros aliquam consequat. Lorem ipsum dolor sit amet
-
-Lorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque elementum dignissim ultricies. Fusce rhoncus ipsum tempor eros aliquam consequat. Lorem ipsum dolor sit amet
-
-Lorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque elementum dignissim ultricies. Fusce rhoncus ipsum tempor eros aliquam consequat. Lorem ipsum dolor sit amet
-
-Lorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque elementum dignissim ultricies. Fusce rhoncus ipsum tempor eros aliquam consequat. Lorem ipsum dolor sit amet

+ 0 - 24
blog/2021-08-01-mdx-blog-post.mdx

@@ -1,24 +0,0 @@
----
-slug: mdx-blog-post
-title: MDX Blog Post
-authors: [slorber]
-tags: [docusaurus]
----
-
-Blog posts support [Docusaurus Markdown features](https://docusaurus.io/docs/markdown-features), such as [MDX](https://mdxjs.com/).
-
-:::tip
-
-Use the power of React to create interactive blog posts.
-
-:::
-
-{/* truncate */}
-
-For example, use JSX to create an interactive button:
-
-```js
-<button onClick={() => alert('button clicked!')}>Click me!</button>
-```
-
-<button onClick={() => alert('button clicked!')}>Click me!</button>

BIN
blog/2021-08-26-welcome/docusaurus-plushie-banner.jpeg


+ 0 - 29
blog/2021-08-26-welcome/index.mdx

@@ -1,29 +0,0 @@
----
-slug: welcome
-title: Welcome
-authors: [slorber, yangshun]
-tags: [facebook, hello, docusaurus]
----
-
-[Docusaurus blogging features](https://docusaurus.io/docs/blog) are powered by the [blog plugin](https://docusaurus.io/docs/api/plugins/@docusaurus/plugin-content-blog).
-
-Here are a few tips you might find useful.
-
-{/* truncate */}
-
-Simply add Markdown files (or folders) to the `blog` directory.
-
-Regular blog authors can be added to `authors.yml`.
-
-The blog post date can be extracted from filenames, such as:
-
-- `2019-05-30-welcome.md`
-- `2019-05-30-welcome/index.md`
-
-A blog post folder can be convenient to co-locate blog post images:
-
-![Docusaurus Plushie](./docusaurus-plushie-banner.jpeg)
-
-The blog supports tags as well!
-
-**And if you don't want a blog**: just delete this directory, and use `blog: false` in your Docusaurus config.

+ 0 - 25
blog/authors.yml

@@ -1,25 +0,0 @@
-yangshun:
-  name: Yangshun Tay
-  title: Ex-Meta Staff Engineer, Co-founder GreatFrontEnd
-  url: https://linkedin.com/in/yangshun
-  image_url: https://github.com/yangshun.png
-  page: true
-  socials:
-    x: yangshunz
-    linkedin: yangshun
-    github: yangshun
-    newsletter: https://www.greatfrontend.com
-
-slorber:
-  name: Sébastien Lorber
-  title: Docusaurus maintainer
-  url: https://sebastienlorber.com
-  image_url: https://github.com/slorber.png
-  page:
-    # customize the url of the author page at /blog/authors/<permalink>
-    permalink: '/all-sebastien-lorber-articles'
-  socials:
-    x: sebastienlorber
-    linkedin: sebastienlorber
-    github: slorber
-    newsletter: https://thisweekinreact.com

+ 0 - 19
blog/tags.yml

@@ -1,19 +0,0 @@
-facebook:
-  label: Facebook
-  permalink: /facebook
-  description: Facebook tag description
-
-hello:
-  label: Hello
-  permalink: /hello
-  description: Hello tag description
-
-docusaurus:
-  label: Docusaurus
-  permalink: /docusaurus
-  description: Docusaurus tag description
-
-hola:
-  label: Hola
-  permalink: /hola
-  description: Hola tag description

+ 11 - 0
docs/intro.md

@@ -0,0 +1,11 @@
+---
+slug: /
+title: Educação
+description: Método, materiais e guias de Alfredo Gazperi sobre trabalho com IA, Claude e Claude Code.
+---
+
+# Educação
+
+Bem-vindo. Este é o espaço onde publico método, materiais e guias sobre trabalho com IA — foco em Claude e Claude Code.
+
+Conteúdo em construção. Volte em breve.

+ 0 - 47
docs/intro.mdx

@@ -1,47 +0,0 @@
----
-sidebar_position: 1
----
-
-# Tutorial Intro
-
-Let's discover **Docusaurus in less than 5 minutes**.
-
-## Getting Started
-
-Get started by **creating a new site**.
-
-Or **try Docusaurus immediately** with **[docusaurus.new](https://docusaurus.new)**.
-
-### What you'll need
-
-- [Node.js](https://nodejs.org/en/download/) version 20.0 or above:
-  - When installing Node.js, you are recommended to check all checkboxes related to dependencies.
-
-## Generate a new site
-
-Generate a new Docusaurus site using the **classic template**.
-
-The classic template will automatically be added to your project after you run the command:
-
-```bash
-npm init docusaurus@latest my-website classic
-```
-
-You can type this command into Command Prompt, Powershell, Terminal, or any other integrated terminal of your code editor.
-
-The command also installs all necessary dependencies you need to run Docusaurus.
-
-## Start your site
-
-Run the development server:
-
-```bash
-cd my-website
-npm run start
-```
-
-The `cd` command changes the directory you're working with. In order to work with your newly created Docusaurus site, you'll need to navigate the terminal there.
-
-The `npm run start` command builds your website locally and serves it through a development server, ready for you to view at http://localhost:3000/.
-
-Open `docs/intro.md` (this page) and edit some lines: the site **reloads automatically** and displays your changes.

+ 0 - 8
docs/tutorial-basics/_category_.json

@@ -1,8 +0,0 @@
-{
-  "label": "Tutorial - Basics",
-  "position": 2,
-  "link": {
-    "type": "generated-index",
-    "description": "5 minutes to learn the most important Docusaurus concepts."
-  }
-}

+ 0 - 23
docs/tutorial-basics/congratulations.mdx

@@ -1,23 +0,0 @@
----
-sidebar_position: 6
----
-
-# Congratulations!
-
-You have just learned the **basics of Docusaurus** and made some changes to the **initial template**.
-
-Docusaurus has **much more to offer**!
-
-Have **5 more minutes**? Take a look at **[versioning](../tutorial-extras/manage-docs-versions.mdx)** and **[i18n](../tutorial-extras/translate-your-site.mdx)**.
-
-Anything **unclear** or **buggy** in this tutorial? [Please report it!](https://github.com/facebook/docusaurus/discussions/4610)
-
-## What's next?
-
-- Read the [official documentation](https://docusaurus.io/)
-- Modify your site configuration with [`docusaurus.config.js`](https://docusaurus.io/docs/api/docusaurus-config)
-- Add navbar and footer items with [`themeConfig`](https://docusaurus.io/docs/api/themes/configuration)
-- Add a custom [Design and Layout](https://docusaurus.io/docs/styling-layout)
-- Add a [search bar](https://docusaurus.io/docs/search)
-- Find inspirations in the [Docusaurus showcase](https://docusaurus.io/showcase)
-- Get involved in the [Docusaurus Community](https://docusaurus.io/community/support)

+ 0 - 34
docs/tutorial-basics/create-a-blog-post.mdx

@@ -1,34 +0,0 @@
----
-sidebar_position: 3
----
-
-# Create a Blog Post
-
-Docusaurus creates a **page for each blog post**, but also a **blog index page**, a **tag system**, an **RSS** feed...
-
-## Create your first Post
-
-Create a file at `blog/2021-02-28-greetings.md`:
-
-```md title="blog/2021-02-28-greetings.md"
----
-slug: greetings
-title: Greetings!
-authors:
-  - name: Joel Marcey
-    title: Co-creator of Docusaurus 1
-    url: https://github.com/JoelMarcey
-    image_url: https://github.com/JoelMarcey.png
-  - name: Sébastien Lorber
-    title: Docusaurus maintainer
-    url: https://sebastienlorber.com
-    image_url: https://github.com/slorber.png
-tags: [greetings]
----
-
-Congratulations, you have made your first post!
-
-Feel free to play around and edit this post as much as you like.
-```
-
-A new blog post is now available at [http://localhost:3000/blog/greetings](http://localhost:3000/blog/greetings).

+ 0 - 57
docs/tutorial-basics/create-a-document.mdx

@@ -1,57 +0,0 @@
----
-sidebar_position: 2
----
-
-# Create a Document
-
-Documents are **groups of pages** connected through:
-
-- a **sidebar**
-- **previous/next navigation**
-- **versioning**
-
-## Create your first Doc
-
-Create a Markdown file at `docs/hello.md`:
-
-```md title="docs/hello.md"
-# Hello
-
-This is my **first Docusaurus document**!
-```
-
-A new document is now available at [http://localhost:3000/docs/hello](http://localhost:3000/docs/hello).
-
-## Configure the Sidebar
-
-Docusaurus automatically **creates a sidebar** from the `docs` folder.
-
-Add metadata to customize the sidebar label and position:
-
-```md title="docs/hello.md" {1-4}
----
-sidebar_label: 'Hi!'
-sidebar_position: 3
----
-
-# Hello
-
-This is my **first Docusaurus document**!
-```
-
-It is also possible to create your sidebar explicitly in `sidebars.js`:
-
-```js title="sidebars.js"
-export default {
-  tutorialSidebar: [
-    'intro',
-    // highlight-next-line
-    'hello',
-    {
-      type: 'category',
-      label: 'Tutorial',
-      items: ['tutorial-basics/create-a-document'],
-    },
-  ],
-};
-```

+ 0 - 43
docs/tutorial-basics/create-a-page.mdx

@@ -1,43 +0,0 @@
----
-sidebar_position: 1
----
-
-# Create a Page
-
-Add **Markdown or React** files to `src/pages` to create a **standalone page**:
-
-- `src/pages/index.js` → `localhost:3000/`
-- `src/pages/foo.md` → `localhost:3000/foo`
-- `src/pages/foo/bar.js` → `localhost:3000/foo/bar`
-
-## Create your first React Page
-
-Create a file at `src/pages/my-react-page.js`:
-
-```jsx title="src/pages/my-react-page.js"
-import React from 'react';
-import Layout from '@theme/Layout';
-
-export default function MyReactPage() {
-  return (
-    <Layout>
-      <h1>My React page</h1>
-      <p>This is a React page</p>
-    </Layout>
-  );
-}
-```
-
-A new page is now available at [http://localhost:3000/my-react-page](http://localhost:3000/my-react-page).
-
-## Create your first Markdown Page
-
-Create a file at `src/pages/my-markdown-page.md`:
-
-```mdx title="src/pages/my-markdown-page.md"
-# My Markdown page
-
-This is a Markdown page
-```
-
-A new page is now available at [http://localhost:3000/my-markdown-page](http://localhost:3000/my-markdown-page).

+ 0 - 31
docs/tutorial-basics/deploy-your-site.mdx

@@ -1,31 +0,0 @@
----
-sidebar_position: 5
----
-
-# Deploy your site
-
-Docusaurus is a **static-site-generator** (also called **[Jamstack](https://jamstack.org/)**).
-
-It builds your site as simple **static HTML, JavaScript and CSS files**.
-
-## Build your site
-
-Build your site **for production**:
-
-```bash
-npm run build
-```
-
-The static files are generated in the `build` folder.
-
-## Deploy your site
-
-Test your production build locally:
-
-```bash
-npm run serve
-```
-
-The `build` folder is now served at [http://localhost:3000/](http://localhost:3000/).
-
-You can now deploy the `build` folder **almost anywhere** easily, **for free** or very small cost (read the **[Deployment Guide](https://docusaurus.io/docs/deployment)**).

+ 0 - 168
docs/tutorial-basics/markdown-features.mdx

@@ -1,168 +0,0 @@
----
-sidebar_position: 4
----
-
-# Markdown Features
-
-Docusaurus supports **[Markdown](https://daringfireball.net/projects/markdown/syntax)** and a few **additional features**.
-
-## Front Matter
-
-Markdown documents have metadata at the top called [Front Matter](https://jekyllrb.com/docs/front-matter/):
-
-```text title="my-doc.md"
-// highlight-start
----
-id: my-doc-id
-title: My document title
-description: My document description
-slug: /my-custom-url
----
-// highlight-end
-
-Markdown content
-```
-
-## Headings {/* #my-heading-id */}
-
-Markdown headings are supported using the standard “#” syntax and are automatically added to the table of contents. The number of `#` corresponds to the heading level.
-
-```md
-## Headings
-
-My text
-```
-
-### Heading Ids {/* #my-custom-id */}
-
-Add `{/* #my-custom-id */}` after the heading text to assign it an explicit anchor id, used for linking.
-
-```md
-### Heading Ids {/_ #my-custom-id _/}
-```
-
-## Links
-
-Regular Markdown links are supported, using url paths or relative file paths.
-
-```md
-Let's see how to [Create a page](/create-a-page).
-```
-
-```md
-Let's see how to [Create a page](./create-a-page.mdx).
-```
-
-**Result:** Let's see how to [Create a page](./create-a-page.mdx).
-
-## Images
-
-Regular Markdown images are supported.
-
-You can use absolute paths to reference images in the static directory (`static/img/docusaurus.png`):
-
-```md
-![Docusaurus logo](/img/docusaurus.png)
-```
-
-![Docusaurus logo](/img/docusaurus.png)
-
-You can reference images relative to the current file as well. This is particularly useful to colocate images close to the Markdown files using them:
-
-```md
-![Docusaurus logo](./img/docusaurus.png)
-```
-
-## Code Blocks
-
-Markdown code blocks are supported with Syntax highlighting.
-
-````md
-```jsx title="src/components/HelloDocusaurus.js"
-function HelloDocusaurus() {
-  return <h1>Hello, Docusaurus!</h1>;
-}
-```
-````
-
-```jsx title="src/components/HelloDocusaurus.js"
-function HelloDocusaurus() {
-  return <h1>Hello, Docusaurus!</h1>;
-}
-```
-
-## Admonitions
-
-Docusaurus has a special syntax to create admonitions and callouts:
-
-```md
-:::tip[My tip]
-
-Use this awesome feature option
-
-:::
-
-:::danger[Take care]
-
-This action is dangerous
-
-:::
-```
-
-:::tip[My tip]
-
-Use this awesome feature option
-
-:::
-
-:::danger[Take care]
-
-This action is dangerous
-
-:::
-
-## MDX and React Components
-
-[MDX](https://mdxjs.com/) can make your documentation more **interactive** and allows using any **React components inside Markdown**:
-
-```jsx
-export const Highlight = ({children, color}) => (
-  <span
-    style={{
-      backgroundColor: color,
-      borderRadius: '20px',
-      color: '#fff',
-      padding: '10px',
-      cursor: 'pointer',
-    }}
-    onClick={() => {
-      alert(`You clicked the color ${color} with label ${children}`)
-    }}>
-    {children}
-  </span>
-);
-
-This is <Highlight color="#25c2a0">Docusaurus green</Highlight> !
-
-This is <Highlight color="#1877F2">Facebook blue</Highlight> !
-```
-
-export const Highlight = ({children, color}) => (
-  <span
-    style={{
-      backgroundColor: color,
-      borderRadius: '20px',
-      color: '#fff',
-      padding: '10px',
-      cursor: 'pointer',
-    }}
-    onClick={() => {
-      alert(`You clicked the color ${color} with label ${children}`);
-    }}>
-    {children}
-  </span>
-);
-
-This is <Highlight color="#25c2a0">Docusaurus green</Highlight> !
-
-This is <Highlight color="#1877F2">Facebook blue</Highlight> !

+ 0 - 7
docs/tutorial-extras/_category_.json

@@ -1,7 +0,0 @@
-{
-  "label": "Tutorial - Extras",
-  "position": 3,
-  "link": {
-    "type": "generated-index"
-  }
-}

BIN
docs/tutorial-extras/img/docsVersionDropdown.png


BIN
docs/tutorial-extras/img/localeDropdown.png


+ 0 - 55
docs/tutorial-extras/manage-docs-versions.mdx

@@ -1,55 +0,0 @@
----
-sidebar_position: 1
----
-
-# Manage Docs Versions
-
-Docusaurus can manage multiple versions of your docs.
-
-## Create a docs version
-
-Release a version 1.0 of your project:
-
-```bash
-npm run docusaurus docs:version 1.0
-```
-
-The `docs` folder is copied into `versioned_docs/version-1.0` and `versions.json` is created.
-
-Your docs now have 2 versions:
-
-- `1.0` at `http://localhost:3000/docs/` for the version 1.0 docs
-- `current` at `http://localhost:3000/docs/next/` for the **upcoming, unreleased docs**
-
-## Add a Version Dropdown
-
-To navigate seamlessly across versions, add a version dropdown.
-
-Modify the `docusaurus.config.js` file:
-
-```js title="docusaurus.config.js"
-export default {
-  themeConfig: {
-    navbar: {
-      items: [
-        // highlight-start
-        {
-          type: 'docsVersionDropdown',
-        },
-        // highlight-end
-      ],
-    },
-  },
-};
-```
-
-The docs version dropdown appears in your navbar:
-
-![Docs Version Dropdown](./img/docsVersionDropdown.png)
-
-## Update an existing version
-
-It is possible to edit versioned docs in their respective folder:
-
-- `versioned_docs/version-1.0/hello.md` updates `http://localhost:3000/docs/hello`
-- `docs/hello.md` updates `http://localhost:3000/docs/next/hello`

+ 0 - 88
docs/tutorial-extras/translate-your-site.mdx

@@ -1,88 +0,0 @@
----
-sidebar_position: 2
----
-
-# Translate your site
-
-Let's translate `docs/intro.md` to French.
-
-## Configure i18n
-
-Modify `docusaurus.config.js` to add support for the `fr` locale:
-
-```js title="docusaurus.config.js"
-export default {
-  i18n: {
-    defaultLocale: 'en',
-    locales: ['en', 'fr'],
-  },
-};
-```
-
-## Translate a doc
-
-Copy the `docs/intro.md` file to the `i18n/fr` folder:
-
-```bash
-mkdir -p i18n/fr/docusaurus-plugin-content-docs/current/
-
-cp docs/intro.md i18n/fr/docusaurus-plugin-content-docs/current/intro.md
-```
-
-Translate `i18n/fr/docusaurus-plugin-content-docs/current/intro.md` in French.
-
-## Start your localized site
-
-Start your site on the French locale:
-
-```bash
-npm run start -- --locale fr
-```
-
-Your localized site is accessible at [http://localhost:3000/fr/](http://localhost:3000/fr/) and the `Getting Started` page is translated.
-
-:::caution
-
-In development, you can only use one locale at a time.
-
-:::
-
-## Add a Locale Dropdown
-
-To navigate seamlessly across languages, add a locale dropdown.
-
-Modify the `docusaurus.config.js` file:
-
-```js title="docusaurus.config.js"
-export default {
-  themeConfig: {
-    navbar: {
-      items: [
-        // highlight-start
-        {
-          type: 'localeDropdown',
-        },
-        // highlight-end
-      ],
-    },
-  },
-};
-```
-
-The locale dropdown now appears in your navbar:
-
-![Locale Dropdown](./img/localeDropdown.png)
-
-## Build your localized site
-
-Build your site for a specific locale:
-
-```bash
-npm run build -- --locale fr
-```
-
-Or build your site to include all the locales at once:
-
-```bash
-npm run build
-```

+ 52 - 0
documentation/add-article.md

@@ -0,0 +1,52 @@
+---
+description: How to add a chronological article inside the Educação section. Blog plugin is disabled — articles live as docs entries with a date in frontmatter.
+load-when: Publishing a dated article (e.g. an essay or analysis piece) on the site.
+---
+
+# Add an article
+
+<scope>
+Articles share the docs plugin with guides — the Docusaurus blog plugin is disabled in this project. Use date frontmatter so articles can be ordered chronologically when listed.
+</scope>
+
+<instructions>
+<step n="1">
+**Pick a slug + date.** Slug: kebab-case, descriptive (e.g. `como-claude-code-mudou-meu-fluxo`). Date: ISO `YYYY-MM-DD`.
+</step>
+
+<step n="2">
+**Create the file.** `docs/articles/<YYYY-MM-DD>-<slug>.md` (folder keeps articles grouped). Frontmatter:
+
+```markdown
+---
+title: Título do artigo
+description: Resumo de uma linha.
+date: 2026-05-09
+tags: [claude-code, fluxo]
+---
+
+# Título do artigo
+
+Texto do artigo.
+```
+</step>
+
+<step n="3">
+**Sidebar position.** Articles sort newest-first inside the `articles/` category by default. Override with `sidebar_position` in frontmatter only if you need a specific order.
+</step>
+
+<step n="4">
+**Update `static/llms.txt`.** Add a line under a `## Artigos` section (create the section the first time): `- [<title>](https://gazperi.com/educacao/articles/<file>.md) (<date>): <one-line summary>`.
+</step>
+</instructions>
+
+<conventions>
+- Articles stay in `docs/articles/`. Other guides keep their own structure.
+- Date frontmatter is informational; sort order is filename-based (so the `<YYYY-MM-DD>-` prefix matters).
+- If RSS / Atom feed is needed later, re-enable the blog plugin in a separate spec — do not silently flip it on here.
+</conventions>
+
+<antipattern>
+- Creating an `articles/` entry without date prefix — sort order becomes unpredictable.
+- Mixing articles into the top-level `docs/` flat list — clutters the sidebar.
+</antipattern>

+ 55 - 0
documentation/add-guide.md

@@ -0,0 +1,55 @@
+---
+description: How to add a new guide to the Educação section.
+load-when: Adding a guide / how-to / reference page under /educacao.
+---
+
+# Add a guide
+
+<scope>
+Guides live under `docs/`. Each guide is a single Markdown file. Sidebar autogenerates from the folder structure.
+</scope>
+
+<instructions>
+<step n="1">
+**Decide the slug.** Pick a kebab-case slug for the URL (e.g. `claude-code-overview`). The file becomes `docs/<slug>.md` and the URL becomes `/educacao/<slug>`.
+</step>
+
+<step n="2">
+**Create the file.** `docs/<slug>.md` with frontmatter:
+
+```markdown
+---
+title: Título do guia
+description: Resumo de uma linha para meta description e cards sociais.
+---
+
+# Título do guia
+
+Conteúdo em Markdown.
+```
+
+For nested guides, group under a folder (e.g. `docs/claude-code/getting-started.md`). Each folder becomes a sidebar category.
+</step>
+
+<step n="3">
+**Verify locally.** `npm run start`. Open `/educacao/<slug>` and confirm the guide renders, the sidebar entry appears, and any code blocks highlight.
+</step>
+
+<step n="4">
+**Update `static/llms.txt`.** Add a line under `## Educação` linking to the new entry's `.md` source: `- [<title>](https://gazperi.com/educacao/<slug>.md): <one-line summary>`. Keeps LLM index in sync.
+</step>
+</instructions>
+
+<conventions>
+- Markdown only. No MDX unless the guide needs React components — and if it does, use `.mdx` instead of `.md`.
+- No HTML in the body unless necessary.
+- Title in frontmatter and in the H1 should match.
+- Description ≤ 160 chars (fits meta description and OG description).
+- Voice: PT-BR, follow `DOCS/brand-voice/` rules.
+</conventions>
+
+<antipattern>
+- Skipping the `description` frontmatter — falls back to a generated snippet that often misrepresents the page in social previews.
+- Forgetting to update `static/llms.txt` — site stays browser-readable but the LLM index drifts.
+- Creating a `.mdx` file when plain `.md` would do — adds JSX parser overhead with no payoff.
+</antipattern>

+ 68 - 0
documentation/add-section.md

@@ -0,0 +1,68 @@
+---
+description: How to add a new top-level section to the Educação area (a folder with multiple guides + a category landing page).
+load-when: Creating a new content category under /educacao (e.g. "Claude Code", "Método", "Materiais").
+---
+
+# Add a section
+
+<scope>
+A section = a folder under `docs/` with its own category landing page and one or more child entries. Sidebar autogenerates the category from the folder.
+</scope>
+
+<instructions>
+<step n="1">
+**Create the folder.** `docs/<section-slug>/`. Slug kebab-case.
+</step>
+
+<step n="2">
+**Add the category landing page.** `docs/<section-slug>/index.md` (or `_category_.json` for advanced control). Recommended:
+
+```markdown
+---
+title: Nome da seção
+description: Frase explicando o escopo da seção.
+slug: /<section-slug>
+---
+
+# Nome da seção
+
+O que está aqui, para quem, e como navegar.
+
+- [Primeiro guia](./<first-guide>)
+- [Segundo guia](./<second-guide>)
+```
+
+URL: `/educacao/<section-slug>`.
+</step>
+
+<step n="3">
+**Add child entries** following `add-guide.md`.
+</step>
+
+<step n="4">
+**Order the section** in the sidebar by giving the folder an `_category_.json`:
+
+```json
+{
+  "label": "Nome da seção",
+  "position": 2,
+  "link": {
+    "type": "doc",
+    "id": "<section-slug>/index"
+  }
+}
+```
+
+Lower `position` = higher in the sidebar.
+</step>
+
+<step n="5">
+**Update `static/llms.txt`.** Add a `## <Nome da seção>` block listing each child entry's `.md` URL.
+</step>
+</instructions>
+
+<conventions>
+- Section slugs in URL kebab-case, in PT-BR.
+- Each section has a landing page — never an empty category.
+- Two levels of nesting maximum (`docs/<section>/<sub-section>/`). Deeper trees hurt navigation.
+</conventions>

+ 49 - 0
documentation/brand-tokens.md

@@ -0,0 +1,49 @@
+---
+description: How brand tokens (colors, fonts) map onto Infima CSS variables in this project. Source of truth is ASSETS/brand/CLAUDE.md.
+load-when: Adjusting site colors, fonts, or any visual token.
+---
+
+# Brand tokens → Infima
+
+<scope>
+This project styles itself by overriding Infima CSS variables in `src/css/custom.css`. Brand source of truth lives in `/Users/gazperi/Developer/ASSETS/brand/CLAUDE.md`. Do not invent tokens here — pull values from there.
+</scope>
+
+<conventions>
+## Color tokens
+
+| Brand token       | Value     | Infima var                                  |
+| ----------------- | --------- | ------------------------------------------- |
+| `--gazperi-bg`    | `#f5f0e8` | `--ifm-background-color`                    |
+| `--gazperi-ink`   | `#1a1918` | `--ifm-color-content`                       |
+| `--gazperi-accent`| `#b85c38` | `--ifm-color-primary` (+ dark/light scales) |
+| `--gazperi-ink-muted` | `rgba(26, 25, 24, 0.55)` | `--ifm-color-content-secondary` |
+| `--gazperi-rule`  | `rgba(26, 25, 24, 0.15)` | `--ifm-toc-border-color`        |
+
+Dark-mode variant lives in `[data-theme='dark']` and inverts `--gazperi-bg` / `--gazperi-ink`. Accent scales lighten in dark mode for readability.
+
+## Font tokens
+
+| Layer        | Font            | Files in `static/fonts/`                                | Infima var                  |
+| ------------ | --------------- | ------------------------------------------------------- | --------------------------- |
+| Wordmark     | Share Tech 400  | `ShareTech-Regular.ttf`                                 | (used in `.navbar__brand`)  |
+| Headings     | Spectral 600    | `Spectral-SemiBold.ttf`, `Spectral-SemiBoldItalic.ttf`  | `--ifm-heading-font-family` |
+| Body         | Source Serif 4  | `SourceSerif4-Regular.ttf`, `SourceSerif4-Italic.ttf`   | `--ifm-font-family-base`    |
+| UI / meta    | Outfit 400 / 500| `Outfit-Regular.ttf`, `Outfit-Medium.ttf`               | `--ifm-font-family-monospace` (overloaded) and inline component classes |
+
+Fonts are loaded via `@font-face` in `custom.css` from `/fonts/*.ttf` (served from `static/fonts/`). No external CDN.
+</conventions>
+
+<instructions>
+<step n="1">
+**Adding a new color.** Update `ASSETS/brand/CLAUDE.md` first if the token is brand-level. Then add a `--gazperi-*` var in `:root` and `[data-theme='dark']` in `src/css/custom.css`. Map to an Infima var only if Docusaurus components consume that var.
+</step>
+
+<step n="2">
+**Adding a new font.** Drop the TTF/WOFF2 in `static/fonts/`. Add an `@font-face` block at the top of `src/css/custom.css`. Reference via `font-family` in component CSS.
+</step>
+
+<step n="3">
+**Changing accent color.** Update both light and dark scales (`--ifm-color-primary` + `-dark`, `-darker`, `-darkest`, `-light`, `-lighter`, `-lightest`) for visual consistency. Use a tool like `https://docusaurus.io/docs/styling-layout#styling-your-site-with-infima` to generate the scale.
+</step>
+</instructions>

+ 64 - 0
documentation/cloudflare-deploy.md

@@ -0,0 +1,64 @@
+---
+description: Cloudflare Pages deploy procedure for gazperi.com — Wrangler from local repo, custom domain, rollback.
+load-when: Deploying the site, debugging a deploy failure, or rotating Cloudflare access.
+---
+
+# Cloudflare deploy
+
+<scope>
+Hosting: Cloudflare Pages, project name `gazperi-com`. Domain `gazperi.com` is on Cloudflare nameservers (registrar: Hostinger). Deploys are triggered manually from the local repo via Wrangler — no CI, no GitHub remote.
+</scope>
+
+<conventions>
+## One-time setup
+
+| Item                              | Where                                                        |
+| --------------------------------- | ------------------------------------------------------------ |
+| Cloudflare account                | https://dash.cloudflare.com                                   |
+| Pages project                     | `gazperi-com` (created via dashboard or `wrangler pages project create gazperi-com`). |
+| Wrangler login                    | `npx wrangler login` once per machine.                        |
+| Custom domain                     | Bound via Pages → Custom Domains → `gazperi.com` and `www.gazperi.com`. |
+| DNS                               | `gazperi.com` zone managed in Cloudflare; nameservers set at Hostinger. |
+| Environment variables             | Pages → Settings → Environment Variables: `POSTHOG_KEY`, `POSTHOG_HOST` (production + preview). |
+</conventions>
+
+<instructions>
+<step n="1">
+**Build locally.** `cd /Users/gazperi/Developer/SITE/site-com && npm run build`. Confirm `build/` is fresh and `npm run serve` smoke-passes.
+</step>
+
+<step n="2">
+**Deploy.** `npx wrangler pages deploy build --project-name=gazperi-com`. Wrangler prints a `*.pages.dev` preview URL (current deploy) and the production URL. Visit the preview URL first to verify the build before traffic flips.
+</step>
+
+<step n="3">
+**Promote to production.** Cloudflare Pages auto-promotes the latest deploy to production. To deploy to a named branch instead: `--branch=preview` (or other). Production lives on `main`-equivalent default branch alias.
+</step>
+
+<step n="4">
+**Verify live.** `curl -I https://gazperi.com` returns 200 and the right headers. Open the site, click Educação, confirm the docs route works, confirm PostHog events fire.
+</step>
+
+<step n="5">
+**Rollback.** Cloudflare Pages → project → Deployments → choose a previous successful deploy → "Rollback to this deployment". Or redeploy a previous local build state.
+</step>
+</instructions>
+
+<conventions>
+## DNS cutover (one-time, when migrating from another host)
+
+1. In Cloudflare, add the `gazperi.com` zone. Cloudflare scans existing DNS records; review and accept.
+2. In Hostinger console, change the domain's nameservers to the two Cloudflare values shown.
+3. Wait for propagation (`dig NS gazperi.com` — should return `ns1.cloudflare.com` / similar). Typically minutes; can be hours.
+4. Bind `gazperi.com` and `www.gazperi.com` (CNAME redirect to apex) to the Pages project under Custom Domains.
+5. Cloudflare provisions SSL automatically. Verify HTTPS works.
+6. The previous host (Netlify) remains live until propagation completes — no downtime if records are correct.
+
+**Rollback the cutover:** revert nameservers at Hostinger to the previous values. Domain returns to the old host within propagation window.
+</conventions>
+
+<antipattern>
+- `wrangler pages deploy` without `--project-name` — picks an interactive prompt, fragile in scripts.
+- Editing DNS records directly in Cloudflare while nameservers still point at Hostinger — changes have no effect.
+- Skipping the preview-URL check before promoting to production.
+</antipattern>

+ 35 - 0
documentation/dev-build-deploy.md

@@ -0,0 +1,35 @@
+---
+description: Local dev, production build, preview, and deploy commands for the Docusaurus site.
+load-when: Running, building, or deploying the site locally.
+---
+
+# Dev / Build / Deploy
+
+<scope>
+Day-to-day commands for the Docusaurus project at `SITE/site-com/`. Project uses npm. Node ≥ 18.
+</scope>
+
+<instructions>
+<step n="1">
+**Local dev server.** From `SITE/site-com/`: `npm run start`. Opens `http://localhost:3000`. Hot reload on save. Routes available: `/` (landing), `/educacao` (docs index), `/educacao/<slug>` per docs entry.
+</step>
+
+<step n="2">
+**Production build.** `npm run build`. Output in `build/`. Strict link checking is on (`onBrokenLinks: 'throw'`); broken internal links fail the build. Sitemap auto-generated at `build/sitemap.xml`. `robots.txt`, `llms.txt`, font files, and icons copied from `static/`. Raw `.md` for every docs entry copied to `build/educacao/*.md` by the `copy-md` plugin.
+</step>
+
+<step n="3">
+**Preview built site.** `npm run serve`. Serves `build/` on port 3000. Use this to smoke-test before any deploy.
+</step>
+
+<step n="4">
+**Deploy.** `npm run build && wrangler pages deploy build --project-name=gazperi-com`. See `cloudflare-deploy.md` for the full deploy procedure.
+</step>
+</instructions>
+
+<conventions>
+- Node version: 18+ (Docusaurus v3 requirement). Use `nvm use 20` or system Node.
+- Package manager: npm (locked via `package-lock.json`).
+- Dev server port: 3000 default; override with `--port`.
+- Build output: `build/` — gitignored, regenerated on every build.
+</conventions>

+ 56 - 0
documentation/posthog-events.md

@@ -0,0 +1,56 @@
+---
+description: PostHog analytics setup, env vars, and event taxonomy for the site.
+load-when: Adding a new analytics event, debugging missing events, or rotating the PostHog project key.
+---
+
+# PostHog events
+
+<scope>
+Region: PostHog EU cloud (`https://eu.i.posthog.com`). Client-side only — no server-side capture in this project. Initialization lives in a custom theme module loaded via `clientModules` in `docusaurus.config.ts`.
+</scope>
+
+<conventions>
+## Environment variables
+
+| Var              | Purpose                                  |
+| ---------------- | ---------------------------------------- |
+| `POSTHOG_KEY`    | Project key (`phc_…`).                   |
+| `POSTHOG_HOST`   | `https://eu.i.posthog.com` (EU region).  |
+
+Lives in `.env.local` for dev. For production deploys, set both as Cloudflare Pages environment variables on the project.
+
+## Event taxonomy
+
+| Event                  | When                                            | Properties                                  |
+| ---------------------- | ----------------------------------------------- | ------------------------------------------- |
+| `$pageview`            | Auto on every navigation.                       | `$current_url`, `$pathname`, `$referrer`.   |
+| `outbound_link_clicked`| Click on `a[href]` whose host is not gazperi.com| `href`, `text`, `pathname` (origin page).   |
+| `internal_nav_clicked` | Click on internal `<a>` (Docusaurus `<Link>`).  | `to`, `text`, `pathname`.                   |
+| `docs_entry_visited`   | Route change into `/educacao/*`.                | `slug`, `title`, `referrer_pathname`.       |
+
+No personal data captured. No `identify()` call. Anonymous device-level analytics only.
+</conventions>
+
+<instructions>
+<step n="1">
+**Add a new event.** Edit the PostHog client module (`src/posthog/init.ts` or wherever the listeners live). Use `posthog.capture('event_name', {key: value})`. Keep names snake_case.
+</step>
+
+<step n="2">
+**Test locally.** `npm run start`, open the page, trigger the action. Check PostHog Live Events panel. Events should appear within ~5s.
+</step>
+
+<step n="3">
+**Add to dashboard.** In PostHog UI → "Site — starter" dashboard, add an Insight on the new event. Pin to dashboard.
+</step>
+
+<step n="4">
+**Rotate the project key.** Generate a new key in PostHog. Update `.env.local` for dev and the Cloudflare Pages env var for production. Redeploy.
+</step>
+</instructions>
+
+<antipattern>
+- Capturing PII (email, full name) in event properties — violates LGPD posture for this project.
+- Adding events without updating this file's taxonomy table — dashboard drifts from code.
+- Using the US PostHog host (`app.posthog.com`) — project is EU-only.
+</antipattern>

+ 63 - 0
documentation/seo-and-llms.md

@@ -0,0 +1,63 @@
+---
+description: SEO baseline (robots, sitemap, meta, OG, JSON-LD) and LLM-discoverability baseline (llms.txt, raw .md exposure, AI-bot policy) for the site.
+load-when: Updating any SEO surface or LLM-serving artifact.
+---
+
+# SEO + LLM serving
+
+<scope>
+Two adjacent surfaces: classic SEO for search engines, and LLM-discoverability for AI agents that read sites. This file covers both.
+</scope>
+
+<conventions>
+## SEO surfaces
+
+| Surface                    | Where                                                       | Owner                                       |
+| -------------------------- | ----------------------------------------------------------- | ------------------------------------------- |
+| `robots.txt`               | `static/robots.txt`                                         | Hand-written; references sitemap.           |
+| Sitemap                    | Generated → `build/sitemap.xml`                             | `@docusaurus/plugin-sitemap` (classic preset). |
+| Per-page meta              | `themeConfig.metadata` in `docusaurus.config.ts` + per-doc frontmatter | Default tags + page-level overrides. |
+| OG / Twitter card image    | `static/img/og-default.png`                                  | Replace with branded 1200×630 image.        |
+| JSON-LD `Organization`     | `headTags` in `docusaurus.config.ts`                         | Hand-edited.                                |
+| JSON-LD `Article`          | Per-doc — not yet wired                                      | Add when content production starts.         |
+| 404 page                   | `src/pages/404.tsx`                                          | Brand-styled.                               |
+| App icons                  | `static/img/icons/`                                          | `apple-touch-icon`, `icon-192`, `icon-512`. |
+| Web manifest               | `static/img/icons/site.webmanifest`                          | Hand-edited.                                |
+
+## LLM surfaces
+
+| Surface                    | Where                                                       | Notes                                                  |
+| -------------------------- | ----------------------------------------------------------- | ------------------------------------------------------ |
+| `llms.txt`                 | `static/llms.txt`                                            | Follows llmstxt.org schema.                            |
+| Raw `.md` exposure         | Generated → `build/educacao/*.md`                            | `plugins/copy-md/` copies docs sources into `build/`.  |
+| AI-bot allow directives    | `static/robots.txt`                                          | Explicit `Allow:` for GPTBot, ClaudeBot, Google-Extended, PerplexityBot. |
+</conventions>
+
+<instructions>
+<step n="1">
+**Per-page meta.** Default site-level tags live in `themeConfig.metadata` (`docusaurus.config.ts`). For a docs entry, override via frontmatter (`title`, `description`, `image`).
+</step>
+
+<step n="2">
+**Update OG default image.** Replace `static/img/og-default.png` with a 1200×630 branded card. Reference path is set in `themeConfig.image` — no code change needed.
+</step>
+
+<step n="3">
+**Add `Article` JSON-LD per doc.** When a docs entry is substantive enough to warrant rich-result candidacy, add a `<head>` block via Docusaurus's per-doc head injection (or via a swizzle of `DocItem`).
+</step>
+
+<step n="4">
+**Update `llms.txt` after every new entry.** Add the new entry under the matching `## Section` block with a link to the `.md` URL. The `copy-md` plugin auto-publishes the raw `.md` — no additional step needed.
+</step>
+
+<step n="5">
+**Change the AI-bot policy.** Edit `static/robots.txt` directly. Allow / Disallow per User-agent. Re-deploy. No config change needed — it is a static file.
+</step>
+</instructions>
+
+<antipattern>
+- Generating sitemap manually — the plugin handles it; manual edits get overwritten.
+- Hardcoding OG image per page when one default suffices — over-engineering for a foundation site.
+- Forgetting to update `llms.txt` after a new docs entry — site stays browser-readable but the LLM index drifts.
+- Removing the `copy-md` plugin without updating `llms.txt` URLs — `.md` URLs would 404.
+</antipattern>

+ 76 - 20
docusaurus.config.ts

@@ -15,30 +15,86 @@ const config: Config = {
   baseUrl: '/',
 
   onBrokenLinks: 'throw',
-  onBrokenMarkdownLinks: 'warn',
+  markdown: {
+    hooks: {
+      onBrokenMarkdownLinks: 'warn',
+    },
+  },
 
   i18n: {
     defaultLocale: 'pt-BR',
     locales: ['pt-BR'],
   },
 
+  headTags: [
+    {
+      tagName: 'link',
+      attributes: {
+        rel: 'manifest',
+        href: '/img/icons/site.webmanifest',
+      },
+    },
+    {
+      tagName: 'link',
+      attributes: {
+        rel: 'apple-touch-icon',
+        sizes: '180x180',
+        href: '/img/icons/apple-touch-icon.png',
+      },
+    },
+    {
+      tagName: 'meta',
+      attributes: {
+        name: 'theme-color',
+        content: '#b85c38',
+      },
+    },
+    {
+      tagName: 'script',
+      attributes: {
+        type: 'application/ld+json',
+      },
+      innerHTML: JSON.stringify({
+        '@context': 'https://schema.org',
+        '@type': 'Organization',
+        name: 'Gazperi',
+        alternateName: 'Alfredo Gazperi',
+        url: 'https://gazperi.com',
+        logo: 'https://gazperi.com/img/logo-light.svg',
+        sameAs: [
+          'https://linkedin.com/in/gazperi',
+        ],
+        founder: {
+          '@type': 'Person',
+          name: 'Alfredo Gazperi',
+          email: 'alfredo@gazperi.com',
+          jobTitle: 'Educador e consultor em IA',
+        },
+      }),
+    },
+  ],
+
+  clientModules: ['./src/posthog/init.ts'],
+
+  plugins: [
+    [
+      './plugins/copy-md/index.ts',
+      {
+        sourceDir: 'docs',
+        routeBasePath: 'educacao',
+      },
+    ],
+  ],
+
   presets: [
     [
       'classic',
       {
         docs: {
           sidebarPath: './sidebars.ts',
+          routeBasePath: '/educacao',
         },
-        blog: {
-          showReadingTime: true,
-          feedOptions: {
-            type: ['rss', 'atom'],
-            xslt: true,
-          },
-          onInlineTags: 'warn',
-          onInlineAuthors: 'warn',
-          onUntruncatedBlogPosts: 'warn',
-        },
+        blog: false,
         theme: {
           customCss: './src/css/custom.css',
         },
@@ -48,6 +104,14 @@ const config: Config = {
 
   themeConfig: {
     image: 'img/og-default.png',
+    metadata: [
+      {name: 'description', content: 'Alfredo Gazperi — consultoria e educação no novo mundo de trabalho com IA. Foco em Claude e Claude Code para empresas e colaboradores.'},
+      {name: 'keywords', content: 'IA, Claude, Claude Code, Anthropic, consultoria, educação, Alfredo Gazperi'},
+      {property: 'og:type', content: 'website'},
+      {property: 'og:locale', content: 'pt_BR'},
+      {property: 'og:site_name', content: 'Gazperi'},
+      {name: 'twitter:card', content: 'summary_large_image'},
+    ],
     colorMode: {
       respectPrefersColorScheme: true,
     },
@@ -58,15 +122,7 @@ const config: Config = {
         src: 'img/logo-light.svg',
         srcDark: 'img/logo-dark.svg',
       },
-      items: [
-        {
-          type: 'docSidebar',
-          sidebarId: 'tutorialSidebar',
-          position: 'left',
-          label: 'Tutorial',
-        },
-        {to: '/blog', label: 'Blog', position: 'left'},
-      ],
+      items: [],
     },
     footer: {
       style: 'light',

+ 421 - 0
package-lock.json

@@ -13,6 +13,7 @@
         "@docusaurus/preset-classic": "3.10.1",
         "@mdx-js/react": "^3.0.0",
         "clsx": "^2.0.0",
+        "posthog-js": "^1.372.10",
         "prism-react-renderer": "^2.3.0",
         "react": "^19.0.0",
         "react-dom": "^19.0.0"
@@ -4845,6 +4846,252 @@
         "node": ">= 8"
       }
     },
+    "node_modules/@opentelemetry/api": {
+      "version": "1.9.1",
+      "resolved": "https://registry.npmjs.org/@opentelemetry/api/-/api-1.9.1.tgz",
+      "integrity": "sha512-gLyJlPHPZYdAk1JENA9LeHejZe1Ti77/pTeFm/nMXmQH/HFZlcS/O2XJB+L8fkbrNSqhdtlvjBVjxwUYanNH5Q==",
+      "license": "Apache-2.0",
+      "engines": {
+        "node": ">=8.0.0"
+      }
+    },
+    "node_modules/@opentelemetry/api-logs": {
+      "version": "0.208.0",
+      "resolved": "https://registry.npmjs.org/@opentelemetry/api-logs/-/api-logs-0.208.0.tgz",
+      "integrity": "sha512-CjruKY9V6NMssL/T1kAFgzosF1v9o6oeN+aX5JB/C/xPNtmgIJqcXHG7fA82Ou1zCpWGl4lROQUKwUNE1pMCyg==",
+      "license": "Apache-2.0",
+      "dependencies": {
+        "@opentelemetry/api": "^1.3.0"
+      },
+      "engines": {
+        "node": ">=8.0.0"
+      }
+    },
+    "node_modules/@opentelemetry/core": {
+      "version": "2.2.0",
+      "resolved": "https://registry.npmjs.org/@opentelemetry/core/-/core-2.2.0.tgz",
+      "integrity": "sha512-FuabnnUm8LflnieVxs6eP7Z383hgQU4W1e3KJS6aOG3RxWxcHyBxH8fDMHNgu/gFx/M2jvTOW/4/PHhLz6bjWw==",
+      "license": "Apache-2.0",
+      "dependencies": {
+        "@opentelemetry/semantic-conventions": "^1.29.0"
+      },
+      "engines": {
+        "node": "^18.19.0 || >=20.6.0"
+      },
+      "peerDependencies": {
+        "@opentelemetry/api": ">=1.0.0 <1.10.0"
+      }
+    },
+    "node_modules/@opentelemetry/exporter-logs-otlp-http": {
+      "version": "0.208.0",
+      "resolved": "https://registry.npmjs.org/@opentelemetry/exporter-logs-otlp-http/-/exporter-logs-otlp-http-0.208.0.tgz",
+      "integrity": "sha512-jOv40Bs9jy9bZVLo/i8FwUiuCvbjWDI+ZW13wimJm4LjnlwJxGgB+N/VWOZUTpM+ah/awXeQqKdNlpLf2EjvYg==",
+      "license": "Apache-2.0",
+      "dependencies": {
+        "@opentelemetry/api-logs": "0.208.0",
+        "@opentelemetry/core": "2.2.0",
+        "@opentelemetry/otlp-exporter-base": "0.208.0",
+        "@opentelemetry/otlp-transformer": "0.208.0",
+        "@opentelemetry/sdk-logs": "0.208.0"
+      },
+      "engines": {
+        "node": "^18.19.0 || >=20.6.0"
+      },
+      "peerDependencies": {
+        "@opentelemetry/api": "^1.3.0"
+      }
+    },
+    "node_modules/@opentelemetry/otlp-exporter-base": {
+      "version": "0.208.0",
+      "resolved": "https://registry.npmjs.org/@opentelemetry/otlp-exporter-base/-/otlp-exporter-base-0.208.0.tgz",
+      "integrity": "sha512-gMd39gIfVb2OgxldxUtOwGJYSH8P1kVFFlJLuut32L6KgUC4gl1dMhn+YC2mGn0bDOiQYSk/uHOdSjuKp58vvA==",
+      "license": "Apache-2.0",
+      "dependencies": {
+        "@opentelemetry/core": "2.2.0",
+        "@opentelemetry/otlp-transformer": "0.208.0"
+      },
+      "engines": {
+        "node": "^18.19.0 || >=20.6.0"
+      },
+      "peerDependencies": {
+        "@opentelemetry/api": "^1.3.0"
+      }
+    },
+    "node_modules/@opentelemetry/otlp-transformer": {
+      "version": "0.208.0",
+      "resolved": "https://registry.npmjs.org/@opentelemetry/otlp-transformer/-/otlp-transformer-0.208.0.tgz",
+      "integrity": "sha512-DCFPY8C6lAQHUNkzcNT9R+qYExvsk6C5Bto2pbNxgicpcSWbe2WHShLxkOxIdNcBiYPdVHv/e7vH7K6TI+C+fQ==",
+      "license": "Apache-2.0",
+      "dependencies": {
+        "@opentelemetry/api-logs": "0.208.0",
+        "@opentelemetry/core": "2.2.0",
+        "@opentelemetry/resources": "2.2.0",
+        "@opentelemetry/sdk-logs": "0.208.0",
+        "@opentelemetry/sdk-metrics": "2.2.0",
+        "@opentelemetry/sdk-trace-base": "2.2.0",
+        "protobufjs": "^7.3.0"
+      },
+      "engines": {
+        "node": "^18.19.0 || >=20.6.0"
+      },
+      "peerDependencies": {
+        "@opentelemetry/api": "^1.3.0"
+      }
+    },
+    "node_modules/@opentelemetry/otlp-transformer/node_modules/@opentelemetry/resources": {
+      "version": "2.2.0",
+      "resolved": "https://registry.npmjs.org/@opentelemetry/resources/-/resources-2.2.0.tgz",
+      "integrity": "sha512-1pNQf/JazQTMA0BiO5NINUzH0cbLbbl7mntLa4aJNmCCXSj0q03T5ZXXL0zw4G55TjdL9Tz32cznGClf+8zr5A==",
+      "license": "Apache-2.0",
+      "dependencies": {
+        "@opentelemetry/core": "2.2.0",
+        "@opentelemetry/semantic-conventions": "^1.29.0"
+      },
+      "engines": {
+        "node": "^18.19.0 || >=20.6.0"
+      },
+      "peerDependencies": {
+        "@opentelemetry/api": ">=1.3.0 <1.10.0"
+      }
+    },
+    "node_modules/@opentelemetry/resources": {
+      "version": "2.7.1",
+      "resolved": "https://registry.npmjs.org/@opentelemetry/resources/-/resources-2.7.1.tgz",
+      "integrity": "sha512-DeT6KKolmC4e/dRQvMQ/RwlnzhaqeiFOXY5ngoOPJ07GgVVKxZOg9EcrNZb5aTzUn+iCrJldAgOfQm1O/QfPAQ==",
+      "license": "Apache-2.0",
+      "dependencies": {
+        "@opentelemetry/core": "2.7.1",
+        "@opentelemetry/semantic-conventions": "^1.29.0"
+      },
+      "engines": {
+        "node": "^18.19.0 || >=20.6.0"
+      },
+      "peerDependencies": {
+        "@opentelemetry/api": ">=1.3.0 <1.10.0"
+      }
+    },
+    "node_modules/@opentelemetry/resources/node_modules/@opentelemetry/core": {
+      "version": "2.7.1",
+      "resolved": "https://registry.npmjs.org/@opentelemetry/core/-/core-2.7.1.tgz",
+      "integrity": "sha512-QAqIj32AtK6+pEVNG7EOVxHdE06RP+FM5qpiEJ4RtDcFIqKUZHYhl7/7UY5efhwmwNAg7j8QbJVBLxMerc0+gw==",
+      "license": "Apache-2.0",
+      "dependencies": {
+        "@opentelemetry/semantic-conventions": "^1.29.0"
+      },
+      "engines": {
+        "node": "^18.19.0 || >=20.6.0"
+      },
+      "peerDependencies": {
+        "@opentelemetry/api": ">=1.0.0 <1.10.0"
+      }
+    },
+    "node_modules/@opentelemetry/sdk-logs": {
+      "version": "0.208.0",
+      "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-logs/-/sdk-logs-0.208.0.tgz",
+      "integrity": "sha512-QlAyL1jRpOeaqx7/leG1vJMp84g0xKP6gJmfELBpnI4O/9xPX+Hu5m1POk9Kl+veNkyth5t19hRlN6tNY1sjbA==",
+      "license": "Apache-2.0",
+      "dependencies": {
+        "@opentelemetry/api-logs": "0.208.0",
+        "@opentelemetry/core": "2.2.0",
+        "@opentelemetry/resources": "2.2.0"
+      },
+      "engines": {
+        "node": "^18.19.0 || >=20.6.0"
+      },
+      "peerDependencies": {
+        "@opentelemetry/api": ">=1.4.0 <1.10.0"
+      }
+    },
+    "node_modules/@opentelemetry/sdk-logs/node_modules/@opentelemetry/resources": {
+      "version": "2.2.0",
+      "resolved": "https://registry.npmjs.org/@opentelemetry/resources/-/resources-2.2.0.tgz",
+      "integrity": "sha512-1pNQf/JazQTMA0BiO5NINUzH0cbLbbl7mntLa4aJNmCCXSj0q03T5ZXXL0zw4G55TjdL9Tz32cznGClf+8zr5A==",
+      "license": "Apache-2.0",
+      "dependencies": {
+        "@opentelemetry/core": "2.2.0",
+        "@opentelemetry/semantic-conventions": "^1.29.0"
+      },
+      "engines": {
+        "node": "^18.19.0 || >=20.6.0"
+      },
+      "peerDependencies": {
+        "@opentelemetry/api": ">=1.3.0 <1.10.0"
+      }
+    },
+    "node_modules/@opentelemetry/sdk-metrics": {
+      "version": "2.2.0",
+      "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-metrics/-/sdk-metrics-2.2.0.tgz",
+      "integrity": "sha512-G5KYP6+VJMZzpGipQw7Giif48h6SGQ2PFKEYCybeXJsOCB4fp8azqMAAzE5lnnHK3ZVwYQrgmFbsUJO/zOnwGw==",
+      "license": "Apache-2.0",
+      "dependencies": {
+        "@opentelemetry/core": "2.2.0",
+        "@opentelemetry/resources": "2.2.0"
+      },
+      "engines": {
+        "node": "^18.19.0 || >=20.6.0"
+      },
+      "peerDependencies": {
+        "@opentelemetry/api": ">=1.9.0 <1.10.0"
+      }
+    },
+    "node_modules/@opentelemetry/sdk-metrics/node_modules/@opentelemetry/resources": {
+      "version": "2.2.0",
+      "resolved": "https://registry.npmjs.org/@opentelemetry/resources/-/resources-2.2.0.tgz",
+      "integrity": "sha512-1pNQf/JazQTMA0BiO5NINUzH0cbLbbl7mntLa4aJNmCCXSj0q03T5ZXXL0zw4G55TjdL9Tz32cznGClf+8zr5A==",
+      "license": "Apache-2.0",
+      "dependencies": {
+        "@opentelemetry/core": "2.2.0",
+        "@opentelemetry/semantic-conventions": "^1.29.0"
+      },
+      "engines": {
+        "node": "^18.19.0 || >=20.6.0"
+      },
+      "peerDependencies": {
+        "@opentelemetry/api": ">=1.3.0 <1.10.0"
+      }
+    },
+    "node_modules/@opentelemetry/sdk-trace-base": {
+      "version": "2.2.0",
+      "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-trace-base/-/sdk-trace-base-2.2.0.tgz",
+      "integrity": "sha512-xWQgL0Bmctsalg6PaXExmzdedSp3gyKV8mQBwK/j9VGdCDu2fmXIb2gAehBKbkXCpJ4HPkgv3QfoJWRT4dHWbw==",
+      "license": "Apache-2.0",
+      "dependencies": {
+        "@opentelemetry/core": "2.2.0",
+        "@opentelemetry/resources": "2.2.0",
+        "@opentelemetry/semantic-conventions": "^1.29.0"
+      },
+      "engines": {
+        "node": "^18.19.0 || >=20.6.0"
+      },
+      "peerDependencies": {
+        "@opentelemetry/api": ">=1.3.0 <1.10.0"
+      }
+    },
+    "node_modules/@opentelemetry/sdk-trace-base/node_modules/@opentelemetry/resources": {
+      "version": "2.2.0",
+      "resolved": "https://registry.npmjs.org/@opentelemetry/resources/-/resources-2.2.0.tgz",
+      "integrity": "sha512-1pNQf/JazQTMA0BiO5NINUzH0cbLbbl7mntLa4aJNmCCXSj0q03T5ZXXL0zw4G55TjdL9Tz32cznGClf+8zr5A==",
+      "license": "Apache-2.0",
+      "dependencies": {
+        "@opentelemetry/core": "2.2.0",
+        "@opentelemetry/semantic-conventions": "^1.29.0"
+      },
+      "engines": {
+        "node": "^18.19.0 || >=20.6.0"
+      },
+      "peerDependencies": {
+        "@opentelemetry/api": ">=1.3.0 <1.10.0"
+      }
+    },
+    "node_modules/@opentelemetry/semantic-conventions": {
+      "version": "1.40.0",
+      "resolved": "https://registry.npmjs.org/@opentelemetry/semantic-conventions/-/semantic-conventions-1.40.0.tgz",
+      "integrity": "sha512-cifvXDhcqMwwTlTK04GBNeIe7yyo28Mfby85QXFe1Yk8nmi36Ab/5UQwptOx84SsoGNRg+EVSjwzfSZMy6pmlw==",
+      "license": "Apache-2.0",
+      "engines": {
+        "node": ">=14"
+      }
+    },
     "node_modules/@peculiar/asn1-cms": {
       "version": "2.7.0",
       "resolved": "https://registry.npmjs.org/@peculiar/asn1-cms/-/asn1-cms-2.7.0.tgz",
@@ -5049,6 +5296,85 @@
       "integrity": "sha512-wwQAWhWSuHaag8c4q/KN/vCoeOJYshAIvMQwD4GpSb3OiZklFfvAgmj0VCBBImRpuF/aFgIRzllXlVX93Jevww==",
       "license": "MIT"
     },
+    "node_modules/@posthog/core": {
+      "version": "1.28.4",
+      "resolved": "https://registry.npmjs.org/@posthog/core/-/core-1.28.4.tgz",
+      "integrity": "sha512-wmtUYHYqA3zIAKDKvYWRNWAQsWOIBwxV08e+bWzVy0wQQzpaS/LzzRupXWRMRrLOk+1x3JKFxbqA3n0QGvpqsQ==",
+      "license": "MIT",
+      "dependencies": {
+        "@posthog/types": "1.372.10"
+      }
+    },
+    "node_modules/@posthog/types": {
+      "version": "1.372.10",
+      "resolved": "https://registry.npmjs.org/@posthog/types/-/types-1.372.10.tgz",
+      "integrity": "sha512-KuT3vLu3LSFsNWCwasS4gqjH/ysAyIUcB/aJSmKyNhDd/85hAznHRz1eSSl0sMvtsDTYiQIq0I0ybduVbrpPew==",
+      "license": "MIT"
+    },
+    "node_modules/@protobufjs/aspromise": {
+      "version": "1.1.2",
+      "resolved": "https://registry.npmjs.org/@protobufjs/aspromise/-/aspromise-1.1.2.tgz",
+      "integrity": "sha512-j+gKExEuLmKwvz3OgROXtrJ2UG2x8Ch2YZUxahh+s1F2HZ+wAceUNLkvy6zKCPVRkU++ZWQrdxsUeQXmcg4uoQ==",
+      "license": "BSD-3-Clause"
+    },
+    "node_modules/@protobufjs/base64": {
+      "version": "1.1.2",
+      "resolved": "https://registry.npmjs.org/@protobufjs/base64/-/base64-1.1.2.tgz",
+      "integrity": "sha512-AZkcAA5vnN/v4PDqKyMR5lx7hZttPDgClv83E//FMNhR2TMcLUhfRUBHCmSl0oi9zMgDDqRUJkSxO3wm85+XLg==",
+      "license": "BSD-3-Clause"
+    },
+    "node_modules/@protobufjs/codegen": {
+      "version": "2.0.5",
+      "resolved": "https://registry.npmjs.org/@protobufjs/codegen/-/codegen-2.0.5.tgz",
+      "integrity": "sha512-zgXFLzW3Ap33e6d0Wlj4MGIm6Ce8O89n/apUaGNB/jx+hw+ruWEp7EwGUshdLKVRCxZW12fp9r40E1mQrf/34g==",
+      "license": "BSD-3-Clause"
+    },
+    "node_modules/@protobufjs/eventemitter": {
+      "version": "1.1.0",
+      "resolved": "https://registry.npmjs.org/@protobufjs/eventemitter/-/eventemitter-1.1.0.tgz",
+      "integrity": "sha512-j9ednRT81vYJ9OfVuXG6ERSTdEL1xVsNgqpkxMsbIabzSo3goCjDIveeGv5d03om39ML71RdmrGNjG5SReBP/Q==",
+      "license": "BSD-3-Clause"
+    },
+    "node_modules/@protobufjs/fetch": {
+      "version": "1.1.0",
+      "resolved": "https://registry.npmjs.org/@protobufjs/fetch/-/fetch-1.1.0.tgz",
+      "integrity": "sha512-lljVXpqXebpsijW71PZaCYeIcE5on1w5DlQy5WH6GLbFryLUrBD4932W/E2BSpfRJWseIL4v/KPgBFxDOIdKpQ==",
+      "license": "BSD-3-Clause",
+      "dependencies": {
+        "@protobufjs/aspromise": "^1.1.1",
+        "@protobufjs/inquire": "^1.1.0"
+      }
+    },
+    "node_modules/@protobufjs/float": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmjs.org/@protobufjs/float/-/float-1.0.2.tgz",
+      "integrity": "sha512-Ddb+kVXlXst9d+R9PfTIxh1EdNkgoRe5tOX6t01f1lYWOvJnSPDBlG241QLzcyPdoNTsblLUdujGSE4RzrTZGQ==",
+      "license": "BSD-3-Clause"
+    },
+    "node_modules/@protobufjs/inquire": {
+      "version": "1.1.1",
+      "resolved": "https://registry.npmjs.org/@protobufjs/inquire/-/inquire-1.1.1.tgz",
+      "integrity": "sha512-mnzgDV26ueAvk7rsbt9L7bE0SuAoqyuys/sMMrmVcN5x9VsxpcG3rqAUSgDyLp0UZlmNfIbQ4fHfCtreVBk8Ew==",
+      "license": "BSD-3-Clause"
+    },
+    "node_modules/@protobufjs/path": {
+      "version": "1.1.2",
+      "resolved": "https://registry.npmjs.org/@protobufjs/path/-/path-1.1.2.tgz",
+      "integrity": "sha512-6JOcJ5Tm08dOHAbdR3GrvP+yUUfkjG5ePsHYczMFLq3ZmMkAD98cDgcT2iA1lJ9NVwFd4tH/iSSoe44YWkltEA==",
+      "license": "BSD-3-Clause"
+    },
+    "node_modules/@protobufjs/pool": {
+      "version": "1.1.0",
+      "resolved": "https://registry.npmjs.org/@protobufjs/pool/-/pool-1.1.0.tgz",
+      "integrity": "sha512-0kELaGSIDBKvcgS4zkjz1PeddatrjYcmMWOlAuAPwAeccUrPHdUqo/J6LiymHHEiJT5NrF1UVwxY14f+fy4WQw==",
+      "license": "BSD-3-Clause"
+    },
+    "node_modules/@protobufjs/utf8": {
+      "version": "1.1.1",
+      "resolved": "https://registry.npmjs.org/@protobufjs/utf8/-/utf8-1.1.1.tgz",
+      "integrity": "sha512-oOAWABowe8EAbMyWKM0tYDKi8Yaox52D+HWZhAIJqQXbqe0xI/GV7FhLWqlEKreMkfDjshR5FKgi3mnle0h6Eg==",
+      "license": "BSD-3-Clause"
+    },
     "node_modules/@rspack/binding": {
       "version": "1.7.11",
       "resolved": "https://registry.npmjs.org/@rspack/binding/-/binding-1.7.11.tgz",
@@ -6409,6 +6735,13 @@
         "@types/node": "*"
       }
     },
+    "node_modules/@types/trusted-types": {
+      "version": "2.0.7",
+      "resolved": "https://registry.npmjs.org/@types/trusted-types/-/trusted-types-2.0.7.tgz",
+      "integrity": "sha512-ScaPdn1dQczgbl0QFTeTOmVHFULt394XJgOQNoyVhZ6r2vLnMLJfBPd53SB52T/3G36VI1/g2MZaX0cwDuXsfw==",
+      "license": "MIT",
+      "optional": true
+    },
     "node_modules/@types/unist": {
       "version": "3.0.3",
       "resolved": "https://registry.npmjs.org/@types/unist/-/unist-3.0.3.tgz",
@@ -8827,6 +9160,15 @@
         "url": "https://github.com/fb55/domhandler?sponsor=1"
       }
     },
+    "node_modules/dompurify": {
+      "version": "3.4.2",
+      "resolved": "https://registry.npmjs.org/dompurify/-/dompurify-3.4.2.tgz",
+      "integrity": "sha512-lHeS9SA/IKeIFFyYciHBr2n0v1VMPlSj843HdLOwjb2OxNwdq9Xykxqhk+FE42MzAdHvInbAolSE4mhahPpjXA==",
+      "license": "(MPL-2.0 OR Apache-2.0)",
+      "optionalDependencies": {
+        "@types/trusted-types": "^2.0.7"
+      }
+    },
     "node_modules/domutils": {
       "version": "3.2.2",
       "resolved": "https://registry.npmjs.org/domutils/-/domutils-3.2.2.tgz",
@@ -9529,6 +9871,12 @@
         "node": ">=0.4.0"
       }
     },
+    "node_modules/fflate": {
+      "version": "0.4.8",
+      "resolved": "https://registry.npmjs.org/fflate/-/fflate-0.4.8.tgz",
+      "integrity": "sha512-FJqqoDBR00Mdj9ppamLa/Y7vxm+PRmNWA67N846RvsoYVMKB4q3y/de5PA7gUmRMYK/8CMz2GDZQmCRN1wBcWA==",
+      "license": "MIT"
+    },
     "node_modules/file-loader": {
       "version": "6.2.0",
       "resolved": "https://registry.npmjs.org/file-loader/-/file-loader-6.2.0.tgz",
@@ -11617,6 +11965,12 @@
       "integrity": "sha512-xfBaXQd9ryd9dlSDvnvI0lvxfLJlYAZzXomUYzLKtUeOQvOP5piqAWuGtrhWeqaXK9hhoM/iyJc5AV+XfsX3HQ==",
       "license": "MIT"
     },
+    "node_modules/long": {
+      "version": "5.3.2",
+      "resolved": "https://registry.npmjs.org/long/-/long-5.3.2.tgz",
+      "integrity": "sha512-mNAgZ1GmyNhD7AuqnTG3/VQ26o760+ZYBPKjPvugO8+nLbYfX6TVpJPseBvopbdY+qpZ/lKUnmEc1LeZYS3QAA==",
+      "license": "Apache-2.0"
+    },
     "node_modules/longest-streak": {
       "version": "3.1.0",
       "resolved": "https://registry.npmjs.org/longest-streak/-/longest-streak-3.1.0.tgz",
@@ -16221,6 +16575,37 @@
         "postcss": "^8.4.31"
       }
     },
+    "node_modules/posthog-js": {
+      "version": "1.372.10",
+      "resolved": "https://registry.npmjs.org/posthog-js/-/posthog-js-1.372.10.tgz",
+      "integrity": "sha512-ZQslIenDM8UpwKhmeeEnJ+t2nXr8mOIjCG+Ej3DCJnTBk9NX9Sr5RMuwHeGG8UJitwvtGOADyiY7DignOXaZwg==",
+      "license": "SEE LICENSE IN LICENSE",
+      "dependencies": {
+        "@opentelemetry/api": "^1.9.0",
+        "@opentelemetry/api-logs": "^0.208.0",
+        "@opentelemetry/exporter-logs-otlp-http": "^0.208.0",
+        "@opentelemetry/resources": "^2.2.0",
+        "@opentelemetry/sdk-logs": "^0.208.0",
+        "@posthog/core": "1.28.4",
+        "@posthog/types": "1.372.10",
+        "core-js": "^3.38.1",
+        "dompurify": "^3.3.2",
+        "fflate": "^0.4.8",
+        "preact": "^10.28.2",
+        "query-selector-shadow-dom": "^1.0.1",
+        "web-vitals": "^5.1.0"
+      }
+    },
+    "node_modules/preact": {
+      "version": "10.29.1",
+      "resolved": "https://registry.npmjs.org/preact/-/preact-10.29.1.tgz",
+      "integrity": "sha512-gQCLc/vWroE8lIpleXtdJhTFDogTdZG9AjMUpVkDf2iTCNwYNWA+u16dL41TqUDJO4gm2IgrcMv3uTpjd4Pwmg==",
+      "license": "MIT",
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/preact"
+      }
+    },
     "node_modules/pretty-error": {
       "version": "4.0.0",
       "resolved": "https://registry.npmjs.org/pretty-error/-/pretty-error-4.0.0.tgz",
@@ -16308,6 +16693,30 @@
       "integrity": "sha512-vtK/94akxsTMhe0/cbfpR+syPuszcuwhqVjJq26CuNDgFGj682oRBXOP5MJpv2r7JtE8MsiepGIqvvOTBwn2vA==",
       "license": "ISC"
     },
+    "node_modules/protobufjs": {
+      "version": "7.5.7",
+      "resolved": "https://registry.npmjs.org/protobufjs/-/protobufjs-7.5.7.tgz",
+      "integrity": "sha512-NGnrxS/nLKUo5nkbVQxlC71sB4hdfImdYIbFeSCidxtwATx0AHRPcANSLd0q5Bb2BkoSWo2iisQhGg5/r+ihbA==",
+      "hasInstallScript": true,
+      "license": "BSD-3-Clause",
+      "dependencies": {
+        "@protobufjs/aspromise": "^1.1.2",
+        "@protobufjs/base64": "^1.1.2",
+        "@protobufjs/codegen": "^2.0.5",
+        "@protobufjs/eventemitter": "^1.1.0",
+        "@protobufjs/fetch": "^1.1.0",
+        "@protobufjs/float": "^1.0.2",
+        "@protobufjs/inquire": "^1.1.1",
+        "@protobufjs/path": "^1.1.2",
+        "@protobufjs/pool": "^1.1.0",
+        "@protobufjs/utf8": "^1.1.1",
+        "@types/node": ">=13.7.0",
+        "long": "^5.0.0"
+      },
+      "engines": {
+        "node": ">=12.0.0"
+      }
+    },
     "node_modules/proxy-addr": {
       "version": "2.0.7",
       "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz",
@@ -16387,6 +16796,12 @@
         "url": "https://github.com/sponsors/ljharb"
       }
     },
+    "node_modules/query-selector-shadow-dom": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/query-selector-shadow-dom/-/query-selector-shadow-dom-1.0.1.tgz",
+      "integrity": "sha512-lT5yCqEBgfoMYpf3F2xQRK7zEr1rhIIZuceDK6+xRkJQ4NMbHTwXqk4NkwDwQMNqXgG9r9fyHnzwNVs6zV5KRw==",
+      "license": "MIT"
+    },
     "node_modules/queue-microtask": {
       "version": "1.2.3",
       "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz",
@@ -18996,6 +19411,12 @@
         "url": "https://github.com/sponsors/wooorm"
       }
     },
+    "node_modules/web-vitals": {
+      "version": "5.2.0",
+      "resolved": "https://registry.npmjs.org/web-vitals/-/web-vitals-5.2.0.tgz",
+      "integrity": "sha512-i2z98bEmaCqSDiHEDu+gHl/dmR4Q+TxFmG3/13KkMO+o8UxQzCqWaDRCiLgEa41nlO4VpXSI0ASa1xWmO9sBlA==",
+      "license": "Apache-2.0"
+    },
     "node_modules/webpack": {
       "version": "5.106.2",
       "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.106.2.tgz",

+ 1 - 0
package.json

@@ -20,6 +20,7 @@
     "@docusaurus/preset-classic": "3.10.1",
     "@mdx-js/react": "^3.0.0",
     "clsx": "^2.0.0",
+    "posthog-js": "^1.372.10",
     "prism-react-renderer": "^2.3.0",
     "react": "^19.0.0",
     "react-dom": "^19.0.0"

+ 39 - 0
plugins/copy-md/index.ts

@@ -0,0 +1,39 @@
+import path from 'node:path';
+import fs from 'node:fs/promises';
+import type {Plugin} from '@docusaurus/types';
+
+type Options = {
+  sourceDir: string;
+  routeBasePath: string;
+};
+
+export default function copyMdPlugin(
+  _context: unknown,
+  options: Options,
+): Plugin<void> {
+  return {
+    name: 'copy-md',
+    async postBuild({siteDir, outDir}) {
+      const src = path.join(siteDir, options.sourceDir);
+      const dest = path.join(outDir, options.routeBasePath);
+      await fs.mkdir(dest, {recursive: true});
+      await copyMarkdownFiles(src, dest);
+    },
+  };
+}
+
+async function copyMarkdownFiles(src: string, dest: string): Promise<void> {
+  const entries = await fs.readdir(src, {withFileTypes: true});
+  for (const entry of entries) {
+    const srcPath = path.join(src, entry.name);
+    const destPath = path.join(dest, entry.name);
+    if (entry.isDirectory()) {
+      await fs.mkdir(destPath, {recursive: true});
+      await copyMarkdownFiles(srcPath, destPath);
+      continue;
+    }
+    if (entry.isFile() && /\.(md|mdx)$/i.test(entry.name)) {
+      await fs.copyFile(srcPath, destPath);
+    }
+  }
+}

+ 26 - 0
src/pages/404.tsx

@@ -0,0 +1,26 @@
+import type {ReactNode} from 'react';
+import Link from '@docusaurus/Link';
+import Layout from '@theme/Layout';
+
+import styles from './index.module.css';
+
+export default function NotFound(): ReactNode {
+  return (
+    <Layout title="404 — Não encontrado" description="Página não encontrada.">
+      <main className={styles.page}>
+        <header className={styles.header}>
+          <span className={styles.wordmark}>GAZPERI</span>
+          <h1>404</h1>
+          <span className={styles.tag}>Página não encontrada</span>
+        </header>
+        <section className={styles.section}>
+          <p>O endereço que você acessou não existe ou foi movido.</p>
+          <p>
+            <Link to="/">Voltar para a página inicial</Link> ou ir direto para a{' '}
+            <Link to="/educacao">Educação</Link>.
+          </p>
+        </section>
+      </main>
+    </Layout>
+  );
+}

+ 130 - 15
src/pages/index.module.css

@@ -1,23 +1,138 @@
-/**
- * CSS files with the .module.css suffix will be treated as CSS modules
- * and scoped locally.
- */
+.page {
+  max-width: 720px;
+  margin: 0 auto;
+  padding: 80px 32px 100px;
+  font-family: 'Source Serif 4', Georgia, serif;
+  font-size: 17px;
+  line-height: 1.6;
+  color: var(--gazperi-ink);
+}
+
+.header {
+  margin-bottom: 4rem;
+  padding-bottom: 2.5rem;
+  border-bottom: 1px solid var(--gazperi-rule);
+}
+
+.wordmark {
+  font-family: 'Share Tech', sans-serif;
+  font-weight: 400;
+  font-size: 0.85rem;
+  letter-spacing: 0.14em;
+  text-transform: uppercase;
+  color: var(--gazperi-ink);
+  display: block;
+  margin-bottom: 0.5rem;
+}
+
+.header h1 {
+  font-family: 'Spectral', Georgia, serif;
+  font-weight: 600;
+  font-size: clamp(2.5rem, 6vw, 4rem);
+  letter-spacing: -0.01em;
+  line-height: 1.2;
+  color: var(--gazperi-ink);
+  margin-bottom: 0.5rem;
+}
+
+.tag {
+  font-family: 'Outfit', sans-serif;
+  font-size: 0.85rem;
+  letter-spacing: 0.04em;
+  color: var(--gazperi-ink-muted);
+  display: block;
+  margin-top: 0.5rem;
+}
+
+.section {
+  margin-top: 3.5rem;
+}
+
+.section p {
+  font-size: 1.05rem;
+  margin-bottom: 1rem;
+  color: var(--gazperi-ink);
+}
+
+.label {
+  font-family: 'Outfit', sans-serif;
+  font-size: 0.75rem;
+  font-weight: 500;
+  text-transform: uppercase;
+  letter-spacing: 0.18em;
+  color: var(--gazperi-ink-muted);
+  margin-bottom: 1rem;
+}
 
-.heroBanner {
-  padding: 4rem 0;
+.cta {
+  margin-top: 4rem;
+  padding: 2.5rem 0;
+  border-top: 1px solid var(--gazperi-rule);
+  border-bottom: 1px solid var(--gazperi-rule);
   text-align: center;
-  position: relative;
-  overflow: hidden;
 }
 
-@media screen and (max-width: 996px) {
-  .heroBanner {
-    padding: 2rem;
-  }
+.ctaButton {
+  display: inline-block;
+  font-family: 'Outfit', sans-serif;
+  font-weight: 500;
+  font-size: 1rem;
+  letter-spacing: 0.08em;
+  text-transform: uppercase;
+  color: var(--gazperi-bg);
+  background: var(--gazperi-accent);
+  padding: 0.9rem 2rem;
+  border-radius: 2px;
+  text-decoration: none;
+  transition: background 0.2s ease;
 }
 
-.buttons {
+.ctaButton:hover {
+  background: var(--ifm-color-primary-darker);
+  color: var(--gazperi-bg);
+  text-decoration: none;
+}
+
+.ctaHint {
+  font-family: 'Outfit', sans-serif;
+  font-size: 0.85rem;
+  color: var(--gazperi-ink-muted);
+  margin-top: 1rem;
+}
+
+.contatos {
+  list-style: none;
   display: flex;
-  align-items: center;
-  justify-content: center;
+  flex-direction: column;
+  gap: 0.75rem;
+  margin-top: 1rem;
+  padding-left: 0;
+}
+
+.contatos li {
+  font-size: 1rem;
+  color: var(--gazperi-ink);
+}
+
+.footer {
+  margin-top: 5rem;
+  padding-top: 2rem;
+  border-top: 1px solid var(--gazperi-rule);
+  font-size: 0.85rem;
+  color: var(--gazperi-ink-muted);
+}
+
+@media (max-width: 600px) {
+  .page {
+    padding: 48px 24px 60px;
+  }
+
+  .header {
+    margin-bottom: 2.5rem;
+    padding-bottom: 2rem;
+  }
+
+  .section {
+    margin-top: 2.5rem;
+  }
 }

+ 53 - 31
src/pages/index.tsx

@@ -1,43 +1,65 @@
 import type {ReactNode} from 'react';
-import clsx from 'clsx';
 import Link from '@docusaurus/Link';
-import useDocusaurusContext from '@docusaurus/useDocusaurusContext';
 import Layout from '@theme/Layout';
-import HomepageFeatures from '@site/src/components/HomepageFeatures';
-import Heading from '@theme/Heading';
 
 import styles from './index.module.css';
 
-function HomepageHeader() {
-  const {siteConfig} = useDocusaurusContext();
-  return (
-    <header className={clsx('hero hero--primary', styles.heroBanner)}>
-      <div className="container">
-        <Heading as="h1" className="hero__title">
-          {siteConfig.title}
-        </Heading>
-        <p className="hero__subtitle">{siteConfig.tagline}</p>
-        <div className={styles.buttons}>
-          <Link
-            className="button button--secondary button--lg"
-            to="/docs/intro">
-            Docusaurus Tutorial - 5min ⏱️
-          </Link>
-        </div>
-      </div>
-    </header>
-  );
-}
-
 export default function Home(): ReactNode {
-  const {siteConfig} = useDocusaurusContext();
   return (
     <Layout
-      title={`Hello from ${siteConfig.title}`}
-      description="Description will go into a meta tag in <head />">
-      <HomepageHeader />
-      <main>
-        <HomepageFeatures />
+      title="Gazperi — Educação sobre IA"
+      description="Alfredo Gazperi. Educo empresas e profissionais a trabalhar com IA no dia a dia. Foco em Claude e Claude Code.">
+      <main className={styles.page}>
+        <header className={styles.header}>
+          <span className={styles.wordmark}>GAZPERI</span>
+          <h1>Alfredo Gazperi</h1>
+          <span className={styles.tag}>Educador sobre IA</span>
+        </header>
+
+        <section className={styles.section}>
+          <p>
+            Ajudo empresas e profissionais a trabalhar com IA no dia a dia. Meu foco principal é no Claude e Claude Code — apresentando casos de uso reais em todas as áreas e perfis de atuação de uma empresa.
+          </p>
+          <p>
+            Como já atuei em quase todas as funções técnicas, é muito mais fácil ajudar desenvolvedores, designers, marketeiros, administradores, dentistas e todo tipo de trabalhador que precisa se adaptar a essa nova mudança tecnológica. Meu negócio não é escalável, não consigo atender vários clientes e alunos, isso é proposital. Você fala comigo direto e sou eu ajudando as pessoas, não abro mão disso.
+          </p>
+        </section>
+
+        <section className={styles.section}>
+          <p className={styles.label}>O que faço</p>
+          <p>
+            Não me pergunte sobre qual curso eu vendo, não é assim. Minha consultoria é para ajudar você, seu time e sua empresa especificamente. Sem parar tudo para isso, implementamos na rotina diária das tarefas.
+          </p>
+          <p>
+            Se você quer avançar na carreira e ainda não sabe por onde começar com IA, tem um programa individual pra isso. A gente trabalha junto, no seu ritmo, por meio de aulas que você tem total liberdade para marcar através do meu link de agendamento. Você aprende no seu ritmo e focado nas suas atividades.
+          </p>
+          <p>
+            Já trabalhei como desenvolvedor, designer e tech lead — então consigo discutir o mesmo problema com engenharia, produto e gestão sem mudar o tom técnico que certas funções exigem. Já fui diretor de marketing. É isso, sou um explorador da tecnologia.
+          </p>
+        </section>
+
+        <section className={styles.section}>
+          <p className={styles.label}>Educação</p>
+          <p>
+            Método, materiais e guias sobre Claude, Claude Code e trabalho com IA: <Link to="/educacao">gazperi.com/educacao</Link>.
+          </p>
+        </section>
+
+        <section className={styles.section}>
+          <p className={styles.label}>Como me encontrar</p>
+          <ul className={styles.contatos}>
+            <li>
+              LinkedIn: <a href="https://linkedin.com/in/gazperi">linkedin.com/in/gazperi</a>
+            </li>
+            <li>
+              E-mail: <a href="mailto:alfredo@gazperi.com">alfredo@gazperi.com</a>
+            </li>
+          </ul>
+        </section>
+
+        <footer className={styles.footer}>
+          <p>© {new Date().getFullYear()} Gazperi · Vila Velha, ES</p>
+        </footer>
       </main>
     </Layout>
   );

+ 69 - 0
src/posthog/init.ts

@@ -0,0 +1,69 @@
+import posthog from 'posthog-js';
+import ExecutionEnvironment from '@docusaurus/ExecutionEnvironment';
+
+const POSTHOG_KEY = 'phx_PEujL3LwMCfjwpnqyDtewBbgHZ9F4uoyrtCDjfNo43MHouY8';
+const POSTHOG_HOST = 'https://eu.i.posthog.com';
+
+let initialized = false;
+
+function initPostHog(): void {
+  if (initialized || !ExecutionEnvironment.canUseDOM) return;
+  initialized = true;
+
+  posthog.init(POSTHOG_KEY, {
+    api_host: POSTHOG_HOST,
+    capture_pageview: true,
+    capture_pageleave: true,
+    person_profiles: 'identified_only',
+  });
+
+  document.addEventListener('click', (event) => {
+    const target = event.target as HTMLElement | null;
+    const anchor = target?.closest('a') as HTMLAnchorElement | null;
+    if (!anchor) return;
+
+    const href = anchor.getAttribute('href') ?? '';
+    if (!href || href.startsWith('#')) return;
+
+    const isExternal =
+      /^https?:\/\//.test(href) && !href.includes('gazperi.com');
+
+    if (isExternal) {
+      posthog.capture('outbound_link_clicked', {
+        href,
+        text: anchor.textContent?.trim() ?? '',
+        pathname: window.location.pathname,
+      });
+    } else if (
+      href.startsWith('/') ||
+      href.startsWith('./') ||
+      href.startsWith('../')
+    ) {
+      posthog.capture('internal_nav_clicked', {
+        to: href,
+        text: anchor.textContent?.trim() ?? '',
+        pathname: window.location.pathname,
+      });
+    }
+  });
+}
+
+export function onRouteDidUpdate({location, previousLocation}: {
+  location: {pathname: string};
+  previousLocation: {pathname: string} | null;
+}): void {
+  if (!ExecutionEnvironment.canUseDOM) return;
+  initPostHog();
+
+  if (location.pathname.startsWith('/educacao')) {
+    posthog.capture('docs_entry_visited', {
+      slug: location.pathname,
+      title: document.title,
+      referrer_pathname: previousLocation?.pathname ?? null,
+    });
+  }
+}
+
+if (ExecutionEnvironment.canUseDOM) {
+  initPostHog();
+}

BIN
static/img/icons/apple-touch-icon.png


BIN
static/img/icons/icon-192.png


BIN
static/img/icons/icon-512.png


+ 21 - 0
static/img/icons/site.webmanifest

@@ -0,0 +1,21 @@
+{
+  "name": "Gazperi",
+  "short_name": "Gazperi",
+  "description": "Educação e consultoria em IA",
+  "start_url": "/",
+  "display": "minimal-ui",
+  "background_color": "#f5f0e8",
+  "theme_color": "#b85c38",
+  "icons": [
+    {
+      "src": "/img/icons/icon-192.png",
+      "sizes": "192x192",
+      "type": "image/png"
+    },
+    {
+      "src": "/img/icons/icon-512.png",
+      "sizes": "512x512",
+      "type": "image/png"
+    }
+  ]
+}

BIN
static/img/og-default.png


+ 18 - 0
static/llms.txt

@@ -0,0 +1,18 @@
+# Gazperi
+
+> Alfredo Gazperi — consultoria e educação no novo mundo de trabalho com IA. Foco em Claude e Claude Code (Anthropic) para empresas e profissionais.
+
+Site institucional em construção. Conteúdo público abaixo. Markdown bruto disponível em `<URL>.md` para qualquer documento da seção Educação.
+
+## Sobre
+
+- [Página inicial](https://gazperi.com/): apresentação, atuação, contato.
+
+## Educação
+
+- [Educação — introdução](https://gazperi.com/educacao/intro.md): índice e ponto de entrada da seção. Mais material em breve.
+
+## Contato
+
+- LinkedIn: https://linkedin.com/in/gazperi
+- E-mail: alfredo@gazperi.com

+ 19 - 0
static/robots.txt

@@ -0,0 +1,19 @@
+# https://gazperi.com/robots.txt
+
+User-agent: *
+Allow: /
+
+# AI / agent crawlers — explicit allow
+User-agent: GPTBot
+Allow: /
+
+User-agent: ClaudeBot
+Allow: /
+
+User-agent: Google-Extended
+Allow: /
+
+User-agent: PerplexityBot
+Allow: /
+
+Sitemap: https://gazperi.com/sitemap.xml