LandingCoin Cartel

Design system · v0.1

STYLE GUIDE

Every screen in Coin Cartel is built from the components below. Each section is live — the demos are the real components with real state, so what you see here is what ships. Where a component has a rule that is not obvious from the API, that rule is written underneath it.

Use the motion switch in the header to compare the animated and reduced states. Everything respects prefers-reduced-motion by default.

01Foundations

Colour, surfaces & texture

Three surface tiers, five signal ramps, and a set of pattern treatments for the game's risk motif. Everything below resolves to these tokens — no ad-hoc hex values in components.

Base · Noir

Page, section and overlay surfaces. Darkest values sit at the back.

Primary Brand · Acid

Cash, product, growth, and anything trending up.

Prestige · Gold

The wordmark, banked money, rank, and the ready state.

Legitimate · Ice

Bank, energy, information, and anything the state can see.

Danger · Heat

Raids, damage, busts, and the bounty on your head.

Cost · Rust

Dirty money and anything that spends more than it returns.

Text

Primary white on noir surfaces; slate-400 is the workhorse secondary.

tailwind.config.ts → theme.extendSix ramp families cover the entire product. Adding a colour outside these ramps needs a design review — that's the point of the constraint.

Surface tiers

Base

Page

bg-noir-950

Raised

Sidebar, table

bg-noir-900

Card

Default panel

bg-noir-700

Radii

rounded-md

6px · Inline chips, tiny tags

rounded-lg

10px · Buttons, inputs

rounded-xl

14px · Nav rows, list items

rounded-2xl

18px · Cards, panels

rounded-full

9999px · Pills, dots, avatars

Elevation & glow

shadow-panel

Default resting elevation on a card

shadow-panel-lg

Modals, popovers, the top bar

shadow-glow-acid

Primary action, cash, growth

shadow-glow-gold

Banked money, rank, ready state

shadow-glow-ice

Bank channel, energy, information

shadow-glow-heat

Danger: raids, damage, busts

shadow-glow-rust

Dirty money and expensive actions

shadow-*Glows are not decoration. They mark the one thing on screen that wants a click, or the one thing that has just become ready.

Texture

The pattern layer. Grid and haze carry the noir; hazard tape is the raid motif and is the only one allowed to sit on a card that has content.

bg-cartel-grid

Blueprint grid. Page-level, under content.

bg-cartel-haze

Two corner radials. The ambient brand wash.

bg-cartel-scanlines

CRT scanlines. One per screen, ≤6% opacity.

bg-cartel-hazard

Gold hazard tape. The raid / bust motif.

bg-cartel-hazard-heat

Red variant, for an active bust.

.hazard-edgeHazard tape is a 4px mask on the top edge, never a fill. A filled panel would drown the payout and risk numbers that the player came to read.
02Typography

Type scale

One display face for the wordmark, one for headings, and a mono face reserved for anything that counts. Numerals are always tabular so timers don't jitter as digits change.

wordmark36px / 1.0

COIN CARTEL

Logo only. Anton is a display face and has no lowercase — never for UI copy.

h130px / 1.1

Weed Farm

Screen titles. Uppercase, one per screen.

h220px / 1.2

Grow Plots

Section headers, card group titles.

h316px / 1.4

Dirt Weed — Plot 2

Card titles, list item names. Sentence case is fine here.

subtitle14px / 1.5

Runner · Level 5 · The Ridgeline

Supporting line beneath a header.

body14px / 1.65

Six grow plots behind a cedar fence. The whole thing smells, and everyone knows it.

Prose, job descriptions, flavour text.

label11px / 1.4

Raid risk

All-caps eyebrow above values and bars.

numeric18px / 1.2

04:12

Timers and counters. Always tabular so digits don't jitter.

numeric-lg24px / 1.1

$1,250

Hero currency counters and the ledger.

font-mono + tabularAny value that changes over time must use the mono face with tabular figures. Proportional digits shift the layout on every tick, which reads as jitter at 10Hz.
font-wordmarkAnton ships without a lowercase and collapses at small sizes. It is loaded for the logo only, and never appears in UI copy.
03Badges & tier

Tier pills, status & risk

Tier says which side of the law a job is on. Status says what a place is doing right now. Risk says what it can cost you. Three axes, three components, never shared.

Tier

Selected: Underground
CleanCorporateCleanClean

Corporate — Launder it, file it, buy the block. Slow, quiet, and it compounds.

DirtyUndergroundDirtyDirty

Underground — Keep it raw and spend it fast. Every payout raises your heat.

TierPill · TierToggleThe toggle commits the player to a path. The pill only labels one. Never use a pill where a choice is being made — a player who can't tell them apart will tap the wrong thing.

Status badges

CleanRunningExposedGuardedHotLocked
xs · glow:CleanRunningExposedRunningHot
  • CleancleanNo active heat here. Standard risk, standard take.
  • RunningrunningSomething is live in this building right now.
  • ExposedexposedUnits on the door. Raid chance roughly doubled while it holds.
  • GuardedguardedArmed detail inside. Entry costs more and alarms sooner.
  • HothotActively being raided. Nothing can be claimed until it burns out.
  • LockedlockedNeeds a level or a contact you have not earned yet.
StatusBadge status=… size=… glow?glow is meant for exactly one badge in view at a time — the location the player is standing in. Glowing everything is the same as glowing nothing.

Risk — the raid ring

A shape, not a bar. Every payout in the game is paired with the probability of losing it, and it has to read as a different kind of number from health, energy and heat.

Inline form:5%12%30%Bands: <10% low · 10–24% moderate · ≥25% high
riskBand(pct)The bands are fixed, not relative to the job on screen. A 20% raid is “moderate” whether or not a 40% one is on the same screen — a scale that shifts under the player is a scale they can't learn.
liveThe rotating sweep only runs when the job is actually in flight. Four static rings spinning at once is noise, and the sweep would pull the eye away from the countdown next to it.

Composed — how they read together

DirtyHarbor — import runExposed34%

Tier says the job is underground. Status says units are on the dock. The ring says the run gets rolled a third of the time. All three are readable before the player spends a point of energy.

ComposedTier on the left, the job in the middle, status and risk on the right, actions beneath. Keep this reading order consistent across every panel.

Band reference

  • lowUnder 10%. The default state of most street work.
  • moderate10–24%. Noticeable enough that a bad night costs a plot.
  • highAbove 25%. Only worth it when the payout is disproportionate.
04Header counters

Currencies, vitals & heat

The top bar. Three balances, two meters, one bar that is bad when it is full, and the player identity pill that anchors the layout.

Currencies

957588888
  • cashSpendable now. Earned by jobs. Loses value to nothing — it just leaves.
  • bankProtected and passive. The only balance that earns interest and lowers heat.
  • dirtyUntraceable. Worth less than cash, raises heat, and must be laundered to be spent on anything real.
CurrencyPill currency=… size=… animated?Three balances and never a fourth. animated eases the number to its new value so a payout reads as a tick-up. Keep it on the header counters only — animating a hundred ledger rows is noise.

Vitals, heat & combat

Health100/100

A loss in a hostile job costs the full difference.

100% remaining
Energy85/100

Spent on every job. Restores over time and at the Hospital.

85% remaining
HeatNoticed31/100
Attack13+2Defense13Power26
VitalityBar · HeatMeter · StatChipHealth, energy and heat are capped meters. Attack, defense and power climb forever, so they get a chip instead of a bar — a bar implies a ceiling the player can't see. Heat is deliberately the inverse of every other meter: full is bad, and it is the only bar that drains on its own.

Player profile

Kevin WilliamsLV 5
Underground · United States
42% to 6
Kevin WilliamsLV 5
Runner
42% to 6

Full variant, then the compact one. The compact variant drops the subtitle and the trailing balance — it survives down to a 380px viewport where the full pill would wrap.

ProfilePill size=…The level bar is the same ProgressBar as the vitals at xs height. One component, two jobs — if level progress ever needs a different visual, it should be a different component.
06Timers

Countdowns & cycles

The core verb of the game. Use the transport controls to drive one to completion and watch the state transition to Ready to claim.

Cycle cards

Skunk — Grow

Presses itself out on a timer. Claim before the neighbours notice.

Time remaining02:00
In progress
0% · cycle 2m

Laundering Cycle

Turns dirty into bank. Completes on its own; there is no skill in waiting.

Time remaining04:00
In progress
0% · cycle 4m

Getaway Driver

Holds the car at the safehouse. Skip it and the heat follows you home.

Time remaining03:00
In progress
0% · cycle 3m

Custody Hold

Your person is in holding. Nothing escapes until this one clears.

Time remaining05:00
In progress
0% · cycle 5m
Claimed this session:nothing yet
CountdownCard durationSec=…Use Skip to ready to jump straight to the completed state instead of waiting out a fifteen-minute cycle. The bar fills toward ready — it never refills from zero, which would read as a glitch. On completion the card takes the gold glow, which is the only gold on the screen until you claim it.

Cycle row — the same hook, no card

Energy regen

01:45

useCountdown({ durationMs })The hook is the reusable part. It ticks off an absolute end timestamp, so it stays accurate through tab throttling, and it exposes percent for the bar plus isComplete for the transition.
07Jobs

Job cards & the risk ring

The workhorse. Every job shows the same four things in the same order — what it is, what it pays, what it can cost you, and the one button that starts it.

Grow plots

Buy a seed, start the cycle, come back when the bar says ready. The risk ring is the whole decision — it is the price of leaving the plot running long enough to be worth harvesting.

Dirt Weed

Plot 1 · The Ridgeline

Dirty
Pays out$200dirty

Dirt Weed

Plot 2 · The Ridgeline

Dirty
Pays out$200dirty

Empty plot

Plot 3 · The Ridgeline

Dirty
Pays out$0dirty
JobCard tier=… value=… riskPct=…An unplanted plot keeps the card mounted and disabled rather than collapsing it, so the grid never reflows when a harvest lands. The action label is the state change — “Harvest" vs “Plant a seed” — not a generic “Go”.

Strains — the trade

  • Skunk

    Pungent and profitable. A solid mid-tier strain.

    Seed
    $500
    Grow
    5m
    Yield
    $900
    Multiple
    1.8×
    Raid
    12%
    LV 1
  • Northern Lights

    Premium strain. High yield, but the smell attracts attention.

    Seed
    $1,500
    Grow
    10m
    Yield
    $3,000
    Multiple
    2.0×
    Raid
    20%
    LV 3
  • OG Kush

    Top-shelf genetics. Massive yield, serious raid risk.

    Seed
    $4,000
    Grow
    15m
    Yield
    $9,000
    Multiple
    2.3×
    Raid
    30%
    LV 5
STRAINS in lib/data.tsThe multiple (yield ÷ seed) is the number players actually optimise, so it gets its own column. A strain locked behind a level shows the level rather than a disabled button — the tease is the feature.

A job in flight

Harbor — import run

Containers in at low tide

Exposed
Pays out$11,400banked
01:30 remaining

Import run

Runs on its own. Claim before the manifest clears.

Time remaining01:30
In progress
0% · cycle 1m

The card on the left is the same job rendered as a running JobCard with a timer prop, fed by the same useCountdown the card on the right owns.

timer={{ percent, remaining }}Passing a timer switches the card to running: the ring gets its live sweep, the payout block is replaced by a progress bar, and the action is suppressed. One card, one state — not two components.

Composed — job list

Weed Farm

The Ridgeline · Running

Northern Lights

Plot 2 · 10m cycle

Dirty
Pays out$3,000dirty
Energy
12
XP
45
Grow
10m

OG Kush

Plot 1 · top shelf

Exposed
Pays out$9,000dirty
Energy
15
XP
80
Grow
15m
Claimed:nothing yet
08Locations

Fast-travel rows

Right-sidebar rows. Select one to see the selected treatment; the active row is the only place a status badge gets its glow.

Weed Farm

The Ridgeline · Product

Running

Six grow plots behind a cedar fence. The whole thing smells, and everyone knows it.

Travel
18m
Energy
−8
Payout
$900 – $9k
Status
Running

Fast travel

LocationList locations=… activeId=… onSelect?The whole row is the hit target, and the active row is the only one that gets the acid glow — so “where am I” is answerable without reading.

By district

The Exchange

The Ridgeline

The Flats

Old Town

LOCATIONS in lib/data.tsDistricts are a view concern, not a data field — they are derived at render time so adding a place never means editing a second list. Order the map, not the component.

Variants

Without travel time

BankFinanceThe ExchangeGuarded
Weed FarmProductThe RidgelineRunning
HarborImportThe FlatsExposed
AirportCrossingThe FlatsClean

showTravel={false} — for lists where travel time would be noise.

Status ladder

CleanNothing happening
RunningSomething live
GuardedArmed detail
ExposedUnits on the door
HotActive raid
LockedNot yet earned

Severity increases left to right; hue is never the only signal.

StatusBadgeThe status ramp runs acid → rust → gold → heat with slate for locked. Gold sits between exposed and hot on purpose: a guarded building is a cost, not a threat.
09Toasts

Notification suite

Six variants on react-hot-toast, one per outcome. Each carries its own channel label, icon and glow so the player can read the result from the corner of their eye.

Fire one

pushToast(variant, payload, options?)Every message routes through one function, so a new notification type is a variant definition plus a named wrapper — never a bespoke toast() call at the call site. The wrappers carry the game's voice; the components carry the chrome.

Static reference

Every variant, rendered without dismissing. Use this to compare the borders and glows side by side rather than chasing live toasts.

Cash · Clean

Payout cleared

Counted in the back of the lot. The account is legitimate again.

+$2,400

cash-in · Cash · Clean

Dirty · Underground

Sold on the street

Cash in hand. Untraceable, and worth less than it looks.

+$1,850

dirty-in · Dirty · Underground

Banked · Protected

Laundering cycle complete

The balance is clean. The paperwork is not, but nobody reads it.

+$888

bank-in · Banked · Protected

Raid

Raid on the Ridgeline farm

Two plots seized. Heat is up and your contact is not answering.

−$200

raid · Raid

Cycle complete

Northern Lights, Plot 1 is ready

Collect it now or it starts losing value while you wait.

ready · Cycle complete

Coin Cartel

The heat cools

Laying low drops your wanted level over the next few cycles.

info · Coin Cartel

Channel labelThe small-caps eyebrow says the channel before the title says the event: Cash · Clean, Dirty · Underground, Banked · Protected. The player learns which column a payout lands in by its colour and its label together.
meta chipThe mono chip on the right is the only place a delta appears, and it is always signed. A player glancing at the corner can see whether they gained or lost without reading a word.

Voice rules

  • One outcome per toast

    Never stack a win and a loss into one message. The player has to be able to answer “did that cost me anything?” without reading twice.

  • Lead with the outcome

    “Raid on the Ridgeline farm”, not “A raid occurred”. The title is the news; the description is the colour.

  • No exclamation marks

    A game's toast is a terminal line, not a marketing banner. The rarity is the styling, not the punctuation.

  • Failures always cost something visible

    Every negative toast carries a signed meta chip. “Something went wrong” is not a message — “−$11,400” is.

  • Ready beats done

    A completion toast says what is ready and what happens if it is left. Waiting is itself a decision with a cost.

lib/toasts.tsxThe named wrappers live here rather than inside the component so a non-React module can trigger a notification without importing a component. Rules belong with the copy they govern.
10Actions

Buttons & inputs

Buttons are named for what they do to the world. Destructive and dirty-money actions get their own treatment so a misclick is hard.

Variants

Button variant=…primary for the one thing you want done. dirty, bank and danger are the only ones that get a glow — they are the only ones that move money irreversibly.

Sizes

Button size=…Minimum 28px hit target on desktop, 36px minimum anywhere tappable. The xs size is for dense rows and demo controls only. A disabled button carries the reason in its label — a greyed-out button with no explanation is a support ticket.

Inputs

Two-step destructive confirm

Burning a plot is the only action with no undo in the game, so it takes two clicks and the second one names the target out loud.

Armed stateAnything irreversible gets a two-step confirm, and the destructive button only exists after intent. A stray click can never cost a player a whole season of growing.
11Composed shell

The dashboard, assembled

Every block above, in the arrangement the real dashboard uses. This is the reference for assembling the game screens.

The reference arrangement. Every screen in the game drops a subset of these blocks.

← Back to the landing page

Vitals

Health100/100
100% remaining
Energy85/100
85% remaining
HeatNoticed31/100
Kevin WilliamsLV 5
Underground · United States
42% to 6
957588888
Attack13+2Defense13Power26

Jobs

The Ridgeline · three plots online

Exposed

Skunk

Plot 1 · 5m cycle

Dirty
Pays out$900dirty
Energy
8
XP
20

Northern Lights

Plot 2 · 10m cycle

Dirty
Pays out$3,000dirty
Energy
12
XP
45

Timers

Two cycles running

Northern Lights — Grow

Premium strain. High yield, but the smell attracts attention.

Time remaining10:00
In progress
0% · cycle 10m

Laundering Cycle

Turns dirty into bank. Completes on its own; there is no skill in waiting.

Time remaining04:00
In progress
0% · cycle 4m

Fast travel

Ledger

This session

  • Harbor import run+$11,400
  • Skunk harvest+$900
  • Northern Lights seed−$1,500
  • Raid on Plot 3−$200

Wanted

34%5%30%

Live jobs, wanted rivals and the standings all read from the same risk bands as the cards.

Composed shellThree columns, always in this order: navigation and the player's own state on the left, actions in the middle, the world and the ledger on the right. Money moves right to left — the ledger on the right is the record of what the buttons in the middle just did.