---
version: alpha
name: Envisioning
description: Calm, structured, confident — the visual identity of an emerging-technology research institute and advisory. A single saturated lime accent over low-chroma graphite and near-white surfaces.
colors:
  # ---- Brand ----
  accent: "#D6F249"            # signature lime, dark-mode primary, Highlight marker, CTA on dark
  accent-down: "#aacc00"       # muted lime, light-mode primary, focus-ring tint

  # ---- Ink family (dark backgrounds; also the on-accent / on-lime text color) ----
  navy-deepest: "#1e1f27"
  navy-bg: "#25262e"           # dark-mode background
  navy-elevated: "#393b48"
  navy: "#2d2e38"              # standard dark, body on dark, text on lime
  navy-muted: "#656877"

  # ---- Neutral scaffolding ----
  gray-soft: "#c0c3d1"
  gray: "#d9d9de"              # section dividers
  off-white: "#f2f5f9"         # alternating section background

  # ---- Semantic surfaces (light theme) ----
  background: "#ffffff"
  foreground: "#0a0a0a"
  primary: "#aacc00"           # = accent-down; the surface-level primary for light theme
  on-primary: "#000000"
  card: "#ffffff"
  on-card: "#0a0a0a"
  muted: "#f5f5f5"
  on-muted: "#737373"

  # ---- Status ----
  destructive: "#ef4444"
  destructive-dark: "#7f1d1d"
  success: "#34d399"
  error: "#fb7185"

  # ---- Affordances ----
  focus-ring: "#1f4fff"        # the distinctive blue focus outline
  footer-muted: "#c0c4d5"      # one-off footer eyebrow color

typography:
  display-title:
    fontFamily: Envisioning Octa
    fontSize: 42px
    fontWeight: "400"
    lineHeight: 1.1
  display-section:
    fontFamily: Envisioning Octa
    fontSize: 36px
    fontWeight: "400"
    lineHeight: 1.1
  page-title:
    fontFamily: Inter
    fontSize: 36px
    fontWeight: "300"
    lineHeight: 1.1
  section-title:
    fontFamily: Inter
    fontSize: 36px
    fontWeight: "300"
    lineHeight: 1.1
  section-header:
    fontFamily: Inter
    fontSize: 30px
    fontWeight: "300"
    lineHeight: 1.25
  headline:
    fontFamily: Inter
    fontSize: 24px
    fontWeight: "300"
    lineHeight: 1.25
  prose-h1:
    fontFamily: Inter
    fontSize: 24px
    fontWeight: "700"
    lineHeight: 1.25
  prose-h2:
    fontFamily: Inter
    fontSize: 20px
    fontWeight: "500"
    lineHeight: 1.25
    letterSpacing: -0.025em
  prose-h3:
    fontFamily: Inter
    fontSize: 18px
    fontWeight: "500"
    lineHeight: 1.5
    letterSpacing: -0.025em
  body-lg:
    fontFamily: Inter
    fontSize: 18px
    fontWeight: "400"
    lineHeight: 1.625
  body-md:
    fontFamily: Inter
    fontSize: 18px
    fontWeight: "400"
    lineHeight: 1.5
  body-sm:
    fontFamily: Inter
    fontSize: 14px
    fontWeight: "400"
    lineHeight: 1.5
  label-button:
    fontFamily: Inter
    fontSize: 12px
    fontWeight: "500"
    lineHeight: 1
  eyebrow:
    fontFamily: Inter
    fontSize: 12px
    fontWeight: "400"
    lineHeight: 1
    letterSpacing: 0.05em

rounded:
  md: 6px      # buttons, inputs, badges
  lg: 8px      # standard cards / panels
  xl: 12px     # hero elements / large cards
  2xl: 16px    # home principle cards
  full: 9999px

spacing:
  unit: 8px
  gap-sm: 8px
  gap-md: 16px
  gap-lg: 24px           # standard grid gap
  gap-xl: 32px           # major section grids
  section-px-mobile: 24px
  section-px-desktop: 48px
  section-py-mobile: 48px
  section-py-desktop: 64px
  hero-pt-mobile: 176px  # clears the fixed top nav
  hero-pt-desktop: 224px
  container-md: 768px
  container-lg: 896px    # articles, forms
  container-xl: 1152px   # primary
  container-2xl: 1280px  # footer / wide

components:
  button-primary:
    backgroundColor: "{colors.foreground}"
    textColor: "{colors.background}"
    typography: "{typography.label-button}"
    rounded: "{rounded.md}"
    height: 36px
    padding: 0 16px
  button-primary-hover:
    backgroundColor: "#1a1a1a"
  button-accent:
    backgroundColor: "{colors.accent}"
    textColor: "{colors.navy}"
    typography: "{typography.label-button}"
    rounded: "{rounded.md}"
    height: 36px
    padding: 0 16px
  button-primary-light:
    backgroundColor: "{colors.primary}"
    textColor: "{colors.on-primary}"
    typography: "{typography.label-button}"
    rounded: "{rounded.md}"
    height: 36px
    padding: 0 16px
  button-ghost:
    backgroundColor: transparent
    textColor: "{colors.foreground}"
    typography: "{typography.label-button}"
    rounded: "{rounded.md}"
    height: 36px
    padding: 0 16px
  badge-default:
    backgroundColor: "{colors.muted}"
    textColor: "{colors.foreground}"
    typography: "{typography.label-button}"
    rounded: "{rounded.md}"
    padding: 2px 8px
  badge-destructive:
    backgroundColor: "{colors.destructive-dark}"
    textColor: "{colors.background}"
    typography: "{typography.label-button}"
    rounded: "{rounded.md}"
    padding: 2px 8px
  status-destructive-fill:
    backgroundColor: "{colors.destructive}"
    textColor: "{colors.foreground}"
    typography: "{typography.label-button}"
    rounded: "{rounded.md}"
    padding: 2px 8px
  badge-free:
    backgroundColor: rgba(214, 242, 73, 0.15)
    textColor: "{colors.navy}"
    typography: "{typography.label-button}"
    rounded: "{rounded.full}"
    padding: 2px 10px
  card:
    backgroundColor: "{colors.card}"
    textColor: "{colors.on-card}"
    rounded: "{rounded.lg}"
    padding: 24px
  card-principle:
    backgroundColor: rgba(255, 255, 255, 0.03)
    textColor: "{colors.background}"
    rounded: "{rounded.2xl}"
    padding: 32px
  input-field:
    backgroundColor: "{colors.background}"
    textColor: "{colors.foreground}"
    typography: "{typography.body-md}"
    rounded: "{rounded.md}"
    height: 40px
    padding: 0 12px
  input-field-focus:
    backgroundColor: "{colors.background}"
    textColor: "{colors.foreground}"
  link:
    backgroundColor: transparent
    textColor: "{colors.foreground}"
    typography: "{typography.body-lg}"
  link-accent:
    backgroundColor: transparent
    textColor: "{colors.accent-down}"
    typography: "{typography.body-lg}"
  highlight-marker:
    backgroundColor: "{colors.accent}"
    textColor: "{colors.navy}"
    typography: "{typography.headline}"
    padding: 0 4px
  eyebrow-label:
    backgroundColor: transparent
    textColor: "{colors.footer-muted}"
    typography: "{typography.eyebrow}"
  section-default:
    backgroundColor: "{colors.background}"
    textColor: "{colors.foreground}"
    typography: "{typography.body-lg}"
    padding: 64px 48px
  section-alt:
    backgroundColor: "{colors.off-white}"
    textColor: "{colors.foreground}"
    typography: "{typography.body-lg}"
    padding: 64px 48px
  section-dark:
    backgroundColor: "{colors.navy-bg}"
    textColor: "{colors.background}"
    typography: "{typography.body-lg}"
    padding: 64px 48px
  section-dark-elevated:
    backgroundColor: "{colors.navy-elevated}"
    textColor: "{colors.background}"
    typography: "{typography.body-lg}"
    padding: 64px 48px
  section-divider:
    backgroundColor: "{colors.gray}"
    height: 1px
  section-divider-subtle:
    backgroundColor: "{colors.gray-soft}"
    height: 1px
  text-muted:
    backgroundColor: transparent
    textColor: "{colors.on-muted}"
    typography: "{typography.body-sm}"
  text-on-dark-muted:
    backgroundColor: transparent
    textColor: "{colors.navy-muted}"
    typography: "{typography.body-sm}"
  footer:
    backgroundColor: "{colors.navy-deepest}"
    textColor: "{colors.footer-muted}"
    typography: "{typography.body-sm}"
    padding: 64px 48px
  focus-outline:
    backgroundColor: transparent
    textColor: "{colors.focus-ring}"
  status-success:
    backgroundColor: "{colors.success}"
    textColor: "{colors.foreground}"
    typography: "{typography.label-button}"
    rounded: "{rounded.md}"
    padding: 2px 8px
  status-error:
    backgroundColor: "{colors.error}"
    textColor: "{colors.foreground}"
    typography: "{typography.label-button}"
    rounded: "{rounded.md}"
    padding: 2px 8px
---

## Overview

Envisioning is an emerging-technology research institute and advisory, operating since 2010. The visual identity reads like a serious research lab that ships, not a consultancy selling slides — *calm, structured, confident.* The brand has two defining visual moves and one defining verbal move:

1. **A single saturated lime accent (`#D6F249`)** used sparingly but confidently, as hero splashes, the inline `Highlight` marker, and CTA accents on dark.
2. **A low-chroma graphite-ink supporting palette** (`#1e1f27 → #2d2e38`) instead of true black, set against white and off-white surfaces.
3. **Sentence case everywhere**, no emoji ever, and headings written as plain statements rather than stacked compound nouns.

The voice is institutional but declarative: first-person plural ("We did not set out to change foresight"), short and punchy when naming a principle, longer when explaining methodology. CTAs are imperative and short ("Run a free tech scan," "Read our manifesto"). Hype adjectives (*revolutionary, disruptive, next-gen, game-changing*) are forbidden.

**Copy is governed by **VOICE.md**, our copy standard.** Read it before writing any rendered text. Two rules there supersede earlier versions of this file: em dashes and en dashes are out of body copy entirely, and invented compound nouns (*intelligence infrastructure, decision velocity*) are no longer used as headings. Names of real products and methods (Multi-Model Convergence, Signal Scan, Signals Workspace) stay.

## Colors

The palette is deliberately narrow. Lime is the only chromatic accent; everything else is white, off-white, or a graphite ink. There are no gradients, anywhere, on any band. A "radial lime glow on dark heroes" exception lived here until 8 September 2026 and was misread as "any dark band"; in dark mode every band is dark, so a glow stops being an accent and becomes a stain. Flat colour only.

- **Accent (`#D6F249`).** The signature lime. Used as the inline Highlight marker behind a single key word in a headline, as CTA fills on dark surfaces, and as the brand's strongest tonal note. Always paired with `navy` (`#2d2e38`) text — never with white or true black.
- **Accent-down (`#aacc00`).** The muted lime; serves as `primary` on light theme and as the focus-ring tint. Used where the saturated lime would feel loud (small affordances, link underlines).
- **Ink family (`#1e1f27 → #2d2e38`).** The dark-mode background and footer surface. It keeps the depth of printed ink while sitting halfway closer to neutral graphite.
- **Neutrals (`#f2f5f9`, `#d9d9de`, `#c0c3d1`).** Cool neutral grays. `off-white` is the alternating section background; `gray` is for section dividers.
- **Status (`#ef4444`, `#34d399`, `#fb7185`).** Reserved for actual semantic state. Never used decoratively.
- **Focus ring (`#1f4fff`).** A distinctive blue, intentionally off-brand from the lime, so focus is unmistakable.

Imagery (people, product) is presented warm-neutral to cool-blue with no filters, grain, or duotones, and is **never full-bleed under text** — images live inside framed cards.

## Typography

**Envisioning Octa** is the display face, drawn by Thomaz Rezende and the typeface the wordmark itself is set in. Two independent variable axes — `wght` 100–900 and `wdth` 100–125, both defaulting to 400 and 100 — with 18 named instances and 172 glyphs. Widening does not thin the stems and weight does not change the proportions. It sets the wordmark, short word-like titles and covers, and nothing else: never long headings, body copy, buttons or labels, never under 18px, and it only earns its keep above roughly 24px. Pick one point on each axis and hold it for a whole layout — a page carrying four widths reads as an accident. Titles here are fixed at `"wght" 400, "wdth" 100`. The files are at [`/fonts/EnvisioningOcta-VF.woff2`](https://www.envisioning.com/fonts/EnvisioningOcta-VF.woff2) and the specimen at [/about/brand/octa](https://www.envisioning.com/about/brand/octa).

**Inter** is the surface typeface everywhere — variable weight (100–900) with a slant axis. CJK locales swap to weight-paired Noto Sans. The body base is **18px** (visualization routes drop to 16px). Inter's `font-variation-settings` hinting is left at its defaults; no custom OpenType features.

The signature typographic move is that **page and section titles are font-light (300)**, not bold — a deliberately quiet display register that lets the lime Highlight marker carry the visual weight. Prose headings (h2/h3) drop to weight 500 with tight letter-spacing (`-0.025em`). Buttons are 12px at every size — deliberately small against the 18px body to keep CTAs feeling precise rather than shouty.

- **page-title / section-title** — 36px desktop, 30px mobile, weight 300, balanced wrap.
- **section-header** — 30px desktop, 24px mobile, weight 300.
- **headline** — 24px desktop, 20px mobile, weight 300; the standard supporting paragraph under a title.
- **prose-h2/h3** — 20px / 18px, weight 500, letter-spacing `-0.025em`.
- **body-lg** — 18px, weight 400, line-height 1.625; the prose default.
- **label-button** — 12px, weight 500; *all* buttons, regardless of size variant.
- **eyebrow** — 12px, uppercase, letter-spacing `0.05em`, color `footer-muted`. Reserved for footer column heads and a few section labels.

The **Highlight marker** is the brand's most recognizable typographic device: a span with `accent` background and `navy` text, 4px horizontal padding, no italic, applied to a single word inside a headline. Treat it as part of the writing, not as decoration.

## Layout

The layout is mobile-first with `sm:` (640px) as the dominant breakpoint. Most grids go from one column on mobile to two or three on desktop.

- **Container.** Primary: `container-xl` (1152px). Footer goes wider to `container-2xl` (1280px). Articles narrow to `container-lg` (896px) or `container-md` (768px).
- **Section padding.** `section-px-mobile` × `section-py-mobile` on mobile (24 × 48), `section-px-desktop` × `section-py-desktop` on desktop (48 × 64).
- **Hero top.** `hero-pt-mobile` / `hero-pt-desktop` (176 / 224 px) clears the fixed top nav.
- **Grid rhythm.** 8px base unit. Standard grid gap is `gap-lg` (24px); major section grids use `gap-xl` (32px).
- **Borders.** Almost always 1px. `#d1d5db` on inputs, `#e4e4e7` on minimal cards, `gray` (`#d9d9de`) on section dividers. Dark mode swaps to `#525252` / `#404040`.

The fixed top nav lives at `z-10` and is the only sticky chrome. Imagery and animated canvas surfaces (radar demo, commit-wave grid, animated-signals banner) sit inside the same container grid as text — never bleeding edge-to-edge under copy.

## Elevation & Depth

Depth comes from a four-tier neutral shadow scale and one transparency recipe — **never** from colored shadows or backdrop-blur.

- `shadow-sm` — `0 1px 2px 0 rgb(0 0 0 / 0.05)` — subtle hairline.
- `shadow` — `0 1px 3px 0 rgb(0 0 0 / 0.10), 0 1px 2px -1px rgb(0 0 0 / 0.10)` — button default.
- `shadow-md` — `0 4px 6px -1px rgb(0 0 0 / 0.10), 0 2px 4px -2px rgb(0 0 0 / 0.10)` — standard card.
- `shadow-lg` — `0 10px 15px -3px rgb(0 0 0 / 0.10), 0 4px 6px -4px rgb(0 0 0 / 0.10)` — button hover, elevated panels.

The distinctive **dark-card recipe** for the home principle cards: `border-white/10` (1px) over `bg-white/[0.03]` on near-black, no shadow. Translucency does the work that elevation would on light.

## Shapes

Corner radii follow a 4-step progression matched to surface size. Nothing squared-off; nothing super-rounded.

- `rounded.md` (6px) — buttons, inputs, badges. The default UI radius.
- `rounded.lg` (8px) — standard cards and panels.
- `rounded.xl` (12px) — hero elements and large cards.
- `rounded.2xl` (16px) — the home principle cards.
- `rounded.full` — avatars, circular pills, the "free" badge.

Iconography is **Lucide React** at 24px viewBox with 1.5–2px strokes. Stroke caps are square; stroke joins are round on logo lockups, default on icons. No icon font, no emoji, no hand-drawn or stylized SVGs. Custom inline SVGs are reserved for three places: footer social marks (filled, 20×20), the Envisioning symbol, and content-card SVG art injected with the accent color.

## Components

### Buttons

- **`button-primary`** — black-on-white default. Foreground fill, background-color text, `rounded.md`, 12px label. Hover darkens to `#1a1a1a` and promotes shadow from `shadow` to `shadow-lg`. Press drops back to `shadow` (no scale transform).
- **`button-accent`** — lime fill with navy text; reserved for high-emphasis CTAs on dark hero sections.
- **`button-ghost`** — transparent surface, used inline with text or in the top nav.
- All buttons take `label-button` typography — 12px, weight 500. CTAs typically pair with a `lucide-react` `ArrowRight` icon to the right of the label.

### Cards

- **`card`** — neutral white surface, `rounded.lg`, 24px padding, `shadow-md`.
- **`card-principle`** — the dark recipe: `bg-white/[0.03]` over near-black, 1px `border-white/10`, `rounded.2xl`. No shadow; transparency carries the elevation. SVG art inside accepts the accent color via the parent's color scheme.

### Badges

- **`badge-default`** — muted gray pill, `rounded.md`, 12px label.
- **`badge-free`** — lime tint at 15% opacity, navy text, `rounded.full`. Used to mark a free tier or open resource.

### Inputs

- **`input-field`** — white surface, 1px `border` (`#d1d5db`), `rounded.md`, body-md typography. Focus shows the 3px `focus-ring` outline at 3px offset.

### The Highlight marker

The brand's signature inline mark. A `<span>` with `accent` background and `navy` text, 4px horizontal padding, no italic. Apply to a single key word inside a `headline` or `section-title`. Don't stack two highlights in one sentence; the marker loses its weight.

### Selection & focus

```css
::selection         { background: #D6F249; color: #2d2e38; }
:focus-visible      { outline: 3px solid #1f4fff; outline-offset: 3px; }
```

On a full lime surface, selection inverts to navy with lime text so it does
not disappear into the background. This applies to the surface and its
contents, including inline Highlight markers.

## Do's and Don'ts

**Do**

- Use the lime accent confidently but rarely. One Highlight per headline, one accent button per hero.
- Set headlines in font-light (300). The quietness is the point.
- Frame imagery inside cards. Keep text on flat color.
- Use Lucide for every icon. Match stroke weight (~1.75) and 24px viewBox.
- Use sentence case for every title, CTA, and eyebrow except the explicitly tracked uppercase footer eyebrows.
- Respect `prefers-reduced-motion`; collapse durations to 0.01ms.

**Don't**

- No emoji. Anywhere. Ever.
- No gradients, on any band, hero included.
- No low-opacity lime as a section background in dark mode. Lime tinted over
  white reads as a pastel (`badge-free` is the sanctioned version of this).
  Lime tinted over the dark navy background reads as army green at any
  opacity, because yellow-green optically mixes with dark blue into olive —
  no percentage fixes it. On a dark surface, keep the accent solid (a real CTA
  fill, navy text) or off the background entirely (icon, dot, underline) and
  use a flat neutral dark surface instead.
- No colored shadows. Shadows are neutral only.
- No hype adjectives (*revolutionary, disruptive, next-gen, game-changing*).
- No title case on headlines. This includes feature and benefit names on services pages.
- No em dashes or en dashes in rendered copy. See **VOICE.md**, our copy standard.
- No full-bleed imagery behind text.
- No bouncy springs, no spring overshoot, no parallax.
- No backdrop-blur effects.
- Don't recolor the logo lockup ad-hoc — use one of the four shipped variants. Don't strip the trailing accent stroke from the wordmark; it is part of the mark.

## Motion

Motion is purposeful, not decorative.

- **Default transition.** `transition-all duration-200` — 200ms across hover, focus, and small state changes.
- **Opacity crossfades.** 500ms `ease-in-out`.
- **Modal / panel entrance.** 220ms `cubic-bezier(0.22, 1, 0.36, 1)`, scale `0.985 → 1`, plus `translateY(8px → 0)`.
- **Framer Motion** is reserved for the home radar demo, the commit-wave grid, and principle-card pulses.
- **Canvas + `requestAnimationFrame`** powers `animated-signals` and the typing banner.
- **Hover.** Buttons darken (`hover:bg-black/90`) and promote shadow. Links pick up the accent color or thicken their underline. Cards rarely animate on hover — the codebase prefers click affordance over movement.
- **Press.** Buttons drop back to `shadow` with no scale transform. Mobile tap-highlight is `rgba(39, 42, 61, 0.12)`.

## Light and dark

This section is the shared contract for every Envisioning web surface: the marketing site, Core, Meet, Signals, Docs, NCB, the event microsites, the benchmark viewers and every app built on `@envisioning/app`. Each repo keeps its own palette values and its own token names. What it does not get to choose is the mechanism. An audit of eleven repos found the same handful of mistakes in nearly all of them; the rules below are those mistakes, inverted.

**One switch per document, stated once.** A page is themed either by the OS (`@media (prefers-color-scheme)`) or by one attribute on `<html>` (`class="dark"` or `data-theme="…"`). Never both. The moment a root class is introduced, Tailwind must be told about it (`@custom-variant dark (&:where(.dark, .dark *))` in v4, `darkMode: "class"` in v3); otherwise the class flips the variables while every `dark:` utility keeps following the OS, and the page splits in two. Three repos shipped exactly that seam. A component may read the theme from JavaScript, but it reads the same single source (`matchMedia` on an OS-driven page, the root attribute on a pinned one) and never ORs the two together.

**Pick the switch by what the surface is.**

| Surface | Switch | Toggle | Storage | First paint |
|---|---|---|---|---|
| Public site, reading, marketing (WWW, NCB, event microsites, Docs) | OS media query, no JavaScript | none | none | correct by construction |
| Signed-in product (Core, Meet, the Signals app) | root attribute set from a stored preference | `Auto / Light / Dark` | local to the surface, never the account | blocking script in `<head>` |
| Visualization, embed, white-label (`@envisioning/app` projects, research viewers, radars, iframes) | root class pinned by config or `?theme=` | none for the reader | none | server-rendered class |

A dark-only surface is the third row with one theme. It declares `color-scheme: dark`, ships no `.light` block, no toggle config and no `dark:` classes, and its upgrade path is re-tinting `:root`, not a latent second palette nobody can reach. Docs is the model.

**Dark or light by default is a product decision, carried by one constant.** Meet opens dark whatever the OS says; the marketing site follows the OS. Either is fine. What is not fine is the default living in three places that disagree: the pre-paint in `index.html`, the fallback in the theme module, and the platform clients. One constant, and the pre-paint matches it.

**Components never know which theme they are in.** Every color is a semantic custom property declared once on `:root` and redeclared once per theme, bridged into Tailwind through `@theme inline` (v4) or `theme.extend.colors` (v3), and consumed as `bg-panel`, `text-ink`, `var(--surface)`. The `dark:` variant is not a styling tool. The clean repos in the estate have zero `dark:` utilities; the messy ones have hundreds of files of them, each a hand-written pair that drifts from the tokens on its own schedule. New code writes none. Existing pairs are migrated to a token when the file is touched.

**A theme is a vocabulary, not a hex list.** Names differ by repo (`--surface` in Meet, `--panel` in the apps, `--card` in Core) and that is acceptable. What every theme must define is the same set of roles, so a rule can always reach for one:

- a surface ladder: canvas, raised, sunken, and the hover and press lifts on top of it
- an ink ladder: ink, muted, faint, and the RGB channels for translucent inks
- edges: hairline, border, strong border
- the accent as `fill`, `fill-pressed`, `fill-hover`, and `on-accent`; never an accent to use as ink (see the accent rule under The apps)
- semantic hues with their own soft inks: danger, success, warning, info
- focus

**Literals are the bug.** A `#fafafa`, an `rgba(255, 255, 255, 0.06)`, a `bg-white/5` or a `text-white/60` is a dark-first assumption that turns invisible the day the page goes light; whole sections of Meet went white-on-white this way. Translucent inks take a channel token (`rgb(var(--ink-rgb) / 0.72)`). Hover and press values go through the lift tokens, because `@media (hover: none)` blanks those and a literal opts out. The literals that survive are named and few: text crossing live video with its own scrim, the black inside a shadow, canvas pixels that get exported as an image, and `@media print`, which is always black on white regardless of theme.

**Declare each palette exactly once.** The estate had a dark palette transcribed three times in one file, a light block copy-pasted so it could serve both an explicit choice and the OS, two 1,000-line stylesheets in one monorepo drifting apart, and fifty-eight generated light blocks nothing could switch on. If two selectors need the same values, join them with `:is()` or use `light-dark()`; if a script generates them, only the script is edited. Nothing is hand-copied.

**`color-scheme` is mandatory.** On `html`, matching the active theme: `light dark` on an OS-driven page, the explicit value on a pinned one. Without it scrollbars, form controls, `<select>` popups and `::selection` render in the wrong scheme against the page. Seven of eleven repos were missing it. The `theme-color` meta, the web manifest and the favicon script read the same token values and are emitted from one place; OS-driven sites give the meta a light and a dark entry.

**The first paint is the theme.** OS-driven pages have nothing to do. Pages that restore a stored preference set the root attribute from a blocking `<script>` in `<head>`, before the stylesheet, and mark `<html suppressHydrationWarning>`. A provider mounted deep inside `<body>`, or a `useEffect`, runs after paint and flashes. The storage read is wrapped in `try`, because `localStorage` throws under locked-down privacy settings and a throw in a `useState` initializer takes the page down. Pinned pages render the class on the server; a client-side `classList.add` after hydration is a flash of the wrong palette.

**Do not repaint utilities.** `.dark .bg-white { … }`, `.dark .rounded-lg { … }` and `button[class*="bg-black"][class*="text-white"] { … !important }` are retro-fits that replace the token system with substring matching. They fire on `hover:text-white` and `md:bg-black` alike and they cannot be removed once components depend on them. Fix the component to use a token instead.

**Third parties get the resolved theme, never a hardcoded one.** Turnstile, LiveKit, code and markdown editors, map tiles and chart libraries each take a theme option. Pass `auto` where it exists, otherwise the value resolved from the single source above. Vendor variables (`--lk-*`) are bridged onto the local tokens once, in one block.

### Audit, per repo

Run these before declaring a surface done. Every hit is a finding, not a style preference.

```bash
grep -rn "@custom-variant dark\|darkMode" --include=*.css --include=*.ts .   # present iff a root class is used
grep -rln "dark:" --include=*.tsx --include=*.css . | wc -l                     # target: 0
grep -rn "color-scheme" --include=*.css --include=*.tsx . | head               # at least one, on html
grep -rnE "#fff\b|#fafafa|rgba\(255, ?255, ?255|(bg|text|border)-(white|black)/" --include=*.css --include=*.tsx . | wc -l
grep -rn "prefers-color-scheme" --include=*.css . | wc -l                       # 1 block per palette, or 0 on a pinned page
grep -rn "classList.add(\"dark\")\|classList.add('dark')\|documentElement.className" --include=*.tsx .   # must be in a head script, not an effect
grep -rn "theme_color\|theme-color\|themeColor" . --include=*.json --include=*.tsx --include=*.html --include=*.mjs   # one source
grep -rn "theme: *\"light\"\|theme: *\"dark\"\|theme=\"dark\"" --include=*.tsx .   # third parties hardcoded
```

Then by hand: the `:root` block and each theme block declare the same token names in the same order; no `.light` or `.dark` block exists that nothing can switch on; no `tailwind.config` survives under Tailwind 4 without an `@config` that loads it; the pre-paint, the default constant and the manifest agree.


## The apps

Core and Meet, the SwiftUI apps, do not reimplement this file. They pull a shared design layer from two public repositories, and anything working on them should read those rather than infer from the web:

- [`envisioning/envisioning-ui`](https://github.com/envisioning/envisioning-ui) — the accent and its rule, the mark, radius, semantics, button styles and the shared shells. Both apps track `main`.
- [`envisioning/envisioning-octa`](https://github.com/envisioning/envisioning-octa) — the typeface and the Swift package that vends it. `envisioning-ui` pins it exactly, so there is one font file in the estate and neither app carries a copy.

**Surfaces are deliberately not shared.** The layer has no `background`, `panel`, `elevated`, `field`, `border`, `primary` or `secondary`. Those belong to the platform. Owning them is what drifted an app away from iOS once already — places that switched system chrome off and repainted it, losing the scroll-edge treatment, the bar materials and the separator insets. Two apps do not read as siblings because they share a grey. They read as siblings because they share a mark, a face, and a lime that behaves the same way in both.

**The accent rule, stated once: the lime is a fill carrying ink, and never ink itself on a light surface.** Black on `#D6F249` is 16.66:1 and black on `#aacc00` is 11.35:1, but `#D6F249` as ink on white is 1.26:1 and `#aacc00` is 1.85:1. Darkening does not rescue the foreground case — the first lime clearing 3:1 on white is `#87992E`, an olive rather than the brand. The apps put that rule in the naming: there is no `accent` to reach for, only `fill` (`#D6F249`), `fillPressed` (`#aacc00`), `fillHover` (`#e0f76f`) and `ink`, so a call site asking for the lime as a foreground reads as wrong on sight. One value in light and dark; only the interaction state changes. The single exception is where the brand has to *be* the ink — a selected tab, a chosen row — and `#aacc00` is used there knowingly, for brand rather than for contrast.

**Native corner radius is three rungs chosen by what a thing is**, not by how large it happens to be: 12 for a field or other sunken well, 14 for a control, 18 for a card. The web's five-step scale above is its own. The two are not the same numbers and are not meant to be reconciled by rounding.

**The mark is a `Shape`, not an asset** — one path that renders in the app, the tab bar and a Live Activity alike, at any size and in any tint.

Octa is sized fixed rather than scaled with Dynamic Type. It anchors a layout — a clock, a wordmark — and reflowing it to an accessibility size breaks the thing it anchors. Body text stays on the system face, which does scale.

## Logos & Glyphs

Logo assets ship in [the brand page](https://www.envisioning.com/about/brand). Treat the height of the symbol's center bar as the minimum clear space around any lockup. Never place a lockup over a photograph or full-bleed image — always frame in flat color.

Two other marks live outside that folder and are **not** part of the public brand kit: `public/badge.svg` (the radar badge, tinted per hub by `lib/research-badge-svg.ts`) and `public/envisioning-logo.svg` (a filled variant used in offer PDFs and transactional email). Leave both where they are; they are wired into features, not identity.

### Symbol — the Envisioning mark (26 × 25)

The geometric Envisioning symbol. Used in the footer, print badges, and as the source for `app/_components/brand-mark.tsx`. Recolor by overriding the two `fill="#000000"` attributes — invert to `#FFFFFF` for dark backgrounds, or to `#D6F249` for accent treatments.

```svg
<?xml version="1.0" encoding="UTF-8"?>
<svg width="26px" height="25px" viewBox="0 0 26 25" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
    <defs>
        <polygon id="path-1" points="0 0 9.8 0 9.8 19.6 0 19.6"></polygon>
    </defs>
    <g id="symbol-mark2025" stroke="none" fill="none" transform="translate(0.5, 0)" fill-rule="evenodd" stroke-width="1">
        <g id="logo-copy-2" transform="translate(-1, -1)">
            <g id="Group-5" transform="translate(4.2, 4.2)">
                <polygon id="Fill-1" fill="#000000" points="0 13.29944 7.000025 13.29944 7.000025 6.29944 0 6.29944"></polygon>
                <g id="Group-4" transform="translate(9.8, 0)">
                    <mask id="mask-2" fill="white">
                        <use xlink:href="#path-1"></use>
                    </mask>
                    <g id="Clip-3"></g>
                    <path d="M7.000025,19.6 L0,19.6 L0,14.7 C0,14.37478 0.0452901618,14.05117 0.134680481,13.73848 L2.80001,4.40979 L2.80001,0 L9.800035,0 L9.800035,4.9 C9.800035,5.22522 9.75474484,5.54883 9.66535452,5.86152 L7.000025,15.19021 L7.000025,19.6 Z" id="Fill-2" fill="#000000" mask="url(#mask-2)"></path>
                </g>
            </g>
        </g>
    </g>
</svg>
```

### Wordmark — "ENVISIONING" (348 × 36)

Stroked geometric wordmark. Stroke `#000000`, stroke-width `7.61`, square caps, round joins. Never re-stroke or fill — the wordmark is a stroked outline by design.

```svg
<?xml version="1.0" encoding="UTF-8"?>
<svg width="348px" height="36px" viewBox="0 0 348 36" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
    <g id="products-Identity" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd" stroke-linecap="square" stroke-linejoin="round">
        <g id="Envisioning-Icon" transform="translate(-593, -78.76)" stroke="#000000">
            <g id="Group" transform="translate(597, 82.76)">
                <path d="M0.287518813,14 L14.404983,14 M20.1698804,0 L0,0 L0,14 L0,28 L20.1698804,28" stroke-width="7.61125117"/>
                <polyline stroke-width="7.61125117" points="31.6941572 27.9988712 31.6941572 0.00169320701 40.337147 0.00169320701 48.983041 27.9988712 57.6260308 27.9988712 57.6260308 0.00169320701"/>
                <polyline stroke-width="7.61125117" points="69.1520501 0.000846603508 69.1520501 5.5997178 77.7950399 27.9980246 83.5599373 27.9980246 92.2029271 5.5997178 92.2029271 0.000846603508"/>
                <path d="M103.728366,27.9988712 L115.255256,27.9988712 M109.490359,0.00169320702 L109.490359,27.9988712 M103.728366,0.00169320701 L115.255256,0.00169320701" stroke-width="7.61125117"/>
                <polyline stroke-width="7.61125117" points="149.83012 5.88050796 149.83012 5.6011288 144.068126 0.00225760935 132.541236 0.00225760935 126.779243 5.6011288 126.779243 8.4005644 149.83012 19.5983068 149.83012 22.4005644 144.068126 27.9994356 132.541236 27.9994356 126.779243 22.4005644 126.779243 22.1183632"/>
                <path d="M161.355558,27.9988712 L172.882449,27.9988712 M167.117551,0.00169320702 L167.117551,27.9988712 M161.355558,0.00169320701 L172.882449,0.00169320701" stroke-width="7.61125117"/>
                <polygon stroke-width="7.61125117" points="190.1693 27.9988712 184.407306 22.4 184.407306 5.6005644 190.1693 0.00169320701 201.69619 0.00169320701 207.458183 5.6005644 207.458183 22.4 201.69619 27.9988712"/>
                <polyline stroke-width="7.61125117" points="218.982751 27.9988712 218.982751 0.00169320701 227.62574 0.00169320701 236.271634 27.9988712 244.914624 27.9988712 244.914624 0.00169320701"/>
                <path d="M256.440644,27.9988712 L267.967534,27.9988712 M262.202637,0.00169320702 L262.202637,27.9988712 M256.440644,0.00169320701 L267.967534,0.00169320701" stroke-width="7.61125117"/>
                <polyline stroke-width="7.61125117" points="279.491521 27.9988712 279.491521 0.00169320701 288.13451 0.00169320701 296.780404 27.9988712 305.423394 27.9988712 305.423394 0.00169320701"/>
                <polyline stroke-width="7.61125117" points="337.118640 0.00225760935 322.711116 0.00225760935 316.949123 5.6011288 316.949123 22.4005644 322.711116 27.9994356 340 27.9994356 340 14.0008466 331.355921 14.0008466"/>
            </g>
        </g>
    </g>
</svg>
```

### Lockups

**`lockup-primary.svg`** (410 × 40) is the default mark: the Envisioning symbol at 40px tall, a 22px gap, then the wordmark optically centered against it. **`lockup-reverse.svg`** is the same geometry in white for dark surfaces. These are composed from `symbol.svg` and `wordmark.svg`, so a change to either source has to be recomposed into both.

The accent wordmarks (600 × 60) are the wordmark plus a terminal accent polyline of the form `points="0 45.42, 0 40.88, 13.625 4.54, 13.625 0"` at `stroke-width="12"`. That stroke is part of the mark; never strip it.

- **`envisioning_light.svg`** — for *light backgrounds*. Wordmark stroke `#1D1D2E`, accent stroke `#A0CC47`.
- **`envisioning_dark.svg`** — for *dark backgrounds*. Wordmark stroke `#FFFFFF`, accent stroke `#CCFF66`.

Note that those two accent strokes are neither `accent` (`#D6F249`) nor `accent-down` (`#aacc00`). They predate the current palette and are frozen inside the mark. Do not sample them for UI, and do not "correct" them to the palette without redrawing both lockups.

**Trap.** Both accent lockups were originally exported with JSX-style attribute names (`strokeWidth`, `fillRule`) instead of `stroke-width` and `fill-rule`. SVG renderers ignore those, so the marks silently fell back to a 1px default stroke and rendered as hairlines. Fixed 28 July 2026. If a lockup ever looks unusually thin again, check the attribute casing first.

### Raster and reverse variants

Every mark ships as SVG plus a transparent PNG, and every black mark has a white counterpart:

| Mark | Light surface | Dark surface | PNG size |
|---|---|---|---|
| Lockup | `lockup-primary` | `lockup-reverse` | 2400 × 234 |
| Wordmark | `wordmark` | `wordmark-reverse` | 2400 × 248 |
| Accent wordmark | `envisioning_light` | `envisioning_dark` | 2400 × 240 |
| Symbol | `symbol` | `symbol-reverse` | 1024 × 1024 |

`public/envisioning-brand-assets.zip` bundles all of the above with a README, and is the "Download all assets" target on `/about/brand`. Regenerate it whenever a mark changes.

The `*1200x630`, `*1920x1080`, `*800x600`, `opengraph-image`, and `twitter-image` PNGs in the same folder are **social cards** (navy mark on a full lime field), not logo files. Do not offer them as logo downloads.

## Sources

- Brand page, marks, and the asset bundle: <https://www.envisioning.com/about/brand>
- Envisioning Octa, the display face, with specimen and downloads: <https://www.envisioning.com/about/brand/octa>
- Octa variable font, straight to a stylesheet: <https://www.envisioning.com/fonts/EnvisioningOcta-VF.woff2>
- What the site knows about itself, for LLMs: <https://www.envisioning.com/llms.txt>
- The shared design layer the Core and Meet apps build on: <https://github.com/envisioning/envisioning-ui>
- The typeface, and the package that vends it: <https://github.com/envisioning/envisioning-octa>
- Any page of this site as Markdown: send `Accept: text/markdown`.
- This file follows the [`@google/design.md`](https://github.com/google-labs-code/design.md) format.

Questions about brand use, or a format that is not on the brand page:
<https://www.envisioning.com/contact>.
