# RedFox — Sistema de diseño
Este archivo es la versión legible por máquina del brandbook de RedFox
(https://redfoxdev.com/brandbook). Es el mismo contenido que la página, en texto
plano, para poder pegárselo a un asistente o enlazarlo desde una herramienta.
Se genera desde docs/design.md y docs/ui-patterns.md en el repositorio del sitio,
así que no puede quedar desfasado respecto de la página.
El sitio es en español y para LATAM. Todo material de marca se escribe en español.
---
# Design System — RedFox
> The canvas is black, the content is white, and colour appears only to identify a service.
> Archivo says things to people; Space Mono labels data. Nothing is rounded, nothing is gradient,
> and no button is ever coloured.
---
## Status — read this first
**This file is the design system.** It is not a description of something else — it is the record
every page is audited against, and both `/brandbook` (for people) and `/brandbook.md` (for
assistants and tooling) are rendered from it. If a page disagrees with this file, the page is
wrong.
The black system replaced the navy/red one across all 23 route templates in six gated waves,
tracked in `docs/plan-rebrand-migration.md`. The exploration it was designed in
(`/rebrand-test`, `components/rebrand/`) was deleted in Wave 6 once its components were promoted;
nothing references it any more. The navy-era token aliases are gone too — `grep` for `brand-red`
or `brand-navy` returns zero.
Every route is migrated, `/lp/*` included. The grep for navy-era values returns zero across
`app`, `components` and `lib`.
**Changing this file changes what the public brandbook says.** Both surfaces rebuild from it, so
a careless edit ships. Keep the values in step with `app/globals.css`.
---
## Confirmed vs. Estimated Values
| Value | Status |
|-------|--------|
| Canvas `#000000`, surface `#0B0B0B`, panel `#141414` | ✅ Confirmed — shipped tokens |
| Text scale `#FFFFFF` / `#A1A1AA` / `#71717A` | ✅ Confirmed — shipped tokens |
| Five service accents (see §2) | ✅ Confirmed — shipped tokens |
| Hairline `rgba(255,255,255,0.08)` / strong `0.16` | ✅ Confirmed — shipped tokens |
| Archivo (display + body) · Space Mono (utility) | ✅ Confirmed — `app/layout.tsx` |
| Border radius `0` everywhere | ✅ Confirmed — brandbook ch. 05 |
| No gradients, no shadows | ✅ Confirmed — brandbook ch. 03, 05 |
| Container `max-w-[1200px]`, gutter inside | ✅ Confirmed — `components/layout/Container.tsx` |
| CTA white-on-black, never coloured | ✅ Confirmed — brandbook ch. 03, 08 |
| Photography roles and treatment | ✅ Confirmed — brandbook ch. 06 |
| Exact type sizes per breakpoint, section padding | `~` Estimated from the built components |
| Long-form body colour `--color-dim` on black (§11 Template C) | ✅ Confirmed — Wave 4, measured against WCAG |
| Long-form measure, leading, prose scale (§11 Template C) | `~` Estimated — written in Wave 4, tuned against the live articles |
| Form and input spec (§14) | `~` Estimated — written in Wave 5 ahead of the three forms |
| Accessible error state (§14) | ✅ Confirmed — `aria-invalid` / `aria-describedby` / `role="alert"` shipped in Wave 5 |
---
## 1. Visual Theme & Atmosphere
RedFox looks like an instrument panel, not a brochure. The page is pure black — not navy, not
charcoal — and everything on it is a hairline, a white word or a number. The restraint is the
argument: a company that builds the system a business runs on should look like a system, and
systems do not decorate themselves.
The single rule that governs everything: **colour identifies, it never decorates.** Five accents
exist, one per service, and each appears only to say which service you are reading about. Red is
one of those five — the accent for Inteligencia Artificial — and the logo. It is no longer *the*
brand colour, and it never appears on a button.
Structure is carried by line and surface rather than by colour or shadow. Sections are separated
by a 1px hairline at 8% white, never by a background change. Cards are not cards: they are cells
in one slab, divided by seams, sharing borders rather than floating apart. Nothing has a rounded
corner and nothing casts a shadow. Type does the hierarchy, and Space Mono marks the difference
between something a person reads and something a machine would read.
**Key characteristics:**
- Pure black `#000000` canvas on every page, including long-form reading surfaces
- Five service accents, used as a 2px bar, an icon, or a diagram branch — nothing else
- Primary CTA is white-on-black; secondary is a 1px white outline. Never a coloured button
- Zero border radius on every component, without exception
- Zero gradients. The one surviving `linear-gradient` in the codebase is the marquee `mask-image`
- Zero shadows on content; depth is suggested by hairline and surface only
- Archivo carries display *and* body; Space Mono carries labels, counts and durations only
- Sections separated by a hairline, never by an alternating background
- Photography is always desaturated so the only colour on a page stays the service accent
---
## 2. Color Palette & Roles
### Base
| Token | Value | Role |
|---|---|---|
| `--color-canvas` | `#000000` | Page background, everywhere |
| `--color-surface` | `#0B0B0B` | Lifted sections and cells |
| `--color-surface-2` | `#141414` | Panels and menus sitting on a surface |
| `--color-ink` | `#FFFFFF` | Headings, figures, primary CTA |
| `--color-dim` | `#A1A1AA` | Body copy and descriptions |
| `--color-faint` | `#71717A` | Metadata, eyebrows, footnotes |
| `--color-hairline` | `rgba(255,255,255,0.08)` | Section rules, cell seams |
| `--color-hairline-strong` | `rgba(255,255,255,0.16)` | Outline buttons, emphasis rules |
### Service accents
Red is one of five. It is not the brand accent and it does not lead.
| Service | Token | Value | Icon (Phosphor, duotone) |
|---|---|---|---|
| Consultoría | `--color-svc-consultoria` | `#B168F9` | `Compass` |
| Inteligencia Artificial | `--color-svc-ia` | `#FF3B5C` | `Brain` |
| Automatización | `--color-svc-automatizacion` | `#FFA23C` | `Lightning` |
| Desarrollo | `--color-svc-desarrollo` | `#7C9CFF` | `Code` |
| Data | `--color-svc-data` | `#3DE0A5` | `ChartLineUp` |
### Discipline of colour (brandbook ch. 03)
- **Do** use an accent for the service mark (a 2px bar), the service icon, or a diagram branch.
- **Do** keep it to at most two appearances of colour per card or block.
- **Don't** put colour on a button. The primary CTA is white on black, always — including IA.
- **Don't** put colour on text that has to be read, and never on a section background.
- **Don't** put colour in a gradient. There are no gradients in this brand.
### The accent rule on a service page (brandbook ch. 07)
Inside a service page the only accent that appears is that service's own. The other four return
only at the end, in "Cómo se conecta", when the page offers somewhere to go next. The reader has
already chosen; the accent now confirms where they are standing rather than distinguishing between
five options.
### Red, specifically
Red survives in three places: the logo mark (`#DD172F`, unchanged — the mark itself is never
recoloured or turned white), the IA accent `#FF3B5C`, which is a brighter red tuned for a
pure-black ground, and the homepage hero's scattered accent dots, which are the logo red rather
than any service colour. The hero is the one standing exception to "colour never goes on a section
background"; do not read it as licence for a second one.
The logo ships in two files, both with the same red mark. `redfox-logo.svg` sets the wordmark in
white and is the default; `redfox-logo-light.svg` sets it in black for white and printed grounds.
The **mark** is what never changes — the wordmark follows its background.
### Semantic
There is no separate semantic palette. Validation errors use the IA accent `#FF3B5C`; success and
warning states are carried by copy and by a hairline state change, not by a colour chip.
### Gradients
None **as colour**. Three gradients exist in the codebase and all three are masks or textures,
never a fill a visitor reads as colour:
- `.rb-mask-x` — the edge mask on the client-logo marquee.
- `ChipAnimation`'s `radialGradient` — an SVG opacity mask fading its dot grid.
- The homepage hero's `FADE` and its dot-grid `radial-gradient` — a mask and a 1px dot texture.
Anything else is a defect. A gradient that carries colour, or that fills a panel, is the pattern
this rule exists to stop — `components/lp/BeforeAfter.tsx` shipped one briefly and it was removed.
---
## 3. Typography Rules
### Font families
- **Archivo** — display *and* body. One family, separated by weight and size rather than by a
second typeface. Loaded in `app/layout.tsx`, exposed as `--font-heading` and `--font-sans`.
- **Space Mono** (400, 700) — utility only. Loaded as `--font-mono`, applied through `.rb-meta`
and `.rb-num`.
- No third family, ever.
### The mono rule
Space Mono is for anything a machine would read: service names, counts, durations, dates, section
eyebrows, footnotes, figures. **Prose is never mono.** A paragraph, a heading, a sentence of any
kind is Archivo. This is the single typographic rule that gets broken most easily and it is not
negotiable.
```tsx
// Machine-read — mono
Precio fijo por proyecto · Primer resultado antes de 30 días
+40
// Prose — Archivo, never mono
Construimos el sistema que sí funciona.
```
### Type scale
| Role | Size | Weight | Line height | Tracking | Notes |
|---|---|---|---|---|---|
| Display hero | 40 / 52 / 60px | 700 | 1.05 | −0.035em | Mobile → sm → lg |
| H1 page | 44 / 64 / 76px | 700 | 1.02 | −0.035em | Brandbook cover scale |
| H2 section | 30 / 38px | 700 | 1.15 | −0.025em | |
| H3 | 24 / 28px | 600 | 1.2 | −0.022em | |
| Statement | 26 / 32px | 600 | 1.3 | −0.022em | A positioning line, not a heading |
| Card title | 19px | 600 | 1.3 | 0 | |
| Body large | 17px | 400 | 1.65 | 0 | Hero and chapter intros |
| Body | 16px | 400 | 1.6–1.65 | 0 | |
| Body small | 14px | 400 | 1.6 | 0 | Cell descriptions |
| Meta (`.rb-meta`) | 11px | 400 | 1.5 | 0.08em | UPPERCASE, Space Mono |
| Figure (`.rb-num`) | 17–28px | 700 | 1.0–1.2 | 0 | Space Mono, tabular |
| Button label | 14px | 600 | 1 | 0 | |
### Typography principles
- Negative tracking is written in `em`, never `px`, so it scales with the size.
- Headings are white; body copy is `--color-dim`; metadata is `--color-faint`. Three steps, no more.
- **No italic `` in headings.** The italic key word was the navy brand's signature move and is
not part of this system. The base-layer rule in `globals.css` survives only until the last
unmigrated page stops emitting it, and is marked as a Wave 6 removal.
- Eyebrows are `.rb-meta` preceded by a 24px hairline, not a coloured label.
- Body copy never goes below `line-height: 1.6`.
---
## 4. Component Stylings
### Buttons
**Primary CTA — white on black**
- Background `#FFFFFF`, label `#000000`, 14px / weight 600
- Padding `14px 24px`, **radius `0`**, no border, no shadow
- Hover: `filter: brightness(0.9)`; active: `scale(0.98)`; transition 150ms
- One primary CTA per section. Everything else is an outline or a link.
**Secondary — outline, with the A1 wipe**
- Transparent ground, `1px solid #FFFFFF`, label white 14px / weight 600
- Padding `14px 24px`, radius `0`
- Hover: a white fill wipes in from the left (`scaleX(0 → 1)`, 300ms) and the label inverts to
black 80ms behind it. Implemented by `.rb-btn-wipe` + `__fill` + `__label` in `globals.css`.
**Text link — with the F3 drawn underline**
- White 14px, optional `→` that shifts `translateX(4px)` on hover
- `.rb-link-draw` + `.rb-link-draw__line`: a 1px rule draws from the left in 280ms and retracts to
the right on leave. Use `--inset` for links that carry horizontal padding, such as nav items.
There is no coloured button in this system. Not for IA, not for errors, not for anything.
### Cells and slabs
Cards do not float. A group of related cells is one slab: a container with a hairline border whose
background is the hairline colour, and children on `--color-canvas` separated by `gap-px`, so the
seams *are* the gaps.
```tsx
…
```
- Padding `24–36px`, radius `0`, no shadow
- Hover: `background: rgba(255,255,255,0.035)`, 150ms. No lift, no border glow.
- The service mark on a cell is a `2px × 16px` bar in the service accent that grows to 24px on
hover. Never a dot, never a loose icon.
### Navigation
- Sticky, `h-16`, `background: rgba(0,0,0,0.72)` with `backdrop-blur-xl`, bottom hairline
- Logo left at `h-6`, links in Archivo 14px white with the F3 drawn underline
- Services dropdown: panel on `--color-surface-2`, `--color-hairline-strong` border, each row an
accent-coloured duotone icon + service name + outcome
- Right CTA is white-on-black. Nav labels say **"Servicios"** — never "Módulos"; see `lessons.md`.
### Inputs
**Specified in §14.** A field is a rectangle one surface step above its ground, with a
`--color-hairline-strong` edge that escalates to `--color-faint` on hover and `--color-ink` on
focus. Radius `0`, no ring, no glow, no colour — except the error state, which is the one place in
this system where colour is not a service mark. Full spec, including the accessible error state and
form layout, in §14.
### Eyebrow / meta label
`.rb-meta` in `--color-faint`, preceded by a `1px × 24px` rule in `--color-hairline-strong`. No
pill, no background, no colour.
### Image treatment
- Every photograph gets `filter: grayscale(1) contrast(1.08) brightness(0.88)` (`.rb-img`), so the
only colour left on the page is the service accent. `.rb-img-raw` exists to demonstrate the
untreated state in the brandbook and must not ship on a real page.
- **Photography only.** `.rb-img` applies to the three photographic roles in §12, not to every
image. **Product UI screenshots are exempt** — on `/productos` the screenshot *is* the thing
being sold, and desaturating it hides the product's own interface. Diagrams, logos and
screenshots keep their own colour. If an image is a picture of the world, it gets `.rb-img`;
if it is a picture of a screen, it does not.
- Text over a photo only with a 55% black scrim, and only on an already-treated image.
- Always `next/image`, never a bare ``.
- No rounding, no shadow.
---
## 5. Icon System
- **Package:** `@phosphor-icons/react`, weight **`duotone`**, always. Never regular, bold, thin or
fill.
- **Size:** 24–32px on cells and panels, 22–26px inline.
- **Colour:** a service icon carries its service accent. Every other icon inherits the text colour.
- Icons are always paired with a label — never decorative background art.
Service icon assignments are in §2. `lib/servicios-config.ts` is the machine-readable source
and wins over this table.
---
## 6. Layout Principles
### Container — one constant
```ts
export const CONTAINER = "mx-auto w-full max-w-[1200px] px-5 sm:px-8 lg:px-10";
```
There is exactly one container in the system, and **the gutter lives inside the max-width box.**
Putting the gutter on an outer wrapper shifts the content edge by the gutter width and knocks every
row out of alignment with the header — that is the live site's existing bug, where the logo sits
40px to the right of the H1 and it worsens on wider screens. Never wrap `CONTAINER` in another
padded element.
### Spacing
- Base unit 4px; the working scale is `4 / 8 / 12 / 16 / 20 / 28 / 36 / 48 / 80 / 112px`
- Section vertical padding `80px` mobile → `112px` desktop (`py-20 lg:py-28`)
- Cell padding `24–36px`; grid gaps are `gap-px` where cells share seams, `16–36px` otherwise
### Section separation
Sections are separated by a **1px hairline at 8% white**, never by a background change. There is no
A/B background alternation in this system — the whole page is black. A section may sit on
`--color-surface` when it genuinely needs to lift, and that is the exception, not a rhythm.
### Section chrome — `Section`, and when not to use `RevealContent`
Every section is built from `components/shared/Section.tsx`, which carries the container gutter and
the D3 drawn rule. A page's first section takes `flush` — a hero draws no rule above it. Do not
hand-roll ``; that produces a second, slightly different rhythm.
**`RevealContent` sets `initial={{ opacity: 0 }}`.** Content inside it is therefore invisible in the
server-rendered HTML and only appears after hydration and an intersection. That is acceptable for a
section the reader scrolls to. It is **not** acceptable for:
- **Any `
` or hero copy.** The most important element on the page must not depend on JS.
- **Link grids that are a crawl path** — the blog index, related services. These carry internal link
equity and must render at full opacity with or without JS.
The rule of thumb: if it is above the fold, or if a crawler following links is the point, render it
plainly. `prefers-reduced-motion` already renders everything fully, so this only affects the
JS-disabled and pre-hydration cases — which are exactly the ones that matter for the H1.
### Border radius
**`0px`. Every component, no exception.** There is no radius scale. Tailwind's `rounded-*`
utilities should not appear in migrated files; `rounded-none` is used only where a third-party
default has to be overridden.
### Depth
No shadows on content. Depth is suggested by the hairline and by the three-step surface scale
(`canvas → surface → surface-2`). The one shadow in the system is on the nav dropdown, where a menu
genuinely floats over the page.
---
## 7. Depth & Elevation
| Level | Treatment | Use |
|---|---|---|
| 0 — flat | nothing | Everything by default |
| 1 — seam | `1px` hairline at 8% white | Section rules, cell seams |
| 2 — surface | `--color-surface` `#0B0B0B` | A section that needs to lift off the canvas |
| 3 — panel | `--color-surface-2` `#141414` + `--color-hairline-strong` | Menus, overlays |
| 4 — floating | `0 24px 64px rgba(0,0,0,0.8)` | Nav dropdown only |
No `box-shadow` on a card, an image, a button or an input.
---
## 8. Micro-Interactions & Motion
Motion explains a relationship or it does not happen. Nothing moves to set a mood. (Brandbook
ch. 09.)
| Name | Spec |
|---|---|
| Entrance | `opacity 0 → 1` with 20px rise, 450ms ease-out, once per element |
| Stagger | 80–100ms between siblings, never more than five staggered items |
| D3 — drawn rule | The section's own top hairline draws `scaleX(0 → 1)` in 300ms; content follows at +180ms in 250ms. No extra element is added — the line that already separates the section is simply drawn |
| E3 — masked headline | Each headline line rises from behind its own `overflow-hidden` mask, 600ms, `cubic-bezier(0.16, 1, 0.3, 1)` |
| A1 — button wipe | White fill wipes in from the left, 300ms; label inverts 80ms behind it |
| A3 — brackets | 9px hairline corner brackets fade and settle inward, 180ms |
| B1 — focus row | Hovering one cell drops its siblings to `opacity: 0.35`, 220ms |
| C1 — diagram hover | Hovering a branch dims the others to 22% |
| F2 — reading meter | The service bar lightens the active service in 200ms as you read |
| F3 — link underline | 1px rule draws from the left in 280ms, retracts to the right on leave |
| Diagram pulse | Branches draw on load and a pulse travels them — the only looping animation on the site |
| Reduced motion | `prefers-reduced-motion: reduce` sets `animation: none` globally; every Framer component renders in its final state |
Transforms and opacity only. No animated colour, no animated layout.
**Known limitation:** the D3 rule replaced the static `.rb-divider` border with an animated one, so
without JavaScript or before hydration the section hairlines are missing. Cosmetic, accepted. The
fix — keep the static border and animate a white line over it — costs one element per section and
is available if the polish pass wants it.
---
## 9. Do's and Don'ts
### Do
- Use `#000000` as the page background — pure black, on every page including the blog
- Use a service accent only as a mark, an icon or a diagram branch, at most twice per block
- Keep every CTA white-on-black, and every secondary a 1px white outline
- Set every machine-read string in Space Mono via `.rb-meta` / `.rb-num`
- Separate sections with a 1px hairline at 8% white
- Build groups of cells as one slab with `gap-px` seams, sharing borders
- Put the gutter inside the 1200px box, using the single `CONTAINER` constant
- Desaturate every photograph with `.rb-img` before it goes on a page — photographs only, never
product UI screenshots (see §4)
- Use the photography roles in order: Operación real → Materia → Retrato
### Don't
- Don't use navy `#0A0E1A`, `#0D1220` or the light `#F0F0F0` surface — all three are retired
- Don't colour a button, including for Inteligencia Artificial
- Don't set a paragraph, a heading or any prose in Space Mono
- Don't use `` italic in a heading — that was the navy brand, not this one
- Don't round a corner. Radius is `0`, everywhere
- Don't add a gradient. The marquee `mask-image` is the only one that survives
- Don't add a `box-shadow` to a card, image, button or input
- Don't alternate section backgrounds — the page is one black canvas
- Don't put two photography directions on the same page, and never a photo behind a form or a data
block
- Don't label navigation, buttons or headings "Módulos". The client-facing word is **"Servicios"**;
the operating-system metaphor is only ever used inside a sentence that explains it (`lessons.md`)
---
## 10. Responsive Behavior
| Name | Width | Key changes |
|---|---|---|
| Mobile | <640px | 1-col, gutter 20px, section padding 80px, display hero 40px |
| Small | 640–1024px | 2-col grids, gutter 32px, display hero 52px |
| Desktop | >1024px | Full multi-col, gutter 40px, 1200px box, display hero 60px |
- Navigation collapses to a full-screen black overlay; links stack, services expand inline
- Slabs collapse to 1-col; the `gap-px` seams become horizontal rules automatically
- The system diagram is desktop-only; below `lg` it degrades to a row of accent-marked service chips
- `.rb-claims` writes its own seams in CSS rather than swapping Tailwind padding classes, because
`pl-0` and `pl-4` on one element resolve by **stylesheet order, not class order**, and silently
indented a cell. Where two utilities could collide, write the CSS
---
## 11. Page Templates
### Template A — Homepage
```
1. Nav sticky, rgba(0,0,0,0.72) + blur, bottom hairline
2. Hero eyebrow → masked H1 → body → CTA pair → claims panel | system diagram
3. Client logos marquee, .rb-mask-x edge mask
4. Services slab 5 cells, one slab, accent mark + duotone icon per cell
5. Diagnóstico the offer, stated plainly
6. Cómo trabajamos numbered steps
7. Por qué RedFox cells with hairline seams
8. Herramientas logo strip
9. Cierre one statement, one white CTA
10. Footer
```
Every section from 3 onward carries a D3 drawn rule as its top hairline.
### Template B — Service page (brandbook ch. 07, in this order, always)
```
01 Encabezado breadcrumb · icon · name · promise; diagram at right with this branch lit
and the others at 22%
02 El problema the pain in the client's words + three symptoms they recognise
03 Qué incluye three deliverables in one slab with seams
04 Evidencia one full-bleed photograph of real operations, black scrim
05 Cómo se conecta the two services it combines with most — the other accents return here
06 Preguntas real objections answered plainly, then one white CTA
```
- Only this service's accent appears before step 05.
- The service bar stays pinned to the service you are reading (F2 reading meter).
- Delivery window and starting scope sit together under the CTA, in mono.
- **No prices on a service page.** Pricing lives in the proposal; the page shows "alcance de
arranque" instead.
### Template C — Long-form reading (blog, articles)
The blog is the only place on this site where someone reads 1,500 words instead of scanning a
section, and it is now black like everything else — the `#F0F0F0` light surface is retired. Every
value below exists to answer one question: what does the rest of this system have to do
differently when the reader stays on the page for six minutes instead of six seconds?
```
01 Encabezado back link · H1 · eyebrow (vertical) + year in mono
02 Portada one treated photograph at the reading measure
03 Cuerpo the article, one 660px column
04 Servicios the related services slab — the internal links
05 Cierre one white CTA
06 Footer
```
#### The reading column
| Value | Setting | Why |
|---|---|---|
| Measure | `max-w-[660px]` ≈ **68–72 characters** | Below ~55 the eye jumps too often; above ~80 it loses the line on the return sweep. 660px is the same number at 18px Archivo |
| Column | One, centred. Title, cover and body share it | Two widths inside one article make the left edge move as you scroll, which reads as a layout bug |
| Section padding | `py-14 lg:py-20` | Shorter than the marketing `py-20 lg:py-28`. A reader who came for the text should not have to scroll past a landing-page gap first |
#### Body copy
| Value | Setting | Why |
|---|---|---|
| Size | **18px**, every breakpoint (`prose-lg`) | Marketing body is 16–17px because it is scanned next to a heading. Read continuously, 16px on black is small; the measure narrows on mobile instead of the type shrinking |
| Line height | **1.78** | Marketing copy floors at 1.6. Long-form on a dark ground needs more: each line is a bright band on black, and tight leading makes consecutive bands interfere. 1.75–1.8 is the working range |
| Paragraph spacing | **24px** (`1.33em`), no indent | Larger than the leading, so a paragraph break is visible without a rule or an indent |
| Weight | 400. `` is 600 in `--color-ink` | A bolder base weight blooms on black. Emphasis comes from colour stepping up to white, not from more ink |
| Tracking | `0` | Negative tracking is a display move. At 18px it closes the counters and slows reading |
#### Body colour — the one value that is not white
**Body copy is `--color-dim` `#A1A1AA`, not `--color-ink`.**
`#FFFFFF` on `#000000` is a **21:1** contrast ratio. That is not "maximum legibility", it is the
condition that produces halation: on an emissive screen the white glyphs bleed into the black
counters, the stems thicken visually, and after a few hundred words the reader is tired without
being able to say why. The effect is invisible in a screenshot and only shows up in the reading,
which is exactly why this has to be written down rather than judged by eye.
`#A1A1AA` on black measures **8.2:1** — above WCAG AAA for body text (7:1), with roughly 60% of
the luminance of pure white. It is also already the system's body colour (§3), so long-form does
not need a token of its own.
- Body, list items, captions, table cells → `--color-dim`
- Headings, ``, links, blockquote text → `--color-ink`. Short bursts; white is the
contrast step that marks them, and no burst is long enough to fatigue
- Metadata, markers, counters, rules → `--color-faint`
Three steps, same as everywhere else. Long-form does not get a fourth.
#### Heading scale inside an article
Article headings are **smaller than section headings**. An H2 in a landing section announces a new
subject to someone scanning; an H2 in an article interrupts someone already reading, and only has
to be findable, not loud.
| Role | Size | Weight | Tracking | Space above | Colour |
|---|---|---|---|---|---|
| H1 (article title) | 34 / 46px | 700 | −0.03em | — | `ink` |
| H2 | 30px | 700 | −0.025em | `1.87em` (≈56px) | `ink` |
| H3 | 24px | 600 | −0.02em | `1.6em` (≈38px) | `ink` |
| H4 | 18px | 600 | 0 | `1.6em` | `ink` |
Space above a heading is always about three times the space below it — a heading belongs to the
text under it, not to the paragraph it follows. No `` italic in any of them (§3).
#### Links inside prose
A prose link cannot use the F3 drawn underline. F3 draws on hover, and a link that is
indistinguishable from body text until the pointer reaches it is not a link — in a 1,500-word
article that is dozens of links a reader never learns are there. F3 also needs a nested
`` element, which Markdown-rendered anchors do not have.
So, in prose only:
- Link text is `--color-ink` (white against `--color-dim` body — the colour step is the first
signal)
- **1px underline in `--color-hairline-strong`**, `text-underline-offset: 3px`, always visible
- On hover/focus the underline goes to solid `--color-ink` in 150ms. The rule brightens, it does
not appear
`rb-link-draw` stays the treatment for every link we author the markup for: the back link, the
card links, the related-services links, nav and footer. Prose is the exception, and it is the
only one.
#### Lists, quotes, rules
- **Bulleted list:** `list-style: square` — this system has no rounded shapes, and that includes
a bullet. Marker in `--color-faint`, `1.5em` indent, `0.6em` between items
- **Numbered list:** counters in **Space Mono** via `--color-faint`. A list index is machine-read
(§3), and tabular figures keep double digits aligned
- **Blockquote:** 1px left rule in `--color-hairline-strong` (never a service accent — a quote is
not a service), `20px` padding, text `--color-ink` at 19px/1.6, **not italic**, no quote glyph
- **``:** a full-measure hairline at 8% white with `3em` above and below. It is the D3 rule
standing still
#### Code and captions
- **Inline code:** Space Mono `0.875em`, `--color-ink`, on `--color-surface-2`, `2px 6px`
padding, radius `0`, no backtick glyphs
- **Code block:** `--color-surface` ground, 1px `--color-hairline` border, radius `0`, 14px/1.6
mono, horizontal scroll rather than wrapping. Never the accent-coloured code of the navy era
- **Images in prose and the cover:** `.rb-img` treatment like every other photograph, radius `0`
- **Caption:** `.rb-meta` in `--color-faint`, `12px`, centred under the image
#### What long-form does *not* change
The measure, the leading and the body colour are the only concessions. Section padding, the
hairline dividers, the D3 rules, the white-on-black CTA, the zero radius and the mono rule are
identical to every other page. A reading surface that invents its own chrome stops looking like
the same site two scrolls in.
---
## 12. Photography
Three roles, in a fixed order of preference. Never all three on one page, and never two directions
on one page.
### 1 — Operación real (primary)
Documentary photography of a client's actual operation: warehouses, plants, production floors,
offices in use. People working, not people posing. It is the only direction that supplies proof
rather than texture.
- **Use:** case studies · service pages · blog headers
- **Do:** real scenes shot on site with the mess that is already there; wide framing that shows the
whole system; people with their backs turned or absorbed in the task
- **Don't:** stock of people smiling at a camera in a meeting room; screens with fake dashboards
composited on; any frame where the work looks clean and quiet
- **Cost:** requires site access and written permission. That cost is real, and it is why the second
direction exists
### 2 — Materia (support)
Macro of material, machine and industrial texture. Used where no operations photo exists and the
text needs a pause. Never competes with Operación real on the same page — it replaces it when there
isn't one.
- **Use:** section breaks · quote grounds · resource covers
- **Do:** one material per frame, very tight crop; high contrast with real black in the shadows;
large and full-bleed, never icon-sized
- **Don't:** 3D renders, particles, polygon meshes, generic "AI" imagery; textures whose own colour
competes with the accents; the same texture in two adjacent sections
- **Cost:** stock or a short in-house shoot. The cheap fallback, not the first choice
### 3 — Retrato (reserved)
Team and client portraits on black. Only where the person is the subject.
- **Use:** sobre nosotros · testimonials · author bylines
- **Do:** continuous black ground with no set behind it; a single hard light source; consistent
framing across the whole series
- **Don't:** light or coloured grounds; mixing our own portraits with stock people; saturated
clothing that survives the treatment
- **Cost:** requires a directed shoot. Without one, the section runs without a portrait
### Placement
- One photograph per service page, full-bleed, as a cut between sections
- Text over a photo only with a 55% black scrim on an already-treated image
- Never a photo behind a data block or a form
> The nine photos under `public/rebrand/img/` are Unsplash material used only to prove the
> treatment. None are ours and **none may be published.** Wave 6 deletes them.
---
## 13. Voice
Direct and concrete, with no brochure adjectives. We write like someone who has already stood in the
client's operation.
| Instead of | Write | Why |
|---|---|---|
| "Soluciones innovadoras de transformación digital de última generación." | "Construimos el sistema que refleja cómo ya trabajas." | Name what we do, not the category we get filed under |
| "Contáctanos para conocer más sobre nuestros servicios." | "Cuéntanos el problema. En 30 minutos sabemos si podemos ayudarte." | Say what happens after the click, and how long it takes |
| "Precios competitivos adaptados a tus necesidades." | "Precio fijo por proyecto, no por hora. Alcance escrito antes de empezar." | A figure or a verifiable rule beats an adjective |
**Vocabulary rule.** "Cada servicio es un módulo del sistema operativo de tu empresa" is fine — the
metaphor lives inside a sentence that explains it. "Módulos" as a nav label, a button or a heading
is not: without the explaining sentence the client does not know what a module is. Client-facing
label is always **"Servicios"**. See `lessons.md`.
---
## 14. Forms and Inputs
A form is the only place on this site where the visitor writes instead of reads, and it is the only
place where the system has to admit that something went wrong. Everything below exists to answer two
questions the rest of the document never asks: what does a rectangle look like when a person is
allowed to type into it, and what does this brand do when the answer is rejected.
Written in Wave 5, ahead of the three forms, for the same reason §11 Template C was written ahead of
the article template: three forms built against three improvised input styles is the failure this
ordering prevents.
### The one exception to the colour rule
§2 says colour identifies a service and never decorates. **The error state is the exception**, and
it is the only one. An invalid field is bordered in `--color-svc-ia` `#FF3B5C` and its message is set
in the same value. This is deliberate: the IA accent is already the system's declared validation
colour (§2 Semantic), and an error a user cannot find is worse than a stretched rule. Colour is the
*second* signal — `aria-invalid` and the message text are the first. Nothing else in a form is
coloured: not the submit, not a selected option, not a success state.
### The text input
| Property | Value |
|---|---|
| Height | `46px` — `12px 14px` padding on 15px/1.5 text |
| Type | Archivo 15px, weight 400, `--color-ink`. Placeholder `--color-faint` |
| Ground | One step above the field's ground: `--color-surface` on canvas, `--color-surface-2` on a surface panel |
| Border | `1px solid var(--color-hairline-strong)` |
| Radius | `0`. No shadow, no inner shadow, no ring |
| Hover | `border-color: var(--color-faint)`, 150ms |
| Focus | `border-color: var(--color-ink)` **plus** the global square 2px white `:focus-visible` outline at `2px` offset, already in `globals.css` |
| Transition | `border-color 150ms` only. Never an animated background |
The border escalates in three steps — `hairline-strong → faint → ink` — and each step is an existing
token. A field never grows a coloured focus ring; the navy era's `ring-1 ring-brand-red/30` is
retired along with the colour it used.
### Textarea
The text input's box, with `min-height: 120px` (4 rows), `line-height: 1.6`, and
`resize: vertical` — never `resize: none`, because a person writing 400 words should be able to see
them, and never `both`, because horizontal resize breaks the column.
### Select
The text input's box with `appearance: none`, `padding-right: 40px`, and a 16px chevron in
`--color-faint` pinned `14px` from the right edge, `pointer-events: none`. The native option list
cannot be styled beyond ground and colour, so each `