diff --git a/README.md b/README.md index dd65ed7..8fb70b7 100644 --- a/README.md +++ b/README.md @@ -42,6 +42,10 @@ point at these rather than duplicating them, so there's one source of truth. And each subdirectory: +- **`docs/brand/`** — a snapshot of how the surfaces look today (offworldlabs.com, + retina.fm, dash.retina.fm, and the dark map.retina.fm): the shared foundations + and the per-surface differences, so a new page can match the existing design. + Ships a `tokens.css` starter alongside the guide. Descriptive, not prescriptive. - **`docs/contracts/`** — the source of truth for cross-service interfaces: API schemas, event/message formats, and shared data structures. When two services communicate, the contract lives here and consuming repos reference it instead of diff --git a/docs/brand/brand-guide.md b/docs/brand/brand-guide.md new file mode 100644 index 0000000..3021bca --- /dev/null +++ b/docs/brand/brand-guide.md @@ -0,0 +1,379 @@ +# Brand guide + +> A snapshot of how the Offworld Labs surfaces look today, gathered so a new page +> can match them without guessing. This captures **what we have**, not a target +> we are aiming for: where the sites disagree, that is recorded rather than +> resolved. Treat it as a description to build against, and update it when the +> sites change rather than the other way round. +> +> Companion file: [`tokens.css`](tokens.css) holds the same values as CSS custom +> properties, so a page can consume them instead of transcribing hex codes. + +The design lives in four hand-authored surfaces, none of which shares a +stylesheet with the others: + +- **offworldlabs.com** ([`landing-page-owl`](https://github.com/offworldlabs/landing-page-owl)) — the lab +- **retina.fm** ([`landing-page-retina`](https://github.com/offworldlabs/landing-page-retina)) — the product +- **dash.retina.fm** (`Tower-Finder/dashboard`) — the admin console +- **map.retina.fm** (`Tower-Finder/frontend`) — the live radar map + +They read as one family, but each has its own voice. The map is the odd one out: +it is the only dark surface, a Flightradar24-style operational console. + +--- + +## 1. Brand architecture: one family, four voices + +The surfaces share a spine (a blue accent, green/amber status colours, a mono +micro-label, hairline rules) and then diverge in personality. The split maps +onto audience. Three are light; the map is dark. + +| | **owl** — offworldlabs.com | **retina** — retina.fm | **dash** — dash.retina.fm | **map** — map.retina.fm | +|---|---|---|---|---| +| **Role** | The lab / parent org | The product you buy | The console you operate | The live picture | +| **Audience** | Researchers, funders, press | Prospective node owners | Logged-in operators | Anyone watching the map | +| **Feel** | Editorial, institutional | Commercial, confident | Utilitarian, dense | Dark ops console (FR24-style) | +| **Serif** | Source Serif 4, wt 400 | Fraunces, wt 600 | none | none | +| **Sans** | Inter | DM Sans | system stack | Inter (only font) | +| **Mono** | IBM Plex Mono | JetBrains Mono | SF Mono | none (generic) | +| **Canvas** | warm paper `#f7f6f2` | warm paper `#fafaf9` | cool slate `#f1f5f9` | **navy `#0d1b2a`** | +| **Blue** | muted `#5b8dd9` | punchy `#2563eb` | `#3b82f6` → `#2563eb` | sky `#38bdf8` | +| **Radius** | 4–6px | 8–20px | 4–8px | 3–10px | +| **Buttons** | flat | lift + shadow | flat | flat | +| **Dark UI** | none | one dark section | none | **fully dark** | + +A rough rule of thumb for a new page: **who is it for?** Reaching outward to the +scientific or funding world reads as owl. Selling or explaining the kit reads as +retina. Anything behind a login reads as dash. Anything that _is_ the live radar +picture (a map, a track view, an operational overlay) reads as map. + +--- + +## 2. Foundations + +The parts that stay roughly constant across all four, and are the safest thing +to carry into new work. + +- **A paper-like canvas.** The three outward surfaces use a near-white + background (warm on the marketing sites, cool slate on the console) with + near-black text. The **map is the exception**: it is fully dark navy with light + slate text. So "light paper" is the norm for anything that explains or sells, + and dark is reserved for the live operational view. +- **A blue accent.** owl sits soft and desaturated, retina and dash land on the + brighter `#2563eb`/`#3b82f6`, and the map goes brighter still (sky `#38bdf8`) + to carry on dark. Blue is the only chromatic accent in the UI chrome; + everything else is ink on canvas. (The map adds an amber `#fbbf24` as a + selection/focus highlight.) +- **Green and amber as status colours.** Green marks "live" / validated / ADS-B + truth; amber marks the radar echo, anomalies, and attention. The console adds + red for outright errors. (The exact shades drift between surfaces; see §10.) +- **A small uppercase micro-label.** Small, uppercase, letter-spaced, muted. + Used for section eyebrows, stat labels, table headers, nav-section titles, and + diagram annotations. This is the single most reused idiom and the quickest way + to make a new page read as "ours". The marketing sites and console set it in + their mono; the map keeps the uppercase-and-tracked treatment for panel and + list headers even though it has no mono font. +- **Hairline rules and grid dividers.** 1px borders at low contrast separate + sections, table rows, fact lists, and card grids. Structure comes from lines, + not shadows or fills. +- **Flat and quiet.** Minimal ornamentation, few shadows (retina uses a subtle + button lift; dash reserves shadow for one dropdown), small status dots for + liveness. +- **The radar diagram.** owl and retina both open with a hand-built SVG of the + passive-radar geometry (transmitter → target → node) with animated signal + paths. It is the strongest single motif the two marketing sites share. See §8. + +--- + +## 3. Colour + +Values are grouped by role. Per-surface specifics sit in the columns; foundation +roles (semantics) are shared unless a surface overrides them. + +### Canvas and ink + +| Role | owl | retina | dash | map | +|---|---|---|---|---| +| Canvas | `#f7f6f2` | `#fafaf9` | `#f1f5f9` | `#0d1b2a` | +| Sunk surface | `#edecea` | `#f5f4f0` | `#f8fafc` | `#0f2035` | +| Card / panel | `#ffffff` | `#ffffff` | `#ffffff` | `#132240` | +| Dark section | — | `#090904` / `#242422` | — | (all dark) | +| Ink (primary) | `#0e0e0c` | `#1a1a18` | `#0f172a` | `#e2e8f0` | +| Ink (muted) | `#444440` | `#6b6b63` | `#475569` | `#94a3b8` | +| Ink (subtle) | `#888882` | `#9c9c93` | `#94a3b8` | `#64748b` | +| Border | `rgba(14,14,12,.1)` | `#e8e8e3` | `#e2e8f0` | `rgba(100,180,255,.12)` | + +The marketing canvases are warm (a hint of yellow); the console is cool slate; +the map inverts to dark navy. The map's ink ramp is the dark mirror of the +console's slate ramp (`#94a3b8` / `#64748b` recur), and its hairlines are a +blue-tinted translucent white rather than a solid grey. + +### Accent + +| | owl | retina | dash | map | +|---|---|---|---|---| +| Accent | `#5b8dd9` | `#2563eb` | `#3b82f6` | `#38bdf8` | +| Hover / strong | `#4a7cc8` | `#3b82f6` | `#2563eb` | `#7dd3fc` | +| Wash | `rgba(91,141,217,.15)` | `rgba(37,99,235,.06)` | `rgba(59,130,246,.10)` | `rgba(56,189,248,.15)` | + +owl's blue is deliberately soft and low-contrast (an editorial choice, though it +costs link legibility, see §10). retina and dash share the saturated blue, with +dash resting one step lighter and hovering to retina's resting value. The map +pushes to a brighter sky-blue so the accent reads on navy, and adds an amber +`#fbbf24` for the selection/focus highlight the others do not have. Four distinct +blues in all. + +### Semantics + +| Role | Marketing (owl, retina) | Console (dash) | Map (map) | +|---|---|---|---| +| Success / good | `#16a34a` | `#10b981` (emerald) | `#4ade80` | +| Warning / attention | `#d97706` | `#f59e0b` (amber-500) | `#fbbf24` | +| Error | (unused) | `#ef4444` (red-500) | `#f43f5e` | + +Same three roles, brightened a step on the dark map so they carry on navy. Each +pairs with a wash (~10% on light, ~15% on dark) for tinted pill backgrounds. The +console's charts extend the accent into a categorical palette (`#3b82f6, +#10b981, #f59e0b, #ef4444, #8b5cf6, #ec4899, #06b6d4, #84cc16, #f97316, +#14b8a6`, with `#94a3b8` for an "others" slice); reuse that ordering for any new +dashboard chart. + +The map's live data colours are a separate, radar-specific scheme, not these +status roles: "truth" (solver agreeing with ADS-B) is **teal `#2dd4bf`**, the +ground-truth dot is **cyan `#22d3ee`**, and radar detections run a **Doppler +gradient** from dark-blue approaching (`#1e3a8a`) through cyan at zero (`#22d3ee`) +to dark-red receding (`#991b1b`). Green there means coverage polygons, not truth. +See §8. + +--- + +## 4. Typography + +Up to three registers per surface: a **display** face for headings, a **body** +face for running text and UI, and a **mono** face for labels and data. The +console collapses everything into the system stack; the map collapses everything +into Inter. + +| Register | owl | retina | dash | map | +|---|---|---|---|---| +| Display | Source Serif 4 | Fraunces | system sans | Inter | +| Body | Inter | DM Sans | system sans | Inter | +| Mono | IBM Plex Mono | JetBrains Mono | SF Mono | generic `monospace` | + +Loaded from Google Fonts on the marketing sites and the map (Inter); the console +loads no web fonts at all (its CSP restricts `font-src`). Only the two marketing +sites use a serif; the two operational surfaces (dash, map) are sans-only. + +**Heading weight is the other big tell.** owl sets headings at 400 (light, +serious, editorial); retina at 600 (present, product-confident); dash and map at +700 (compact, functional). Marketing headings carry a tight `letter-spacing` +around `-0.02em` and line-height near 1.1; the operational surfaces run text +small (down to ~0.62rem) and dense. + +**Scale, as used:** + +- Hero H1: `clamp(3rem, 6vw, 5rem)` on owl, `clamp(2.8rem, 5vw, 3.8rem)` on + retina. Fluid, serif, tight leading. +- Section H2 / titles: `clamp(1.6rem, 3vw, 2.4rem)` (owl), `clamp(1.75rem, 3vw, + 2.25rem)` (retina). Console page titles are a flat 24px. +- Body: 16px base, line-height ~1.6–1.7, colour = ink-muted for prose. +- Micro-label: ~0.65–0.75rem, mono, uppercase, `letter-spacing` 0.04–0.14em, + colour = ink-subtle. The `.label` class in `tokens.css` captures the common + case. + +--- + +## 5. Shape, space and motion + +### Shape + +| | owl | retina | dash | map | +|---|---|---|---|---| +| Radius (default) | 4px | 12px | 8px | 6px | +| Radius (small) | 3px | 8px | 4px | 3px | +| Radius (large) | 6px | 20px | 12px | 10px | + +Crisp on owl, soft on retina, moderate on dash, tight on the map. Borders are +always 1px hairlines; nothing uses a heavy stroke. The map builds elevation from +backdrop-blur (14–20px) and subtle shadow rather than cards, since its panels +float over the map. + +### Space + +- Marketing content sits in a centred column: `max-width` 860px for prose, + 1120–1200px for wide/hero rows. The console is full-width and fluid with a + 250px sidebar. The map is full-bleed: a 280px left aircraft list (collapsing + to 36px), a 280px right detail panel, and a full-width bottom playback bar. +- Nav / header height: 56px (owl), 64px (retina), 56px (dash). +- Section padding: ~5–6rem vertical on the marketing sites, 24px content padding + in the console. +- Cards/panels: ~20px body padding, 16px header; grids gap ~16px. + +### Motion + +Three patterns recur: + +1. **Reveal on scroll.** Elements start at `opacity: 0` + a small `translateY` + and transition in via an `IntersectionObserver` toggling a class. ~0.45–0.5s. +2. **Live pulse.** The "live" / status dot pulses opacity on a ~2s loop. +3. **Signal march.** The radar diagram animates `stroke-dashoffset` so the dashed + signal paths flow from transmitter to target to node. + +owl and dash animate in CSS; retina's hero mixes in inline SVG SMIL (``). +None of the surfaces currently honour `prefers-reduced-motion`; the `tokens.css` +`.reveal` helper adds the guard, and new work should keep it. + +--- + +## 6. Components + +- **Nav.** Fixed top bar, `backdrop-filter: blur(...)` over a translucent + canvas, a hairline bottom border. Logo left (mono wordmark, or logo + mono + text on owl), links centre/left, a primary CTA right. Collapses to a hamburger + under ~860–960px. +- **Buttons.** Primary = solid ink (marketing) or solid accent (console), + inverse text, small radius. owl and dash keep them flat; retina lifts them + (`translateY(-1px)` + soft shadow) on hover. Secondary = text link with a + hairline underline (owl), or an outline button (retina/dash). +- **Section eyebrow.** A `.label` micro-label above each H2 naming the section + ("About", "Flagship project", "Capabilities"). The consistent opener for a + marketing section. +- **Fact rows / metrics.** Label-value pairs on hairline-divided rows (owl's + RETINA facts), or a bordered 4-up metrics bar (retina). Values in the display + face, labels muted. A tidy way to present specs. +- **Cards.** White, 1px border, surface radius, no shadow. Grids of cards use a + 1px gap over a border-coloured background so the dividers read as shared lines + (retina's steps/features, dash's panels). +- **Badges / pills.** Rounded (12px), tinted `-wash` background with matching + text colour, often a leading status dot. Success/warning/error on the console; + "live" on the marketing sites. +- **Footer.** Hairline top border, mono wordmark left, muted links, a `© 2026` + line. Understated. + +--- + +## 7. Voice and copy + +Plain, specific, quietly confident. The marketing copy leads with what the thing +does ("See the sky with radio echoes", "Building the instruments science is +missing"), states numbers without hype (35 mi range, VHF/UHF, open source), and +cites literature rather than asserting authority. Technical terms are used +directly, not dumbed down, but each is given a one-line plain-English gloss. + +Note the public sites use **US spelling** ("color", "Atlanta", "meter"), which +differs from the British house style used for internal docs like this one. Keep a +page consistent with its surface: US spelling on the public sites. + +--- + +## 8. The radar diagram motif + +owl and retina both open with a bespoke SVG of the passive-radar geometry. It is +worth reusing (or extending) rather than reinventing, because it carries most of +the "this is a radar project" signal. In the **marketing diagram** the colour +semantics are: + +- **Blue** = the reference / illumination signal from the broadcast transmitter, + and node antennas. +- **Amber (`#d97706`)** = the echo returning from the target. +- **Green (`#16a34a`)** = a live node LED / status. +- **Ink / grey** = structure: ground line, tower lattice, node body, and mono + annotations (`FM TRANSMITTER`, `NODE A`, `Δt → range & velocity`). + +The diagram is annotated with the same micro-label used elsewhere, which ties it +to the rest of the page. Coverage circles use a low-opacity accent fill with a +dashed accent stroke. + +**One caveat worth knowing:** this simplified green/amber scheme is a marketing +device and does _not_ match how the live map (map.retina.fm) actually colours +radar data. On the real map, "truth" is teal/cyan, radar detections run a +blue↔red Doppler gradient, and green is used for coverage polygons (see §3). So +a diagram and the live map deliberately tell the same story two different ways: +keep the green/amber convention for explanatory illustrations, and the +Doppler/teal scheme for anything showing genuine detections. + +--- + +## 9. Using the tokens + +[`tokens.css`](tokens.css) carries every value above as CSS custom properties. +The token names are unified across the four surfaces (the live sites each use +their own naming), so a page gets one vocabulary. + +```html + + +

+ +``` + +Add exactly one surface class (`owl`, `retina`, `dash`, or `map`) to ``; +it sets that surface's fonts, canvas, ink, accent, and radii. The `map` class is +dark, so `var(--ink)` is light and `var(--canvas)` is navy: components written +against the tokens invert automatically. The shared `:root` layer provides the +semantic colours, base rhythm, and the `.label` / `.reveal` helpers. + +--- + +## 10. Improvements, gaps and discrepancies + +Observations from reading the four surfaces side by side. Recorded here, not +filed as work; a snapshot of loose ends rather than a plan. + +**Divergence that looks accidental rather than intentional** + +- **Four unrelated type systems.** owl, retina and dash use three different + serifs, sans and monos between them; the map adds a fourth by going Inter-only. + The tiered personality is deliberate; the choice of *different families* for + each register reads more like independent authoring than a considered split. A + shared mono (or reusing Inter, already on two surfaces), would cost little and + tie things together. +- **Four distinct blues.** `#5b8dd9` / `#2563eb` / `#3b82f6` / `#38bdf8`. dash and + retina nearly agree; owl and map each go their own way. One accent value, or a + documented light/dark pair, would read as more intentional. +- **Token naming drift.** owl uses `--ink` / `--paper`, retina uses `--text` / + `--bg`, dash uses `--bg-primary` / `--text-primary`, and the map has no custom + properties at all (hardcoded hex throughout). Nothing is portable between them. + `tokens.css` proposes one naming; adopting it in the live sites would remove + the drift. +- **Semantic colours drift by a shade.** Green is `#16a34a` (marketing), + `#10b981` (console), `#4ade80` (map); amber and red shift similarly. Close + enough to look like one intent, far enough to not match. +- **The "truth" colour story is inconsistent.** The marketing diagrams say green + = ADS-B truth; the live map says teal/cyan = truth and reserves green for + coverage. Defensible (illustration vs data viz), but a viewer moving from the + homepage to the map meets two different colour languages for the same idea. +- **No shared stylesheet.** Every value is inlined per surface, so drift is the + default. `tokens.css` is the first step toward a single source. + +**Accessibility** + +- **owl's link/accent contrast fails.** `#5b8dd9` on `#f7f6f2` is roughly 3:1, + below the 4.5:1 AA threshold for normal text. retina's `#2563eb` is comfortably above + it. Darkening owl's blue for links (its `--accent-strong` is a start) would fix + the most common case. +- **No `prefers-reduced-motion`.** All of them run infinite animations (pulses, + dashed signal flow, ring expansions, the map's pulsing "LIVE" badge) with no + reduced-motion guard. The + `tokens.css` `.reveal` helper adds one; the SVG loops need the same treatment. +- **Focus styles.** None of the surfaces define a visible `:focus-visible` + treatment; keyboard focus rides on the browser default. + +**Polish** + +- **Missing social metadata.** None of the three set `og:image` / + `twitter:card` / canonical, so link previews are bare. A single shared OG image + per surface would help. +- **The operational surfaces carry little brand.** dash dropped the type system + for system fonts and cool slate; the map runs Inter-only on dark navy with a + brighter blue. Both are defensible for tools, but between them they share + almost nothing visible with the marketing brand beyond a blue accent and the + uppercase micro-label. Standardising the micro-label and reusing one blue would + connect all four without slowing the tools down. +- **The dark map is unaudited for contrast here.** The accessibility notes above + were checked against the light surfaces; the map's light-slate-on-navy ramp + looks reasonable but has not been contrast-tested, and it inherits the same + no-`prefers-reduced-motion`, no-`:focus-visible` gaps. +- **Inconsistent animation approach.** retina's hero uses inline SVG SMIL + (``) while owl and dash use CSS keyframes. Standardising on CSS would + make the shared radar motif portable. diff --git a/docs/brand/preview.html b/docs/brand/preview.html new file mode 100644 index 0000000..d43bffd --- /dev/null +++ b/docs/brand/preview.html @@ -0,0 +1,285 @@ + + + + + +Brand tokens preview — owl / retina / dash + + + + + + + + + + + + +
+ + +
+
offworldlabs.com · .owl
+

Building the instruments science is missing.

+

Open-source passive radar infrastructure for scientific research.

+ + + +
+ Card +

RETINA

Radio Echo Tracking by Inter-Node Analysis.

+
+ +
+ Facts +
+
Range35+ mi
+
BandsVHF / UHF
+
LicenseOpen source
+
+
+ +
+ Status +
+ live + echo + error +
+
+ +
+ Accent · wash · sunk +
accent
wash
sunk
+
+
+ +
+
retina.fm · .retina
+

See the sky with radio echoes.

+

A distributed passive radar network built on TV and radio signals.

+ +
+ Buttons + Order a node kit + View source +
+ +
+ Card +

Passive detection

No transmitter, no license. VHF/UHF broadcasts as illumination.

+
+ +
+ Facts +
+
Range35 mi
+
BandsVHF / UHF
+
Open source100%
+
+
+ +
+ Status +
+ live + echo + error +
+
+ +
+ Accent · wash · sunk +
accent
wash
sunk
+
+
+ +
+
dash.retina.fm · .dash
+

Fleet overview

+

Node health, detections, and network status for logged-in operators.

+ +
+ Buttons + Add node + Export +
+ +
+ Card +

node-atl-03

Last seen 12s ago · 1,204 detections today.

+
+ +
+ Facts +
+
Uptime99.4%
+
Reputation0.87
+
Frames/s770
+
+
+ +
+ Status +
+ healthy + degraded + offline +
+
+ +
+ Accent · wash · sunk +
accent
wash
sunk
+
+
+ +
+
map.retina.fm · .map
+

Live radar

+

The dark operational picture: tracks, detections, and coverage.

+ +
+ Buttons + Follow track + Layers +
+ +
+ Card +

UAL2456

FL340 · 471 kt · solver + ADS-B agree.

+
+ +
+ Facts +
+
Tracks38
+
Detections/s1,940
+
Nodes online6
+
+
+ +
+ Status +
+ live + selected + anomaly +
+
+ +
+ Accent · wash · sunk +
accent
wash
sunk
+
+
+ +
+ + diff --git a/docs/brand/tokens.css b/docs/brand/tokens.css new file mode 100644 index 0000000..30c72ba --- /dev/null +++ b/docs/brand/tokens.css @@ -0,0 +1,245 @@ +/* ============================================================================ + Offworld Labs / RETINA — design tokens + ---------------------------------------------------------------------------- + A snapshot of the values the existing sites use, gathered into one place so a + new page can start from them instead of re-deriving colours and fonts by eye. + + This describes what the four surfaces do TODAY, not a target design. Where + the sites disagree (see docs/brand/brand-guide.md §"Improvements, gaps & + discrepancies") this file keeps the disagreement visible rather than papering + over it: the shared spine lives in :root, and each surface's own values live + in a class you add to . + + Usage: + … // pick owl | retina | dash | map + colour: var(--ink); + background: var(--canvas); + + The token NAMES here are unified (the live sites each use their own naming — + --ink/--paper on owl, --text/--bg on retina, --bg-primary/--text-primary on + dash). A page built from this file gets one vocabulary across all three. +============================================================================ */ + +:root { + /* — Type registers (families are set per-surface below) ————————————— */ + --font-display: Georgia, 'Times New Roman', serif; /* headings */ + --font-body: system-ui, sans-serif; /* running text, UI */ + --font-mono: ui-monospace, monospace; /* labels, data, code */ + + /* Base rhythm shared by every surface */ + --text-base: 16px; + --leading: 1.6; + + /* — Semantic colours ———————————————————————————————————————————————— + The marketing surfaces (owl, retina) use these values. The console (dash) + shifts to a slate-native set; see the .dash block. Kept as one role each so + the radar legend and status pills stay legible across surfaces. */ + --success: #16a34a; /* "live", ADS-B truth, healthy */ + --success-wash: rgba(22, 163, 74, 0.10); + --warning: #d97706; /* echo, anomaly, attention */ + --warning-wash: rgba(217, 119, 6, 0.10); + --danger: #dc2626; /* error/failure (only dash uses it today) */ + --danger-wash: rgba(220, 38, 38, 0.10); + + /* — Hairlines & shape (per-surface radii below) ————————————————————— */ + --border-width: 1px; + + /* — Motion ———————————————————————————————————————————————————————————— + Reveal-on-scroll, a slow pulse for "live" dots, and a dashed march for the + radar signal paths. Wrap any of these in @media (prefers-reduced-motion: + reduce) when you use them; the live sites do not, which the guide flags. */ + --reveal-duration: 0.45s; + --pulse-duration: 2s; +} + +/* ============================================================================ + owl — offworldlabs.com (the lab / parent) + Editorial restraint: warm paper, muted blue, light-weight serif headings, + crisp small radii, flat controls. +============================================================================ */ +.owl { + --font-display: 'Source Serif 4', Georgia, serif; + --font-body: 'Inter', system-ui, sans-serif; + --font-mono: 'IBM Plex Mono', monospace; + + --canvas: #f7f6f2; /* paper */ + --canvas-sunk: #edecea; /* paper-2 */ + --surface: #ffffff; + + --ink: #0e0e0c; + --ink-muted: #444440; + --ink-subtle: #888882; + + --accent: #5b8dd9; /* soft, desaturated */ + --accent-strong: #4a7cc8; /* darker for hover (derived) */ + --accent-wash: rgba(91, 141, 217, 0.15); + + --border: rgba(14, 14, 12, 0.10); + --border-strong: rgba(14, 14, 12, 0.20); + + --radius: 4px; + --radius-sm: 3px; + --radius-lg: 6px; + + --heading-weight: 400; /* light, editorial */ +} + +/* ============================================================================ + retina — retina.fm (the product) + Commercial energy: warm paper, punchy blue, bolder expressive serif, softer + radii, buttons that lift on hover, one dark section. +============================================================================ */ +.retina { + --font-display: 'Fraunces', serif; + --font-body: 'DM Sans', sans-serif; + --font-mono: 'JetBrains Mono', monospace; + + --canvas: #fafaf9; + --canvas-sunk: #f5f4f0; /* bg-warm */ + --surface: #ffffff; + --canvas-dark: #090904; /* the dark features section */ + --surface-dark: #242422; + + --ink: #1a1a18; + --ink-muted: #6b6b63; + --ink-subtle: #9c9c93; + + --accent: #2563eb; /* saturated */ + --accent-strong: #3b82f6; /* accent-light, used for hovers/highlights */ + --accent-wash: rgba(37, 99, 235, 0.06); + --accent-border: rgba(37, 99, 235, 0.15); + + --border: #e8e8e3; + --border-strong: #f0f0eb; /* the site calls this border-light */ + + --radius: 12px; + --radius-sm: 8px; + --radius-lg: 20px; + + --heading-weight: 600; /* product-bold */ +} + +/* ============================================================================ + dash — dash.retina.fm (the admin console) + Utilitarian ops tool: cool slate, brighter blue, system fonts (no brand + typefaces), dense tables, the full success/warning/error set. +============================================================================ */ +.dash { + --font-display: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; + --font-body: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; + --font-mono: 'SF Mono', 'Fira Code', monospace; + + --canvas: #f1f5f9; /* slate-100, cool not warm */ + --canvas-sunk: #f8fafc; /* slate-50 */ + --surface: #ffffff; + + --ink: #0f172a; /* slate-900 */ + --ink-muted: #475569; /* slate-600 */ + --ink-subtle: #94a3b8; /* slate-400 */ + + --accent: #3b82f6; /* blue-500, one step lighter than retina at rest */ + --accent-strong: #2563eb; /* blue-600 hover — matches retina's resting blue */ + --accent-wash: rgba(59, 130, 246, 0.10); + + /* Console semantics run one shade off the marketing set (Tailwind-native) */ + --success: #10b981; /* emerald-500 */ + --success-wash: rgba(16, 185, 129, 0.10); + --warning: #f59e0b; /* amber-500 */ + --warning-wash: rgba(245, 158, 11, 0.10); + --danger: #ef4444; /* red-500 */ + --danger-wash: rgba(239, 68, 68, 0.10); + + --border: #e2e8f0; /* slate-200 */ + --border-strong: #cbd5e1; /* slate-300 */ + + --radius: 8px; + --radius-sm: 4px; + --radius-lg: 12px; + + --heading-weight: 700; + + --sidebar-width: 250px; + --header-height: 56px; +} + +/* ============================================================================ + map — map.retina.fm (the live radar console) + The only DARK surface. A Flightradar24-style operational map: deep navy + chrome, a single Inter UI font (no serif, no branded mono), a brighter + sky-blue accent plus an amber selection highlight, tight small radii, + blue-tinted translucent hairlines, backdrop-blur panels over the map. + + Because the canvas is dark and the ink is light, --btn-primary (ink on + canvas) reads as a light button on dark — the inverse of the other three. +============================================================================ */ +.map { + --font-display: 'Inter', system-ui, sans-serif; /* no serif here */ + --font-body: 'Inter', system-ui, sans-serif; + --font-mono: ui-monospace, monospace; /* generic; hex badges only */ + + --canvas: #0d1b2a; /* app / map root */ + --canvas-sunk: #0f2035; /* left sidebar */ + --surface: #132240; /* toolbar / panel body */ + + --ink: #e2e8f0; /* light slate */ + --ink-muted: #94a3b8; + --ink-subtle: #64748b; + + --accent: #38bdf8; /* brighter sky-blue than the other surfaces */ + --accent-strong: #7dd3fc; /* light-sky, for badges/hovers */ + --accent-wash: rgba(56, 189, 248, 0.15); + --accent-select: #fbbf24; /* amber — selected aircraft / focus glow */ + + /* Dark-surface semantics (brighter so they carry on navy) */ + --success: #4ade80; + --success-wash: rgba(74, 222, 128, 0.15); + --warning: #fbbf24; + --warning-wash: rgba(251, 191, 36, 0.15); + --danger: #f43f5e; + --danger-wash: rgba(244, 63, 94, 0.15); + + --border: rgba(100, 180, 255, 0.12); /* blue-tinted hairline */ + --border-strong: rgba(100, 180, 255, 0.22); + + --radius: 6px; + --radius-sm: 3px; + --radius-lg: 10px; + + --heading-weight: 700; + + /* Data-viz colours are NOT UI chrome and live in the map code, but the two + that a legend or overlay might reuse: "truth" is teal/cyan, not green, and + radar detections run a Doppler gradient rather than a single colour. */ + --data-truth: #2dd4bf; /* solver + ADS-B agreement (teal) */ + --data-adsb: #22d3ee; /* ground-truth dot (cyan) */ +} + +/* ============================================================================ + Shared idioms + The one convention every surface reuses verbatim: the mono micro-label. + Uppercase, tracked out, muted. Section eyebrows, stat labels, table headers, + nav-section titles are all this. +============================================================================ */ +.label { + font-family: var(--font-mono); + font-size: 0.7rem; + letter-spacing: 0.06em; + text-transform: uppercase; + color: var(--ink-subtle); +} + +/* Reveal-on-scroll. Toggle .is-visible from an IntersectionObserver. */ +.reveal { + opacity: 0; + transform: translateY(12px); + transition: opacity var(--reveal-duration) ease, + transform var(--reveal-duration) ease; +} +.reveal.is-visible { + opacity: 1; + transform: none; +} + +@media (prefers-reduced-motion: reduce) { + .reveal { opacity: 1; transform: none; transition: none; } +}