-
Notifications
You must be signed in to change notification settings - Fork 0
feat(landing): M2/M5/M6/M7 editorial moments — sage & cream #721
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
njrini99-code
merged 14 commits into
batch/baseball-fixes
from
feat/entry-world-editorial
Jul 2, 2026
Merged
Changes from all commits
Commits
Show all changes
14 commits
Select commit
Hold shift + click to select a range
13bf6bd
feat(landing): First Light foundation — scaffold, glass grammar, scro…
ad25e8c
feat(landing): sage & cream palette respec — token repoint + sage fam…
efe0803
wip: inherited partial work (pre-sage)
3476c36
Merge remote-tracking branch 'origin/feat/entry-world-foundation' int…
200ea2f
wip: sage partial work (builder killed mid-build)
95e5ba9
feat(landing): M2/M5/M6/M7 editorial moments — sage & cream
36ec31a
fix(landing): M2 ledger rules draw in (Fable spec)
f1f3656
feat(landing): IMMACULATE foundation — aurora field, light pools, cta…
9f3e434
Merge remote-tracking branch 'origin/feat/entry-world-foundation' int…
3526855
feat(landing): editorial immaculate pass — aurora field, light pools,…
11fd8df
feat(landing): §D2 more cream, no white — demote cream-high to lips/t…
68944a3
feat(landing): brand foundation — HelmMark/HelmRosette/SportGlyph lin…
bbd2cb3
Merge remote-tracking branch 'origin/feat/entry-world-foundation' int…
a581444
feat(landing): editorial architecture — asymmetric splits, bento, gol…
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,6 +1,6 @@ | ||
| /// <reference types="next" /> | ||
| /// <reference types="next/image-types/global" /> | ||
| import "./.next/types/routes.d.ts"; | ||
| import "./.next/dev/types/routes.d.ts"; | ||
|
|
||
| // NOTE: This file should not be edited | ||
| // see https://nextjs.org/docs/app/api-reference/config/typescript for more information. | ||
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Large diffs are not rendered by default.
Oops, something went wrong.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,76 @@ | ||
| import { getSvgA11yProps } from './svgA11y'; | ||
|
|
||
| export interface HelmMarkProps { | ||
| /** Rendered width/height in px. viewBox is a fixed 24×24 unit square, so | ||
| * the 1.5px stroke visually thins as `size` shrinks below 24 — this mark | ||
| * is meant for 18px+ use (GlassNav, M9 watermark); for 10–14px punctuation | ||
| * use `HelmRosette` instead, which holds its stroke weight at small sizes. */ | ||
| size?: number; | ||
| className?: string; | ||
| /** Accessible name. Omit for a purely decorative mark (aria-hidden). */ | ||
| title?: string; | ||
| } | ||
|
|
||
| const CX = 12; | ||
| const CY = 12; | ||
| const RING_R = 7.4; | ||
| const SPOKE_INNER_R = 6.6; | ||
| const SPOKE_OUTER_R = 10.4; | ||
| // 8 spokes, 45° apart, starting at 12 o'clock — matches Helm-Logo-New-Main.png. | ||
| const SPOKE_ANGLES_DEG = [0, 45, 90, 135, 180, 225, 270, 315]; | ||
|
|
||
| function pointOnCircle(angleDeg: number, r: number) { | ||
| const rad = (angleDeg * Math.PI) / 180; | ||
| return { x: CX + r * Math.sin(rad), y: CY - r * Math.cos(rad) }; | ||
| } | ||
|
|
||
| /** | ||
| * The Helm mark — a faithful line-art tracing of the 8-spoke ship's-helm | ||
| * wheel + ship silhouette in `public/Helm-Logo-New-Main.png`. Stroke-based | ||
| * (`fill="none"`, `stroke="currentColor"`) so it recolors per surface | ||
| * (sage-ink on cream, brass as jewelry, cream on sage-ink bands) instead of | ||
| * carrying that PNG's flat kelly fill. | ||
| * | ||
| * Kelly raster (`Helm-Logo-New-Main.png` and the sport-variant PNGs) NEVER | ||
| * renders on landing/auth chrome — this component (plus `HelmRosette` and | ||
| * `SportGlyph`) is the only brand-mark rendering path there. See | ||
| * CONTRACTS.md "Brand components" for the full consumer map. | ||
| * | ||
| * Ship silhouette, top to bottom: mast crossbar (small rect) → deckhouse | ||
| * cabin (wider rect) → hull bow (triangular path, `strokeLinejoin="round"` | ||
| * softens the top corners toward the source PNG's pentagon read) → | ||
| * waterline (a shallow arc wider than the hull). | ||
| */ | ||
| export function HelmMark({ size = 24, className, title }: HelmMarkProps) { | ||
| const a11y = getSvgA11yProps({ title }); | ||
| return ( | ||
| <svg | ||
| viewBox="0 0 24 24" | ||
| width={size} | ||
| height={size} | ||
| className={className} | ||
| fill="none" | ||
| stroke="currentColor" | ||
| strokeWidth={1.5} | ||
| strokeLinecap="round" | ||
| strokeLinejoin="round" | ||
| {...a11y} | ||
| > | ||
| {title ? <title>{title}</title> : null} | ||
| {SPOKE_ANGLES_DEG.map((angle) => { | ||
| const inner = pointOnCircle(angle, SPOKE_INNER_R); | ||
| const outer = pointOnCircle(angle, SPOKE_OUTER_R); | ||
| return <line key={angle} x1={inner.x} y1={inner.y} x2={outer.x} y2={outer.y} />; | ||
| })} | ||
| <circle cx={CX} cy={CY} r={RING_R} /> | ||
| {/* mast crossbar */} | ||
| <rect x={10.4} y={7.8} width={3.2} height={1} rx={0.5} /> | ||
| {/* deckhouse cabin */} | ||
| <rect x={9.4} y={9.1} width={5.2} height={1.3} rx={0.65} /> | ||
| {/* hull (bow silhouette) */} | ||
| <path d="M8.6,10.6 L15.4,10.6 L12,14.2 Z" /> | ||
| {/* waterline */} | ||
| <path d="M7.6,14 Q12,15.6 16.4,14" /> | ||
| </svg> | ||
| ); | ||
| } |
68 changes: 68 additions & 0 deletions
68
src/components/marketing/first-light/brand/HelmRosette.tsx
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,68 @@ | ||
| import { getSvgA11yProps } from './svgA11y'; | ||
|
|
||
| export interface HelmRosetteProps { | ||
| size?: number; | ||
| className?: string; | ||
| title?: string; | ||
| } | ||
|
|
||
| const CX = 12; | ||
| const CY = 12; | ||
| const RING_R = 7.4; | ||
| const SPOKE_INNER_R = 6.6; | ||
| const SPOKE_OUTER_R = 10.4; | ||
| // Same wheel geometry as HelmMark — the two components read as one mark at | ||
| // different scales. | ||
| const SPOKE_ANGLES_DEG = [0, 45, 90, 135, 180, 225, 270, 315]; | ||
|
|
||
| function pointOnCircle(angleDeg: number, r: number) { | ||
| const rad = (angleDeg * Math.PI) / 180; | ||
| return { x: CX + r * Math.sin(rad), y: CY - r * Math.cos(rad) }; | ||
| } | ||
|
|
||
| /** | ||
| * The Helm wheel reduced to ring + 8 spokes — no ship — for 10–14px section | ||
| * punctuation (Amendment 3 §A.2): the eyebrow hairlines of M2/M5/M7 meet a | ||
| * brass rosette at center instead of bare line ends; M6's "readiness" | ||
| * vignette gets a neutral (sage) rosette per §B.3. | ||
| * | ||
| * Draws with `vector-effect="non-scaling-stroke"` — a fixed screen-pixel | ||
| * stroke regardless of the viewBox→viewport scale. At 10–14px a stroke that | ||
| * scales down with the icon (HelmMark's default behavior, correct at 18px+) | ||
| * would thin into anti-aliasing mush; a rosette this small needs a constant | ||
| * hairline instead, so the base `strokeWidth` here (1.25) is intentionally | ||
| * lower than HelmMark's 1.5 — it's the literal on-screen weight at every size. | ||
| */ | ||
| export function HelmRosette({ size = 12, className, title }: HelmRosetteProps) { | ||
| const a11y = getSvgA11yProps({ title }); | ||
| return ( | ||
| <svg | ||
| viewBox="0 0 24 24" | ||
| width={size} | ||
| height={size} | ||
| className={className} | ||
| fill="none" | ||
| stroke="currentColor" | ||
| strokeWidth={1.25} | ||
| strokeLinecap="round" | ||
| {...a11y} | ||
| > | ||
| {title ? <title>{title}</title> : null} | ||
| {SPOKE_ANGLES_DEG.map((angle) => { | ||
| const inner = pointOnCircle(angle, SPOKE_INNER_R); | ||
| const outer = pointOnCircle(angle, SPOKE_OUTER_R); | ||
| return ( | ||
| <line | ||
| key={angle} | ||
| x1={inner.x} | ||
| y1={inner.y} | ||
| x2={outer.x} | ||
| y2={outer.y} | ||
| vectorEffect="non-scaling-stroke" | ||
| /> | ||
| ); | ||
| })} | ||
| <circle cx={CX} cy={CY} r={RING_R} vectorEffect="non-scaling-stroke" /> | ||
| </svg> | ||
| ); | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,84 @@ | ||
| import { getSvgA11yProps } from './svgA11y'; | ||
|
|
||
| export type SportGlyphSport = 'golf' | 'baseball' | 'lift'; | ||
|
|
||
| export interface SportGlyphProps { | ||
| sport: SportGlyphSport; | ||
| size?: number; | ||
| className?: string; | ||
| title?: string; | ||
| } | ||
|
|
||
| /** | ||
| * Tiny per-sport line glyphs, 16–20px by default (Amendment 3 §B). Same | ||
| * stroke contract as HelmMark/HelmRosette: `fill="none"`, | ||
| * `stroke="currentColor"`, shared 24×24 viewBox so all three brand | ||
| * components compose at consistent relative scale. | ||
| * | ||
| * - **golf** — flag-on-horizon: flagstick + pennant + ground line, plus a | ||
| * small ringed dot at the base standing in for the golf-ball motif on | ||
| * `public/helm-golf-logo-transparent.png`'s wheel center. Echoes M4's | ||
| * "horizon + flag + green-contour arcs" background motif language (§B.1) | ||
| * at icon scale — that larger background motif is a separate asset the | ||
| * `landing-portals+cta` lane builds; this glyph is the small icon variant | ||
| * for portal cards / vignette headers (§B.2, §B.3), NOT that background. | ||
| * - **baseball** — the chalk foul-line V + base-path arc, the | ||
| * "groundskeeper's blueprint" geometry described in | ||
| * `docs/baseball/ENTRY_SCENES_DESIGN.md`. That doc's literal | ||
| * `EntryField.tsx` scene component doesn't exist in this branch yet (it | ||
| * belongs to the not-yet-built `entry-scenes` lane) — this geometry is | ||
| * derived from the design doc's description (two foul lines converging at | ||
| * home plate + a shallow infield arc), not read from that file. If | ||
| * `EntryField.tsx` lands with different exact angles, reconcile the two. | ||
| * - **lift** — barbell: bar + two concentric plate circles per sport, echoing | ||
| * an `IronRoomField` scene component that also doesn't exist yet in this | ||
| * branch; built from Amendment 3 §B's literal recipe instead. | ||
| * | ||
| * Consumers may also render this at much larger sizes (via `size`) at low | ||
| * opacity for a decorative background motif rather than an icon — the paths | ||
| * stay proportionate at any scale since everything is defined in the shared | ||
| * 24-unit viewBox. | ||
| */ | ||
| export function SportGlyph({ sport, size = 18, className, title }: SportGlyphProps) { | ||
| const a11y = getSvgA11yProps({ title }); | ||
| return ( | ||
| <svg | ||
| viewBox="0 0 24 24" | ||
| width={size} | ||
| height={size} | ||
| className={className} | ||
| fill="none" | ||
| stroke="currentColor" | ||
| strokeWidth={1.5} | ||
| strokeLinecap="round" | ||
| strokeLinejoin="round" | ||
| {...a11y} | ||
| > | ||
| {title ? <title>{title}</title> : null} | ||
| {sport === 'golf' && ( | ||
| <> | ||
| <path d="M4,17 H20" /> | ||
| <path d="M9,17 V6" /> | ||
| <path d="M9,6 L15,8.2 L9,10.4 Z" /> | ||
| <circle cx={6.3} cy={17} r={1.1} /> | ||
| </> | ||
| )} | ||
| {sport === 'baseball' && ( | ||
| <> | ||
| <path d="M12,19 L4,5" /> | ||
| <path d="M12,19 L20,5" /> | ||
| <path d="M8.4,12.7 Q12,10.9 15.6,12.7" /> | ||
| </> | ||
| )} | ||
| {sport === 'lift' && ( | ||
| <> | ||
| <path d="M2,12 H22" /> | ||
| <circle cx={6} cy={12} r={3.4} /> | ||
| <circle cx={6} cy={12} r={1.6} /> | ||
| <circle cx={18} cy={12} r={3.4} /> | ||
| <circle cx={18} cy={12} r={1.6} /> | ||
| </> | ||
| )} | ||
| </svg> | ||
| ); | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,11 @@ | ||
| /** | ||
| * Brand components (Amendment 3 §A/§B) — see CONTRACTS.md "Brand components" | ||
| * for the full consumer map. Re-exported from the top-level first-light | ||
| * `index.ts`; import from there in moment files, not from this path directly. | ||
| */ | ||
| export { HelmMark } from './HelmMark'; | ||
| export type { HelmMarkProps } from './HelmMark'; | ||
| export { HelmRosette } from './HelmRosette'; | ||
| export type { HelmRosetteProps } from './HelmRosette'; | ||
| export { SportGlyph } from './SportGlyph'; | ||
| export type { SportGlyphProps, SportGlyphSport } from './SportGlyph'; |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,25 @@ | ||
| /** | ||
| * Shared accessibility contract for the brand SVG icon family (HelmMark, | ||
| * HelmRosette, SportGlyph). Decorative by default — `aria-hidden` + | ||
| * `focusable="false"` (the latter kills the old-Edge/IE11 "SVGs are | ||
| * keyboard-focusable by default" quirk) — and becomes a labeled image | ||
| * (`role="img"` + `aria-label` + a `<title>` child the caller renders) | ||
| * only when a `title` is supplied. | ||
| */ | ||
| export interface SvgA11yInput { | ||
| title?: string; | ||
| } | ||
|
|
||
| export interface SvgA11yProps { | ||
| role?: 'img'; | ||
| 'aria-hidden'?: boolean; | ||
| 'aria-label'?: string; | ||
| focusable: 'false'; | ||
| } | ||
|
|
||
| export function getSvgA11yProps({ title }: SvgA11yInput): SvgA11yProps { | ||
| if (title) { | ||
| return { role: 'img', 'aria-label': title, focusable: 'false' }; | ||
| } | ||
| return { 'aria-hidden': true, focusable: 'false' }; | ||
| } |
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
3. Dev-only routes type import
🐞 Bug☼ ReliabilityAgent Prompt
ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools