/* ============================================================================
   VPC — raking light
   ----------------------------------------------------------------------------
   The thesis, in Steve's own words about Lucidato: the plaster is "highly
   reflective from an angle view and nearly non reflective from the front".
   Marmorino has "gradual movement between light and shade". The sparkle
   finishes "catch the light differently from every angle".

   Everything VPC sells is a surface that changes as you move past it — and
   the site was showing all of it as photographs that never change. So the one
   idea here is light travelling across a surface, and it is spent in two
   places: the hero, and the sample boards. Everything else is quiet.

   Safety rules this file keeps:
     * Nothing is hidden unless JS has said motion is on (.js-motion on <html>).
       With JS off or broken, every element is simply visible.
     * prefers-reduced-motion: reduce turns all of it off and pins the final
       state, at the bottom of this file.
     * Only opacity / transform / clip-path animate, so no layout shift. The
       measured CLS on this page must stay where it is.
   ========================================================================== */

/* Lets the highlight position be animated rather than jumping. Unsupported
   browsers ignore this and the light simply sits centred. */
@property --rake-x {
	syntax: '<percentage>';
	inherits: true;
	initial-value: 50%;
}

/* ---------------------------------------------------------------- 1. Hero */

.vpc-home-hero { overflow: hidden; }

/* The specular band itself. Screen blend so it lifts the highlights in the
   photograph rather than laying grey haze over the whole thing. */
.vpc-home-hero::after {
	content: "";
	position: absolute;
	inset: 0;
	pointer-events: none;
	mix-blend-mode: screen;
	background: radial-gradient(
		55% 130% at var(--rake-x) 45%,
		rgba(255, 244, 226, 0.20) 0%,
		rgba(255, 244, 226, 0.09) 30%,
		rgba(255, 244, 226, 0) 62%);
}

/* Ambient drift, slow enough to read as changing light rather than an effect.
   Paused the moment a pointer takes over. */
.js-motion .vpc-home-hero {
	animation: vpc-rake-drift 19s ease-in-out infinite alternate;
}
.js-motion .vpc-home-hero.is-pointer-lit {
	animation: none;
}
@keyframes vpc-rake-drift {
	from { --rake-x: 22%; }
	to   { --rake-x: 78%; }
}

/* Load sequence: the light arrives, then the words. */
.js-motion .vpc-home-title,
.js-motion .vpc-home-subtitle {
	opacity: 0;
	will-change: transform, opacity;
	animation: vpc-rise 1s cubic-bezier(0.22, 0.61, 0.36, 1) forwards;
}
.js-motion .vpc-home-title    { animation-delay: 0.22s; }
.js-motion .vpc-home-subtitle { animation-delay: 0.42s; }
@keyframes vpc-rise {
	from { opacity: 0; transform: translateY(20px); }
	to   { opacity: 1; transform: none; }
}

/* ------------------------------------------------- 2. Signature finish bands
   The image is drawn across rather than faded up — plaster is laid on from one
   side, so the reveal runs the way the work does, and each band wipes from the
   side its photograph sits on. */

.js-motion .vpc-home-feature .vpc-feature-image {
	clip-path: inset(0 100% 0 0);
	transition: clip-path 1.15s cubic-bezier(0.22, 0.61, 0.36, 1);
}
.js-motion .vpc-home-feature .vpc-feature-reverse .vpc-feature-image {
	clip-path: inset(0 0 0 100%);
}
.js-motion .vpc-home-feature.is-revealed .vpc-feature-image {
	clip-path: inset(0 0 0 0);
}

.js-motion .vpc-home-feature .vpc-feature-content > * {
	opacity: 0;
	transform: translateY(16px);
	transition: opacity 0.7s ease, transform 0.7s cubic-bezier(0.22, 0.61, 0.36, 1);
}
.js-motion .vpc-home-feature.is-revealed .vpc-feature-content > * {
	opacity: 1;
	transform: none;
}
/* the heading leads, the copy follows, the button lands last */
.js-motion .vpc-home-feature.is-revealed .vpc-feature-content > *:nth-child(1) { transition-delay: 0.30s; }
.js-motion .vpc-home-feature.is-revealed .vpc-feature-content > *:nth-child(2) { transition-delay: 0.40s; }
.js-motion .vpc-home-feature.is-revealed .vpc-feature-content > *:nth-child(3) { transition-delay: 0.50s; }

/* ------------------------------------------------------- 3. Service cards */

.js-motion .vpc-home-services .icon-item {
	opacity: 0;
	transform: translateY(18px);
	transition: opacity 0.55s ease, transform 0.55s cubic-bezier(0.22, 0.61, 0.36, 1);
}
.js-motion .vpc-home-services.is-revealed .icon-item {
	opacity: 1;
	transform: none;
}
.js-motion .vpc-home-services.is-revealed .icon-item:nth-child(2) { transition-delay: 0.06s; }
.js-motion .vpc-home-services.is-revealed .icon-item:nth-child(3) { transition-delay: 0.12s; }
.js-motion .vpc-home-services.is-revealed .icon-item:nth-child(4) { transition-delay: 0.18s; }
.js-motion .vpc-home-services.is-revealed .icon-item:nth-child(5) { transition-delay: 0.24s; }
.js-motion .vpc-home-services.is-revealed .icon-item:nth-child(6) { transition-delay: 0.30s; }

.vpc-home-services .icon-item .icon-item-icon {
	display: inline-block;
	transition: transform 0.3s cubic-bezier(0.22, 0.61, 0.36, 1);
}
.vpc-home-services .icon-item:hover .icon-item-icon,
.vpc-home-services .icon-item:focus-visible .icon-item-icon {
	transform: translateY(-3px) scale(1.06);
}

/* ---------------------------------------------------------- 4. Space cards
   Depth on approach: the card lifts (already in vpc.css) and the photograph
   pushes very slightly forward inside its frame. */

.space-card-image { overflow: hidden; }
.space-card-image img {
	transition: transform 0.5s cubic-bezier(0.22, 0.61, 0.36, 1);
}
.space-card:hover .space-card-image img,
.space-card:focus-visible .space-card-image img {
	transform: scale(1.05);
}

.js-motion .vpc-home-spaces .space-card {
	opacity: 0;
	transform: translateY(20px);
	transition: opacity 0.6s ease, transform 0.6s cubic-bezier(0.22, 0.61, 0.36, 1),
	            box-shadow 0.25s ease;
}
.js-motion .vpc-home-spaces.is-revealed .space-card { opacity: 1; transform: none; }
.js-motion .vpc-home-spaces.is-revealed .space-card:nth-child(2) { transition-delay: 0.07s; }
.js-motion .vpc-home-spaces.is-revealed .space-card:nth-child(3) { transition-delay: 0.14s; }
.js-motion .vpc-home-spaces.is-revealed .space-card:nth-child(4) { transition-delay: 0.21s; }
.js-motion .vpc-home-spaces.is-revealed .space-card:nth-child(5) { transition-delay: 0.28s; }
.js-motion .vpc-home-spaces.is-revealed .space-card:nth-child(6) { transition-delay: 0.35s; }
/* the lift has to win over the reveal transform once the card is in */
.js-motion .vpc-home-spaces.is-revealed .space-card:hover,
.js-motion .vpc-home-spaces.is-revealed .space-card:focus-visible {
	transform: translateY(-4px);
}

/* ========================================================== 5. Sample boards
   "Sample boards made up so you can judge a finish in your own light" — VPC's
   own service copy, on this page. A testimonial here is not a floating quote:
   it is a job, with the finish on it and the client's name under it, mounted
   the way a plasterer mounts a sample.
   ======================================================================== */

/* Three at most. auto-fit went to four on a wide screen and squeezed the
   quotes into slivers. Rows stretch rather than sizing to content, so the
   boards in a row share a bottom edge — these are mounted samples, not a
   ragged pile. */
.sample-boards {
	display: grid;
	grid-template-columns: 1fr;
	gap: 1.75rem;
	text-align: left;
}
@media (min-width: 620px) {
	.sample-boards { grid-template-columns: repeat(2, 1fr); }
}
@media (min-width: 1000px) {
	.sample-boards { grid-template-columns: repeat(3, 1fr); }
}

.sample-board {
	position: relative;
	display: flex;
	flex-direction: column;
	/* vpc.css styles every blockquote with padding: 2em, a background, a max-width
	   and a larger font. The old testimonial card happened to override the padding
	   and inherited the rest by accident; this one has to clear all of it, or the
	   swatch sits inset in the middle of the board instead of filling its top. */
	margin: 0;
	padding: 0;
	max-width: none;
	width: auto;
	font-size: 1rem;
	line-height: inherit;
	background: var(--color-secondary-dark);
	border-radius: var(--border-radius-small);
	overflow: hidden;
	box-shadow: 0 6px 22px rgba(42, 55, 64, 0.18);
	transition: transform 0.3s cubic-bezier(0.22, 0.61, 0.36, 1), box-shadow 0.3s ease;
}
.sample-board:hover,
.sample-board:focus-within {
	transform: translateY(-5px);
	box-shadow: 0 16px 36px rgba(42, 55, 64, 0.28);
}

/* The material itself. Fixed ratio so a long quote never distorts the swatch
   and the row of boards keeps a common top edge. */
.sample-board-swatch {
	position: relative;
	aspect-ratio: 4 / 3;
	overflow: hidden;
	background: var(--color-secondary);
}
.sample-board-swatch img {
	width: 100%;
	height: 100%;
	object-fit: cover;
	display: block;
}
/* the same light as the hero, passing over the sample */
.sample-board-swatch::after {
	content: "";
	position: absolute;
	inset: -10%;
	pointer-events: none;
	mix-blend-mode: screen;
	background: linear-gradient(104deg,
		rgba(255, 244, 226, 0) 34%,
		rgba(255, 244, 226, 0.34) 50%,
		rgba(255, 244, 226, 0) 66%);
	transform: translateX(-130%);
	transition: transform 1s cubic-bezier(0.22, 0.61, 0.36, 1);
}
.sample-board:hover .sample-board-swatch::after,
.sample-board:focus-within .sample-board-swatch::after {
	transform: translateX(130%);
}

/* A board with no job photograph still gets a surface, not a blank panel. */
.sample-board-no-swatch .sample-board-label { padding-top: 1.9rem; }
.sample-board-no-swatch::before {
	content: "";
	display: block;
	height: 5px;
	background: linear-gradient(90deg, var(--color-primary), var(--color-bronze));
}

.sample-board-label {
	display: flex;
	flex-direction: column;
	flex: 1;
	padding: 1.5rem 1.5rem 1.6rem;
	color: var(--color-cream);
}
.sample-board-quote {
	font-family: var(--font-heading);
	font-size: 1.08rem;
	line-height: 1.65;
	font-style: italic;
	color: var(--color-cream);
	margin: 0 0 1.15rem;
	/* Boards in a row share a height, set by the longest quote. A short one
	   left a hole above its label; centring it in the space it has makes the
	   board look composed rather than half-empty. */
	flex: 1;
	display: flex;
	align-items: center;
}
/* the pencil rule on a sample board */
.sample-board-meta {
	border-top: 1px solid rgba(232, 228, 217, 0.28);
	padding-top: 0.85rem;
	font-family: var(--font-heading-h2);
	font-size: 0.86rem;
	line-height: 1.5;
	letter-spacing: 0.02em;
}
.sample-board-name {
	display: block;
	color: var(--color-text-white);
	font-weight: 600;
}
.sample-board-where {
	display: block;
	color: rgba(232, 228, 217, 0.72);
}
.sample-board-link {
	display: inline-block;
	margin-top: 0.7rem;
	color: var(--color-primary-light);
	font-weight: 600;
	text-decoration: none;
}
.sample-board-link:hover,
.sample-board-link:focus-visible { text-decoration: underline; }
/* the whole board is the target, but the link keeps real text for a screen
   reader rather than becoming an unlabelled box */
.sample-board-link::after {
	content: "";
	position: absolute;
	inset: 0;
}
.sample-board:focus-within {
	outline: 2px solid var(--color-primary);
	outline-offset: 3px;
}

.js-motion .vpc-home-testimonials .sample-board {
	opacity: 0;
	transform: translateY(22px);
}
.js-motion .vpc-home-testimonials.is-revealed .sample-board {
	opacity: 1;
	transform: none;
	transition: opacity 0.6s ease, transform 0.6s cubic-bezier(0.22, 0.61, 0.36, 1),
	            box-shadow 0.3s ease;
}
.js-motion .vpc-home-testimonials.is-revealed .sample-board:nth-child(2) { transition-delay: 0.08s; }
.js-motion .vpc-home-testimonials.is-revealed .sample-board:nth-child(3) { transition-delay: 0.16s; }
.js-motion .vpc-home-testimonials.is-revealed .sample-board:nth-child(4) { transition-delay: 0.24s; }
.js-motion .vpc-home-testimonials.is-revealed .sample-board:nth-child(5) { transition-delay: 0.32s; }
.js-motion .vpc-home-testimonials.is-revealed .sample-board:nth-child(6) { transition-delay: 0.40s; }
.js-motion .vpc-home-testimonials.is-revealed .sample-board:hover,
.js-motion .vpc-home-testimonials.is-revealed .sample-board:focus-within {
	transform: translateY(-5px);
}

.vpc-home-testimonials .vpc-boards-lead {
	max-width: 46em;
	margin: 0 auto 2.5rem;
	color: var(--color-text-secondary);
}

/* ====================================================== 6. Reduced motion
   Everything above is decoration. Someone who has asked their system for less
   motion gets the finished state immediately and nothing moves — including the
   ambient light, which is the one thing that would otherwise keep going.
   ======================================================================== */

/* ------------------------------------------------- 5. Service pages

   Opt-in per page via `motion: true` in front matter, which puts .motion-page on
   the wrapper. On Bespoke while the treatment is settled.

   First attempt at this was fade-and-rise on every section, which is the
   vocabulary every site uses and said nothing about the page. What worked on the
   front page was not fades — it was the light, and the bands wiping in from the
   side the work was laid on. So: the same two moves, and nothing else. Four
   things move on this page, not nine.

   The page is about work made by hand, once, on site. So things are worked into
   being rather than faded in. */

/* -- 5a. Hero: the raking light, as on the front page --------------------- */

.motion-page .service-hero { overflow: hidden; }
.motion-page .service-hero::after {
	content: "";
	position: absolute;
	inset: 0;
	z-index: 1;                 /* over the photograph and its scrim, under the words */
	pointer-events: none;
	mix-blend-mode: screen;
	background: radial-gradient(
		55% 130% at var(--rake-x) 45%,
		rgba(255, 244, 226, 0.22) 0%,
		rgba(255, 244, 226, 0.10) 30%,
		rgba(255, 244, 226, 0) 62%);
}
.js-motion .motion-page .service-hero { animation: vpc-rake-drift 19s ease-in-out infinite alternate; }
.js-motion .motion-page .service-hero.is-pointer-lit { animation: none; }

.js-motion .motion-page .service-title,
.js-motion .motion-page .service-excerpt {
	opacity: 0;
	animation: vpc-rise 0.9s cubic-bezier(0.22, 0.61, 0.36, 1) forwards;
}
.js-motion .motion-page .service-title   { animation-delay: 0.22s; }
.js-motion .motion-page .service-excerpt { animation-delay: 0.42s; }

/* -- 5b. The cream band, laid on ----------------------------------------- */

/* The cream moves onto a ::before so the ground can be wiped across the page
   while the words on it stay still and simply arrive — plaster going on, then
   the surface being readable. Wiping the section itself would drag the text
   sideways with it. */
.motion-page .service-included { position: relative; background: none; overflow: hidden; }
.motion-page .service-included::before {
	content: "";
	position: absolute;
	inset: 0;
	z-index: 0;
	background: var(--color-cream);
}
.motion-page .service-included > .content-block { position: relative; z-index: 1; }

.js-motion .motion-page .service-included::before {
	clip-path: inset(0 100% 0 0);
	transition: clip-path 1.05s cubic-bezier(0.32, 0.72, 0.3, 1);
}
.js-motion .motion-page .service-included.is-revealed::before { clip-path: inset(0 0 0 0); }

/* The heading and the cards follow the trowel rather than racing it. */
.js-motion .motion-page .service-included h2,
.js-motion .motion-page .service-included .cover-card,
.js-motion .motion-page .service-included .included-content li {
	opacity: 0;
	transform: translateY(12px);
	transition: opacity 0.5s ease, transform 0.55s cubic-bezier(0.22, 0.61, 0.36, 1);
}
.js-motion .motion-page .service-included.is-revealed h2,
.js-motion .motion-page .service-included.is-revealed .cover-card,
.js-motion .motion-page .service-included.is-revealed .included-content li {
	opacity: 1;
	transform: none;
}
.js-motion .motion-page .service-included.is-revealed h2 { transition-delay: 0.30s; }
.js-motion .motion-page .service-included.is-revealed .covers-grid > :nth-child(1) { transition-delay: 0.46s; }
.js-motion .motion-page .service-included.is-revealed .covers-grid > :nth-child(2) { transition-delay: 0.55s; }
.js-motion .motion-page .service-included.is-revealed .covers-grid > :nth-child(3) { transition-delay: 0.64s; }
.js-motion .motion-page .service-included.is-revealed .covers-grid > :nth-child(4) { transition-delay: 0.73s; }
.js-motion .motion-page .service-included.is-revealed .covers-grid > :nth-child(5) { transition-delay: 0.82s; }
.js-motion .motion-page .service-included.is-revealed .covers-grid > :nth-child(6) { transition-delay: 0.91s; }
.js-motion .motion-page .service-included.is-revealed .covers-grid > :nth-child(7) { transition-delay: 1.00s; }
.js-motion .motion-page .service-included.is-revealed .covers-grid > :nth-child(8) { transition-delay: 1.09s; }
.js-motion .motion-page .service-included.is-revealed .covers-grid > :nth-child(9) { transition-delay: 1.18s; }
.js-motion .motion-page .service-included.is-revealed .covers-grid > :nth-child(10) { transition-delay: 1.27s; }
.js-motion .motion-page .service-included.is-revealed .covers-grid > :nth-child(11) { transition-delay: 1.36s; }
.js-motion .motion-page .service-included.is-revealed .covers-grid > :nth-child(12) { transition-delay: 1.45s; }
.js-motion .motion-page .service-included.is-revealed .covers-grid > :nth-child(13) { transition-delay: 1.54s; }
.js-motion .motion-page .service-included.is-revealed .covers-grid > :nth-child(14) { transition-delay: 1.63s; }
.js-motion .motion-page .service-included.is-revealed .covers-grid > :nth-child(n+15) { transition-delay: 1.63s; }
.js-motion .motion-page .service-included.is-revealed .included-content li:nth-child(1) { transition-delay: 0.46s; }
.js-motion .motion-page .service-included.is-revealed .included-content li:nth-child(2) { transition-delay: 0.54s; }
.js-motion .motion-page .service-included.is-revealed .included-content li:nth-child(3) { transition-delay: 0.62s; }
.js-motion .motion-page .service-included.is-revealed .included-content li:nth-child(4) { transition-delay: 0.70s; }
.js-motion .motion-page .service-included.is-revealed .included-content li:nth-child(5) { transition-delay: 0.78s; }
.js-motion .motion-page .service-included.is-revealed .included-content li:nth-child(6) { transition-delay: 0.86s; }
.js-motion .motion-page .service-included.is-revealed .included-content li:nth-child(7) { transition-delay: 0.94s; }
.js-motion .motion-page .service-included.is-revealed .included-content li:nth-child(8) { transition-delay: 1.02s; }
.js-motion .motion-page .service-included.is-revealed .included-content li:nth-child(9) { transition-delay: 1.10s; }
.js-motion .motion-page .service-included.is-revealed .included-content li:nth-child(10) { transition-delay: 1.18s; }
.js-motion .motion-page .service-included.is-revealed .included-content li:nth-child(11) { transition-delay: 1.26s; }
.js-motion .motion-page .service-included.is-revealed .included-content li:nth-child(12) { transition-delay: 1.34s; }
.js-motion .motion-page .service-included.is-revealed .included-content li:nth-child(13) { transition-delay: 1.42s; }
.js-motion .motion-page .service-included.is-revealed .included-content li:nth-child(14) { transition-delay: 1.50s; }
.js-motion .motion-page .service-included.is-revealed .included-content li:nth-child(n+15) { transition-delay: 1.50s; }

/* -- 5c. The process, one step at a time --------------------------------- */

/* Four steps in order, so they are laid on in order — the same move as the band
   above. A stagger of fades would have said "list"; this says "sequence", which
   is what the section is.

   The ground moves to a ::before and gets wiped, exactly as the band does, and
   the words follow behind. Wiping the card itself cut the sentences off
   mid-word: on the front page those wipes ran over photographs, where a moving
   edge is natural, and over text it reads as a rendering fault. */
.motion-page .process-step { position: relative; background: none; box-shadow: none; }
.motion-page .process-step::before {
	content: "";
	position: absolute;
	inset: 0;
	z-index: 0;
	background: var(--color-bg-secondary);
	border-radius: inherit;
	box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
	transition: box-shadow 0.3s ease;
}
.motion-page .process-step:hover::before { box-shadow: 0 4px 16px rgba(0, 0, 0, 0.15); }
.motion-page .process-step > * { position: relative; z-index: 1; }

.js-motion .motion-page .service-process .process-step::before {
	clip-path: inset(0 100% 0 0);
	transition: clip-path 0.62s cubic-bezier(0.32, 0.72, 0.3, 1), box-shadow 0.3s ease;
}
.js-motion .motion-page .service-process .process-step .step-content {
	opacity: 0;
	transform: translateY(8px);
	transition: opacity 0.42s ease, transform 0.42s cubic-bezier(0.22, 0.61, 0.36, 1);
}
.js-motion .motion-page .service-process.is-revealed .process-step::before { clip-path: inset(0 0 0 0); }
.js-motion .motion-page .service-process.is-revealed .process-step .step-content { opacity: 1; transform: none; }

/* Same guard as the list above: the step count is content, and a step with no
   delay would jump the queue. */
.js-motion .motion-page .service-process.is-revealed .process-step:nth-child(1)::before { transition-delay: 0.00s; }
.js-motion .motion-page .service-process.is-revealed .process-step:nth-child(2)::before { transition-delay: 0.15s; }
.js-motion .motion-page .service-process.is-revealed .process-step:nth-child(3)::before { transition-delay: 0.30s; }
.js-motion .motion-page .service-process.is-revealed .process-step:nth-child(4)::before { transition-delay: 0.45s; }
.js-motion .motion-page .service-process.is-revealed .process-step:nth-child(5)::before { transition-delay: 0.60s; }
.js-motion .motion-page .service-process.is-revealed .process-step:nth-child(6)::before { transition-delay: 0.75s; }
.js-motion .motion-page .service-process.is-revealed .process-step:nth-child(7)::before { transition-delay: 0.90s; }
.js-motion .motion-page .service-process.is-revealed .process-step:nth-child(8)::before { transition-delay: 1.05s; }
.js-motion .motion-page .service-process.is-revealed .process-step:nth-child(9)::before { transition-delay: 1.20s; }
.js-motion .motion-page .service-process.is-revealed .process-step:nth-child(10)::before { transition-delay: 1.35s; }
.js-motion .motion-page .service-process.is-revealed .process-step:nth-child(n+11)::before { transition-delay: 1.35s; }
.js-motion .motion-page .service-process.is-revealed .process-step:nth-child(1) .step-content { transition-delay: 0.26s; }
.js-motion .motion-page .service-process.is-revealed .process-step:nth-child(2) .step-content { transition-delay: 0.41s; }
.js-motion .motion-page .service-process.is-revealed .process-step:nth-child(3) .step-content { transition-delay: 0.56s; }
.js-motion .motion-page .service-process.is-revealed .process-step:nth-child(4) .step-content { transition-delay: 0.71s; }
.js-motion .motion-page .service-process.is-revealed .process-step:nth-child(5) .step-content { transition-delay: 0.86s; }
.js-motion .motion-page .service-process.is-revealed .process-step:nth-child(6) .step-content { transition-delay: 1.01s; }
.js-motion .motion-page .service-process.is-revealed .process-step:nth-child(7) .step-content { transition-delay: 1.16s; }
.js-motion .motion-page .service-process.is-revealed .process-step:nth-child(8) .step-content { transition-delay: 1.31s; }
.js-motion .motion-page .service-process.is-revealed .process-step:nth-child(9) .step-content { transition-delay: 1.46s; }
.js-motion .motion-page .service-process.is-revealed .process-step:nth-child(10) .step-content { transition-delay: 1.61s; }
.js-motion .motion-page .service-process.is-revealed .process-step:nth-child(n+11) .step-content { transition-delay: 1.61s; }

/* The step number carries the sequence, so it is what lifts on hover. */
.motion-page .process-step .step-title { transition: color 0.25s ease; }
.motion-page .process-step:hover .step-title { color: var(--color-primary-dark); }

/* ------------------------------------------------- 6. Finish pages

   Same two moves. The hero takes the light; the things that show the material —
   the characteristics, the applications, the films — arrive rather than sitting
   there. Nothing loops. */

.motion-page .finish-hero { overflow: hidden; }
.motion-page .finish-hero::after {
	content: "";
	position: absolute;
	inset: 0;
	z-index: 1;
	pointer-events: none;
	mix-blend-mode: screen;
	background: radial-gradient(
		55% 130% at var(--rake-x) 45%,
		rgba(255, 244, 226, 0.22) 0%,
		rgba(255, 244, 226, 0.10) 30%,
		rgba(255, 244, 226, 0) 62%);
}
.js-motion .motion-page .finish-hero { animation: vpc-rake-drift 19s ease-in-out infinite alternate; }
.js-motion .motion-page .finish-hero.is-pointer-lit { animation: none; }

.js-motion .motion-page .finish-category,
.js-motion .motion-page .finish-title {
	opacity: 0;
	animation: vpc-rise 0.9s cubic-bezier(0.22, 0.61, 0.36, 1) forwards;
}
.js-motion .motion-page .finish-category { animation-delay: 0.18s; }
.js-motion .motion-page .finish-title    { animation-delay: 0.32s; }

/* The characteristics are a set of small claims about the material; they land
   together in a quick run rather than one slow reveal each. */
.js-motion .motion-page .finish-characteristics .characteristic-item,
.js-motion .motion-page .finish-applications .cover-card,
.js-motion .motion-page .finish-media-gallery .project-gallery-item,
.js-motion .motion-page .finish-media-gallery .finish-film {
	opacity: 0;
	transform: translateY(12px);
	transition: opacity 0.5s ease, transform 0.55s cubic-bezier(0.22, 0.61, 0.36, 1);
}
.js-motion .motion-page :is(.finish-characteristics, .finish-applications, .finish-media-gallery).is-revealed
	:is(.characteristic-item, .cover-card, .project-gallery-item, .finish-film) {
	opacity: 1;
	transform: none;
}
.js-motion .motion-page :is(.finish-characteristics, .finish-applications, .finish-media-gallery).is-revealed :is(.characteristic-item, .cover-card, .project-gallery-item, .finish-film):nth-child(1) { transition-delay: 0.00s; }
.js-motion .motion-page :is(.finish-characteristics, .finish-applications, .finish-media-gallery).is-revealed :is(.characteristic-item, .cover-card, .project-gallery-item, .finish-film):nth-child(2) { transition-delay: 0.05s; }
.js-motion .motion-page :is(.finish-characteristics, .finish-applications, .finish-media-gallery).is-revealed :is(.characteristic-item, .cover-card, .project-gallery-item, .finish-film):nth-child(3) { transition-delay: 0.10s; }
.js-motion .motion-page :is(.finish-characteristics, .finish-applications, .finish-media-gallery).is-revealed :is(.characteristic-item, .cover-card, .project-gallery-item, .finish-film):nth-child(4) { transition-delay: 0.15s; }
.js-motion .motion-page :is(.finish-characteristics, .finish-applications, .finish-media-gallery).is-revealed :is(.characteristic-item, .cover-card, .project-gallery-item, .finish-film):nth-child(5) { transition-delay: 0.20s; }
.js-motion .motion-page :is(.finish-characteristics, .finish-applications, .finish-media-gallery).is-revealed :is(.characteristic-item, .cover-card, .project-gallery-item, .finish-film):nth-child(6) { transition-delay: 0.25s; }
.js-motion .motion-page :is(.finish-characteristics, .finish-applications, .finish-media-gallery).is-revealed :is(.characteristic-item, .cover-card, .project-gallery-item, .finish-film):nth-child(7) { transition-delay: 0.30s; }
.js-motion .motion-page :is(.finish-characteristics, .finish-applications, .finish-media-gallery).is-revealed :is(.characteristic-item, .cover-card, .project-gallery-item, .finish-film):nth-child(8) { transition-delay: 0.35s; }
.js-motion .motion-page :is(.finish-characteristics, .finish-applications, .finish-media-gallery).is-revealed :is(.characteristic-item, .cover-card, .project-gallery-item, .finish-film):nth-child(9) { transition-delay: 0.40s; }
.js-motion .motion-page :is(.finish-characteristics, .finish-applications, .finish-media-gallery).is-revealed :is(.characteristic-item, .cover-card, .project-gallery-item, .finish-film):nth-child(10) { transition-delay: 0.45s; }
.js-motion .motion-page :is(.finish-characteristics, .finish-applications, .finish-media-gallery).is-revealed :is(.characteristic-item, .cover-card, .project-gallery-item, .finish-film):nth-child(11) { transition-delay: 0.50s; }
.js-motion .motion-page :is(.finish-characteristics, .finish-applications, .finish-media-gallery).is-revealed :is(.characteristic-item, .cover-card, .project-gallery-item, .finish-film):nth-child(12) { transition-delay: 0.55s; }
.js-motion .motion-page :is(.finish-characteristics, .finish-applications, .finish-media-gallery).is-revealed :is(.characteristic-item, .cover-card, .project-gallery-item, .finish-film):nth-child(13) { transition-delay: 0.60s; }
.js-motion .motion-page :is(.finish-characteristics, .finish-applications, .finish-media-gallery).is-revealed :is(.characteristic-item, .cover-card, .project-gallery-item, .finish-film):nth-child(14) { transition-delay: 0.65s; }
.js-motion .motion-page :is(.finish-characteristics, .finish-applications, .finish-media-gallery).is-revealed :is(.characteristic-item, .cover-card, .project-gallery-item, .finish-film):nth-child(n+15) { transition-delay: 0.70s; }

/* ------------------------------------------------- 7. Project pages

   The hero takes the light. Everything else arrives once.

   The tab panels are the interesting problem: their contents live in
   display:none until a tab is chosen, so an observer can never see them and a
   reveal built on opacity would leave them stuck invisible. They use an
   animation keyed on .active instead — it replays every time a panel is shown,
   which makes switching tabs feel like turning to a new set of photographs
   rather than an instant swap. And because it is an animation with no separate
   hidden state, an element is never left at opacity 0 if it does not run. */

.motion-page .project-hero { overflow: hidden; }
.motion-page .project-hero::after {
	content: "";
	position: absolute;
	inset: 0;
	z-index: 1;
	pointer-events: none;
	mix-blend-mode: screen;
	background: radial-gradient(
		55% 130% at var(--rake-x) 45%,
		rgba(255, 244, 226, 0.22) 0%,
		rgba(255, 244, 226, 0.10) 30%,
		rgba(255, 244, 226, 0) 62%);
}
.js-motion .motion-page .project-hero { animation: vpc-rake-drift 19s ease-in-out infinite alternate; }
.js-motion .motion-page .project-hero.is-pointer-lit { animation: none; }

.js-motion .motion-page .project-title,
.js-motion .motion-page .project-meta {
	opacity: 0;
	animation: vpc-rise 0.9s cubic-bezier(0.22, 0.61, 0.36, 1) forwards;
}
.js-motion .motion-page .project-title { animation-delay: 0.20s; }
.js-motion .motion-page .project-meta  { animation-delay: 0.38s; }

/* Sections arrive as blocks. */
.js-motion .motion-page :is(.project-header-split, .project-description-section,
                            .project-client-said, .project-finishes-section) {
	opacity: 0;
	transform: translateY(16px);
	transition: opacity 0.7s ease, transform 0.7s cubic-bezier(0.22, 0.61, 0.36, 1);
}
.js-motion .motion-page :is(.project-header-split, .project-description-section,
                            .project-client-said, .project-finishes-section).is-revealed {
	opacity: 1;
	transform: none;
}

/* Related work: the cards already lift on hover, so they only need to arrive. */
.js-motion .motion-page .related-projects .project-card {
	opacity: 0;
	transform: translateY(16px);
	transition: opacity 0.5s ease, transform 0.55s cubic-bezier(0.22, 0.61, 0.36, 1);
}
.js-motion .motion-page .related-projects.is-revealed .project-card { opacity: 1; transform: none; }
.js-motion .motion-page .related-projects.is-revealed .project-card:nth-child(1) { transition-delay: 0.00s; }
.js-motion .motion-page .related-projects.is-revealed .project-card:nth-child(2) { transition-delay: 0.07s; }
.js-motion .motion-page .related-projects.is-revealed .project-card:nth-child(3) { transition-delay: 0.14s; }
.js-motion .motion-page .related-projects.is-revealed .project-card:nth-child(4) { transition-delay: 0.21s; }
.js-motion .motion-page .related-projects.is-revealed .project-card:nth-child(5) { transition-delay: 0.28s; }
.js-motion .motion-page .related-projects.is-revealed .project-card:nth-child(6) { transition-delay: 0.35s; }
.js-motion .motion-page .related-projects.is-revealed .project-card:nth-child(n+7) { transition-delay: 0.42s; }

/* Tab panels: animation, never a resting hidden state. */
.js-motion .motion-page .project-tab-content.active :is(.project-gallery-item, .project-video-embed, .project-editor-content) {
	animation: vpc-rise 0.45s cubic-bezier(0.22, 0.61, 0.36, 1) both;
}
.js-motion .motion-page .project-tab-content.active :is(.project-gallery-item, .project-video-embed, .project-editor-content):nth-child(1) { animation-delay: 0.000s; }
.js-motion .motion-page .project-tab-content.active :is(.project-gallery-item, .project-video-embed, .project-editor-content):nth-child(2) { animation-delay: 0.035s; }
.js-motion .motion-page .project-tab-content.active :is(.project-gallery-item, .project-video-embed, .project-editor-content):nth-child(3) { animation-delay: 0.070s; }
.js-motion .motion-page .project-tab-content.active :is(.project-gallery-item, .project-video-embed, .project-editor-content):nth-child(4) { animation-delay: 0.105s; }
.js-motion .motion-page .project-tab-content.active :is(.project-gallery-item, .project-video-embed, .project-editor-content):nth-child(5) { animation-delay: 0.140s; }
.js-motion .motion-page .project-tab-content.active :is(.project-gallery-item, .project-video-embed, .project-editor-content):nth-child(6) { animation-delay: 0.175s; }
.js-motion .motion-page .project-tab-content.active :is(.project-gallery-item, .project-video-embed, .project-editor-content):nth-child(7) { animation-delay: 0.210s; }
.js-motion .motion-page .project-tab-content.active :is(.project-gallery-item, .project-video-embed, .project-editor-content):nth-child(8) { animation-delay: 0.245s; }
.js-motion .motion-page .project-tab-content.active :is(.project-gallery-item, .project-video-embed, .project-editor-content):nth-child(9) { animation-delay: 0.280s; }
.js-motion .motion-page .project-tab-content.active :is(.project-gallery-item, .project-video-embed, .project-editor-content):nth-child(10) { animation-delay: 0.315s; }
/* Capped: Andreas has a 17-item tab, and at the original step the last one
   did not land until 1.15s after the click. Anything past the tenth arrives
   with the tenth. */
.js-motion .motion-page .project-tab-content.active :is(.project-gallery-item, .project-video-embed, .project-editor-content):nth-child(n+11) { animation-delay: 0.315s; }

/* ------------------------------------------------- 8. Contact and the
   other prose pages

   One deliberate omission: the form section does not move at all. Partly
   because you do not animate a thing somebody came to use — but mainly because
   Turnstile's challenge overlay is positioned against its nearest transformed
   ancestor, and a transform anywhere above it moves the challenge off the
   widget. The form is simply there, immediately. */

.motion-page .vpc-page-hero { overflow: hidden; }
.motion-page .vpc-page-hero::after {
	content: "";
	position: absolute;
	inset: 0;
	z-index: 1;
	pointer-events: none;
	mix-blend-mode: screen;
	background: radial-gradient(
		55% 130% at var(--rake-x) 45%,
		rgba(255, 244, 226, 0.22) 0%,
		rgba(255, 244, 226, 0.10) 30%,
		rgba(255, 244, 226, 0) 62%);
}
.js-motion .motion-page .vpc-page-hero { animation: vpc-rake-drift 19s ease-in-out infinite alternate; }
.js-motion .motion-page .vpc-page-hero.is-pointer-lit { animation: none; }

.js-motion .motion-page .vpc-page-title,
.js-motion .motion-page .vpc-page-subtitle {
	opacity: 0;
	animation: vpc-rise 0.9s cubic-bezier(0.22, 0.61, 0.36, 1) forwards;
}
.js-motion .motion-page .vpc-page-title    { animation-delay: 0.20s; }
.js-motion .motion-page .vpc-page-subtitle { animation-delay: 0.38s; }

/* Sections arrive. The contact split is excluded above and stays put. */
.js-motion .motion-page :is(.vpc-intro-section, .vpc-values-section,
                            .vpc-form-testimonials, .vpc-quote-checklist,
                            .vpc-important-info, .vpc-about-aside) {
	opacity: 0;
	transform: translateY(16px);
	transition: opacity 0.7s ease, transform 0.7s cubic-bezier(0.22, 0.61, 0.36, 1);
}
.js-motion .motion-page :is(.vpc-intro-section, .vpc-values-section,
                            .vpc-form-testimonials, .vpc-quote-checklist,
                            .vpc-important-info, .vpc-about-aside).is-revealed {
	opacity: 1;
	transform: none;
}

/* The help cards come in one after another. */
.js-motion .motion-page .vpc-values-section .icon-item {
	opacity: 0;
	transform: translateY(14px);
	transition: opacity 0.5s ease, transform 0.55s cubic-bezier(0.22, 0.61, 0.36, 1);
}
.js-motion .motion-page .vpc-values-section.is-revealed .icon-item { opacity: 1; transform: none; }
.js-motion .motion-page .vpc-values-section.is-revealed .icon-item:nth-child(1) { transition-delay: 0.20s; }
.js-motion .motion-page .vpc-values-section.is-revealed .icon-item:nth-child(2) { transition-delay: 0.27s; }
.js-motion .motion-page .vpc-values-section.is-revealed .icon-item:nth-child(3) { transition-delay: 0.34s; }
.js-motion .motion-page .vpc-values-section.is-revealed .icon-item:nth-child(4) { transition-delay: 0.41s; }
.js-motion .motion-page .vpc-values-section.is-revealed .icon-item:nth-child(5) { transition-delay: 0.48s; }
.js-motion .motion-page .vpc-values-section.is-revealed .icon-item:nth-child(6) { transition-delay: 0.55s; }
.js-motion .motion-page .vpc-values-section.is-revealed .icon-item:nth-child(n+7) { transition-delay: 0.55s; }

/* ------------------------------------------------- 9. Archive grids

   Cards arrive one at a time as they are reached, not staggered off a section.
   The projects archive is 137 cards and 38,000px tall; a stagger from one
   observer would fire every card at once with the last one waiting seconds. */

.js-motion .motion-page :is(.project-card, .finish-card, .space-card) {
	opacity: 0;
	transform: translateY(14px);
	transition: opacity 0.5s ease, transform 0.55s cubic-bezier(0.22, 0.61, 0.36, 1);
}
.js-motion .motion-page :is(.project-card, .finish-card, .space-card).is-revealed {
	opacity: 1;
	transform: none;
}

/* The archive heroes take the light like every other hero. */
.motion-page :is(.projects-archive-header, .finishes-archive-header, .spaces-index-hero, .space-hero) { overflow: hidden; position: relative; }
.motion-page :is(.projects-archive-header, .finishes-archive-header, .spaces-index-hero, .space-hero)::after {
	content: "";
	position: absolute;
	inset: 0;
	z-index: 1;
	pointer-events: none;
	mix-blend-mode: screen;
	background: radial-gradient(
		55% 130% at var(--rake-x) 45%,
		rgba(255, 244, 226, 0.20) 0%,
		rgba(255, 244, 226, 0.09) 30%,
		rgba(255, 244, 226, 0) 62%);
}
.js-motion .motion-page :is(.projects-archive-header, .finishes-archive-header, .spaces-index-hero, .space-hero) {
	animation: vpc-rake-drift 19s ease-in-out infinite alternate;
}
.js-motion .motion-page :is(.projects-archive-header, .finishes-archive-header, .spaces-index-hero, .space-hero).is-pointer-lit { animation: none; }

@media (prefers-reduced-motion: reduce) {
	.js-motion .vpc-home-hero { animation: none; }
	.vpc-home-hero::after { background: radial-gradient(
		55% 130% at 50% 45%,
		rgba(255, 244, 226, 0.14) 0%,
		rgba(255, 244, 226, 0) 62%); }

	.js-motion .vpc-home-title,
	.js-motion .vpc-home-subtitle {
		opacity: 1;
		animation: none;
		transform: none;
	}

	.js-motion .vpc-home-feature .vpc-feature-image,
	.js-motion .vpc-home-feature .vpc-feature-reverse .vpc-feature-image {
		clip-path: none;
		transition: none;
	}
	.js-motion .vpc-home-feature .vpc-feature-content > *,
	.js-motion .vpc-home-services .icon-item,
	.js-motion .vpc-home-spaces .space-card,
	.js-motion .vpc-home-testimonials .sample-board,
	.js-motion .motion-page .service-included h2,
	.js-motion .motion-page .service-included .cover-card,
	.js-motion .motion-page .service-included .included-content li {
		opacity: 1;
		transform: none;
		transition: none;
	}
	/* Tick already struck. */
	.js-motion .motion-page .service-included .tick path {
		stroke-dasharray: none;
		stroke-dashoffset: 0;
		transition: none;
	}
	.js-motion .motion-page .service-included::before,
	.js-motion .motion-page .service-process .process-step::before {
		clip-path: none;
		transition: none;
	}
	.js-motion .motion-page .service-process .process-step .step-content {
		opacity: 1;
		transform: none;
		transition: none;
	}

	.js-motion .motion-page .project-card,
	.js-motion .motion-page .finish-card,
	.js-motion .motion-page .space-card {
		opacity: 1;
		transform: none;
		transition: none;
	}
	.js-motion .motion-page :is(.projects-archive-header, .finishes-archive-header, .spaces-index-hero, .space-hero) { animation: none; }

	.js-motion .motion-page .vpc-page-title,
	.js-motion .motion-page .vpc-page-subtitle,
	.js-motion .motion-page .vpc-intro-section,
	.js-motion .motion-page .vpc-values-section,
	.js-motion .motion-page .vpc-values-section .icon-item,
	.js-motion .motion-page .vpc-form-testimonials,
	.js-motion .motion-page .vpc-quote-checklist,
	.js-motion .motion-page .vpc-important-info,
	.js-motion .motion-page .vpc-about-aside {
		opacity: 1;
		transform: none;
		transition: none;
		animation: none;
	}
	.js-motion .motion-page .vpc-page-hero { animation: none; }
	.motion-page .vpc-page-hero::after { background: radial-gradient(
		55% 130% at 50% 45%,
		rgba(255, 244, 226, 0.14) 0%,
		rgba(255, 244, 226, 0) 62%); }

	.js-motion .motion-page .project-title,
	.js-motion .motion-page .project-meta,
	.js-motion .motion-page .project-header-split,
	.js-motion .motion-page .project-description-section,
	.js-motion .motion-page .project-client-said,
	.js-motion .motion-page .project-finishes-section,
	.js-motion .motion-page .related-projects .project-card,
	.js-motion .motion-page .project-tab-content.active :is(.project-gallery-item, .project-video-embed, .project-editor-content) {
		opacity: 1;
		transform: none;
		transition: none;
		animation: none;
	}
	.js-motion .motion-page .project-hero { animation: none; }
	.motion-page .project-hero::after { background: radial-gradient(
		55% 130% at 50% 45%,
		rgba(255, 244, 226, 0.14) 0%,
		rgba(255, 244, 226, 0) 62%); }

	.js-motion .motion-page .finish-characteristics .characteristic-item,
	.js-motion .motion-page .finish-applications .cover-card,
	.js-motion .motion-page .finish-media-gallery .project-gallery-item,
	.js-motion .motion-page .finish-media-gallery .finish-film,
	.js-motion .motion-page .finish-category,
	.js-motion .motion-page .finish-title {
		opacity: 1;
		transform: none;
		transition: none;
		animation: none;
	}
	.js-motion .motion-page .finish-hero { animation: none; }
	.motion-page .finish-hero::after { background: radial-gradient(
		55% 130% at 50% 45%,
		rgba(255, 244, 226, 0.14) 0%,
		rgba(255, 244, 226, 0) 62%); }

	/* Service hero: light pinned centred, words already in place. */
	.js-motion .motion-page .service-hero { animation: none; }
	.motion-page .service-hero::after { background: radial-gradient(
		55% 130% at 50% 45%,
		rgba(255, 244, 226, 0.14) 0%,
		rgba(255, 244, 226, 0) 62%); }
	.js-motion .motion-page .service-title,
	.js-motion .motion-page .service-excerpt {
		opacity: 1;
		animation: none;
		transform: none;
	}

	.sample-board,
	.sample-board-swatch::after,
	.space-card-image img,
	.vpc-home-services .icon-item .icon-item-icon {
		transition: none;
	}
	.sample-board:hover,
	.sample-board:focus-within { transform: none; }
	.sample-board:hover .sample-board-swatch::after,
	.sample-board:focus-within .sample-board-swatch::after { transform: translateX(-130%); }
}
