/* src/logo.css */
/* Logo roles for src/logo.svg (PLAN-harkness-splash Phase 1 §3).
Every traced part is a potrace <path class="part"> whose fill is var(--part);
--part is set per role below, so recolouring a seat is one custom property.
The outline is a stroke painted UNDER the fill (paint-order: stroke): the fill
is the traced coloured body, and the visible hairline is the outer half of the
stroke, so the stroke width is twice the logo's ~3.5 px hairline. */
.lh-logo {
--seat: #51706A;     /* teal figures (median of the PNG's teal fill) */
--floor: #D97C78;    /* coral: whoever has the floor, and the bubble */
--mark: #9BD1AF;     /* mint LH */
--outline: #010301;  /* the hairline */
--rim: var(--seat);  /* the table's perimeter arcs */
--edge: #0F6E56;     /* chords (the page accent) */
--outline-w: 6.8px;
}
@media (prefers-color-scheme: dark) {
.lh-logo {
--seat: #6F938B;
--floor: #E0857F;
--mark: #A7D9B9;
--outline: #0F172A; /* the dark page background: hairlines read as cuts */
--edge: #2DD4BF;
}
}
.lh-logo .part {
fill: var(--part);
stroke: var(--outline);
stroke-width: var(--outline-w);
stroke-linejoin: round;
paint-order: stroke;
}
.lh-logo .arc { --part: var(--rim); }
.lh-logo .seat { --part: var(--seat); }
.lh-logo .seat.is-floor { --part: var(--floor); }
.lh-logo #bubble { --part: var(--floor); }
.lh-logo #lh { --part: var(--mark); }
/* Joints: fill-only traced patches over the seams where one coloured body was
split between two parts (an arc running into a figure; the tail into the
ring), so the split shows no hairline the logo does not have. */
.lh-logo .joint { fill: var(--rim); stroke: none; }
.lh-logo #bubble .joint { fill: var(--part); }
/* src/graph.css */
/* The live graph's scripted layers (PLAN-harkness-splash Phase 2-3): chords, names,
badges, the floor overlays and the visitor's hit targets. Shared by the HyperFrames
composition (hero/, copied by scripts/sync-hero.mjs) and the page (concatenated into
the hashed css by scripts/build.mjs). Colours are role properties on .lh-logo, so the
dark scheme is one media block. */
.lh-logo {
--label: #1A2332;    /* handoff text primary */
--halo: #FFFFFF;     /* the page background: a halo keeps names legible over chords */
--badge-q: #2563EB;  /* the app's Q blue, one step darker for contrast on white */
--badge-t: #A16207;  /* the app's T yellow, darkened for contrast on white */
--badge-n: #5A6B7A;  /* handoff text secondary */
-webkit-tap-highlight-color: transparent;
}
@media (prefers-color-scheme: dark) {
.lh-logo {
--label: #E2E8F0;
--halo: #0F172A;
--badge-q: #60A5FA;
--badge-t: #FBBF24;
--badge-n: #94A3B8;
}
}
/* Chords: width and stroke-opacity are per-weight attributes set by the score
(the app's formula); `opacity` is left free for the projector notes to dim. */
.lh-logo .chord {
fill: none;
stroke: var(--edge);
stroke-linecap: butt;
}
/* The floor overlay: a copy of the seat's body in coral, faded in and out. */
.lh-logo .seat .floor { --part: var(--floor); }
/* Names and badges, in the app's label style (system stack, 500 / bold tally). */
.lh-logo .label,
.lh-logo .badge {
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
font-variant-numeric: tabular-nums;
text-anchor: middle;
paint-order: stroke;
stroke: var(--halo);
stroke-linejoin: round;
}
.lh-logo .label { font-size: 30px; font-weight: 500; fill: var(--label); stroke-width: 9px; }
.lh-logo .badge { font-size: 26px; font-weight: 700; stroke-width: 8px; }
.lh-logo .badge .q { fill: var(--badge-q); }
.lh-logo .badge .t { fill: var(--badge-t); }
.lh-logo .badge .n { fill: var(--badge-n); font-weight: 500; }
/* Visitor hit targets (page only): invisible until pressed or armed. */
.lh-logo .hit {
fill: var(--edge);
fill-opacity: 0;
stroke: var(--edge);
stroke-opacity: 0;
stroke-width: 6px;
pointer-events: all;
cursor: pointer;
}
.lh-logo:not(.is-live) .hit { pointer-events: none; }
.lh-logo .hit:active { fill-opacity: 0.14; }
.lh-logo .hit.is-armed { fill-opacity: 0.14; stroke-opacity: 0.9; }
.lh-logo .hit:focus { outline: none; }
.lh-logo .hit:focus-visible { outline: none; stroke: var(--color-focus-ring, var(--edge)); stroke-opacity: 1; }
/* src/css/splash.css */
/* splash.css: Let's Harkness splash + About (PLAN-harkness-splash Phase 3 §4).
Role-token NAMES from the house TOKEN-CONTRACT, VALUES from the app's design handoff
(SPLASH-PAGE-DESIGN-HANDOFF.md). Shadowless: depth is borders and value steps. The only
circles are the graph's figures. Display face: Source Serif 4 at the Display optical size,
one static instance (opsz 60, wght 600, Latin subset), SIL OFL 1.1: /splash/fonts/OFL.txt */
@font-face {
font-family: 'LH Display';
src: url('/splash/assets/SourceSerif4Display-600-latin.ac39e53f.woff2') format('woff2');
font-weight: 600;
font-style: normal;
font-display: swap;
unicode-range: U+0000-00FF, U+2014, U+2019, U+201C-201D;
}
:root {
color-scheme: light dark;
--color-bg: #FFFFFF;
--color-surface: #F0F4F3;
--color-fg: #1A2332;
--color-fg-muted: #5A6B7A;
--color-border: #D6DDE3;
--color-accent: #0F6E56;
--color-accent-hover: #085041;
--color-accent-fg: #FFFFFF;
--color-focus-ring: #0F6E56;
--color-floor: #D97C78;
--font-display: 'LH Display', 'Source Serif 4', Georgia, 'Times New Roman', serif;
--font-sans: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
--text-sm: 0.9375rem;
--text-base: 1.0625rem;
--text-lg: 1.1875rem;
--text-xl: clamp(1.5rem, 1.1rem + 1.4vw, 2rem);
--text-display: clamp(2.75rem, 1.6rem + 3.2vw, 4rem);
--duration-fast: 120ms;
--duration-base: 200ms;
--duration-slow: 360ms;
--ease-out: cubic-bezier(0.2, 0.7, 0.2, 1);
--gutter: clamp(1.25rem, 5vw, 5rem);
}
@media (prefers-color-scheme: dark) {
:root {
--color-bg: #0F172A;
--color-surface: #1E293B;
--color-fg: #E2E8F0;
--color-fg-muted: #94A3B8;
--color-border: #334155;
--color-accent: #2DD4BF;
--color-accent-hover: #5EEAD4;
--color-accent-fg: #0F172A;
--color-focus-ring: #2DD4BF;
--color-floor: #E0857F;
}
}
*, *::before, *::after { box-sizing: border-box; }
[hidden] { display: none !important; }
html {
background: var(--color-bg);
color: var(--color-fg);
font-family: var(--font-sans);
font-size: 100%;
-webkit-font-smoothing: antialiased;
-webkit-text-size-adjust: 100%;
}
body { margin: 0; font-size: var(--text-base); line-height: 1.5; }
p, h1, h2, h3, ol { margin: 0; }
a { color: var(--color-accent); text-underline-offset: 0.2em; text-decoration-thickness: 1px; }
:focus-visible { outline: 2px solid var(--color-focus-ring); outline-offset: 3px; }
.display,
.proj-intro,
.wordmark,
.stance,
.row-name {
font-family: var(--font-display);
font-weight: 600;
}
.eyebrow {
font: 600 0.8125rem/1.2 var(--font-sans);
letter-spacing: 0.08em;
text-transform: uppercase;
color: var(--color-accent);
}
/* ---------------------------------------------------------------- the stage: hero + projector */
.stage {
display: grid;
grid-template-columns: minmax(0, 2fr) minmax(0, 3fr);
column-gap: clamp(1.5rem, 4vw, 4rem);
padding-left: var(--gutter);
}
.head {
grid-column: 1;
grid-row: 1;
min-height: 55svh;
display: flex;
flex-direction: column;
justify-content: flex-end;
padding-top: 2rem;
}
.display {
font-size: var(--text-display);
line-height: 1.04;
letter-spacing: -0.012em;
text-align: left;
text-wrap: balance;
max-width: 13ch;
}
.lede {
margin-top: 1.25rem;
max-width: 30rem;
font-size: var(--text-lg);
line-height: 1.45;
color: var(--color-fg-muted);
text-wrap: pretty;
}
.act {
grid-column: 1;
grid-row: 2;
min-height: 45svh;
padding-top: 2rem;
}
.cta-row { display: flex; flex-wrap: wrap; align-items: center; gap: 0.75rem 1.25rem; }
.cta {
display: inline-flex;
align-items: center;
min-height: 3rem;
padding: 0 1.75rem;
border-radius: 2px;
background: var(--color-accent);
color: var(--color-accent-fg);
font: 600 var(--text-base)/1 var(--font-sans);
text-decoration: none;
transition: background-color var(--duration-base) var(--ease-out), transform var(--duration-fast) var(--ease-out);
}
.cta:hover { background: var(--color-accent-hover); }
.cta:active { transform: scale(0.97); }
.cta-note { color: var(--color-fg-muted); }
.play { margin-top: 2rem; }
.play.is-waiting { visibility: hidden; }
.hint { color: var(--color-fg-muted); font-size: var(--text-sm); }
.controls { display: flex; gap: 1.5rem; margin-top: 0.25rem; }
.textbtn {
min-height: 44px;
min-width: 44px;
padding: 0 0.125rem;
border: 0;
border-bottom: 1px solid var(--color-border);
border-radius: 0;
background: none;
color: var(--color-fg);
font: 500 var(--text-base)/1 var(--font-sans);
cursor: pointer;
transition: border-color var(--duration-base) var(--ease-out), transform var(--duration-fast) var(--ease-out);
}
.textbtn:hover { border-bottom-color: var(--color-accent); }
.textbtn:active { transform: translateY(1px); border-bottom-color: var(--color-accent); }
/* The graph: the hero SVG itself is the pinned element for the projector section. */
.pin {
grid-column: 2;
grid-row: 1 / span 3;
align-self: start;
position: sticky;
top: 0;
height: 100svh;
display: flex;
align-items: center;
justify-content: flex-end;
}
.pin svg.lh-logo {
display: block;
width: min(100%, calc(100svh - 3rem));
height: auto;
overflow: visible;
touch-action: manipulation;
}
/* On the projector */
.proj {
grid-column: 1;
grid-row: 3;
padding: 6rem 0 62svh; /* the last note reaches mid-viewport while the graph is still pinned */
}
.proj-intro {
margin-top: 0.75rem;
max-width: 20ch;
font-size: var(--text-xl);
line-height: 1.15;
text-wrap: balance;
}
.notes { list-style: none; padding: 0; counter-reset: note; }
.note {
counter-increment: note;
max-width: 24rem;
margin-top: 34svh;
padding: 0.25rem 0 0.25rem 1.25rem;
border-left: 2px solid var(--color-border);
font-size: var(--text-lg);
line-height: 1.45;
color: var(--color-fg-muted);
transition: border-color var(--duration-slow) var(--ease-out), color var(--duration-slow) var(--ease-out);
}
.note::before {
content: counter(note);
display: block;
margin-bottom: 0.25rem;
font: 600 1rem/1 var(--font-display);
color: var(--color-accent);
}
.note.is-active { border-left-color: var(--color-accent); color: var(--color-fg); }
/* ---------------------------------------------------------------- facts + footer */
.facts {
padding: 2rem var(--gutter);
border-top: 1px solid var(--color-border);
font-size: var(--text-lg);
line-height: 1.45;
text-wrap: pretty;
}
.foot {
display: flex;
flex-wrap: wrap;
align-items: center;
gap: 0.5rem 2rem;
padding: 0.75rem var(--gutter) 2.5rem;
border-top: 1px solid var(--color-border);
background: var(--color-surface);
}
.foot-link {
display: inline-flex;
align-items: center;
min-height: 44px;
color: var(--color-fg);
font-weight: 500;
}
.wordmark { font-size: 1.25rem; color: var(--color-accent); }
/* ---------------------------------------------------------------- portrait: stack, pin the top half */
@media (orientation: portrait) and (max-width: 1023px), (max-width: 760px) {
.stage { display: block; padding: 0 var(--gutter); }
.head { min-height: 0; padding-top: clamp(1.25rem, 4svh, 2.5rem); }
.display { max-width: 16ch; }
.lede { margin-top: 0.75rem; }
.pin {
z-index: 2;
height: 50svh;
margin: 0 calc(-1 * var(--gutter));
padding: 0.5rem var(--gutter);
justify-content: center;
background: var(--color-bg);
}
.pin svg.lh-logo { width: auto; height: 100%; max-width: 100%; }
.act { min-height: 0; padding-top: 0.75rem; }
.play { margin-top: 1.25rem; }
.proj { padding: 4rem 0 40svh; }
.note { margin-top: 26svh; }
}
/* ---------------------------------------------------------------- About */
.about .page {
max-width: 64rem;
padding: clamp(1.5rem, 5svh, 3.5rem) var(--gutter) 4rem;
}
.about-head { display: flex; flex-wrap: wrap; align-items: center; gap: 0.5rem 2rem; }
.home {
display: inline-flex;
align-items: center;
gap: 0.625rem;
min-height: 44px;
text-decoration: none;
}
.ring-mark { width: 1.75rem; height: 1.75rem; flex: none; }
.ring-mark circle { fill: none; stroke: var(--color-floor); stroke-width: 4; }
.stance {
margin-top: clamp(2rem, 7svh, 4.5rem);
max-width: 28ch;
font-size: clamp(1.75rem, 1.2rem + 1.8vw, 2.5rem);
line-height: 1.15;
letter-spacing: -0.008em;
text-wrap: pretty;
}
.loop { margin-top: 1.5rem; max-width: 36rem; font-size: var(--text-lg); line-height: 1.55; color: var(--color-fg-muted); }
.ledger-h { margin-top: 3.5rem; }
.ledger { list-style: none; padding: 0; margin-top: 1rem; }
.row {
display: grid;
grid-template-columns: minmax(0, 1fr) 12rem;
column-gap: 2.5rem;
padding: 1.5rem 0 1.75rem;
border-top: 1px solid var(--color-border);
}
.row > :not(.row-status) { grid-column: 1; }
.row-name { font-size: 1.5rem; line-height: 1.2; }
.row-name a { display: inline-block; padding: 0.375rem 0; color: var(--color-fg); text-decoration-color: var(--color-accent); }
.row-problem { margin-top: 0.25rem; max-width: 38rem; line-height: 1.5; }
.row-does { margin-top: 0.5rem; max-width: 38rem; color: var(--color-fg-muted); line-height: 1.5; }
.row-status {
grid-column: 2;
grid-row: 1 / span 3;
align-self: start;
margin-top: 0.5rem;
padding: 0.125rem 0 0.125rem 0.75rem;
border-left: 2px solid var(--color-accent);
font-size: var(--text-sm);
line-height: 1.4;
color: var(--color-fg-muted);
}
@media (max-width: 640px) {
.row { grid-template-columns: minmax(0, 1fr); }
.row-status { grid-column: 1; grid-row: auto; margin-top: 0.875rem; }
}
/* About — company sections */
.principles { margin-top: 1rem; }
.principle { padding: 1.25rem 0 1.5rem; border-top: 1px solid var(--color-border); max-width: 42rem; }
.principle dt { font-family: var(--font-display); font-size: 1.375rem; line-height: 1.2; font-weight: 600; }
.principle dd { margin: 0.5rem 0 0; line-height: 1.55; color: var(--color-fg-muted); max-width: 38rem; }
.company { margin-top: 1rem; max-width: 42rem; }
.fact { display: grid; grid-template-columns: 11rem minmax(0, 1fr); column-gap: 2rem; padding: 0.875rem 0; border-top: 1px solid var(--color-border); }
.fact dt { font-size: var(--text-sm); line-height: 1.5; color: var(--color-fg-muted); letter-spacing: 0.02em; }
.fact dd { margin: 0; line-height: 1.5; }
@media (max-width: 640px) {
.fact { grid-template-columns: minmax(0, 1fr); row-gap: 0.125rem; }
}
@media (prefers-reduced-motion: reduce) {
.cta, .textbtn, .note { transition: none; }
.cta:active, .textbtn:active { transform: none; }
}
