# Delhi Pulse — Midnight

Midnight is the dark companion to the current **Ring & River, crisp v4** identity. It keeps the same silhouette, local typography, restrained lilac/lime/mint/coral accents and original SVG icon family. It does not revive the retired v2 design. Read `public/design/brand/BRAND.md` for the base identity and `SESSION.md` for current implementation status.

The app's main experience is one interactive Three.js glass dome with Delhi on its flat base. Readings and Sky view are alternate ways to read the same city. Theme changes must preserve the scene, camera, active layers, selected object, saved places and live source state.

## Implementation

`public/design/dark-theme.css` contains the CSS implementation. Load it **after** `unified-atlas.css` and any atlas layout overrides. Set `body.dataset.theme` to `dark` or `light`; absence of `dark` keeps the current light UI. `public/design/atlas-preferences.js` implements the shared control, initial preference resolution, storage, theme-colour metadata and renderer events. This integration was checked in the browser; see `design/QA-ATLAS-2026-09-30.md`.

Integration contract:

1. Use one theme control in the shared app header. Use the existing `sun` / `moon` SVG icons, an accessible action label such as “Switch to light theme”, and a state announcement. No emoji.
2. Recommended preference key: `delhi-pulse-theme`, with values `light` and `dark`. When no explicit preference exists, resolve `prefers-color-scheme`. Once the user makes a choice, keep it across navigation and reload. Confirm the actual entry-point implementation before describing that behavior as shipped.
3. Apply the resolved theme before first paint. Set `document.documentElement.style.colorScheme` and update the `theme-color` meta tag to the resolved canvas colour.
4. Notify the Three renderer of theme changes. Read its `--scene-*` tokens from `getComputedStyle(document.body)` and update material/light colours without recreating the renderer, camera or model pools.
5. Change the basemap to a deliberate dark cartographic palette. Do not invert the map canvas or models. Streets, street names, station names and building edges need their own readable map colours.
6. Theme selection controls visual appearance. It must not change observation times, weather forecasts, astronomical positions or any data meaning.

Native atlas styling uses `@scope (#atlas-root)` and defines light tokens on that root. The dark stylesheet deliberately redefines tokens on `body[data-theme="dark"] #atlas-root`. Setting body tokens alone leaves the native layer dock and source drawers light.

## Interface palette

| Role | Colour | Use |
| --- | --- | --- |
| Midnight canvas | `#11131A` | App background and canvas surround |
| Surface | `#1B1E29` | Layer dock, source drawers, dialogs, cards |
| Raised surface | `#252937` | Quiet inner rows and secondary panels |
| Hover surface | `#2C3040` | Hovered controls and interactive rows |
| Primary text | `#F3F4F8` | Headings, readings, labels and SVG action icons |
| Secondary text | `#ADB5C7` | Source times, explanatory copy and inactive labels |
| Separator | `#3B4254` | Decorative panel and row dividers |
| Strong outline | `#59637A` | Stronger boundaries; not a substitute for focus treatment |
| Lilac | `#C6B6F3` | Current destination, active switches, primary actions, air reading card |
| Lime | `#DFFF7A` | Weather reading card and live ticker label |
| Mint | `#B9E1D3` | River/Metro accents and scene lighting balance |
| Coral | `#F27D5B` | Sparse motion/infrastructure accents |
| Ink on accent | `#13151A` | Text/icons on lilac, lime, mint and coral |
| Secondary on accent | `#42494F` | Reading source text on bright card backgrounds |

The interface is mainly charcoal and restrained surfaces. Do not paint every control with an accent. Dark cards require light text; bright brand cards require ink text. Air's lilac and weather's lime are brand grouping colours, **not AQI severity or forecast warning scales**. Source categories and official Metro line hues retain their labelled meanings.

## Type, symbols and geometry

Use the same locally served **DM Sans** weights as light mode: 700/800 for display and section headings, 500/600 for controls and card labels, 400 for explanatory copy. Use **IBM Plex Mono** for source times, coordinates and compact numeric context. Do not switch to a technology-themed display font in dark mode.

Use `currentColor` SVG symbols from the existing 63-icon family. Keep the same 12 px main panel corners, 8–10 px control corners and existing mobile touch targets. Focus outlines are lilac, 2 px minimum, with 3 px separation. A selected switch uses an ink thumb on a lilac track; a disabled layer keeps readable secondary text, rather than fading the whole row.

The CSS renders existing monochrome ink wordmarks/marks as white using a shape-preserving filter. The supplied `brand-wordmark-white.svg` and `brand-mark-white.svg` are preferred if the entry point swaps asset paths. Do not invert scene imagery, coloured icons, miniature assets or the app avatar. Keep the avatar's lilac field and ink mark.

## Three.js scene palette

The renderer receives these CSS variables. They describe presentation, never geographic or scientific truth.

| Token | Light | Midnight | Role |
| --- | --- | --- | --- |
| `--scene-background` | `#F7F8FA` | `#11131A` | Clear colour / surrounding stage |
| `--scene-ground` | `#DBDFE7` | `#252A38` | Flat context surface below the dome |
| `--scene-context` | `#A7AFBF` | `#66728B` | Passive map context outside the raised Delhi island |
| `--scene-pedestal` | `#242833` | `#242833` | Dark solid pedestal; both themes keep its silhouette |
| `--scene-rim` | `#C6B6F3` | `#C6B6F3` | Sparse branded ring and pedestal trim |
| `--scene-water` | `#689EAE` | `#427C94` | River presentation accent |
| `--scene-island-side` | `#878C9B` | `#666D87` | Side wall of the raised Delhi boundary |
| `--scene-glass-highlight` | `#FFFFFF` | `#DBD3F7` | Glass reflections and edge highlights |
| `--scene-glass-attenuation` | `#D9E4F2` | `#C9D1E2` | Gentle glass tint; do not obscure the city |
| `--scene-label` | `#13151A` | `#F3F4F8` | Screen labels and tooltips |
| `--scene-label-surface` | `#FFFFFF` | `#1B1E29` | Label backplate when needed |
| `--scene-light` | `#FFFFFF` | `#F1ECFF` | Key/rim illumination colour |

In Midnight, use pale restrained reflections to reveal the dome silhouette against the charcoal background. Keep the island and models readable with soft illumination; “dark” must not turn the city into black silhouettes. Miniature materials may retain sandstone, brushed metal, smoked glazing and porcelain. Layer identity comes from labels and controlled accent placement, not by washing every model lilac.

The dome should occupy the available viewport, not sit in a small card. The same theme tokens must work at whole-city scale, street scale, expanded view and mobile. Never use a CSS vignette or a reflection overlay that prevents selecting or reading objects. Space satellites below the constellation shell; lighting cannot fix intersecting scene geometry.

## Contrast checks

Computed using WCAG sRGB solid-colour relative luminance, rounded to two decimals:

| Foreground / background | Contrast |
| --- | --- |
| Primary text / canvas | 16.88:1 |
| Primary text / surface | 15.11:1 |
| Secondary text / surface | 8.08:1 |
| Secondary text / raised surface | 7.04:1 |
| Ink / lilac | 9.90:1 |
| Ink / lime | 16.28:1 |
| Ink / mint | 12.85:1 |
| Ink / coral | 6.84:1 |
| Secondary on accent / lilac | 4.96:1 |
| Secondary on accent / lime | 8.15:1 |
| Lilac focus / surface | 9.01:1 |

These checks cover the defined solid text pairs; they do not certify glass, transparency, map labels, every source colour or rendered 3D scene. Native data badges with inline source backgrounds need an appropriate foreground; the stylesheet uses ink on bright fields and white on the known muted slate/violet fields. For newly added official route colours, select an appropriate foreground from computed luminance rather than assuming white works.

## Review checklist for future cities

- Light and dark versions share the same Ring & River silhouette, font, icon strokes, tab order and data-source language.
- Full atlas is the first/default destination; Readings and Sky view do not create another map.
- Theme switching keeps camera position and layer selection, including when a source drawer is open.
- Bright air/weather cards, dialogs, source links, saved-place state, native layer cards and miniature tooltips all remain readable.
- Basemap roads, road labels and 3D buildings stay readable at the closest supported zoom in both themes.
- Glass remains transparent enough to inspect the island. Its rim is visible at the initial camera distance and its geometry can extend beyond the screen during close exploration.
- Mobile uses the same semantic colours and SVG symbols; the layer sheet and bottom navigation do not merge into an unreadable slab.
- A future city's name, boundary and source configuration change independently of the brand. Do not regenerate the logo or introduce unrelated palettes for each city.

The dated QA record documents browser theme switching, persistence, preserved camera, map retint, mobile navigation and the Midnight guide. This is a responsive web implementation; a native Android package and physical-device profiling are not part of that evidence.
