/* The Route (round 3). Chapters stop being identical glass cards over the world. Each one opens with its exit tab and a large heading standing over the road, then a reading panel, then a "next exit" sign; between chapters the page leaves a stretch of open road so the roadside districts show. Navigation becomes a route map. Every added label is CSS generated content or an aria-label, so the semantic document is unchanged. Imported after wayfinding.css, before print.css. */ /* ---------- Chapter openers over the road ---------- */ @media screen { .reading-stage .portfolio-section { margin-block: clamp(220px, 44vh, 480px) 0; padding-block-end: clamp(150px, 20vh, 230px); } .reading-stage .portfolio-section:not([data-layout='project-stage']) { padding-inline: 0; padding-block-start: 3rem; border: 0; border-radius: 0; background: none; box-shadow: none; } .reading-stage .portfolio-section[data-current] { animation: none; } /* The exit tab now hangs above the heading, not on a panel edge. */ .reading-stage .portfolio-section:not([data-layout='project-stage'])[data-exit]::before { inset-inline-start: 0; translate: none; padding: 0.55rem 0.85rem 0.5rem; font-size: 0.78rem; } .reading-stage :is(.portfolio-section, .project-stage__lead) > h2 { inline-size: fit-content; max-inline-size: 100%; /* Round 5: a tighter title stack (tag, title, rule) of about 180 px at 1440, so the chapter body starts higher under the beam. */ margin-block: 0 clamp(18px, 2.6vh, 30px); color: var(--text); font-size: clamp(50px, 5.6vw, 100px); font-weight: 620; line-height: 0.94; letter-spacing: -0.058em; text-wrap: balance; /* A dark halo instead of a plate: the heading stands in the scene but keeps its contrast over the sun and the neon horizon. */ text-shadow: 0 0 2px rgb(9 4 22 / 95%), 0 0.02em 0.16em rgb(9 4 22 / 92%), 0 0.06em 0.6em rgb(9 4 22 / 78%); } .reading-stage :is(.portfolio-section, .project-stage__lead) > h2::after { content: '' / ''; display: block; inline-size: min(2.4em, 100%); block-size: 5px; margin-block-start: 0.2em; border-radius: 3px; background: var(--chapter-accent, var(--accent)); box-shadow: 0 0 22px color-mix(in srgb, var(--chapter-accent, var(--accent)) 70%, transparent); } .reading-stage .portfolio-section > .section-eyebrow { margin-block-end: 0.9rem; text-shadow: 0 0 10px rgb(9 4 22 / 90%); } /* The reading panel carries the chrome the whole section used to. */ .reading-stage .chapter-body { position: relative; padding: clamp(28px, 3.4vw, 56px); border: 1px solid color-mix(in srgb, var(--chapter-accent, var(--accent)) 30%, transparent); border-radius: var(--radius-surface); background: radial-gradient(ellipse at 100% 0%, rgb(var(--chapter-glow, 40 70 110) / 18%), transparent 65%), var(--surface); box-shadow: var(--panel-shadow), inset 0 1px color-mix(in srgb, var(--chapter-accent, var(--accent)) 42%, transparent); } .reading-stage .chapter-body > * + * { margin-block-start: var(--space-3); } .reading-stage [data-layout='right-rail'] .chapter-body > p { font-size: clamp(19px, 1.5vw, 24px); line-height: 1.6; letter-spacing: -0.02em; } @media (prefers-reduced-motion: no-preference) { .reading-stage .portfolio-section[data-current] .chapter-body { animation: exit-panel-light 1.6s cubic-bezier(0.2, 0.75, 0.2, 1) both; } } /* End of chapter: the next exit, signed like a roadside advance sign. */ .reading-stage .portfolio-section[data-exit]::after { content: 'NEXT EXIT ' attr(data-next-exit) '\A' attr(data-next) ' \2192' / ''; inset-block: auto clamp(36px, 6vh, 76px); inset-inline: auto 0; translate: none; padding: 0.85rem 1.15rem 0.8rem; border: 2px solid color-mix(in srgb, var(--chapter-accent, var(--accent)) 70%, transparent); border-radius: 6px; background: rgb(12 6 28 / 90%); box-shadow: 0 0 28px color-mix(in srgb, var(--chapter-accent, var(--accent)) 22%, transparent); color: var(--text); font: 640 0.8rem/1.55 var(--mono); letter-spacing: 0.14em; text-align: start; } .reading-stage .portfolio-section[data-layout='left-timeline'][data-exit]::after { inset-inline: 0 auto; } .reading-stage .portfolio-section[data-exit='08']::after { content: 'END OF ROUTE' / ''; } .reading-stage .portfolio-section[data-exit='08'][data-trip]::after { content: 'END OF ROUTE\A' attr(data-trip) / ''; } } /* Phones: the same opener, scaled to the column; the chapter gap stays long enough to see the road between chapters. */ @media screen and (max-width: 767px) { .reading-stage .portfolio-section { margin-block: clamp(120px, 24vh, 220px) 0; padding-block-end: clamp(40px, 8vh, 80px); } .reading-stage .portfolio-section:not([data-layout='project-stage']) { padding-block-start: 2.6rem; } .reading-stage :is(.portfolio-section, .project-stage__lead) > h2 { font-size: clamp(42px, 12.5vw, 64px); } .reading-stage .chapter-body { padding: clamp(20px, 5.5vw, 28px); } } @media screen and (min-width: 521px) and (max-width: 767px) { .reading-stage .portfolio-section { padding-block-end: 130px; } } /* ---------- Destination: the ending is a place, not a small card ---------- */ @media screen { /* The avatar stands at the roadside beside the heading, above the panel. */ .reading-stage #contact .contact-avatar { inset-block: auto calc(100% + 14px); inset-inline-end: clamp(12px, 3vw, 40px); } } @media screen and (min-width: 768px) { .reading-stage .portfolio-section[data-layout='destination'] { inline-size: min(900px, 64%); /* The destination is the last stop: it ends just above the lower letterbox bar (and its credit), so the whole stack, heading included, sits below the upper bar when the page is scrolled to the end. */ padding-block-end: 0; margin-block-end: 0; } /* Padding, not margin: a trailing margin collapses out of the column. */ .reading-stage .portfolio-main:has(> [data-layout='destination']) { padding-block-end: calc(min(8vh, 72px) + 28px); } /* The end-of-route sign is filed inside the panel's lower right, level with the social links, instead of hanging over the credit below. It is static: nothing about it depends on the arrived state. */ .reading-stage .portfolio-section[data-layout='destination']::after { inset-block: auto clamp(22px, 3.4vh, 40px); inset-inline: auto clamp(24px, 3vw, 44px); padding: 0; border: 0; background: none; box-shadow: none; color: rgb(255 226 196 / 82%); font-size: 0.68rem; line-height: 1.6; text-align: end; } .reading-stage [data-layout='destination'] > h2 { font-size: clamp(64px, min(10vw, 15vh), 168px); } .reading-stage [data-layout='destination'] .chapter-body { padding: clamp(22px, 3.4vh, 40px) clamp(24px, 3vw, 44px); } .reading-stage [data-layout='destination'] .contact-destination__copy { font-size: clamp(22px, min(2.4vw, 3.6vh), 38px); line-height: 1.22; letter-spacing: -0.03em; } .reading-stage [data-layout='destination'] .contact-link { min-block-size: 56px; padding-inline: 1.6rem; font-size: 1.05rem; } } @media screen and (min-width: 768px) and (max-height: 780px) { .reading-stage .portfolio-section[data-layout='destination'] { padding-block-start: 2.25rem; } .reading-stage [data-layout='destination'] > h2 { font-size: clamp(64px, 13vh, 168px); margin-block-end: 0.9rem; } .reading-stage [data-layout='destination'] .contact-destination__copy { font-size: 23px; } .reading-stage [data-layout='destination'] .contact-social { margin-block-start: 0.9rem; } } /* ---------- Case studies: evidence beside media that stays in view ---------- */ @media screen and (min-width: 1100px) { .reading-stage .project-case-study[data-media-layout]:not([data-media-layout='none']) { display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); column-gap: clamp(32px, 3.6vw, 72px); align-items: start; } .reading-stage .project-case-study[data-media-layout]:not([data-media-layout='none']) > :not(.project-media-gallery) { grid-column: 1; } .reading-stage .project-case-study[data-media-layout]:not([data-media-layout='none']) > .project-media-gallery { grid-column: 2; grid-row: 1 / span 3; position: sticky; inset-block-start: calc(var(--sign-reading-top, 220px) + 16px); margin-block: 0; } /* Evidence reads as a spec sheet: the lettered stop in a rail, text beside it. */ .reading-stage .project-evidence > div { display: grid; grid-template-columns: minmax(9rem, 0.34fr) minmax(0, 1fr); column-gap: 1.4rem; align-items: baseline; } .reading-stage .project-case-study[data-media-layout]:not([data-media-layout='none']) .project-evidence { grid-template-columns: minmax(0, 1fr); } .reading-stage .project-case-study[data-media-layout]:not([data-media-layout='none']) .project-evidence ul { columns: auto; } } /* ---------- Route map navigation ---------- */ @media screen and (min-width: 768px) { .site-navigation ul { position: relative; } /* The road: a thin line under the exits, lit up to the current one. */ .site-navigation ul::before, .site-navigation ul::after { content: '' / ''; position: absolute; inset-block-end: 3px; inset-inline-start: calc(100% / var(--route-count, 8) / 2); block-size: 2px; border-radius: 2px; pointer-events: none; } .site-navigation ul::before { inline-size: calc(100% - 100% / var(--route-count, 8)); background: rgb(198 122 209 / 22%); } .site-navigation ul::after { inline-size: calc((100% - 100% / var(--route-count, 8)) * var(--route-index, 0) / (var(--route-count, 8) - 1)); background: linear-gradient(90deg, var(--rose, #ff83c2), var(--violet, #c4a2ff), var(--accent), var(--coral, #ffa68d)); box-shadow: 0 0 10px rgb(255 131 194 / 45%); transition: inline-size 520ms cubic-bezier(0.22, 1, 0.36, 1); } .site-navigation li { position: relative; } /* Exit nodes on the road: hollow ahead, filled once passed, the car marker here. */ .site-navigation li::after { content: '' / ''; position: absolute; z-index: 1; inset-block-end: 0; inset-inline-start: 50%; inline-size: 8px; block-size: 8px; translate: -50% 0; border: 1.5px solid rgb(198 122 209 / 60%); border-radius: 50%; background: rgb(22 12 40); pointer-events: none; transition: background-color 300ms, border-color 300ms; } .site-navigation li[data-route='passed']::after { border-color: var(--rose, #ff83c2); background: var(--rose, #ff83c2); } .site-navigation li[data-route='here']::after { inline-size: 14px; block-size: 8px; inset-block-end: 0; border: 0; border-radius: 4px 4px 2px 2px; background: var(--focus); box-shadow: 0 0 12px var(--focus), -6px 0 10px -2px rgb(255 214 147 / 55%); } .site-navigation[data-motion='false'] ul::after, .site-navigation[data-motion='false'] li::after { transition: none; } } /* Compact screens: the closed summary names where you are on the route. */ @media screen and (max-width: 767px) { .site-navigation summary[data-exit]::after { content: 'EXIT ' attr(data-exit) ' \00b7 ' attr(data-label) / ''; margin-inline-start: 0.7em; padding: 0.2rem 0.45rem; border-radius: 3px; background: rgb(255 131 194 / 16%); color: var(--text); font: 640 0.66rem/1.3 var(--mono); letter-spacing: 0.12em; white-space: pre; } /* The open list is drawn as a single vertical route with exit nodes. */ .site-navigation ul { position: relative; grid-template-columns: minmax(0, 1fr); /* Eight exits in one column outgrow short or zoomed viewports; the route scrolls inside the fixed bar instead of leaving the screen. */ max-block-size: calc(100dvh - var(--site-navigation-block-size, 70px) - 12px); overflow-y: auto; overscroll-behavior: contain; } .site-navigation li { position: relative; } .site-navigation li::before { content: '' / ''; position: absolute; inset-block: 0; inset-inline-start: 10px; inline-size: 2px; background: rgb(198 122 209 / 28%); pointer-events: none; } .site-navigation li::after { content: '' / ''; position: absolute; inset-block-start: 50%; inset-inline-start: 6px; inline-size: 10px; block-size: 10px; translate: 0 -50%; border: 1.5px solid rgb(198 122 209 / 70%); border-radius: 50%; background: rgb(22 12 40); pointer-events: none; } .site-navigation li[data-route='passed']::after { background: var(--rose, #ff83c2); border-color: var(--rose, #ff83c2); } .site-navigation li[data-route='here']::after { background: var(--focus); border-color: var(--focus); box-shadow: 0 0 10px var(--focus); } .site-navigation li a { justify-content: flex-start; padding-inline-start: 30px; text-align: start; } } /* ---------- Resume chip for returning visitors ---------- */ .hero-journey__resume { display: inline-flex; align-items: center; gap: 0.6rem; inline-size: fit-content; min-block-size: 44px; margin-block-start: 0.9rem; padding: 0.5rem 0.9rem; border: 1px solid rgb(255 214 147 / 45%); border-radius: 999px; background: rgb(14 7 30 / 78%); color: var(--text); font: 600 0.74rem/1 var(--mono); letter-spacing: 0.12em; text-decoration: none; } .hero-journey__resume::before { content: 'RESUME' / ''; color: var(--focus); } .hero-journey__resume::after { content: 'EXIT ' attr(data-exit) ' \00b7 ' attr(data-label) ' \2192' / ''; white-space: pre; } .hero-journey__resume:hover { border-color: var(--focus); } .hero-journey__resume:focus-visible { outline: 2px solid var(--focus); outline-offset: 3px; } /* ---------- Forced colours, reduced motion, print ---------- */ @media (forced-colors: active) { .reading-stage .chapter-body { border: 1px solid CanvasText; background: Canvas; } .reading-stage :is(.portfolio-section, .project-stage__lead) > h2 { text-shadow: none; } .reading-stage :is(.portfolio-section, .project-stage__lead) > h2::after, .site-navigation ul::before, .site-navigation ul::after, .site-navigation li::before, .site-navigation li::after { display: none; } .reading-stage .portfolio-section[data-exit]::after, .hero-journey__resume { forced-color-adjust: none; border: 1px solid CanvasText; background: Canvas; color: CanvasText; } } @media print { .hero-journey__resume, .site-navigation summary[data-exit]::after { display: none !important; } } /* ========================================================================== Round 4: Cinema. The title is a title card, the reading panels become sharp night plates instead of rounded glass, and the hero arrives with the opening crane shot. Clip-path reveals only: no opacity, so contrast checks see the final colours from the first frame. ========================================================================== */ /* ---------- Title card ---------- */ @media screen { .hero-journey .hero-name { font-size: clamp(72px, 9.6vw, 164px); font-weight: 720; line-height: 0.86; letter-spacing: -0.07em; background: none; padding-block-end: 0.06em; } /* Outrun chrome, one band per line: a bright sky over a dark horizon cut and a hot reflection below it. */ .hero-journey .hero-name__text { color: #ffc2dc; background: linear-gradient( 180deg, #fffaf2 8%, #ffe2ef 34%, #ff9fcd 48.5%, #5b2591 49.5%, #9b3fb8 54%, #ff63ab 72%, #ffb46e 96% ); background-clip: text; -webkit-background-clip: text; -webkit-text-fill-color: transparent; -webkit-box-decoration-break: clone; box-decoration-break: clone; filter: drop-shadow(0 0.025em 0 rgb(24 6 44)) drop-shadow(0 0 0.32em rgb(255 70 160 / 32%)); } .hero-journey__role { font-size: clamp(20px, 1.7vw, 28px); font-weight: 560; letter-spacing: -0.025em; } .hero-journey__content { inline-size: min(38rem, 44vw); } /* ---------- Night plates ---------- */ .reading-stage .chapter-body { border: 0; border-radius: 2px; /* The plate's opacity lives in its background colour, so the reading surface stays measurably solid; the gradients only tint it. */ background-color: rgb(9 4 22 / 94%); background-image: linear-gradient(180deg, color-mix(in srgb, var(--chapter-accent, var(--accent)) 9%, transparent), transparent 140px), linear-gradient(104deg, rgb(4 1 12 / 40%), transparent 62%, rgb(24 12 48 / 30%)); box-shadow: inset 3px 0 0 var(--chapter-accent, var(--accent)), inset 0 1px 0 color-mix(in srgb, var(--chapter-accent, var(--accent)) 38%, transparent), 0 50px 90px -40px rgb(0 0 0 / 80%); } /* Crop marks: the plate is framed like a film slate, not a card. They are registered on the plate's own corners, like the gantry window's. */ .reading-stage .chapter-body::after { content: '' / ''; position: absolute; inset: 0; pointer-events: none; --mark: color-mix(in srgb, var(--chapter-accent, var(--accent)) 80%, white); background: linear-gradient(var(--mark), var(--mark)) top right / 18px 1px, linear-gradient(var(--mark), var(--mark)) top right / 1px 18px, linear-gradient(var(--mark), var(--mark)) bottom right / 18px 1px, linear-gradient(var(--mark), var(--mark)) bottom right / 1px 18px, linear-gradient(var(--mark), var(--mark)) bottom left / 18px 1px; background-repeat: no-repeat; opacity: 0.85; } .reading-stage .portfolio-section .section-eyebrow { letter-spacing: 0.22em; } } @media screen and (min-width: 1100px) { /* The phone strip beside the evidence: three readable columns, not slivers. */ .reading-stage .project-case-study[data-media-layout='portrait-strip'] > .project-media-gallery { gap: 18px; padding: 28px 22px; } .reading-stage .project-case-study[data-media-layout='portrait-strip'] > .project-media-gallery figcaption { font-size: 0.8rem; line-height: 1.45; } } /* ---------- The hero arrives with the crane shot ---------- */ @media screen and (prefers-reduced-motion: no-preference) { .hero-journey .hero-name { animation: title-card-rise 1.5s cubic-bezier(0.16, 0.84, 0.24, 1) 0.9s both; } .hero-journey__content > :not(.hero-name) { animation: title-card-wipe 1.1s cubic-bezier(0.6, 0, 0.2, 1) both; } .hero-journey__content > :nth-child(2) { animation-delay: 1.7s; } .hero-journey__content > :nth-child(3) { animation-delay: 1.85s; } .hero-journey__content > :nth-child(4) { animation-delay: 2s; } .hero-journey__content > :nth-child(n + 5) { animation-delay: 2.15s; } } @keyframes title-card-rise { from { clip-path: inset(0 -10% 100% -10%); translate: 0 0.3em; } to { clip-path: inset(-20% -10% -20% -10%); translate: 0 0; } } @keyframes title-card-wipe { from { clip-path: inset(-10% 100% -10% 0); } to { clip-path: inset(-10% -4% -10% 0); } } @media (forced-colors: active) { .hero-journey .hero-name__text { background: none; filter: none; -webkit-text-fill-color: CanvasText; color: CanvasText; } .reading-stage .chapter-body::after { display: none; } }