Files
Portfolio/src/styles/wayfinding.css

240 lines
7.6 KiB
CSS

/* 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 ease-out 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 warming up: one soft swell, then steady. Below 3 Hz by design
(photosensitivity): a single rise over the whole duration. */
@keyframes exit-strike {
0% { opacity: 0.25; }
45% { opacity: 0.85; }
60% { opacity: 0.7; }
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; }
}