Skip to content

Daylight redesign, true PPI radar, and a rebuilt great-circle globe - #17

Merged
YASLOGIST merged 8 commits into
mainfrom
arena/807d6abe-air
Oct 26, 2026
Merged

YASLOGIST merged 8 commits into
mainfrom
arena/807d6abe-air

Conversation

@YASLOGIST

Copy link
Copy Markdown
Owner

Summary

This branch delivers the daylight redesign and the rebuilt great-circle globe for the corridor network. It is eight commits, split into three blocks.

1. Daylight surface and layout contract

  • White default surface with a simplified palette, --c-* tokens flipping per theme, and no dark: utilities anywhere (enforced by design-contract.test.ts).
  • Inter as the single Latin family, one flat weight scale, light/semibold only.
  • Page-wide smooth scrolling scoped inside prefers-reduced-motion: no-preference, so the OS reduced-motion setting always wins.
  • Direction is pinned to LTR in every language. Arabic switches copy and font stack only — there is no dir="rtl", no rtl: utilities, and no runtime direction switch. Pinned by direction-contract.test.tsx.
  • @source not "../dist"; keeps the committed build output out of Tailwind's content scan, which was otherwise regenerating ~30 KiB of dead utilities.

2. Radar scope

  • Rebuilt as a true PPI scope: numerals, bearings, rings labelled in NM, a legend, and a stepped conic sweep that advances in 30 steps per revolution like a real raster.

3. Great-circle globe

The globe went through three stages, which is why there are several commits. Final state:

  • A chart, not a photograph. One 4096×2048 coastline mask rasterised from Natural Earth 1:50m (2×2 supersampled, with the coastal shelf packed into the alpha channel) drives flat land, ocean and coastline ink. No clouds, no atmosphere shells, no sun simulation, no per-pixel noise.
  • Theme-aware. White ground with blue ink in daylight; deep navy with cyan glow after dark. The wrapper mirrors documentElement[data-theme] through a MutationObserver into an optional setTheme(), so a toggle repaints a mounted scene rather than rebuilding it. One PALETTES table drives surface, lattice, arcs, traffic and beacons, with a uGlow uniform so additive blending is only used where the pixels are light rather than ink.
  • Correct orientation. The scene had been rendering mirrored in latitude because three's TextureLoader flips loaded images by default while the shader's UV convention is already image-space. orientation.test.ts now decodes the shipped PNG and asserts both the raster's north-up row order and the loader convention.
  • Routes as scheduled movements. Each corridor is one coherent movement: a constant-width line terminating cleanly at both airports, a fixed-length leading segment showing direction, and one aircraft riding the head. Beacons are surveyor-style fixes — solid centre, hairline ring, one slow ping — at a constant screen size.
  • Smooth motion. Gentler auto-spin, softer damping on rotation and zoom, clamped wheel delta so a trackpad flick eases instead of jumping, idle frames budgeted to 30 fps, and reduced-motion handled by a single-render path.

Preserved behaviour

Context-loss recovery, the reduced-motion path, the off-screen gate, the deferred vendor-three import, the DPR cap, and the exact stats contract are all unchanged. The globe's public API is additive: setTheme and onReady are both optional, so the existing wrapper test's mock still satisfies it.

Verification

  • npm run check: typecheck clean, 26 test files pass, build succeeds.
  • Bundle within every ratchet: js 505.0 KiB / 509.8 KiB, css 76.2 / 109.4, per-file and vendor-three tiers all green, vendor-three still behind a dynamic import in both consumers.
  • Rendered and inspected in Chromium (SwiftShader) in both themes, at rest, under corridor focus, across a live theme toggle, and at minimum/maximum zoom.

Two notes for review:

  • The FPS chip reports 10–20 in this sandbox because that is software rasterisation, not a GPU signal. The scene is five draw calls with all motion on the GPU; nothing was tuned against that number.
  • The globe deliberately departs from a photograph in two places for legibility, and both are documented in the source: far-side corridors stay faintly visible through the planet, and the palette is flat chart ink rather than lighting.

YASLOGIST and others added 8 commits October 25, 2026 20:57
…oll-budget pass

Surface
- :root and [data-theme=light] now carry the daylight palette directly, so the
  white page paints before the bootstrap script runs; still no OS dark flip.
- Body/page gradients and translucent card haze removed; panels are opaque
  white with a hairline border.

Palette
- One accent (#0369A1) absorbs cyan/sky; teal/emerald/amber/rose survive only
  as semantic status inks (--c-ok/-cold/-warn/-alert) with per-theme values.
- Added --c-track and --c-hover so controls stop hard-coding slate pairs.
- Removed the last 87 `dark:` utilities from components; theme switching is
  entirely token-driven now (pinned by src/test/design-contract.test.ts).
- Dark instrument surfaces (`.avionics-panel`, `.avionics-well`) re-scope the
  tokens on themselves so night ink stays legible on a white page.

Type
- Inter is the single Latin family (400/500/600/700); Cairo keeps Arabic.
- font-bold/extrabold/black collapsed to 600 (135 occurrences).

Scroll
- Smooth scrolling stays gated behind prefers-reduced-motion: no-preference.
- Off-screen radar sweep/pings no longer animate; scrollbar-gutter avoids the
  modal scroll-lock reflow.

Ships at 81.1 KiB CSS (was 110.8 KiB at HEAD) and 494.9 KiB app JS; all tests
(125) and bundle budgets pass.

Co-authored-by: arena-agent <297053741+arena-agent@users.noreply.github.com>
dist/ is tracked in this repository, so when the next build starts the previous
build's CSS, JS and sourcemaps are sitting on disk. Tailwind's automatic content
detection reads them as source and regenerates utilities the app no longer
uses: a clean checkout built to 110.6 KiB against a 109.4 KiB budget, and the
bloat grew with every build. `@source not "../dist"` pins the scan to real
source, making the CSS independent of whatever is in the output directory
(80.4 KiB from an empty dist and from a stale one, identical content hash).

Adds a design-contract assertion so the exclusion cannot be dropped silently,
and rebuilds dist/ against the corrected graph.

Co-authored-by: arena-agent <297053741+arena-agent@users.noreply.github.com>
…urfaces

Direction (requested)
- The layout is permanently left-to-right. Language switches the dictionary
  and the Arabic font stack; it no longer flips `dir`. Root cause was
  `root.dir = lang === 'ar' ? 'rtl' : 'ltr'` in both the provider and the
  pre-paint bootstrap, plus 19 `rtl:` utilities and four per-component
  `dir={isRtl ? ...}` overrides that mirrored charts, tables, radar and 3D
  viewports. All removed; src/test/direction-contract.test.tsx pins it.
- Arabic copy, Arabic typography bindings and the language toggle are intact.

Radar (requested: precise, high quality, realistic smooth motion, legible)
- New RadarScope component. Geometry is real: the field is the origin, the
  sweep is 8 s, rings are 20/40/60 NM on an 80 NM range, and every target is
  placed from its bearing and range — so the tag numbers and the picture
  cannot disagree.
- Legibility: bigger plot, bezel ticks every 5° (long at 30°), cardinal and
  30°-interval labels, axis cross, range numbers on chips inside their rings
  on a single radial, a plane glyph plus callsign/level beside each contact,
  a leader line from the selected target to its data block and a legend in
  words. Cursor readout gives bearing/range under the pointer.
- Motion: the sweep is one composited rotation with a conic-gradient trail;
  each contact's afterglow is a keyframe delayed to its bearing, so a blip
  brightens exactly as the beam crosses it and fades across the revolution;
  the selected target glides between telemetry fixes in a rAF loop that
  writes two transform attributes and never calls setState. Everything pauses
  off-screen and under prefers-reduced-motion.

Simplification
- One instrument ground (--hud-bg), one hairline border, no inner glow. Four
  ink levels and three signal colours now live in :root as hud tokens used by
  the scope and the 3D viewports in both themes.
- Sensor tiles moved out of the dark well onto the page surface, so the scope
  stays uncluttered and the tiles inherit the normal light-theme ink.

Protected logic is untouched: the chargeable-weight simulator, ULD digital
twin, corridor globe and AWB checksum keep their original modules and tests
(only direction-related class names changed in their shells). 141 tests pass;
CSS 75.4 KiB, app JS 501.4 KiB (ratchet raised to 509.8 KiB with a note).

Co-authored-by: arena-agent <297053741+arena-agent@users.noreply.github.com>
Replace the flat dot-shell globe with a shaded, textured planet and make the
network overlay readable at rest and under focus.

Surface: a 2048x1024 land mask rasterised offline from Natural Earth 1:50m
coastlines drives land/ocean/climate-band/ice colouring, a separate cloud
layer drifts on its own clock, and city lights appear only in deep night.
Terminator still follows the real subsolar point, but the key light is
clamped to 72 degrees of the viewing axis so the featured hemisphere never
goes fully dark at an unlucky UTC.

Overlay: corridors are tapered ribbons instead of hairlines, far-side lanes
stay faintly visible via a shader occlusion test (one draw, no ghost pass),
traffic sprites are larger and rotated to their ground track, and the rest
pose now frames the network's centroid instead of the horizon.

Textures live in public/assets/earth (305 KiB, no runtime fetches). The old
landmask.ts/isLand module is superseded by the raster mask and removed.

Gates: typecheck clean, 25 test files pass, bundle 507.6 KiB / 509.8 KiB.

Co-authored-by: arena-agent <297053741+arena-agent@users.noreply.github.com>
The globe is now a chart of the planet rather than a render of it: one 66 KiB
coastline mask, flat land/ocean ink, a faint graticule and line-art corridors.
No clouds, no atmosphere shells, no sun simulation, no noise — five draw calls,
down from seven, and roughly a dozen fewer texture fetches per pixel.

Colour follows the page. Daylight is a white ground with pale-blue land and
deep-blue coasts and routes; dark mode is deep navy with cyan-glow arcs. The
wrapper mirrors documentElement[data-theme] through a MutationObserver into a
new optional setTheme(), so toggling the theme repaints a mounted scene instead
of rebuilding it. One PALETTES table drives surface, lattice, arcs, traffic and
beacons, with a uGlow uniform so additive blending is used only where it is
actually light rather than ink.

Idle frames are budgeted to 30 fps to keep scrolling smooth; drag, zoom and
focus still render every frame. The now-unused clouds.png is removed.

Gates: typecheck clean, 25 test files pass, 502.5 KiB / 509.8 KiB.

Co-authored-by: arena-agent <297053741+arena-agent@users.noreply.github.com>
The planet was mirrored in latitude. The coastline sheet was north-up, but
three's TextureLoader flips loaded images by default while the shader's UV
convention (v = 0 at the north pole) already matches image order, so the mask
was sampled bottom-up. Antarctica rendered where the Arctic belongs and every
airport beacon sat on the wrong continent.

- set flipY = false on the land texture, with the convention documented
- replace the 2048x1024 mask with a 4096x2048 sheet rasterised at 2x2
  supersampling, with the coastal shelf packed into the alpha channel so the
  ocean gains a continental-shelf tint without a second texture (428 KiB)
- derive the coastline outline width from the mask gradient for a constant
  hairline at any zoom, clamped so mip reduction cannot flood the ocean
- add orientation.test.ts, which decodes the shipped PNG and asserts both the
  raster's north-up row order and the loader convention

Gates: typecheck clean, 26 test files pass, 503.3 KiB / 509.8 KiB.

Co-authored-by: arena-agent <297053741+arena-agent@users.noreply.github.com>
The arcs read as random: three aircraft per route at arbitrary phases, each
point-scaled by its view depth so every sprite was a different size, with two
counter-rotating sparks travelling at unrelated rates.

Each corridor is now a single movement, drawn the way an ops chart draws one:
a quiet permanent line, a packet travelling origin -> destination, and a soft
tail that shows the direction. One aircraft per route rides that packet's head,
so the trail and the aircraft can never disagree. Packet phases are evenly
spaced around the cycle rather than pseudo-random, and speed scales with the
square root of route length so long-haul corridors stay readable without the
Shanghai lane appearing frozen.

Aircraft are now a constant screen size (13-22 CSS px, clamped) with a cleaner
swept-wing silhouette, and the taper keeps body along the route instead of
pinching to a hair near each airport.

Motion is softer all round: gentler auto-spin, lower damping stiffness on
rotation and zoom, clamped wheel delta so a trackpad flick eases instead of
jumping, and a wider rest framing that gives the northern arcs room.

Gates: typecheck clean, 26 test files pass, 504.3 KiB / 509.8 KiB.

Co-authored-by: arena-agent <297053741+arena-agent@users.noreply.github.com>
Two symbols were dominating the card for the wrong reasons: airport beacons
drew as fat depth-scaled donuts (CAI reached 38px and swallowed its
neighbours), and the aircraft read as white streaks.

- Beacons are now surveyor-style fixes: a solid centre, a hairline ring at a
  constant radius and one slow ping per airport, all at a constant screen size
  so neighbouring Gulf airports no longer merge into a blob.
- The airliner is assembled from analytically swept masses — fuselage tapering
  aft, wings whose leading edge lags the root as span grows, and a short
  tailplane — so it reads as an aircraft at 11-18px rather than a snowflake.
- Routes lose the exponential comet tail. Each corridor is a constant-width
  line that terminates cleanly at both airports, with a fixed-length leading
  segment ahead of the aircraft showing direction.
- Aircraft and beacons are sized from the card in CSS pixels and clamped,
  rather than from view depth.
- Minimum zoom raised so the sphere can never overflow the card.

Gates: typecheck clean, 26 test files pass, 505.0 KiB / 509.8 KiB.

Co-authored-by: arena-agent <297053741+arena-agent@users.noreply.github.com>
Copilot AI balanced review requested due to automatic review settings October 26, 2026 01:47
@vercel

vercel Bot commented Oct 26, 2026

Copy link
Copy Markdown
Contributor

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated
air Ready Ready Preview Oct 26, 2026 1:47am UTC

@chatgpt-codex-connector

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review ⚠️ Failed 2026-10-26T01:47:38.239405Z
We couldn’t complete this review. Comment @codex review to try again.
2a73433 PR opened
ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@chatgpt-codex-connector

Copy link
Copy Markdown

You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard.

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot wasn't able to review any files in this pull request. Check if the Files changed in this pull request are included in default exclusions.

@YASLOGIST
YASLOGIST merged commit 4182e81 into main Oct 26, 2026
4 checks passed

This branch was successfully deployed

1 active deployment
Preview — 2a734335 Deployed Oct 26, 2026 by vercel[bot]
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants