Library · design system

Neety's design system, and how to maintain the site

Two parts: the foundations (tokens and components exactly as they are in the Figma «Library» frame, which is the source of truth) and a living guide so Neety's product team can maintain and update the site —with Claude— without losing context. The why behind every decision lives in docs/decisiones.md; code conventions in CLAUDE.md.

Introduction

How to use this library

This page is the manual for the Neety site, and it is generated from the site's own code: every color, button or component you see here is the one rendered in production, so it can never go stale. It has four blocks — these cards take you to each one. The first, “Working with Claude”, is the one you'll use day to day: how to connect to Claude and what to ask it to change the site. The rest is reference material.

Introduction

Site map

Every page of the site as a tree, with its two navigable URLs: the Spanish one and, dimmed, the English one under /en with its translated slug. Routes come from the code itself (lib/i18n.ts), so the map can't go stale. To create a new page, ask Claude and this map updates with it.

neety.com

Review copy (everything, drafts included): today neety.treseiscero.app; it moves to Neety's own test subdomain when the team sets it up.

Each page shows its two URLs: the Spanish one and, dimmed next to it, the English one under /en (with its translated slug). Both are navigable and each page links to its counterpart via hreflang.

Working with Claude

Receiving the project

The whole site lives in a GitHub repository: code, assets, documentation and this very library. This is how you get it and how to get it running on your computer — you only need to do it once.

  1. 1

    Ask for access to the repository

    The project lives at github.com/unaiarambarri/neety (private). You need a GitHub account and an invitation as a collaborator with write permission. If you won't be using GitHub, it can also reach you as a zip (a few MB; with the original assets, via WeTransfer or Drive): the content is the same and the next steps don't change.

  2. 2

    Bring it to your computer

    With Ship Studio: “Import project” and pick the GitHub repository (or the unzipped folder); it clones it and installs what's needed. With a terminal: git clone https://github.com/unaiarambarri/neety.git. With a zip: unzip it (double click on Mac, “Extract all…” on Windows). In all three cases you get a neety/ folder that already is the site.

  3. 3

    Install what's needed (first time only)

    You need Node 20 or later (free, from nodejs.org). Then, in a terminal inside the folder, run npm install (~1 minute): it regenerates the dependencies, which travel neither in the repo nor in the zip. If you imported with Ship Studio, this step is already done.

  4. 4

    Create your .env.local with your Sanity token

    Blog, events, academy and the team are read from Sanity, and the key to read them is kept out of the repository on purpose. In the project folder, copy .env.example as .env.local and fill in SANITY_READ_TOKEN. Each person generates their own token (never shared over chat): sanity.io/manage → Neety project → API → Tokens → Add API token, Viewer permission. You need to be a member of the project; if you aren't, ask for an invitation. Without this file the site starts but those pages fail with “Faltan SANITY_PROJECT_ID / SANITY_READ_TOKEN”. It is read only on startup: if you create it with the server running, restart it. It starts with a dot, so Finder hides it (Cmd + Shift + . to show it).

  5. 5

    That's it — connect to Claude

    With the folder ready, follow the steps in the next section, “Connecting to Claude”. These same instructions live in the project's README.md file, in case you need them offline.

Need to hand the project to someone without GitHub? The exact command to prepare the zip (which folders to exclude and why) is in README.md → «Si eres quien prepara el zip» — or ask Claude: “prepare the project zip so I can send it”.

Working with Claude

Connecting to Claude

The site is maintained by talking to Claude: you ask for changes in plain language and it edits the code for you. No programming knowledge needed. You can connect in several ways depending on the tool you work with.

Working with Ship Studio? There's nothing to set up

If you open the project in Ship Studio, Claude and the preview come already connected: you type the request in the chat, watch the change live in the panel next to it and publish from there. The five steps below are for working outside Ship Studio: with the Claude app (the same one you chat in) or with Claude Code in the terminal, on the project folder.
  1. 1

    Open the Claude app

    Claude's desktop app (Mac/Windows) — the same one you chat with — has a “Code” section for working on projects. Download it at claude.com/download. If you prefer the terminal, the claude command (Claude Code) does the same.

  2. 2

    Open the project folder in “Code”

    Pick the neety/ folder. From there it's a chat like any other, with one difference: Claude can read and edit the site's files. It also automatically reads the project instructions (CLAUDE.md), so it already knows the stack, the colors and the conventions — no need to explain anything.

  3. 3

    Start the preview

    Ask it to “start the development server” and open http://localhost:3000 in your browser: you'll see each change live before publishing. This library lives at /en/library.

  4. 4

    Ask for the change

    In plain language, like talking to a teammate: what you want, where it goes and the expected result. The three usual kinds of request —content, components, new sections— are right below with examples to copy.

  5. 5

    Review and publish

    Check the change in the preview and, once it's right, tell it “deploy the site”: Claude runs the publish script and the change is live in production in a few minutes.

Heads up: the regular chat at claude.ai (without the “Code” section) can't touch the project. Use it if you like to think through copy or prepare content, then bring the result here: “change the hero headline to this one I've prepared: […]”.

Working with Claude

Changing content

The most common request: changing a text or image that already exists. You don't need to know which file anything lives in —Claude finds it— but the more precise you are about the current content and the new one, the better it comes out on the first try.

Copy

Prompt for Claude

“Change the hero headline to: “Detect signals, close sooner.””

Prompt for Claude

“In the menu, rename “Pricing” to “Plans” and add a “Blog” link at the end.”

Prompt for Claude

“Change the “Request a demo” button copy to “Book a demo” across the whole site.”

Prompt for Claude

“Replace the copy of tabs 2–4 in “Use cases” with these final texts: […]”

Tip: paste the exact current text and the new one, so there's no ambiguity. If the change spans several places, say so (“across the whole site”); and to point at a section, use its plain-language name from the catalog (“Site components”).

Images

Watch out for the cache: if you replace a file with the same name, the preview may keep showing the old one. The fix is to use a new name (hero-v2.png) — Claude updates the reference automatically.
Prompt for Claude

“Replace the hero illustration with this image. [attach the file]”

→ Claude saves it in public/assets with a versioned name and updates the component.

Prompt for Claude

“Swap the “Fagor” logo in the animation for this SVG. [attach the file]”

Prompt for Claude

“Replace [degradado-01] from the library with this file. [attach the file]”

→ Uses the [name] placeholder convention that appears on the site.

Prompt for Claude

“The logo I uploaded looks distorted; fix it so it keeps its proportions.”

→ Claude fixes the SVG's preserveAspectRatio (a known gotcha).

Formats: logos and icons as SVG; photos as optimized PNG/JPG (Claude converts them to the final format). Pending image slots are marked with a [name] label — ask Claude to replace it with your file.

Languages (Spanish / English)

The site has been bilingual since Aug 27, 2026: Spanish lives at the root and English under /en with translated URLs (/en/platform, /en/use-cases/…). Every text exists in both languages in the same place (a copy({ es, en }) object next to its section), so a copy change can be requested for one language or both. The footer selector jumps to the same page in the other language, and visitors landing on the homepage from outside Spain get English (only Spain stays in Spanish). The blog and team are translated in the Sanity Studio: each article has linked ES and EN versions.

Prompt for Claude

“Change the English hero subtitle to: “Know which accounts matter today.” (leave the Spanish as is).”

Prompt for Claude

“Review the English translation of /en/platform and adjust the tone: more direct, shorter sentences.”

Prompt for Claude

“Create the /team page with this content […]”

→ Claude creates it in both languages (Spanish route and under /en) and cross-links them; ask it for the translation or hand it yours.

Prompt for Claude

“Translate the article “Cómo priorizar leads” I just published in Sanity into English.”

→ In the Studio: the article's language menu → “English”; Claude can fill in the translation with the seeding script if you ask.

Heads up: we did the English translation ourselves (B2B en-US tone) and it's pending your review, same as the blog's placeholder copy.

Working with Claude

Editing the blog and team (Sanity)

The blog and the “About Neety” team don't live in the code: they live in a content manager (Sanity) with its own panel, the Studio, at neety.sanity.studio. Sign in with the account we invited you with. Everything else on the site (page copy, images, sections) is still Claude's job.

What gets edited where

In the Studio (you)

  • Blog articles: copy, images, tags, date, byline.
  • Blog front page: which article is featured and which go in the side list.
  • Blog tags.
  • Team: name, role, portrait and order. They're the same people who sign the articles (“By …”); with the “Mostrar en Sobre Neety” checkbox unticked someone can sign without appearing in the grid (that's how “Equipo Neety” is set up).
  • The English version of each article and of the front page.

With Claude (code)

  • How the blog and article pages look (design, sizes, typography).
  • New fields on an article (e.g. a video or a table).
  • Copy and images on every other page.
  • How many articles show per page (today 9, the design's 3×3 grid).
  • Writing a whole article for you from a text or an idea: it leaves it as a draft in the Studio (see below).

Adding an article by asking Claude

It's the fast lane if you don't feel like wrestling the editor: you hand Claude the text (or the topic) and the image, and Claude leaves the article as a draft in the Studio. Claude never publishes: you publish after reading it, with the usual “Publish” button. Under the hood it uses studio/scripts/new-post.mjs, which turns a Markdown file into an article with all its fields.

What Claude needs from you

  • The text (pasted, in a .docx/.md, or just the topic and key points if you want Claude to write it).
  • The main image: a large PNG or JPG (minimum 1208 × 768, ideally 2400 wide). AVIF won't do. If any extra figures go inside the article, those too.
  • Tags (from the ones that already exist in the Studio; the first is the primary one) and a byline (a team member; “Equipo Neety” to sign as the company). If you don't say, Claude asks or shows you what's there.
  • Optional: date, standfirst, English version.

What Claude does

  1. Writes or adapts the text to the pieces that have a design: section headings, paragraphs, lists, quotes and captioned figures. Writes the short standfirst for the cards and the long one for the article page.
  2. Checks the tags and byline exist and that no other article shares the same URL.
  3. Uploads the image and creates the draft in the Studio.
  4. Sends you the link to review. You tweak whatever you like and hit “Publish”.

Example request

“Create a blog article with this text: [paste]. The main image is ~/Desktop/portada-leads.png (a panel with buying signals). Tags: Sales, AI Agent. Byline: Equipo Neety. Leave it as a draft in Sanity.”

For English, after publishing the Spanish: “Make the English version of article ‘X’ and leave it as a draft linked to the Spanish one.” Claude creates it already linked (the Studio's “Translations” menu), just as if you had created it by hand.

For this to work, the computer you use with Claude needs to have signed in to Sanity from the terminal once (cd studio && npx sanity login, with the same Studio account). Claude will tell you if it's missing.

Publishing: what happens when you hit the button

  1. Saving doesn't publish. While you edit, the Studio just saves a draft; the site doesn't see it. You can leave an article half-done without worry.
  2. “Publish” makes it visible and triggers the site rebuild on its own: it's live in about 2 minutes. No need to tell anyone or do anything else.
  3. While neety.com is in “coming soon” mode, the blog doesn't appear on the public site: it's reviewed on the review copy (today neety.treseiscero.app; later, Neety's own test subdomain), which is what gets rebuilt with each “Publish”. Launching the blog on neety.com is a decision (you ask Claude), not a Studio button; from that day on, “Publish” updates neety.com directly.
  4. “Unpublish” takes it off the site (also ~2 min). Deleting an article removes it entirely; to take it down temporarily, prefer “Unpublish”.

If it doesn't show up: did you hit Publish (not just save)? Have 2 minutes passed? Is the blog live on neety.com yet, or still only on the review copy? If all that checks out, ask Claude: “Article ‘X’ is published in Sanity but doesn't show on the site.”

Writing an article: field by field

FieldGuideline
TitleOne line. Cards clamp it to two.
Slug (URL)Generated from the title (“Generate”). Don't change it once published: it's the article's address and shared links would break.
StandfirstThe summary on the cards. ~160 characters max: beyond that it clamps to three lines.
Main imageUpload the large original (minimum 1208 × 768, ideally 2400 wide): the site crops it to each slot on its own. The hotspot lets you choose which part must never be cropped out. Alt text is required: describe what's in the picture.
TagsThe first one is the primary tag (the only one visible on the card). A new tag appears in the blog filter on its own; create it under “Etiqueta” before using it.
BylineA team member. To sign as the company, “Equipo Neety”. If someone's missing, create them under “Miembro del equipo” (and untick “Mostrar en Sobre Neety” if they shouldn't appear in the grid).
DateOrders the listing (newest first) and shows on the article page. A past date is fine.
Article standfirstThe one in the article header; longer than the cards' (2–3 sentences). If left empty the short one is used.
Article cover imageOptional. If the article has its own art, here; otherwise the page reuses the main image.
Reading minutesOptional: if left empty it's calculated automatically.
BodyThere are only four designed pieces, and they're enough: section heading (each one opens a section and feeds the article's side index — add one every 3–5 paragraphs), paragraphs, list, pull quote and figure (image + alt text + caption). Bold and italics yes; no other styles, on purpose.
Search descriptionOptional, 1–2 sentences. If empty, the standfirst is used.

Spanish and English inside the Studio

The site has two versions (Spanish at neety.com/…, English at neety.com/en/…) and each reads from the Studio only what's in its language. There's no automatic translation: whatever doesn't exist in English doesn't show in English.

One document per language

  • Articles: each language is a separate article, with its own title, slug and text.
  • Blog front page: one ES and one EN (“Portada del blog (ES)” / “(EN)”), each with its own featured picks.

Shared by both languages

  • Tags: a single name, in English (“AI Agent”, “Sales”…), the same on both sites.
  • Team (and bylines): a single document; the name is shared and the role has an “(inglés)” field next to it.
  1. Write the Spanish article first and publish it (or leave it as a draft).
  2. Create the English one from the article itself: at the top, “Translations” menu → “English” → “Create”. That creates the EN article already linked to the Spanish one. Don't create it as a loose new article: it would end up unlinked (see below why that matters).
  3. Fill in the EN: title, standfirst, body and search description, in English. The slug is generated from the English title (a different URL, /en/blog/…). The main image can be reused as is; the alt text goes in English.
  4. Publish the EN. Until then, the article only shows on the Spanish site and the language selector goes to the English blog listing.

Why the link matters: it's what makes the article page's language button jump to that same article in the other language, and what tells Google both URLs are the same content ( hreflang tags), so it shows each visitor their language's version instead of treating them as duplicates. Translate by hand or ask Claude (“translate article ‘X’ into English”): it writes it and leaves it already linked.

Blog front page and team

Blog front page

One document per language with the featured article (the big card) and exactly 4 for the list next to it, in the order shown. If not configured, the site uses the most recent one and the next four.

Team member

Name, role (and English role), grid order and portrait. The “this could be you” card is added by the site itself at the end. Portraits: the current ones have the brand gradient as background; a new one uploaded as-is would come out without it — hand the photo to Claude to compose it, or upload it pre-composed (300 × 276, at 3×).

Asking Claude

Prompt for Claude

“The article “Cómo priorizar leads” is published in Sanity but doesn't show on neety.com.”

→ Claude checks the latest deploy (GitHub Actions) and the Sanity webhook.

Prompt for Claude

“Compose this portrait for the team card with the brand gradient and upload it to member “Ana Vega”. [attach the photo]”

Prompt for Claude

“Add a “YouTube video” field to articles, rendered below the standfirst.”

→ Touches the Studio schema and the site at once; Claude deploys the Studio and explains the new field.

Prompt for Claude

“Switch the blog listing to 9 articles per page, like the design.”

Prompt for Claude

“Launch the blog on neety.com.”

→ It's the decision to take the blog out of “coming soon” mode: Claude removes the production exclusion and deploys.

Prompt for Claude

“Invite [email] to the Sanity Studio as an editor.”

→ Done from sanity.io/manage → Neety project → Members (Claude walks you through it; you need to be an admin).

Working with Claude

Reusing components

The site is made of reusable pieces (see “Site components”). When you ask for something new, name the existing piece or style it should resemble: Claude reuses what's already there instead of inventing something different, and the site stays consistent.

Prompt for Claude

“Add a secondary button saying “See use cases” next to the hero's main CTA.”

→ Reuses the button component, Outline variant, with the brand tokens.

Prompt for Claude

“Add one more metric to the “Why Neety” section: 3x qualified meetings.”

→ Adds an entry to the METRICS array; the number animates on its own with RollingNumber.

Prompt for Claude

“Add one more logo to the “Client logos”: this Acme SVG. [attach the file]”

→ One more entry in LOGOS; it appears in the desktop grid and the mobile marquee.

Prompt for Claude

“Use the Naranja Core color and the H2 heading style for the new section's title.”

→ Applies the tokens and type scale documented in Foundations.

Tip: name the pieces as they appear in this library —the token (Naranja Core, Berenjena), the button variant (Primary, Outline) or the component's plain-language name (Signal feed, Mobile menu, Before / After…)—. Every component card has chips to copy the name in one click.

Working with Claude

Adding new sections

To grow the site —a new section or a whole page— describe what it contains, where it goes and which part of the site it should resemble. Claude builds it with this library's colors, typefaces and components, so it looks made by the same team.

Prompt for Claude

“Add a testimonials section to the home, between “How it works” and “Integrations”: 3 quotes with name, role and company, in the site's white-card style.”

→ New section built with the existing tokens and patterns, in its place on the home.

Prompt for Claude

“Create the Pricing page with 3 plans using the pill button and the existing cards, and link it in the menu and footer.”

→ New /pricing page + navigation links + its card in this library.

Prompt for Claude

“On /en/platform, add an accordion FAQ section, like the “How it works” one but with a cream background.”

Prompt for Claude

“Create the Contact page with a simple form (name, email, message) on a white card, in the style of the /about-neety hero, and link it in the menu and footer.”

→ One of the site map's “pending” pages stops being pending.

Tip: when it's done, ask it to document the new section in this library (and in the site map if it's a page): the next change will start from an up-to-date catalog.

Working with Claude

Adding a new page

A new page isn't designed from scratch: it's assembled from the same pieces, margins and typography as the others, following a fixed recipe Claude already knows (it's in CLAUDE.md). This is what to ask for, what Claude will do and what to check before signing the page off.

What every page shares

The same structure, top to bottom

  1. Sticky header with the logo and the menu (the same piece everywhere).
  2. A hero card with the grainy brand gradient and the hamburger notch, as in /platform, /pricing or /about-neety.
  3. Sections whose left edge is aligned with the logo, on the Gris Tecnológico background with white cards.
  4. A closing CTA (the “Request a demo” card) and the Footer.

The best model: /pricing

It's assembled almost entirely from other pages' pieces: the /platform hero, the notched cards, the rail and CTA from “How it works”, the FAQ accordion from the use cases and the “Diagnosis” from the home. Only two components are its own. When you ask for a page, say “like /pricing” and name the sections of this library you want reused: that's how the new page looks made by the same team.

What Claude does for every new page

  1. 1

    One page, two languages

    The body lives in components/pages/XPage.tsx; the /nombre and /en/name routes (translated slug) are two minimal files that call it with the language. Every text goes in copy({ es, en }).

  2. 2

    Complete metadata

    Title and description per language, hreflang (alternates(), without it the footer language switcher can't jump) and its own Open Graph (withOpenGraph, otherwise sharing it shows the home's).

  3. 3

    Navigation links

    If it goes in the menu or the footer, an entry in nav-data.ts and Footer.tsx, and the route registered in ROUTES (lib/i18n.ts) so internal links know it.

  4. 4

    Its place in this library

    A node in the site map and, if it brings a new component, its card in “Site components”.

  5. 5

    A draft until it's launched

    While the site is in “coming soon”, the new page carries noindex and sits in the production exclusion list of scripts/deploy.sh: visible on the review copy, not on neety.com. After launch, neither.

  6. 6

    A note if something had to be decided

    Any non-obvious choice (a different background, a section that didn't exist) goes in docs/decisiones.md with its why.

Guardrails: what not to accept

Claude follows these rules on its own; if a new page breaks one, ask it to fix it before publishing:

  • New colors or fonts. Only the tokens in “Foundations”; a new color goes into the @theme block, never loose.
  • A different hero. There are already three kinds (gradient card, the Academy's centered hero, the text hero of /contact). Reuse one.
  • Centered content or custom margins. The left edge always matches the logo, on every screen.
  • Invented buttons. Only the CTA pill from “Buttons”, in its Primary and Outline variants.
  • Text in one language. Every visible sentence exists in Spanish and English from day one.
  • PNG or JPG images. Everything new in AVIF; the heavy original goes to the masters folder in Drive.
  • “Plain” internal links. They use TransitionLink: it's what triggers the brand curtain between pages.
  • Publishing without a mobile check. Every page is checked on the review copy, in both languages and at phone width.

Asking Claude

Prompt for Claude

“Create the /partners page like /pricing: hero with the brand gradient, a logo grid like “Client logos”, the FAQ accordion and the “Request a demo” CTA. Link it in the footer under “Company”. In both languages: /partners and /en/partners.”

→ New page from existing pieces, es/en routes, hreflang, Open Graph, navigation links and its node in this library's site map.

Prompt for Claude

“Check that /partners follows the CLAUDE.md conventions: tokens, TransitionLink, two languages, hreflang and Open Graph. Fix whatever is missing.”

→ A control pass before publishing; Claude lists what it corrected.

Prompt for Claude

“Compare /partners with /pricing on desktop and mobile and adjust whatever doesn't follow the same pattern of margins, typography and spacing.”

Prompt for Claude

“Take /partners out of draft: remove the noindex, drop it from the production exclusion list and deploy to neety.com.”

→ Only after launch; production deploys manually and Claude confirms with you first.

Working with Claude

Best practices when requesting changes

A few rules help Claude get it right the first time and keep the site consistent.

  • Claude already knows the project

    On opening it reads CLAUDE.md: stack, tokens and conventions. No need to explain it each time.

  • Be specific: what, where and result

    “Change X to Y in the hero” works better than “improve the hero”.

  • Reference this library

    “Use the Naranja Core token”, “the Outline button”, “the card style from How to use”.

  • One change per request

    Small, reviewable changes. Easier to see the result and roll back.

  • Review in the preview

    Check the change in the preview before asking for a deploy.

  • Decisions → document them

    If it's a design decision, ask it to note it in docs/decisiones.md.

Foundations

Color

Defined in app/globals.css (the @theme block). Editing there is the only way to change the palette; Tailwind generates the utilities (bg-naranja, text-plum…). Opacities (bg-plum/20) generate the tints.

Brand palette

Naranja Core

#FE8238

Azúl bebe

#A7C5F9

Berenjena

#431B44

Ciruela

#2A0E2C

Mint claro

#EBFFF6

Base Cálida

#F8F7EC

Gris Tecnológico

#F5F7F9

Blanco

#FFFFFF

Opacity ramps

Naranja Core

80
60
40
20
10
5
2

Azúl bebe

80
60
40
20
10
5
2

Berenjena

80
60
40
20
10
5
2

Ciruela

80
60
40
20
10
5
2

Foundations

Typography

Headings in Bricolage Grotesque (font-display), body copy in Switzer (font-sans, the body default) and Inter for long-form text. Each style has its desktop and mobile size.

Headings — 120% line height

H1 · XL

Desktop 112px · Mobile 64px · 120%

H1 · L

Desktop 80px · Mobile 48px · 120%

H1 · M

Desktop 64px · Mobile 40px · 120%

H2

Desktop 48px · Mobile 32px · 120%

H3

Desktop 40px · Mobile 24px · 120%

H4

Desktop 32px · Mobile 20px · 120%

H5

Desktop 24px · Mobile 16px · 120%

H6

Desktop 16px · Mobile 14px · 120%

Body — 150% line height

Large · Far far away, behind the word mountains, live the blind texts.

Desktop 24px · Mobile 20px · 150%

Medium · Far far away, behind the word mountains, live the blind texts.

Desktop 20px · Mobile 18px · 150%

Regular · Far far away, behind the word mountains, live the blind texts.

Desktop 16px · Mobile 16px · 150%

Small · Far far away, behind the word mountains, live the blind texts.

Desktop 14px · Mobile 14px · 150%

Tiny · Far far away, behind the word mountains, live the blind texts.

Desktop 12px · Mobile 12px · 150%

Foundations

Spacing

Spacing token scale (gaps, paddings and margins). The bar shows the desktop value; the label reads desktop / mobile. Structure (container gutters) and Paddings derive from this same scale.

xtiny
4 / 4px
tiny
8 / 6px
xxsmall
16 / 12px
xsmall
20 / 16px
small
24 / 18px
medium
32 / 24px
large
48 / 32px
xlarge
64 / 48px
xxlarge
80 / 64px
huge
96 / 80px
xhuge
112 / 96px
xxhuge
160 / 112px

Section rhythm (home layout)

Every home section uses the same vertical-padding scale on its root <section>: py-12 md:py-16 lg:py-28. Since each section carries padding above and below, the real gap between sections is the sum: 96px on mobile, 128px on tablet and 224px on desktop (the design value).

Horizontal padding: none on mobile —the p-4 on the <main> already provides the design's 16px— and md:px-10 from tablet up. Adding an extra px-* on a section misaligns its content against the other sections and narrows the usable space on mobile.

Foundations

Radii & shadows

Corner radii (Tiny → XXXLarge) and the system's three shadows.

Radii

Tiny
4px

XSmall
6px

Small
8px

Medium
12px

Large
16px

XLarge
20px

XXLarge
24px

XXXLarge
32px

Shadows

Small

Medium

Large

Foundations

Progress gradient

Every progress bar and rail on the site carries the same gradient: the full brand gradient (cream → orange → aubergine → blue → white) compressed into the piece. It lives in app/globals.css as .rail-gradient-x (horizontal), .rail-gradient-y (vertical) and .plataforma-rail-progress (vertical with a height animation). Never build a new one with bg-gradient-to-r.

Horizontal (.rail-gradient-x)

100%
66%
33%

The gradient stretches with the bar (the whole range is visible at any progress): animate the width, not a scaleX. Background track bg-plum/10 (or no track, as in the /platform hero tabs).

Where it's used

  • /en/platform · “How it works”: each step's vertical rail (.plataforma-rail-progress).
  • /en/use-cases · “How it helps you”: step bar on desktop and vertical scroll rail on mobile (ComoAyuda, .rail-gradient-y).
  • /en/request-demo · form stepper (DemoFlow).
  • /en/blog · article page: reading-progress ring, same stops in SVG (ArticleSidebar).

Base components

Buttons (CTA)

The site's real CTA is components/PillCta.tsx: a pill where the label rolls upward on hover while the dot turns into an arrow. The animation is 100% CSS (PillCta.module.css). The examples below are the real component — hover them to see it.

Primary — “Request a demo”

Outline — “Learn more”

Primary · big — footer CTA

Primary · block — mobile menu

Request a demo
  • components/RequestDemoButton.tsx is the “Request a demo” wrapper used by Hero, Metrics, How, Footer and the mobile menu — change the copy or the href there once for the whole site.
  • Default target: without an href (or with “#”), RequestDemoButton goes to the current language's “Request a demo” flow (ROUTES.solicitarDemo).
  • The navbar CTA is a separate, more compact variant (NavCta.module.css, it only swaps the text on hover).

Base components

Brand & favicon

The logo (components/Logo.tsx) accepts a fill prop for the variants. The favicon uses the brandmark over the brand gradient.

Logo — variants

Positive
Positive · Medium
Positive · Dark
Gradiente
Negative · Cream
Negative · Mint
Negative · Naranja
Negative · Berenjena
Negative · Ciruela
Gradiente · BG

Favicon

32px
256px

Product

Site components

Every real component on the site, in tabs by page (“Shared” = used by all of them). Each card carries a real thumbnail, a plain-language name as its title and copy chips: click a chip to copy the plain name, the technical one or the file path, and paste it into your prompt —“change the second step of the How it works”, “add a logo to the Client logos”— so there's no ambiguity. The catalog lives in components/library/catalog.tsx; documenting a new page means adding its entry there.

Shared (all pages)

Base pieces

Logo

Neety wordmark as inline SVG. Props: className (size) and fill (color).

  • Source: Figma export.
  • Default fill = Ciruela (#2A0E2C).

Pulsing dot

Brand dot whose halo breathes with GSAP. Leads the eyebrow of almost every section.

  • 'use client' — useGSAP.
  • Respects prefers-reduced-motion.
  • The halo is a child disc animated with transform/opacity (composited) — it used to animate box-shadow and repaint every frame (Lighthouse, Aug 31).

Pill button (CTA)

The base pill button (see the Buttons section) and its “Request a demo” wrapper used across the site.

  • Primary/outline variants; big, block, compact and spread props.
  • compact: smaller pill only below md — for narrow 2-column cards (/platform's Use cases on mobile).
  • spread: dot anchored to the right edge — for same-width stacked groups with aligned dots (coming soon CTAs).
  • Animation is 100% CSS in PillCta.module.css.
  • Renders a TransitionLink: internal hrefs (“/…”) trigger the transition curtain; with “#”, mailto or external links it behaves like a plain <a>.

Tag pill

Baby-blue pill at 10% with the card's tag. The same piece on /blog and /events cards.

Webinar
  • Without `href` it's an inert <span> (inside cards, which are already one whole link).
  • With `href` it becomes a transition link: on the article page it goes to `/blog?tag=…`, which preselects the listing filter.
View of “+” hover badge

“+” hover badge

Aubergine square with a “+”. In Figma it's painted on a single card per block: it's the card's HOVER state.

  • Appears via the group-hover of the link wrapping the card (and via focus-visible, for keyboard).
  • Shared by /blog and /events cards.

Rolling counter

Odometer-style counter: each digit rolls and lands on entering the viewport. Used by the metrics.

0123456789012345678901234567890123456789%
  • 'use client' — GSAP + IntersectionObserver (fires at 60% visible).
  • Non-numeric characters (+, %, M, k) stay fixed.

Navigation

View of Liquid-pill tabs

Liquid-pill tabs

Tab bar with the brand's white “liquid” pill: on tab change it travels with a stretch proportional to the distance and recovers its shape on arrival (same physics as the navbar hover).

  • 'use client' — useGSAP. Used by the home's “Use cases” selector and /blog's tag filter: change the motion and it changes in both.
  • prop `semantics`: "tablist" (tabs with an associated panel, role=tab/aria-selected) or "filter" (filter buttons without a panel, aria-pressed). A tablist without a tabpanel confuses screen readers.
  • What varies between uses comes in via props (container className, tabClassName, active/inactive colors): the motion is always the same.
  • The bar scrolls horizontally on mobile, never wraps: with 7 tags the wrapped block read as a three-row smudge.
  • The hover is a SECOND liquid pill (hoverPillClassName prop, plum/4% by default) traveling between tabs with the same physics, under the white one; no static per-button hover:bg (feedback 2026-08-28).
  • Respects prefers-reduced-motion (places the pill without animating).
View of Navigation bar

Navigation bar

Desktop bar: logo, links with “liquid” mega-menus (GSAP) and CTAs. NAV_ITEMS is the navigation's source of truth.

  • Links = NAV_ITEMS; menu content = USECASES / RESOURCES / COMPANY.
  • “Request a demo” CTA: compact pill in plum/75 with solid-plum hover (the plum/40 glass looked disabled and solid plum was too heavy; Aug 28) and the text roller from NavCta.module.css.
  • MobileMenu imports NAV_ITEMS and DROPDOWNS from here — edited in one place.
  • Links only show on lg+ screens (on mobile, MobileMenu takes over).
  • The current page is marked with a PulsingDot (4px) + font-medium — also in MobileMenu (weights alone didn't work: semibold too heavy, medium invisible). Dropdown triggers carry it too when the current page is one of their entries (e.g. “Use cases” on /use-cases/*).
View of Mobile menu

Mobile menu

Full-screen mobile/tablet menu overlay with drill-down folder navigation, opened from the hamburger button.

  • No data of its own: reuses Navbar's NAV_ITEMS/DROPDOWNS.
  • Locks body scroll without breakpoint jumps; Escape closes.
View of Sticky header

Sticky header

Sticky container (h-0, overlay) keeping the bar fixed above all sections. Wraps NavBlur + Navbar.

  • Don't add a transform here: it would break the mega-menu's fixed backdrop.
  • Lives in components/pages/HomePage.tsx as a direct child of <main>.
  • Full-bleed: cancels the main's p-4/p-5 with negative margins so NavBlur reaches the viewport edges; the padding and the 1600px cap are restored by the navbar's inner wrapper.
  • The scrolled flag flips when the bar gets pinned (rect.top <= 0), not by scrollY: otherwise the tint lit up before reaching the top and its edge read as a cut.
  • `flush` prop (Aug 27): on pages WITHOUT a gradient card notched around the hamburger (use cases, about, blog, events and their detail pages), on mobile/tablet the bar starts glued to the top (it doesn't drop 16/20px on pinning) and below md the logo sits at the page padding's 16px, aligned with content. Home, /platform and /manifesto (notched) stay non-flush.
View of Edge blurs

Edge blurs

Progressive blurs: NavBlur appears behind the navbar on scroll; BottomBlur softens the viewport's bottom edge.

  • They animate the blur radius (not opacity): backdrop-filter ignores ancestor opacity in Chromium.
  • Tuning in the LAYERS constants (and TINT in NavBlur).
  • The tint is nearly solid while the links pass and then fades into a long gradient (“cloud”): over page gradients it must not read as a rectangle.
  • The band pokes 24/32px above the sticky origin (-top + extra height): its top edge never enters the viewport and the in/out fades can't reveal a cut.
  • BottomBlur only exists with a fine pointer (pointer-fine:block): on touch, the browser's bottom bar (Safari iOS) made it read as a cut over the site.

Page transition

Veil with the brand gradient (the Loader's) fading over the page on navigation: it covers with opacity, the new route mounts underneath and it dissolves. Subtle — no edges or sweeps; the blobs drift while it lasts.

  • 'use client' — GSAP. The provider wraps children in components/RootShell.tsx (both root layouts share it).
  • New internal links: use <TransitionLink href="/..."> (not <a> or <Link>) — it intercepts the click, covers and does router.push.
  • External links, anchors (#), target _blank or modified clicks → native behavior.
  • Reuses .loader-blob-* and .loader-grain; the sheet is .page-curtain-sheet (globals.css).
  • Respects prefers-reduced-motion: quick fade without displacement.

Maintenance

Technical maintenance

Reference for changes that touch the system's foundations (for more technical profiles, or for Claude).

  1. Changing colors, typography or spacing: edit the @theme block in app/globals.css. The whole site and this library update on their own.
  2. Adding a reusable component: create it in components/ and add it to the “Site components” section (components/library/catalog.tsx) so it's documented.
  3. Languages: every visible text goes in copy({ es, en }) (lib/i18n.ts); a new page is a components/pages/XPage.tsx with lang plus two thin routes (app/(es) and app/(en)/en) with alternates() in the metadata — without hreflang the footer selector can't jump. Country auto-routing in functions/_middleware.ts. See decision 2026-08-27 · i18n.
  4. CMS (Sanity): the schema lives in studio/schemaTypes/; after changing it, npx sanity deploy from studio/. The site reads it at build time via components/blog/data.ts and components/sobre/team.ts (sections know nothing about Sanity). Publishing in the Studio → webhook → GitHub Actions → review copy; production only manually from Actions. CI tokens live in the repo secrets; locally, each person has their own in .env.local (see “Receiving the project”). Plan and status: docs/cms-sanity.md.
  5. Making a non-obvious decision: note it in docs/decisiones.md with its why.
  6. Validating: npx tsc --noEmit. Don't run npm run build while Ship Studio's dev server is active.
  7. Deploying: every push to main updates the review copy on its own (GitHub Actions). Production (neety.com) is always manual: Actions → “Deploy” → Run workflow → prod, or ./scripts/deploy.sh prod from the terminal. Details in CLAUDE.md §Deploy; launch checklist in docs/lanzamiento.md.
Pending:
  • Forms and demo scheduler: the backend is Neety's ad-hoc development — Neety's team wires it up on receiving the repository (Sept 2 email). The forms already POST to NEXT_PUBLIC_CONTACT_ENDPOINT / NEXT_PUBLIC_ACADEMY_ENDPOINT and the scheduler embeds via NEXT_PUBLIC_DEMO_SCHEDULER_URL.
  • Real content: blog, events and academy carry placeholder copy in Sanity; the Academy's videos (youtubeId) and downloadable files are still missing.
  • English translation pending Neety's review; the EN legal pages, pending the law firm.