The single source of truth for shared UI. Every component below is rendered live from ct-canon.css + ct-canon.js — the exact same files every real view imports. If the canon is correct, this page is correct, and any screen that looks different from this is provably off-canon.
Why the top bar keeps changing: it is supposed to be identical on every view — the always-today header (§2) + timespan selector (§3) + view tabs (§4). Any drift means a page hand-rolled its own strip instead of calling CT.header(). The assembled shell in §1 is the one true top bar. Match to it.
01The App Shell
The one true top bar — assembledCT-SHL01
Every view opens exactly like this: rail → always-today header → timespan selector → view tabs, then the view body. The top three strips are identical everywhere; only the timespan values and the active tab change. This is the reference the rest of the app must match.
The top-left cross. The CT brand cell is 58px tall — the header strip's exact height. Their bottom edges land on the same line, so the divider under the logo and the divider under the header meet the rail's right edge to form an unbroken +. Rule: the bottom of the logo box is always level with the bottom of the top header bar — never let the brand cell and header heights diverge.
✳Canonical Key
Auspiciousness — the diverging 1–5 scale
Ivy's subjective, regression-backed judgement of a day relative to all days of the year — not a star rating. Never invent new colors, symbols, or labels. Symbols are U+2212 (minus −) and U+00B7 (middle dot ·).
◆Locked Components
Each piece, in isolation
Built only by its CT.* function. Never hand-authored or forked per page.
02Always-today headerCT-HDR01Locked
CT.header()
This is the header that must not change. It shows the current moment — date/time, day ruler, planetary hour, moon, auspice scale, theme — the same on Overview, Calendar, and Orrery. R4-20 currently inlines its own strip instead of calling this, which is the drift you're seeing.
03Timespan selectorCT-TSB01Locked
CT.timespanBar({ kind, count, range })
kind: Day
kind: Week
kind: Month
Structure is identical across every unit — edit-pencil CTA, day-count pill, unit nav. Only the three values change. Never restyle it per view.
04View tabsCT-TAB01Locked
CT.tabs('overview') · 'calendar' · 'orrery'
active: Overview
active: Calendar
05Nav railCT-NAV01Locked
CT.rail('planner') · library · sessions · forums
68px wide · the CT brand cell is 58px — the header's exact height, so its bottom sits level with the header bar; the two form a cross at the top-left (see §1). Planner / Library / Sessions / Forums, then a hairline + Account pinned to the bottom. Active item carries the left accent bar in --blue.
06Auspice scale chipsCT-AUS01Locked
CT.scale(value) · value 1–5, active level filled
Symbol-in-a-box, five levels always shown. The active level is the solid one; the rest sit at their pale band tint.
07Calendar bandCT-CAL01Locked
CT.calBand({ selStart, selEnd })
Neutral days stay plain (faint ·); only low & high points get a filled tint + mark. Selection draws a soft 1px underline; today gets a faint ring.
Ruler glyph + lunar day, an auspice flag only on notable days, working theme, description, then materia & fixed stars as tags.
09Glyphs — always PhysisCT-GLY01Locked
CT.pgWrap(str) · wraps every astro glyph in .pg (Physis)
Planets
Zodiac signs
If a glyph renders as a colored emoji, it fell back to the OS font — a bug. Route every glyph-bearing string through CT.pgWrap().
⛬How Locking Works
Keeping designs from drifting
The lock is not a setting — it's a discipline enforced by structure. Four rules:
1
One source, imported everywhere. Shared UI lives once in ct-canon.css + ct-canon.js. Every page imports both — no page keeps its own copy.
2
Build by calling, never by hand. Markup comes from CT.rail(), CT.header(), CT.timespanBar(), CT.tabs(), CT.calBand(), CT.dayCard(), CT.scale(). If you're typing a <div class="strip"> by hand, you've broken the lock.
3
This page is the contract. It renders the live canon. To confirm a design is correct, compare its chrome against §1 here. Anything that differs is off-canon and should be fixed, not accepted.
4
Change only on the word. These are LOCKED. They change only on an explicit "update the canon" instruction, and every change is logged in CLAUDE.md. No silent edits.
One drift on record right now:R4-20 Week View.html hand-rolls a custom top strip (Current / Span / Overview) instead of calling CT.header(). That's why its top bar doesn't match the others. Say the word and I'll reconcile it to canon — and audit the rest of the R4 set the same way.
Circle Thrice · Canon Reference · rendered live from ct-canon