Design
Version 2. This file governs visual decisions. Tokens live in `tokens/tokens.json` and compile to `src/styles/tokens.css`. If a value is not in the tokens, it does not ship.
The system is the orbit. The orb is the destination: four circles overlapping, one per layer. Inside out: Design, Product, Operations, Strategy. Stars and star map lines come later. The earlier lens field still paints the current pages. Do not add new lens UI.
Type, decided: Bricolage Grotesque ExtraBold for headlines, Instrument Sans for body, Martian Mono for labels. Tagline: Strategy, made visible.
Color
Brand ink and brand paper are the surfaces. Statement color is ultramarine, flat, one statement button per view. Pillars: strategy ultramarine, operations rose, product jade, design persimmon.
The orb circles use paired brand gradients: grove (teal to jade), dusk (ultramarine to orchid), ember (rose, persimmon, apricot), bloom (orchid to rose). On a dark surface the overlap uses Screen at 50 percent. On a light surface it uses Multiply at 65 percent. Match the blend to the surface behind the orb. The glass theme keeps those circle positions in white, from 50 percent to 6 percent, with a white hairline and background blur, and it does not use the overlap bleed.
Paint styles are dusk, bloom, grove, ember (rose, persimmon, apricot), afterglow (ultramarine, orchid, rose), and blend (ultramarine, orchid, rose, persimmon, jade).
No purple to blue page gradient. Gradients stay inside the orb.
Type
Headlines are Bricolage ExtraBold, sentence case. Labels are short, mono, and uppercase. Body is Instrument Sans. The web styles have a desktop, laptop, tablet, and mobile size. Those sizes interpolate between the four anchors. Mono labels stay one size.
Space and layout
An 8 point scale. Every value has a desktop, laptop, tablet, and mobile mode. The order in the table is desktop, laptop, tablet, mobile.
| Token | Desktop | Laptop | Tablet | Mobile | Rule |
| space/xs | 8 | 8 | 8 | 8 | |
| space/sm | 16 | 16 | 14 | 12 | |
| space/md | 24 | 24 | 20 | 16 | |
| space/ml | 32 | 32 | 28 | 24 | |
| space/lg | 48 | 44 | 40 | 32 | |
| space/xl | 96 | 80 | 72 | 56 | |
| space/2xl | 128 | 112 | 96 | 64 | |
| margin/page | 120 | 64 | 40 | 24 | |
| section/gap | 64 | 56 | 48 | 40 | Space between blocks inside a section |
| section/pad-sm | 120 | 96 | 80 | 64 | Bands and short sections |
| section/pad | 160 | 128 | 104 | 80 | Default section top and bottom padding |
| section/pad-lg | 176 | 144 | 120 | 96 | Top and bottom padding for statement sections |
| band/pad-x | 64 | 48 | 40 | 28 | |
| band/pad-y | 64 | 56 | 52 | 48 | |
| grid/gutter | 24 | 24 | 20 | 16 | |
| grid/item-min | 260 | 260 | 260 | 260 | Minimum width of an item in a wrapping row. Rows wrap to one column when items cannot keep this width. |
Desktop is 1440 wide, 12 columns, margin 120, gutter 24. Laptop is 1024 wide, 12 columns, margin 64, gutter 24. Tablet is 768 wide, 8 columns, margin 40, gutter 20. Mobile is 390 wide, 4 columns, margin 24, gutter 16. Columns and gutters bind to grid/gutter and margin/page.
Design at 1440, 1024, 768, and 390. Desktop covers 1280 and up, laptop 1024 to 1279, tablet 640 to 1023, and mobile under 640. Pages are components. Smaller sizes are instances with the Layout collection set to that mode. bp/desktop and bp/mobile switch wide and narrow art. Wide art is desktop and laptop. bp/nav-full and bp/nav-compact switch the nav. The full nav starts at tablet. In code, sizes interpolate between the anchors. Layout switches happen at the breakpoint. Scroll pins shorten to 60 to 80vh or turn off under 640. hero/min-height is 960 / 640.
grid/card-max is 384, 283, 334, 342 so a card grid runs 3, 3, 2, 1 and a lone last card keeps that column. grid/card-max-2 is the 2-up column on a 1200 content width. grid/quad-min is 288, so four pillars run 4, then 2, then 2, then 1. art/band-orb is 420, 300, 150, 150. band/content-max is 672. Section headers cap at 900 large and 640 medium. size/touch is 40 with a pointer and 44 on tablet and mobile. The contact panel is 480 by 1024, inset 16 from tablet up, and full screen on mobile. The nav menu is 380 wide.
Depth
Glass stays rare, about one surface in five. Do not nest a pane inside a pane. The orb is not a pane. Reduced transparency removes the orb blur and turns panes solid.
Motion
Motion brings things together. It travels toward the orb or along an orbit. Nothing flies off screen for effect.
A hero can loop. Everything else moves with the reader. Never two loops in one viewport. Ambient loops take seconds. Anything the reader touches answers within the state duration.
Every animation lists a reduced motion state. Respect prefers-reduced-motion. Reduced motion keeps the end state: no pins, the finished constellation, separated layers, and full bars. The Storybook control uses the same still through `.reduce-motion`.
This slice does not pin the page. Do not override wheel or touch events. Later section motion, when it is built, is scrubbed by default: linear, one to one with the scrollbar, and it plays backward on the way up. That is the constellation, the layers, the mix bars, and depth parallax. Scrubbed motion trails the scrollbar by the scroll catchup duration so a wheel or trackpad does not stutter. Scroll linked progress and the wave use the linear ease, which the sheet lists as none.
The hero holds for 150vh while the constellation draws point by point and the last line reaches the orb. How I work holds for 120vh while the four planes separate and each pillar arrives with its plane. Mix bars fill as a row travels from the bottom of the viewport to its middle, with no pin. Under 640, pins shorten to 60 to 80vh or turn off, and parallax drops to one layer.
Timed motion is not scrubbed. Hovers, presses, the contact panel, copy email, first load resolve, and ambient loops use the duration tokens.
Entering and coming into focus use settle. A leave uses the state duration and the exit ease, including hover out. Breathe and drift use ease-loop, written ease-in-out (sine). Each orb circle drifts on its own loop, from the drift minimum to the drift maximum. The orb breathe and the wave cycle use the loop duration.
Text staggers on the text stagger: eyebrow, headline, then body. Stars gather on the star stagger, stepped by angle. Wave lines stagger on the wave stagger, per line.
A line draw that is not scroll linked, such as a single diagram, uses the draw duration. Stars travel the links in to the orb on the gather duration. Pixel resolve on first load uses the resolve duration. Small reveals inside a section, the adaptive lead, and the focus state use the lead duration. A section change and the stat count use the section duration.
Rules now:
- Animate only transform and opacity. Never animate blur.
- The stat counts once, then rests. Reduced motion shows the final number.
- A button hover uses the tap duration, at the hover opacity. Pressed uses the instant duration, the pressed opacity, and the inner shadow. The focus ring is instant.
- A link hover uses the tap duration: ultramarine, an underline, and the arrow moves 4 px right.
- A work row hover uses the state duration. The title becomes ultramarine, the arrow moves, and the layer mark bands spread.
- A case study card hover uses the state duration. It lifts 4 px, and its stars drift 8 px toward the orb.
- The theme toggle uses the state duration. The active pill slides, and tokens swap on the root.
- The orbit nav takes the theme of the section under its middle. Sections declare data-theme paper or ink. Paper pins light glass and dark text. Ink pins dark glass and light text. Brand sections count as paper. The behavior frame cross fades fill, stroke, and text on the state duration with settle. In code the motion check allows only transform and opacity, so the theme swaps with no color transition. Reduced motion swaps instantly too.
- Scroll down translates the fixed nav away on the state duration. Scroll up brings it back. A move smaller than space/2 does not count. The Work menu and the sheet keep the bar visible. Reduced motion still hides, without the slide. This is the one control that leaves the screen, so the bar covers less of the page. It stays fixed while it is showing.
- A motion set is one Storybook story, with a progress or play control and a reduced motion toggle. Scroll pinning is not built. The levels map dots on the AI presence study use the LevelsDrift orbit.
Components
Each orbit kit component has a story and a "Do not use when" note. Storybook holds foundations, those components, and guideline templates. It does not save live pages, and it does not list the current page shell, including SkillMark. The Node cell is the Figma frame. The story records the same id in `parameters.figma.nodeId`. A state, size, or theme is a story of that component, not a new file. Before adding a file, name the row that was considered. A new file is allowed only when no row fits, and the same change adds the row and the node id. The current page shell has no orbit node.
| Component | Use | Do not use when | Node |
| Orb | The destination mark, including a focus state and a glass treatment | It would sit in a dense star field | 30:174 |
| OrbToPlanes | The four layers flattening from the orb into planes | The orb should stay a mark, or the page would pin to play it | 121:1147 |
| Mark, LogoLockup | The color mark, or the same mark in ink or paper, with the name | Below the nav size, or as a second mark on one view | Mark 30:159. LogoLockup 118:72 |
| ActionButton, TextLink, ThemeToggle | One primary action, a text link, or light and dark | A second statement button, or a link that is the only action | ActionButton 43:321. TextLink 118:82. ThemeToggle 118:93 |
| NavLink, OrbitNav, OrbitFooter | The orbit nav and footer. Work opens the menu. The sheet is the mobile bar | A second nav, or in place of the current page shell | NavLink 118:103. Open 289:7894. Menu item 289:7924. Menu 289:7943. Sheet 290:7964. OrbitNav 118:151. Desktop 118:104. Footer 118:152 |
| Pillar, Chip, Divider, MixBar | The four layers, a category, a rule, or a share | A single pillar, or a share that was not set | Pillar 119:123. Pillar mix 119:134. MixBar 143:2188. Chip 43:322. Divider 119:91 |
| CaseStudyCard, ContactBand | A project card, or the ultramarine close | A card without a real project, or the band on paper | CaseStudyCard 119:164. ContactBand 119:284 |
| TextField, ChoiceChip, CopyEmail, FormAlert, IconButton, ContactPanel | The contact panel and its fields. The forms field group is this row, not the lens field | Until the email and resume are real | TextField 119:305. ChoiceChip 155:1232. CopyEmail 155:1254. FormAlert 155:1263. Field group 156:1316. IconButton 155:1219. ContactPanel 161:851 |
| UnlockGate | The password gate for a case study. The star system sits behind the copy. Default, typing, revealed, error, and unlocked are states | On a public page, or as a second lock on home, about, or writing | 346:16420 |
| PixelStar, PixelGlyph, StarNode, StarLine, OrbitDial, Reticle | Map marks and the dial | As bullets, cursors, or unlabeled icons | PixelStar 119:1308. PixelGlyph 205:5444. StarNode 64:1018. StarLine 190:5226. OrbitDial 64:1004. Reticle 205:5445 |
| OrbitSync | The ambient loop of the four orbits | A loader, a progress meter, or a pinned section | 192:14963 |
| LevelsDrift | The ambient levels map. Each level turns on its own orbit | A loader, a progress meter, or a pinned section | 331:20449 |
| LineDrawIn | A diagram stroke that draws once on load | The stroke should follow scroll, or the view already has a moving diagram | 120:232 |
| SectionHeader | Eyebrow, headline, optional body | The block is only a label, or the headline is a button | 119:102 |
| Stat | One real number and a mono label | The number is not a real result | 119:281 |
| Quote | Exact words, with name and role | The words are paraphrased, or the name is missing | 227:4965 |
| WorkRow | One project, with four layer shares from 0 to 1 | The shares were not set for that project | 119:163 |
| Field | The earlier page background, until those pages are rebuilt | A new screen, or the Storybook canvas | Current shell |
| Pane, Icon, Label, Button, Nav, Footer, SkillMark, PageShell | The current page shell | A new orbit screen that should use the components above | Icon 279:8033. The rest is the current shell |
OrbitSync is the ambient orbit loop in Storybook. LevelsDrift is the levels map loop in Storybook and the orbit of the dots on the AI presence project map. The rings, the Level 0 plate, and the dust on that map stay still. Scroll pinning is not built. The live Button, Nav, Footer, and Field stay on the current pages.
The orbit nav is fixed, space/md from the top, and the page scrolls under the glass. Each section declares data-theme paper or ink for the light page. Ink bands invert when the page theme is dark, and the bar flips with them. The contact band is brand, so it declares paper and data-nav-lock and stays paper. The bar sets data-over when a section edge crosses its middle. Scroll down translates the bar away. Scroll up brings it back. The Work menu and the sheet keep it visible. The sheet keeps the page theme. The Work menu opens in the bar’s current theme.
Voice
Copy rules live in `voice.md`. Offer copy describes capabilities and never names an industry.
Banned
- Glass on every surface
- Nested panes
- Identical three column icon cards
- Stock hero copy
- Emoji bullets
- A purple to blue page gradient
- Scroll or touch hijacking
- Animated blur
- An industry named in offer copy
- Raw hex, raw px, or raw ms outside the token layer
- Invented metrics, quotes, or outcomes
Done
A slice is done when contrast holds on both themes, the phone layout still reads, the banned list is clear, and `npm run check` passes. Overrides go in `docs/decisions.md`. A visitor facing change also gets a file in `src/content/changelog/`.