--- name: space-registry-ui description: Build a page in Celia's proposed UI for the space object registry (her live site at smartrules.ai/spaceregistry, measured; not the generic design-system folder). Trigger on "Celia's registry UI", "the space registry look", "build a registry page in the adopted style", "apply Celia's UI to X", or any request to extend the registry site's design beyond its built pages. Carries the measured tokens, the shared stylesheet and script, the component recipes, and the measuring loop that proves a copy of one of her pages is exact. --- # Celia's registry UI Frank, 02-09-2026: "this is Celia's proposed UI, much better than my version; make a template/skill out of it." Frank, 03-09-2026, after putting the two side by side: "copy exactly celia's UI and reflect in skill." ## The lesson: measure, never copy by eye The first rebuild read her compiled stylesheet and her markup, and still came out wrong. Frank saw it at a glance: the hero glowed warmer, the globe sat lower, the nav was spaced differently, and the title wrapped onto two lines where hers holds one. Reading a stylesheet tells you what a page DECLARES. Only the browser tells you what it PAINTS, and the two parted company in five places at once. The loop that fixed it, and the loop to run on any new copy: 1. Load her page and ours at the same viewport in the same browser. 2. Dump the SAME named landmarks from both with `getBoundingClientRect` and `getComputedStyle`, not her class names: header, logo, each nav link, the button, the hero box, the globe, the badge, h1 (with its rendered LINE COUNT), the dek, the marquee, the search row, the stats, each section, the cards, the entries, the closing call, every footer part. 3. Print the difference and fix every line of it, then dump again. 4. When the boxes agree, screenshot both full pages and diff the pixels, band by band, so what the boxes cannot say (colour, gradient, weight) shows up as a number. The tool is written and lives with the work: `3 - Projects/space-object-registry/captures-mine/exact/probe.py` (`measure` / `diff` / `shot` / `pixdiff`). It scrolls the whole page before measuring, because her cards, entries and closing call arrive from 30px below as they scroll into view and a dump taken at rest catches them mid-flight. Two habits it enforces. **Compare the notations, not the strings**: Tailwind writes colours in oklab and a full radius as `3.35544e+07px`; plain CSS writes `rgba()` and `9999px`. They paint the same pixels, so the diff normalises them and only real differences survive. **Freeze what moves**: her hero canvas animates and her marquee slides, so exclude the marquee band from the pixel number and say so, and drive the globe page with `prefers-color-scheme`'s sibling `prefers-reduced-motion: reduce`, which stops her globe at phi -1.84 and makes a frame-for-frame comparison possible. ## What was wrong, and what it cost | Element | Hers | Ours before | Ours now | |---|---|---|---| | Type | the browser's default sans stack | Inter, loaded from Google Fonts | her stack, no webfont | | h1 at 1440 | 757.8px wide, ONE line | 768px wide, TWO lines | 757.8px, one line | | Hero background | a three.js canvas: near-black with soft blooms | a strong orange and violet CSS glow | a gradient fitted to her canvas, 1.5 per channel off | | Nav | absolute, centred on the viewport, gap 4px | pushed by space-between | absolute, centred, gap 4px | | Nav link box | 14px on a 20px line, 36px tall | 24px line, 40px tall | 20px line, 36px tall | | Hero column | an overlay 1080px wide, 448px tall, centred on the section | a flex child of the section | her overlay | | Dek | 18px on a 28px line | 18px on a 24px line | 28px | | Stats band | py-20 (80px), wrap max-w-7xl with its own padding | py-16, padding on the grid | hers | | Section h2 | text-5xl, line-height 1 | line-height 1.25 | 1 | | Footer | drawn rules, corner squares, a legal strip of its own | a simplified box | hers | | Page height at 1440 | 3655px | 3651px | 3655px | **The font was the whole title bug.** Her site declares Inter through next/font (eight `@font-face` rules) and never renders it: her theme sets `--font-sans: var(--font-sans)`, a self-reference, so the `font-sans` utility resolves to nothing and everything falls back to the browser default. Proof, read in the browser and not in the CSS: `document.fonts.check('60px Inter')` is false, no face reports `loaded`, and the computed family on body and on the h1 is `ui-sans-serif, system-ui, sans-serif, …`. The same h1 string measures 804.33px in that stack and 894.97px in Inter at 60px/500; with tracking-tight that is 757.83 against 848.5, and her h1 box is `max-w-3xl` (768px). Inter wraps her title; the system stack keeps it on one line. **So: load no webfont.** `--font-display` and `--font-body` in `tokens-celia.css` carry her stack verbatim. A page in her look that links a font is already wrong. ## The files All in `3 - Projects/space-object-registry/`. Link the first two from any new page; add the others when the page needs what they do. | File | What it is | |---|---| | `tokens-celia.css` | the `:root`. Every value cited with where it was read. Link it, never paste it inline (see The checks). | | `celia-ui.css` | her whole page as plain CSS: header, hero, marquee, search, stats, cards, entries, closing call, footer, the globe page, and the document pages. Colours only through `var()`. | | `celia-ui.js` | the four things her page does: the marquee at 50 px/s, the stats and the latest entries from the data file, the reveal, the mobile sheet. | | `celia-globe.js` | the globe page's turning Earth, chips and record dialog. | | `celia-orbit.js` | our own hero variation, the ring of types (not hers). | | `cobe-land-map.js` | cobe's own 256x128 land map, lifted from her chunk. | | `celia-data.js` | her eight entries with every field, her four orbits, her seven types, her four numbers. **Never `registry-data.js`**, which is the site's own data file. | ## The pages | Page | What it is | Live | |---|---|---| | `celia-template.html` | her home, exactly | /space-registry/celia-template.html | | `celia-template-the-globe.html` | her `/the-globe/`, exactly | /space-registry/celia-template-the-globe.html | | `celia-template-v2.html` | her `/v2/`, the violet variant, exactly | /space-registry/celia-template-v2.html | | `celia-template-halfglobe.html` | her home, hero variation: the Earth's night limb instead of the dotted globe | /space-registry/celia-template-halfglobe.html | | `celia-template-orbit.html` | her home, hero variation: the seven types circle the globe as icons | /space-registry/celia-template-orbit.html | | `celia-template-orbit-images.html` | the same ring, as cut-out photographs | /space-registry/celia-template-orbit-images.html | | `celia-template-orbit-images-small.html` | the same ring, smaller photographs | /space-registry/celia-template-orbit-images-small.html | | `unoosa1/index.html` | our own page in her look: the three cards for UNOOSA | /space-registry/unoosa1/ | | `unoosa1/note/index.html` | the concept note "How the data reaches UNOOSA" in her look | /space-registry/unoosa1/note/ | Her export also holds `/home2/`, a banking-template scaffold mixed into the same build. It is not part of her registry design; skip it. ## How exact the copies are Mean absolute difference per channel, full page, marquee band excluded, measured against the live pages on 03-09-2026: | Page | 1440x900 | 375x812 | |---|---|---| | home | 1.05 (morning 03-09) → 1.11 (afternoon 03-09, re-matched) | 0.83 → 1.02 | | /v2/ | 0.98 → 1.01 | 0.83 → 0.96 | | /the-globe/ (both frozen) | 0.81 | 1.15 | Band by band on the home page at 1440: stats 0.00, features 0.12, latest 0.37, closing call 1.47, footer 0.01. What is left is the hero, where her canvas animates and ours stands still, and the entry thumbnails, where her PNGs and our JPEGs of the same photographs differ by a few compression pixels. ## She keeps changing the page under us: re-measure, don't assume Frank, 03-09-2026 afternoon: her live page moved again the same day it was first matched, mid-session, while unrelated dark-mode work was running. Caught only because that work re-dumped her page in light mode too and the numbers had drifted from the morning dump. **A copy proven exact this morning is not proven exact this afternoon: she edits the live site same day, more than once.** Re-run the loop before trusting any number more than a few hours old. What moved, this pass (morning value → afternoon value, both read live, not guessed): | Element | Morning (03-09, AM) | Afternoon (03-09, PM) | |---|---|---| | h1 text | "Registry of outer space objects" | "Registry of space objects" (her `