diff --git a/docs/redesign/round5/cards-after-1440.webp b/docs/redesign/round5/cards-after-1440.webp new file mode 100644 index 0000000..1b5a657 Binary files /dev/null and b/docs/redesign/round5/cards-after-1440.webp differ diff --git a/docs/redesign/round5/cards-before-1440.webp b/docs/redesign/round5/cards-before-1440.webp new file mode 100644 index 0000000..6d41250 Binary files /dev/null and b/docs/redesign/round5/cards-before-1440.webp differ diff --git a/src/App.vue b/src/App.vue index 9eaabda..dbe46d3 100644 --- a/src/App.vue +++ b/src/App.vue @@ -13,6 +13,7 @@ import './styles/title-navigation.css'; import './styles/wayfinding.css'; import './styles/route.css'; import './styles/gantry.css'; +import './styles/cards.css'; import './styles/print.css'; defineProps<{ inputs: Readonly }>(); diff --git a/src/styles/cards.css b/src/styles/cards.css new file mode 100644 index 0000000..dede0e0 --- /dev/null +++ b/src/styles/cards.css @@ -0,0 +1,168 @@ +/* ========================================================================== + Round 5: one card rule, and the motion pass. + + The rule: a chapter title sits outside, above its plate. Every card title + sits inside its card, first, under a mono registration plate in the + form "EXIT.N / KIND", the same plate the project cards already carry. + Plates are decorative and generated by CSS (alt text ''), so the server + and hydrated text stay identical and screen readers hear titles only. + + Motion is transform, clip-path and colour only, never on the chapter + headings (journey and audio measure them), and only with + prefers-reduced-motion: no-preference. + ========================================================================== */ + +/* ---------- Registration plates for the cards that lacked one ---------- */ +.experience-timeline { counter-reset: role; } +.experience-timeline > li { counter-increment: role; } +.toolkit-grid { counter-reset: stack; } +.toolkit-group { counter-increment: stack; } + +.experience-record h3::before, +.systems-operations h3::before, +.infrastructure-case > h3::before, +.toolkit-group h3::before { + 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; + text-transform: uppercase; +} +.experience-record h3::before { content: '03.' counter(role) ' / Role' / ''; } +.systems-operations h3::before { content: '03.S / Operations' / ''; } +.infrastructure-case > h3::before { content: '04.1 / Platform' / ''; } +.toolkit-group h3::before { content: '07.' counter(stack) ' / Stack' / ''; } + +@media (forced-colors: active) { + .experience-record h3::before, + .systems-operations h3::before, + .infrastructure-case > h3::before, + .toolkit-group h3::before { + border-color: CanvasText; + color: CanvasText; + } +} +@media print { + .experience-record h3::before, + .systems-operations h3::before, + .infrastructure-case > h3::before, + .toolkit-group h3::before { display: none; } +} + +/* ---------- Tailored cards ---------- */ +/* Project cards and the operations card are panels: a lit top edge in the + chapter accent, which the hover draws out across the card. */ +.project-card, +.systems-operations { + position: relative; +} +.project-card::after, +.systems-operations::after { + content: '' / ''; + position: absolute; + inset: 0 0 auto; + block-size: 2px; + background: linear-gradient( + 90deg, + color-mix(in srgb, var(--chapter-accent, var(--accent)) 85%, transparent), + transparent + ); + pointer-events: none; + transform: scaleX(0.34); + transform-origin: left; +} +@media (forced-colors: active) { + .project-card::after, + .systems-operations::after { display: none; } +} + +/* ---------- Motion ---------- */ +@media (prefers-reduced-motion: no-preference) { + /* Hover and focus: cards lift, their accent edge runs across. */ + .project-card, + .systems-operations, + .experience-record, + .toolkit-group { + transition: + transform 420ms cubic-bezier(0.22, 1, 0.36, 1), + box-shadow 420ms ease; + } + .project-card::after, + .systems-operations::after { + transition: transform 620ms cubic-bezier(0.22, 1, 0.36, 1); + } + .project-card:is(:hover, :focus-within)::after, + .systems-operations:is(:hover, :focus-within)::after { + transform: scaleX(1); + } + .project-card:is(:hover, :focus-within), + .systems-operations:is(:hover, :focus-within) { + transform: translateY(-4px); + box-shadow: 0 22px 48px -24px color-mix(in srgb, var(--chapter-accent, var(--accent)) 55%, transparent); + } + .experience-record:is(:hover, :focus-within), + .toolkit-group:is(:hover, :focus-within) { + transform: translateX(6px); + } + .toolkit-item svg, + .toolkit-item img { + transition: transform 320ms cubic-bezier(0.22, 1, 0.36, 1); + } + .toolkit-item:hover svg, + .toolkit-item:hover img { + transform: translateY(-2px) scale(1.12); + } + + /* Card reveals: each card rises and unmasks as it enters the reading + window. Scroll-linked, so it can never lag behind the reader. */ + @supports (animation-timeline: view()) { + .reading-stage .project-card, + .reading-stage .systems-operations, + .reading-stage .experience-record, + .reading-stage .toolkit-group, + .reading-stage .infrastructure-node { + animation: card-reveal linear both; + animation-timeline: view(); + animation-range: entry 0% entry 55%; + } + } + + /* Chapter transitions: the arriving chapter's plate sweeps a light edge + across and its first cards settle in sequence. */ + .reading-stage .portfolio-section[data-current] .chapter-body > :is(p, dl, ul, .experience-timeline, .toolkit-grid) { + animation: chapter-settle 900ms cubic-bezier(0.16, 1, 0.3, 1) both; + } + .reading-stage .portfolio-section[data-current] .chapter-body > :nth-child(2) { animation-delay: 90ms; } + .reading-stage .portfolio-section[data-current] .chapter-body > :nth-child(3) { animation-delay: 180ms; } + .reading-stage .portfolio-section[data-current] .chapter-body > :nth-child(n + 4) { animation-delay: 270ms; } + + /* The gantry's lane lamps light in sequence along the route. */ + html .site-navigation li:nth-child(2)::after { transition-delay: 40ms; } + html .site-navigation li:nth-child(3)::after { transition-delay: 80ms; } + html .site-navigation li:nth-child(4)::after { transition-delay: 120ms; } + html .site-navigation li:nth-child(5)::after { transition-delay: 160ms; } + html .site-navigation li:nth-child(6)::after { transition-delay: 200ms; } + html .site-navigation li:nth-child(7)::after { transition-delay: 240ms; } + html .site-navigation li:nth-child(8)::after { transition-delay: 280ms; } +} + +@keyframes card-reveal { + from { + transform: translateY(36px); + clip-path: inset(0 0 22% 0); + } + to { + transform: none; + clip-path: inset(-40px -40px -40px -40px); + } +} +@keyframes chapter-settle { + from { transform: translateY(14px); } + to { transform: none; } +}