feat: wayfinding system across chapters, HUD and project plates; cut boot log, headlight and ops strip
This commit is contained in:
@@ -1,184 +0,0 @@
|
||||
/* Night Shift: cinematic layer on top of the night-drive world. Imported last. */
|
||||
|
||||
:root {
|
||||
--ns-grain: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='160' height='160'><filter id='n'><feTurbulence type='fractalNoise' baseFrequency='.9' numOctaves='2' stitchTiles='stitch'/></filter><rect width='100%' height='100%' filter='url(%23n)' opacity='.55'/></svg>");
|
||||
}
|
||||
|
||||
/* Film stock: grain + vignette above the world, below the UI. */
|
||||
body::after {
|
||||
content: '';
|
||||
position: fixed;
|
||||
inset: 0;
|
||||
z-index: var(--z-atmosphere);
|
||||
pointer-events: none;
|
||||
background:
|
||||
radial-gradient(120% 90% at 50% 45%, transparent 55%, rgb(2 4 12 / 55%) 100%),
|
||||
var(--ns-grain);
|
||||
background-size: auto, 160px 160px;
|
||||
mix-blend-mode: soft-light;
|
||||
opacity: 0.5;
|
||||
}
|
||||
|
||||
/* Operations strip */
|
||||
.ops-strip {
|
||||
position: fixed;
|
||||
inset-block-end: 22px;
|
||||
inset-inline: 0;
|
||||
margin-inline: auto;
|
||||
width: max-content;
|
||||
max-width: calc(100vw - 32px);
|
||||
z-index: var(--z-hud);
|
||||
display: none;
|
||||
align-items: center;
|
||||
gap: 0.7rem;
|
||||
padding: 0.5rem 1rem;
|
||||
border: var(--panel-border);
|
||||
border-radius: 999px;
|
||||
background: var(--surface);
|
||||
color: var(--text-muted);
|
||||
font: 500 0.72rem/1 var(--mono);
|
||||
letter-spacing: 0.08em;
|
||||
text-transform: uppercase;
|
||||
pointer-events: none;
|
||||
}
|
||||
.ops-strip__item::before { content: attr(data-label) ' '; }
|
||||
.ops-strip__item::after { content: attr(data-value); color: var(--text); font-weight: 600; }
|
||||
.ops-strip__sep { opacity: 0.4; }
|
||||
.ops-strip__sep::before { content: '/'; }
|
||||
.ops-strip__dot {
|
||||
width: 7px;
|
||||
height: 7px;
|
||||
border-radius: 50%;
|
||||
background: #6ee7a8;
|
||||
box-shadow: 0 0 10px #6ee7a8;
|
||||
}
|
||||
@media (min-width: 1100px) {
|
||||
.ops-strip { display: inline-flex; }
|
||||
}
|
||||
@media (prefers-reduced-motion: no-preference) {
|
||||
.ops-strip__dot { animation: ns-pulse 2.4s ease-in-out infinite; }
|
||||
}
|
||||
@keyframes ns-pulse { 50% { opacity: 0.35; } }
|
||||
|
||||
/* Boot sequence */
|
||||
.boot-sequence {
|
||||
position: fixed;
|
||||
inset: 0;
|
||||
z-index: 100;
|
||||
display: grid;
|
||||
align-content: center;
|
||||
justify-items: start;
|
||||
gap: 2rem;
|
||||
padding: clamp(1.5rem, 8vw, 8rem);
|
||||
background: #02040c;
|
||||
color: var(--accent);
|
||||
font: 500 clamp(0.8rem, 0.7rem + 0.5vw, 1.05rem)/1.8 var(--mono);
|
||||
cursor: pointer;
|
||||
transition: opacity 600ms ease, filter 600ms ease;
|
||||
}
|
||||
.boot-sequence[data-leaving] { opacity: 0; filter: blur(6px); pointer-events: none; }
|
||||
.boot-sequence__log { list-style: none; margin: 0; padding: 0; }
|
||||
.boot-sequence__log li::before { content: '> '; color: var(--world-magenta); }
|
||||
.boot-sequence__log li:last-child::after {
|
||||
content: '_';
|
||||
margin-inline-start: 0.2em;
|
||||
animation: ns-blink 700ms steps(1) infinite;
|
||||
}
|
||||
.boot-sequence__skip { color: var(--text-muted); font-size: 0.75rem; letter-spacing: 0.1em; text-transform: uppercase; }
|
||||
@keyframes ns-blink { 50% { opacity: 0; } }
|
||||
|
||||
/* Headlight: soft beam that follows a fine pointer. */
|
||||
html[data-headlight='on'] .reading-viewport::before {
|
||||
content: '';
|
||||
position: fixed;
|
||||
inset: 0;
|
||||
z-index: -1;
|
||||
pointer-events: none;
|
||||
background: radial-gradient(420px circle at var(--hx, 50%) var(--hy, 50%), rgb(255 214 147 / 10%), transparent 70%);
|
||||
mix-blend-mode: screen;
|
||||
}
|
||||
|
||||
/* Spotlight borders on interactive surfaces. */
|
||||
.project-case-study,
|
||||
.project-card,
|
||||
.contact-destination,
|
||||
.infrastructure-flow-figure {
|
||||
position: relative;
|
||||
isolation: isolate;
|
||||
}
|
||||
html[data-headlight='on'] :is(.project-case-study, .project-card, .contact-destination, .infrastructure-flow-figure)::after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
z-index: -1;
|
||||
border-radius: inherit;
|
||||
pointer-events: none;
|
||||
background: radial-gradient(320px circle at var(--mx, 50%) var(--my, 50%), rgb(120 216 229 / 14%), transparent 65%);
|
||||
opacity: 0;
|
||||
transition: opacity 240ms ease;
|
||||
}
|
||||
html[data-headlight='on'] :is(.project-case-study, .project-card, .contact-destination, .infrastructure-flow-figure):hover::after {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
/* Chapter numerals: outlined, drifting section numbers. */
|
||||
.reading-stage { counter-reset: chapter; }
|
||||
.portfolio-section > h2,
|
||||
.project-stage__lead > h2 { position: relative; }
|
||||
.portfolio-section { counter-increment: chapter; }
|
||||
.portfolio-section > h2::before,
|
||||
.project-stage__lead > h2::before {
|
||||
content: '0' counter(chapter) / '';
|
||||
position: absolute;
|
||||
inset-block-start: 50%;
|
||||
inset-inline-end: 0;
|
||||
translate: 0 -50%;
|
||||
font: 700 var(--step-4)/0.85 var(--mono);
|
||||
letter-spacing: -0.04em;
|
||||
color: transparent;
|
||||
-webkit-text-stroke: 1px rgb(120 216 229 / 38%);
|
||||
pointer-events: none;
|
||||
user-select: none;
|
||||
}
|
||||
@media (max-width: 720px) {
|
||||
.portfolio-section > h2::before,
|
||||
.project-stage__lead > h2::before { font-size: var(--step-3); }
|
||||
}
|
||||
@media (forced-colors: active) {
|
||||
.portfolio-section > h2::before,
|
||||
.project-stage__lead > h2::before,
|
||||
body::after,
|
||||
.reading-viewport::before,
|
||||
:is(.project-case-study, .project-card, .contact-destination, .infrastructure-flow-figure)::after { display: none !important; }
|
||||
}
|
||||
|
||||
/* Kinetic type + scroll-linked reveals (progressive enhancement). */
|
||||
@media (prefers-reduced-motion: no-preference) {
|
||||
.hero-name__text {
|
||||
font-variation-settings: 'wght' 800;
|
||||
transition: font-variation-settings 400ms ease, letter-spacing 400ms ease;
|
||||
}
|
||||
.hero-name:hover .hero-name__text {
|
||||
font-variation-settings: 'wght' 520;
|
||||
letter-spacing: 0.02em;
|
||||
}
|
||||
@supports (animation-timeline: view()) {
|
||||
.reading-stage:not([data-reading-stage='live']) .project-card {
|
||||
animation: ns-rise linear both;
|
||||
animation-timeline: view();
|
||||
animation-range: entry 0% entry 40%;
|
||||
}
|
||||
}
|
||||
}
|
||||
@keyframes ns-rise {
|
||||
from { transform: translateY(28px); }
|
||||
to { transform: none; }
|
||||
}
|
||||
|
||||
@media print {
|
||||
body::after, .ops-strip, .boot-sequence { display: none !important; }
|
||||
.portfolio-section > h2::before,
|
||||
.project-stage__lead > h2::before,
|
||||
.reading-viewport::before,
|
||||
:is(.project-case-study, .project-card, .contact-destination, .infrastructure-flow-figure)::after { display: none !important; }
|
||||
}
|
||||
240
src/styles/wayfinding.css
Normal file
240
src/styles/wayfinding.css
Normal file
@@ -0,0 +1,240 @@
|
||||
/* Wayfinding: one sign language for the route. The roadside signs, chapter
|
||||
plates, navigation, dash cluster and project plates share the same exit
|
||||
numbers (from src/journey/sign-routes.ts) and the chapter accent colours.
|
||||
Every label here is CSS generated content with empty alt text, so the
|
||||
semantic document is identical with and without JavaScript. Imported last. */
|
||||
|
||||
:root {
|
||||
--exit-ink: #160b22;
|
||||
--exit-chamfer: polygon(5px 0, calc(100% - 5px) 0, 100% 5px, 100% 100%, 0 100%, 0 5px);
|
||||
}
|
||||
|
||||
/* Static vignette above the world, below the UI. Plain alpha, no blend mode,
|
||||
so the compositor never re-blends it against the animating canvas. */
|
||||
body::after {
|
||||
content: '';
|
||||
position: fixed;
|
||||
inset: 0;
|
||||
z-index: var(--z-atmosphere);
|
||||
pointer-events: none;
|
||||
background: radial-gradient(130% 95% at 50% 42%, transparent 58%, rgb(8 3 18 / 42%) 100%);
|
||||
}
|
||||
|
||||
/* The hero scrim used to end in a hard edge that read as a dark band while
|
||||
scrolling away from the hero. Feather its bottom with a static mask. */
|
||||
.hero-journey::before {
|
||||
mask-image: linear-gradient(to bottom, #000 72%, transparent);
|
||||
}
|
||||
|
||||
/* ---------- Chapter exit plates ---------- */
|
||||
.portfolio-section[data-exit] {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
/* The exit tab sits on the panel's top edge, like the tab on a motorway sign. */
|
||||
.portfolio-section[data-exit]::before {
|
||||
content: 'EXIT ' attr(data-exit) / '';
|
||||
position: absolute;
|
||||
z-index: 1;
|
||||
inset-block-start: 0;
|
||||
inset-inline-start: clamp(1.1rem, 4vw, 3.2rem);
|
||||
translate: 0 -50%;
|
||||
padding: 0.42rem 0.7rem 0.38rem;
|
||||
clip-path: var(--exit-chamfer);
|
||||
background: var(--chapter-accent, var(--accent));
|
||||
color: var(--exit-ink);
|
||||
font: 720 0.7rem/1 var(--mono);
|
||||
letter-spacing: 0.16em;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
/* "Then" line: the next exit, as on a real gantry. */
|
||||
.portfolio-section[data-exit]::after {
|
||||
content: 'THEN \2192 ' attr(data-next) / '';
|
||||
position: absolute;
|
||||
inset-block-start: 0;
|
||||
inset-inline-end: clamp(1.1rem, 4vw, 3.2rem);
|
||||
translate: 0 -50%;
|
||||
padding: 0.38rem 0.6rem;
|
||||
border: 1px solid color-mix(in srgb, var(--chapter-accent, var(--accent)) 42%, transparent);
|
||||
border-radius: 3px;
|
||||
background: var(--surface-strong);
|
||||
color: var(--text-muted);
|
||||
font: 560 0.64rem/1 var(--mono);
|
||||
letter-spacing: 0.12em;
|
||||
text-transform: uppercase;
|
||||
white-space: pre;
|
||||
pointer-events: none;
|
||||
}
|
||||
.portfolio-section[data-exit='08']::after {
|
||||
content: 'END OF ROUTE' / '';
|
||||
}
|
||||
|
||||
/* The hero is the on-ramp. */
|
||||
.hero-journey__content::before {
|
||||
content: 'EXIT 01 \00b7 ON-RAMP \00b7 BUCHAREST' / '';
|
||||
display: block;
|
||||
inline-size: fit-content;
|
||||
justify-self: start;
|
||||
align-self: start;
|
||||
margin-block-end: clamp(0.8rem, 2vh, 1.4rem);
|
||||
padding: 0.42rem 0.7rem 0.38rem;
|
||||
clip-path: var(--exit-chamfer);
|
||||
background: var(--rose, var(--accent));
|
||||
color: var(--exit-ink);
|
||||
font: 720 0.68rem/1 var(--mono);
|
||||
letter-spacing: 0.16em;
|
||||
white-space: pre;
|
||||
}
|
||||
|
||||
/* ---------- Navigation: the overhead gantry carries the same numbers ---------- */
|
||||
.site-navigation ul {
|
||||
counter-reset: exit;
|
||||
}
|
||||
.site-navigation li {
|
||||
counter-increment: exit;
|
||||
}
|
||||
@media (min-width: 1100px) {
|
||||
.site-navigation a::before {
|
||||
content: counter(exit, decimal-leading-zero) / '';
|
||||
margin-inline-end: 0.55em;
|
||||
color: color-mix(in srgb, var(--nav-accent, var(--accent)) 70%, var(--text-muted));
|
||||
font: 600 0.66em/1 var(--mono);
|
||||
letter-spacing: 0.06em;
|
||||
opacity: 0.8;
|
||||
}
|
||||
.site-navigation a[aria-current='page']::before {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
|
||||
/* ---------- Dash cluster: exit, chapter, route and local time in one unit ---------- */
|
||||
.journey-hud[data-exit]::before {
|
||||
content: 'EXIT ' attr(data-exit) / '';
|
||||
padding: 0.3rem 0.45rem 0.26rem;
|
||||
clip-path: var(--exit-chamfer);
|
||||
background: var(--rose, var(--accent));
|
||||
color: var(--exit-ink);
|
||||
flex: none;
|
||||
white-space: pre;
|
||||
font: 720 0.62rem/1 var(--mono);
|
||||
letter-spacing: 0.14em;
|
||||
}
|
||||
.journey-hud[data-exit='03']::before,
|
||||
.journey-hud[data-exit='07']::before { background: var(--violet, var(--accent)); }
|
||||
.journey-hud[data-exit='04']::before { background: var(--accent); }
|
||||
.journey-hud[data-exit='05']::before,
|
||||
.journey-hud[data-exit='08']::before { background: var(--coral, var(--amber)); }
|
||||
.journey-hud[data-clock]::after {
|
||||
content: attr(data-clock) ' BUC' / '';
|
||||
padding-inline-start: 0.72rem;
|
||||
border-inline-start: 1px solid rgb(126 220 232 / 22%);
|
||||
color: var(--text-muted);
|
||||
font-size: 0.66rem;
|
||||
letter-spacing: 0.1em;
|
||||
}
|
||||
@media (max-width: 767px) {
|
||||
.journey-hud[data-clock]::after { display: none; }
|
||||
}
|
||||
|
||||
/* ---------- Project registration plates ---------- */
|
||||
.project-case-study__header[data-plate]::before,
|
||||
.project-card[data-plate]::before {
|
||||
content: attr(data-plate) / '';
|
||||
display: block;
|
||||
inline-size: max-content;
|
||||
max-inline-size: 100%;
|
||||
margin-block-end: 0.85rem;
|
||||
padding: 0.34rem 0.6rem 0.3rem;
|
||||
border: 1px solid color-mix(in srgb, var(--chapter-accent, var(--accent)) 55%, transparent);
|
||||
border-radius: 3px;
|
||||
color: color-mix(in srgb, var(--chapter-accent, var(--accent)) 72%, var(--text));
|
||||
font: 650 0.64rem/1.2 var(--mono);
|
||||
letter-spacing: 0.14em;
|
||||
}
|
||||
|
||||
/* Case-study evidence reads as a route log: lettered stops. */
|
||||
.project-evidence {
|
||||
counter-reset: stop;
|
||||
}
|
||||
.project-evidence > div {
|
||||
counter-increment: stop;
|
||||
}
|
||||
.project-evidence dt::before {
|
||||
content: counter(stop, upper-alpha) / '';
|
||||
display: inline-grid;
|
||||
place-items: center;
|
||||
inline-size: 1.45em;
|
||||
block-size: 1.45em;
|
||||
margin-inline-end: 0.6em;
|
||||
border: 1px solid currentColor;
|
||||
border-radius: 50%;
|
||||
font: 700 0.78em/1 var(--mono);
|
||||
vertical-align: 0.08em;
|
||||
}
|
||||
|
||||
/* ---------- Arrival: the current chapter's plate strikes on, once ---------- */
|
||||
.portfolio-section[data-current]::before {
|
||||
box-shadow: 0 0 0 1px color-mix(in srgb, var(--chapter-accent, var(--accent)) 60%, transparent);
|
||||
}
|
||||
@media (prefers-reduced-motion: no-preference) {
|
||||
.portfolio-section[data-current]::before {
|
||||
animation: exit-strike 1.1s steps(1, end) both;
|
||||
}
|
||||
.portfolio-section[data-current] {
|
||||
animation: exit-panel-light 1.6s cubic-bezier(0.2, 0.75, 0.2, 1) both;
|
||||
}
|
||||
@supports (animation-timeline: view()) {
|
||||
.reading-stage:not([data-reading-stage='live']) .project-card {
|
||||
animation: wf-rise linear both;
|
||||
animation-timeline: view();
|
||||
animation-range: entry 0% entry 40%;
|
||||
}
|
||||
}
|
||||
}
|
||||
/* A neon tube catching: two short flickers, then steady. */
|
||||
@keyframes exit-strike {
|
||||
0% { opacity: 0.25; }
|
||||
12% { opacity: 1; }
|
||||
18% { opacity: 0.35; }
|
||||
30% { opacity: 1; }
|
||||
36% { opacity: 0.55; }
|
||||
48%, 100% { opacity: 1; }
|
||||
}
|
||||
@keyframes exit-panel-light {
|
||||
0% { border-top-color: color-mix(in srgb, var(--chapter-accent, var(--accent)) 95%, transparent); }
|
||||
100% { border-top-color: color-mix(in srgb, var(--chapter-accent, var(--accent)) 28%, transparent); }
|
||||
}
|
||||
@keyframes wf-rise {
|
||||
from { transform: translateY(28px); }
|
||||
to { transform: none; }
|
||||
}
|
||||
|
||||
/* ---------- Narrow screens ---------- */
|
||||
@media (max-width: 520px) {
|
||||
.portfolio-section[data-exit]::after,
|
||||
.journey-hud[data-exit]::before { display: none; }
|
||||
.hero-journey__content::before { content: 'EXIT 01 \00b7 ON-RAMP' / ''; }
|
||||
}
|
||||
|
||||
/* ---------- Forced colours and print ---------- */
|
||||
@media (forced-colors: active) {
|
||||
body::after { display: none !important; }
|
||||
.portfolio-section[data-exit]::before,
|
||||
.hero-journey__content::before,
|
||||
.journey-hud[data-exit]::before {
|
||||
forced-color-adjust: none;
|
||||
background: Canvas;
|
||||
color: CanvasText;
|
||||
border: 1px solid CanvasText;
|
||||
clip-path: none;
|
||||
}
|
||||
}
|
||||
@media print {
|
||||
body::after,
|
||||
.portfolio-section[data-exit]::before,
|
||||
.portfolio-section[data-exit]::after,
|
||||
.hero-journey__content::before,
|
||||
.project-case-study__header[data-plate]::before,
|
||||
.project-card[data-plate]::before { display: none !important; }
|
||||
}
|
||||
Reference in New Issue
Block a user