The eight-ray spark
A vertical, a horizontal and two diagonals through the centre of a 100-unit box. The four lines are identical in every source — the landing's favicon.svg and Mark.jsx, the app's app/icon.svg and LogoMark — so the geometry is foundation. Only the stroke weight and the line cap belong to a surface, and each is drawn below at 24, 40 and 64px.
--mark-ray-start—--mark-ray-end—--mark-diag-start—--mark-diag-end—The vertical runs from (50, 4) to (50, 96) and the horizontal from (4, 50) to (96, 50); the diagonals from (19, 19) to (81, 81) and (81, 19) to (19, 81). The tokens are unitless numbers because SVG attributes are.
--mark-stroke-inline—--mark-linecap-landing—Mark.jsx's default: stroke 8, square caps, on the bare 100-unit box with overflow visible so the caps can poke past it. The hero's tile passes 10 and the wordmark's sparks 12.
--mark-stroke-favicon—--mark-linecap-landing—favicon.svg: the same lines inside a group scaled to 0.74 about the centre, so the heavier 11-unit stroke fits a tab icon without clipping.
--mark-stroke-app—--mark-linecap-app—app/icon.svg and LogoMark: stroke 12 with butt caps, so the rays stop exactly at their endpoints, and a padded −4 −4 108 108 viewBox that gives the stroke its room instead.
The two surfaces draw two silhouettes of the same mark — square caps at 8 (or 10, 12) on the landing, butt caps at 12 in the app — and neither app says which is canonical. The brand folder in this repo had to pick one for brand/logomark.svg; the tokens record both so neither drawing is lost.
The accent ramp
Five steps around the one hex that is exact and never substituted. 500 is the accent on both surfaces; 600 is the hover; 400 is "for strokes and marks" in the app and the landing's focus ring on dark; 300 is the lavender that carries the accent onto dark grounds; 700 is the deep end of the landing's cobalt band gradient.
--blue-300—Lavender on dark: landing kickers, keywords and the payoff line; both surfaces' accent-soft.--blue-400—App accent-bright and focus ring; landing focus ring on dark grounds.--blue-500—Exact. Both surfaces' --accent resolves here; the check script fails otherwise.--blue-600—Both surfaces' accent-hover; the landing's kicker and accent text on the light page (AA on gray-100).--blue-700—The deep end of the landing's cobalt band.--white on --blue-500, measured ≥ 4.5:1 by the check script.
Signal teal and electric violet
The landing's second and third accents (--accent-2, --accent-3, from tokens/colors.css). Only the landing declares them today, but they are brand palette rather than landing chrome, so they live here for any surface to reach.
--teal-300—--teal-400—--teal-500—--teal-600—--teal-700—
The app's macro category, --cat-macro, is a near-duplicate of --teal-400 that is not the same value; the two teals were picked independently.
--violet-300—--violet-400—--violet-500—--violet-600—--violet-700—
Distinct from the indigo product ramp below, which is a product colour, not an accent.
Cool for the landing, navy for the app
The two apps do not share a grey. The landing's is a cool neutral — "professional, precise, terminal-adjacent" — on a mostly light page where the cobalt is a block of colour. The app's is navy-tinted — "cool and slightly blue rather than neutral-grey, which is what lets a blue accent sit on them without the page reading as two unrelated colour families" — on a mostly dark page where the cobalt is small strokes and fills everywhere. Each surface aliases its own ramp as --gray-50 … --gray-950.
Landing: 100 is the page, 300 the hairline, 900 the near-black text. App: 950 is the dark page, 900 the panel, 50 the strong text; the light theme flips the ramp's roles. Pure white and black are tokens too: --white — and --black —.
Crisp, finance-leaning
Four inks and four pale tints from tokens/colors.css: sage is success / up, oxblood danger / down, slate neutral info, amber warning. The app repeats sage, oxblood and amber as literals and leaves out slate and the tints.
--sage-500—Landing --success; app --up and the equities category.--oxblood-500—Landing --danger and the payoff loss wash; declared in the app, unused there.--slate-500—Landing --info.--amber-500—Landing --warning; app impact-med, the political category and the light bands' featured gold ring.--sage-bg—--oxblood-bg—--slate-bg—--amber-bg—--neutral-cool-700), as the landing bundle's Alert sets it.
The bundle's Badge sets the 500 hue itself as the label on the tint, and at 11px that pair is under AA: --sage-500 on --sage-bg 2.99:1, --amber-500 on --amber-bg 3.17:1, --oxblood-500 on --oxblood-bg 3.98:1 (slate's 5.90 passes). The tokens are the source's; the specimen shows the hue as a dot and inks the label the way the bundle's Alert does.
The app's market red --down is a separate, muted red (so the breaking-news alarm can be louder than price direction) while --up does alias sage-500 — the up/down pair is asymmetric in how it relates to the status hues, and the app's oxblood declaration is unused.
Yellow and gold
Two warm highlights, one per surface, and neither is the amber status hue — "a true star-yellow, deliberately warmer than --amber-500 (which is a muted status hue, not a highlight)".
--signal-yellow—
The app's --today-highlight, --spot-highlight and --star (the star deepens on the light theme). Near-black text on it measures about 12:1.
--signal-gold—
The dark band's featured-preview ring, ink and glow in the landing's trading life cycle (--lp-rm-dark-feature).
The app declares the literal yellow three separate times (today, spot, star) with no shared variable, even though its own reference page describes them as one "signal-yellow family"; --signal-yellow is that family's name.
Six 10-step ramps, two families
From tokens/product-palettes.css, lightest to darkest, one per payoff product: WARM oranges (sun, flame, clay) for the call legs and COLD blues anchored to the cobalt (cobalt, azure, indigo) for the put legs. The landing uses them for per-product theming and the wheel diagrams; the app does not use them yet, so they live here for its eventual product theming to start from.
Sun WARM · golden orange
product-palettes.css: CALL_N · content.js: Upside Wing--sun-50—--sun-100—--sun-200—--sun-300—--sun-400—--sun-500—--sun-600—--sun-700—--sun-800—--sun-900—
Flame WARM · tangerine
product-palettes.css: CALL_P · content.js: Upside Range--flame-50—--flame-100—--flame-200—--flame-300—--flame-400—--flame-500—--flame-600—--flame-700—--flame-800—--flame-900—
Clay WARM · burnt terracotta
product-palettes.css: CALL_NP · content.js: Upside Anchor--clay-50—--clay-100—--clay-200—--clay-300—--clay-400—--clay-500—--clay-600—--clay-700—--clay-800—--clay-900—
Cobalt COLD · the brand cobalt as a product ramp
product-palettes.css: PUT_P · content.js: Downside Range--cobalt-50—--cobalt-100—--cobalt-200—--cobalt-300—--cobalt-400—--cobalt-500—--cobalt-600—--cobalt-700—--cobalt-800—--cobalt-900—
Steps 400–700 are byte-identical to --blue-400 … --blue-700, but --cobalt-300 is not --blue-300: the product's light tint and the accent's soft tint are two different lavenders. Kept as literals, exactly as the source declares them.
Azure COLD · cyan-blue
product-palettes.css: PUT_N · content.js: Downside Wing--azure-50—--azure-100—--azure-200—--azure-300—--azure-400—--azure-500—--azure-600—--azure-700—--azure-800—--azure-900—
Indigo COLD · violet-blue
product-palettes.css: PUT_NP · content.js: Downside Anchor--indigo-50—--indigo-100—--indigo-200—--indigo-300—--indigo-400—--indigo-500—--indigo-600—--indigo-700—--indigo-800—--indigo-900—
The two landing files disagree on which product each ramp codes. product-palettes.css's headers say sun = CALL_N, flame = CALL_P, clay = CALL_NP, cobalt = PUT_P, azure = PUT_N, indigo = PUT_NP; content.js maps sun to Upside Wing, flame to Upside Range, clay to Upside Anchor, azure to Downside Wing, cobalt to Downside Range and indigo to Downside Anchor, and its own comment admits the image mapping is a best guess. The tokens record only the colours; the product mapping needs a decision in the landing.
Two durations, two curves
The motion both surfaces agree on, to the digit: the fast and base durations and the ease-out curve the app's reference calls "the one easing curve, everywhere", plus the landing's symmetric in-out. The slow duration is deliberately NOT here — the landing says 360ms and the app 340ms — so each surface keeps its own --dur-slow.
--ease-out—Everything that arrives: hovers, the segmented thumb, the band hairlines, the payoff line drawing in.--ease-inout—Motion that departs and arrives. Declared by the landing; the app declares no in-out curve.--dur-fast—The hero tile's hover, a colour flip.--dur-base—The pill's hover, the segmented thumb's slide, a popover's fade.--ease-out--ease-inout--dur-fastThe first two lanes run the two curves over six times --dur-base; the third runs ease-out over six times --dur-fast, so the two durations can be compared. The real timings are too quick to read by eye. The replay is CSS only: the checkbox swaps the animation's name, which restarts it. With reduced motion on, the dots sit at the end.
The landing bundle's own Mark.jsx animation (the spin and the shine keyframes) uses the CSS keyword ease-in-out rather than --ease-inout, so the mark's signature loop runs on a different curve than the one the tokens name.
The 4px unit
Both surfaces build their spacing on it: the landing as a rem scale (0.25rem … 8rem), the app as a px scale (4 … 64px) on a 16px root where "every gap, pad and control height is a whole multiple of 4". The unit itself is the one shared number.
--space-unit—The bars are drawn at the unit times the multiplier and measured back, so the page cannot show a width the stylesheet disagrees with. Each surface's own scale is on its page: landing › spacing and app › layout.