/* Crank and Canvas – Canvas spotlight
   A canvas stays pinned while the captions scroll past; each caption lights up
   the part of the canvas it describes. Used on index.html and custom.html.

   Markup (see either page):
     .sp[data-spotlight]      add data-start="full" to open with the whole canvas lit
       .sp-intro            heading
       .sp-stage            sticky; holds one or more .sp-canvas
         .sp-canvas[data-canvas]   --vt/--vr/--vb/--vl crop the photo,
                                   --img-ratio is the photo's w/h
           .sp-art > img.sp-dim + img.sp-lit
       .sp-steps > .sp-feature[data-canvas][data-region="top right bottom left"]
                   (region = % insets on the full photo, lit area only)

   Palette + shape follow styles.css: ink on white, sharp corners. */

.sp {
  --sp-ink: #1a1a1a;
  --sp-muted: #666666;
  --sp-ease: cubic-bezier(0.16, 1, 0.3, 1);
  --sp-gutter: clamp(1rem, 4vw, 3rem);
  --sp-nav: 73px;
  padding-block: clamp(5rem, 10vw, 8rem);
  color: var(--sp-ink);
}

@media (max-width: 640px) {
  .sp { --sp-nav: 61px; }
}

.sp-wrap {
  max-width: 1320px;
  margin-inline: auto;
  padding-inline: var(--sp-gutter);
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
  column-gap: clamp(2rem, 6vw, 6rem);
}

.sp-stage { grid-column: 1; grid-row: 1 / span 2; }
.sp-intro { grid-column: 2; grid-row: 1; }
.sp-steps { grid-column: 2; grid-row: 2; }

.sp-title {
  font-family: 'Cormorant Garamond', Georgia, serif;
  font-weight: 300;
  font-size: clamp(2.5rem, 5.4vw, 4.5rem);
  line-height: 1.02;
  letter-spacing: -0.02em;
  text-wrap: balance;
  font-variant-numeric: lining-nums;
}

.sp-title em {
  font-style: italic;
}

.sp-intro {
  min-height: 40svh;
  display: flex;
  flex-direction: column;
  justify-content: flex-end;
}

/* Heading kept for search engines and screen readers only, so the canvas
   and its first caption open the page with no run-up */
.sp-intro--hidden {
  position: absolute !important;
  width: 1px;
  height: 1px;
  min-height: 0 !important;
  padding: 0 !important;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
}

/* ===== Stage ===== */
.sp-stage {
  position: sticky;
  top: calc(var(--sp-nav) + 2rem);
  height: calc(100svh - var(--sp-nav) - 4rem);
  max-height: 52rem;
  display: flex;
  flex-direction: column;
  gap: 1rem;
}

/* The frame is a size container so each canvas can fit itself exactly,
   keeping its true aspect ratio. The lit regions depend on that. */
.sp-frame {
  flex: 1;
  min-height: 0;
  position: relative;
  container-type: size;
  display: grid;
  place-items: center;
}

.sp-canvas {
  /* Crop of the photo that is shown, in % of the photo */
  --vt: 0; --vr: 0; --vb: 0; --vl: 0;
  --img-ratio: 1;
  --box-ratio: calc(var(--img-ratio) * (100 - var(--vl) - var(--vr)) / (100 - var(--vt) - var(--vb)));

  grid-area: 1 / 1;
  position: relative;
  width: min(100cqw, 100cqh * var(--box-ratio));
  aspect-ratio: var(--box-ratio);
  overflow: hidden;
  opacity: 0;
  transition: opacity 0.8s var(--sp-ease);
}

/* Soft white edges so the photo's backdrop dissolves into the page.
   A static overlay, not a mask: masks force the animated layers underneath
   to be repainted on every frame. */
.sp-canvas::after {
  content: "";
  position: absolute;
  inset: 0;
  box-shadow: inset 0 0 22px 14px #ffffff;
  pointer-events: none;
}

.sp-canvas.is-active {
  opacity: 1;
}

/* The full photo, scaled and shifted so only the crop shows */
.sp-art {
  position: absolute;
  width: calc(100% * 100 / (100 - var(--vl) - var(--vr)));
  height: calc(100% * 100 / (100 - var(--vt) - var(--vb)));
  left: calc(-100% * var(--vl) / (100 - var(--vl) - var(--vr)));
  top: calc(-100% * var(--vt) / (100 - var(--vt) - var(--vb)));
}

.sp-art > img,
.sp-art > .sp-sheet {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: fill;
  display: block;
  max-width: none;
}

.sp-art > img,
.sp-art > .sp-sheet {
  /* Own compositor layers: the faded copy is rasterised once, only the lit
     copy's clip changes while scrolling */
  will-change: transform;
  transform: translateZ(0);
}

.sp-dim {
  /* Faded rather than darkened, so it stays close in tone to the lit area on
     a white page and the highlight reads as focus, not a box */
  /* Opacity only (no contrast filter), so white paper stays pure white */
  opacity: 0.32;
  filter: grayscale(1);
}

.sp-lit {
  clip-path: inset(var(--t, 0%) var(--r, 0%) var(--b, 0%) var(--l, 0%));
  transition: clip-path 0.55s cubic-bezier(0.7, 0, 0.25, 1);
}

/* Scroll-linked mode (set by spotlight.js where ViewTimeline exists): the
   highlight and the canvas swap are scrubbed by scroll position, so the
   time-based transitions must not fight them */
.sp--scrub .sp-lit,
.sp--scrub .sp-canvas,
.sp--scrub .sp-copy {
  transition: none;
}

/* ===== Canvas face built in HTML (.sp-sheet) =====
   Used when there is no photo of the exact canvas: the face is laid out in
   the house design (title, studio car cut-out, subtitle, specs, flag,
   footer). Blocks are absolutely placed in % of the face, so the spotlight
   regions (data-region) can target them exactly. Type scales with the face
   via container query units. spotlight.js clones it for the faded layer. */
.sp-sheet {
  container-type: size;
  background: #ffffff;
  color: #1a1a1a;
  font-family: 'Geist', -apple-system, sans-serif;
  font-weight: 300;
  overflow: hidden;
}

.sp-sheet > * {
  position: absolute;
  left: 9%;
  right: 9%;
  margin: 0;
}

.sheet-title {
  top: 8%;
  height: 9%;
  display: grid;
  place-items: center;
  font-family: 'Cormorant Garamond', Georgia, serif;
  font-style: italic;
  font-weight: 400;
  font-size: 9.4cqw;
  line-height: 1;
  letter-spacing: -0.01em;
  font-variant-numeric: lining-nums;
  white-space: nowrap;
}

.sheet-car {
  top: 20%;
  height: 29%;
  left: 7%;
  right: 7%;
}

.sheet-car img {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: contain;
}

/* Soft studio floor shadow under the car */
.sheet-car::before {
  content: "";
  position: absolute;
  left: 12%;
  right: 8%;
  bottom: 3%;
  height: 12%;
  background: radial-gradient(closest-side, rgb(0 0 0 / 0.28), transparent);
  filter: blur(2px);
}

.sheet-sub {
  top: 51%;
  height: 5%;
  left: 22%;
  right: 22%;
  display: grid;
  place-items: center;
  border-bottom: 1px solid #1a1a1a;
  font-family: 'Cormorant Garamond', Georgia, serif;
  font-size: 4.2cqw;
  font-weight: 400;
  font-variant-numeric: lining-nums;
  white-space: nowrap;
}

.sheet-specs {
  top: 59%;
  height: 21%;
  display: grid;
  grid-template-columns: 1.15fr 1fr;
  gap: 5%;
  font-size: 2.35cqw;
  line-height: 1.55;
}

.sheet-specs dl {
  display: grid;
  grid-template-columns: auto 1fr;
  column-gap: 5cqw;
  align-content: start;
  margin: 0;
  padding-right: 5%;
  border-right: 1px solid #1a1a1a;
}

.sheet-specs dt {
  font-weight: 500;
}

.sheet-specs dd {
  margin: 0;
}

.sheet-specs p {
  margin: 0;
}

.sheet-flag {
  top: 82.5%;
  left: auto;
  right: 10%;
  width: 8.5%;
  height: 4%;
  background: linear-gradient(#141414 0 33.4%, #dd0000 33.4% 66.7%, #ffce00 66.7%);
}

.sheet-foot {
  top: 91%;
  text-align: center;
  font-size: 2cqw;
  letter-spacing: 0.32em;
  text-transform: uppercase;
}

/* ===== 3D canvas (.sp-canvas--3d) =====
   For flat, straight-on product shots: the photo is cropped to the canvas
   face and built into a real object with a gallery-wrapped side edge and a
   wall shadow, angled like the C63 photograph. Structure:
     .sp-canvas.sp-canvas--3d  (opacity + perspective)
       .sp-obj                 (preserve-3d, rotated; turned by scroll in JS)
         .sp-shadow  .sp-face > .sp-art > imgs  .sp-side                    */
.sp-canvas--3d {
  /* A2 is 420 mm wide on a ~18 mm stretcher bar: about 4.5% of the width.
     (It was 9%, which read as a thick block rather than a canvas.) */
  --depth: 4.5%;
  width: min(80cqw, 84cqh * var(--box-ratio));
  overflow: visible;
  perspective: 1800px;
  perspective-origin: 40% 50%;
}

.sp-canvas--3d::after {
  content: none;
}

.sp-obj {
  position: absolute;
  inset: 0;
  transform-style: preserve-3d;
  transform: rotateY(-18deg) rotateX(1.2deg);
  will-change: transform;
}

/* ===== 3D mockup inside a product card (.mock3d-frame) =====
   Same object as the spotlight canvas, on a softly lit wall. */
.mock3d-frame {
  container-type: size;
  display: grid;
  place-items: center;
  background:
    radial-gradient(120% 90% at 30% 20%, #fbfbfa 0%, #efeeeb 60%, #e6e5e1 100%);
}

.mock3d-frame .sp-canvas {
  opacity: 1;
  width: min(56cqw, 74cqh * var(--box-ratio));
}

.mock3d-frame .sp-obj {
  transition: transform 1.1s var(--sp-ease, cubic-bezier(0.16, 1, 0.3, 1));
}

@media (hover: hover) and (prefers-reduced-motion: no-preference) {
  .featured-card:hover .mock3d-frame .sp-obj {
    transform: rotateY(-8deg) rotateX(1deg);
  }
}

.sp-face {
  position: absolute;
  inset: 0;
  overflow: hidden;
  background: #ffffff;
  /* Hairline so the white canvas edge reads against the white page */
  box-shadow: inset 0 0 0 1px rgb(0 0 0 / 0.05);
}

/* Right-hand wrapped edge, folded back into the wall */
.sp-side {
  position: absolute;
  top: 0;
  bottom: 0;
  left: 100%;
  width: calc(var(--depth) * 1);
  transform-origin: left center;
  transform: rotateY(90deg);
  background: linear-gradient(to right, #e2e2de, #c8c8c3);
  box-shadow: inset 1px 0 0 rgb(255 255 255 / 0.7);
}

/* Soft shadow on the wall behind the canvas */
.sp-shadow {
  position: absolute;
  inset: 5% -3% -4% 8%;
  background: rgb(26 26 26 / 0.22);
  filter: blur(26px);
  transform: translateZ(-60px);
}

/* ===== Lead variant: the spotlight opens the home page ===== */
.sp--lead {
  padding-top: calc(var(--sp-nav) + 1.5rem);
}

/* ===== 3D canvas on white (About section) =====
   No backdrop at all: just the canvas, its wrapped edge and a soft shadow. */
.mock3d-frame--plain {
  background: none;
  overflow: visible;
}

.mock3d-frame--plain {
  --depth: 4.5%;
}

/* The canvas fills the column: it is the section's image, not a thumbnail */
.mock3d-frame--plain .sp-canvas {
  width: min(86cqw, 92cqh * var(--box-ratio));
  perspective: 1400px;
}

/* Thicker gallery wrap, shaded because it faces away from the light */
.mock3d-frame--plain .sp-side {
  background: linear-gradient(to right, #d9d9d4, #b9b9b3);
}

/* Turns towards the viewer as the section scrolls past, easing in and out */
@keyframes sp-turn {
  from { transform: rotateY(-26deg) rotateX(1.8deg); }
  to   { transform: rotateY(-12deg) rotateX(0.6deg); }
}

@media (prefers-reduced-motion: no-preference) {
  @supports (animation-timeline: view()) {
    .mock3d-frame--plain .sp-obj {
      animation: sp-turn ease-in-out both;
      animation-timeline: view();
      animation-range: entry 10% exit 20%;
    }
  }
}

/* ===== Captions ===== */
.sp-steps {
  list-style: none;
  margin: 0;
  padding: 0;
}

.sp-feature {
  min-height: 70svh;
  display: flex;
  flex-direction: column;
  justify-content: center;
}

.sp-feature:last-child {
  min-height: 85svh;
}

.sp-copy {
  max-width: 30rem;
  opacity: 0.25;
  transition: opacity 0.35s cubic-bezier(0.4, 0, 0.2, 1);
}

.sp-feature.is-active .sp-copy {
  opacity: 1;
}

.sp-copy h2,
.sp-copy h3 {
  font-family: 'Cormorant Garamond', Georgia, serif;
  font-weight: 300;
  font-size: clamp(2rem, 3.4vw, 2.9rem);
  line-height: 1.08;
  letter-spacing: -0.01em;
  font-variant-numeric: lining-nums;
}

.sp-copy p {
  margin-top: 1rem;
  font-size: 1.05rem;
  line-height: 1.65;
  color: var(--sp-muted);
  text-wrap: pretty;
}

.sp.is-static .sp-copy {
  opacity: 1;
}

/* ===== Phones: canvas pinned on top, captions scroll underneath ===== */
@media (max-width: 860px) {
  .sp-wrap {
    grid-template-columns: 1fr;
  }
  .sp-stage,
  .sp-intro,
  .sp-steps {
    grid-column: 1;
    grid-row: auto;
  }
  .sp-intro {
    min-height: 0;
    padding-bottom: 1.5rem;
  }
  /* Phones: a shorter pinned stage, a canvas that leaves white space around it,
     and captions with room to breathe below the fade */
  .sp-stage {
    top: var(--sp-nav);
    height: 46svh;
    z-index: 1;
    padding-block: 0.75rem 0;
    background: linear-gradient(#ffffff 84%, rgb(255 255 255 / 0));
  }
  .sp-canvas--3d {
    width: min(64cqw, 80cqh * var(--box-ratio));
  }
  .sp-feature,
  .sp-feature:last-child {
    min-height: 48svh;
    justify-content: flex-start;
    padding-top: 1.5rem;
  }
  .sp-copy h2,
  .sp-copy h3 {
    font-size: 1.85rem;
  }
  .sp-copy p {
    margin-top: 0.75rem;
    font-size: 1rem;
    line-height: 1.6;
  }
}

/* Story variant (about.html): longer captions, so on phones the pinned canvas
   gives up a little height and each caption fits whole beneath it */
@media (max-width: 860px) {
  .sp--story .sp-stage {
    height: 40svh;
  }
  .sp--story .sp-feature,
  .sp--story .sp-feature:last-child {
    min-height: 60svh;
  }
}

/* ===== Realism for every 3D canvas (F8, C63, GT3 RS, Turbo S) =====
   It should read as a real stretched canvas hung on the wall, not a card
   floating in space: a thin wrapped edge, linen texture, light falling across
   the face, and a shadow that sits tight to the wall. */

/* Crisp edge plus a faint ambient shadow all round, so no side melts into the page */
.sp-canvas--3d .sp-face {
  box-shadow:
    0 0 0 1px rgb(0 0 0 / 0.06),
    0 1px 2px rgb(0 0 0 / 0.06);
}

/* Light from the upper left falling off across the face, plus a faint linen
   weave (two fine crossed lines), as printed canvas has up close */
.sp-canvas--3d .sp-face::after {
  content: "";
  position: absolute;
  inset: 0;
  z-index: 1;
  pointer-events: none;
  background:
    repeating-linear-gradient(0deg, rgb(0 0 0 / 0.018) 0 1px, transparent 1px 3px),
    repeating-linear-gradient(90deg, rgb(0 0 0 / 0.014) 0 1px, transparent 1px 3px),
    linear-gradient(100deg, rgb(255 255 255 / 0.10) 0%, rgb(0 0 0 / 0) 30%, rgb(0 0 0 / 0.03) 72%, rgb(0 0 0 / 0.08) 100%),
    linear-gradient(180deg, rgb(255 255 255 / 0.05) 0%, rgb(0 0 0 / 0) 40%, rgb(0 0 0 / 0.035) 100%);
  /* the canvas rolls over the stretcher bar: a soft bright lip on the lit
     edges and a darker one where it turns away */
  box-shadow:
    inset 0 0 0 1px rgb(0 0 0 / 0.05),
    inset 2px 2px 1px -1px rgb(255 255 255 / 0.8),
    inset -3px 0 3px -2px rgb(0 0 0 / 0.12);
}

/* Gallery wrap: the canvas continues round the edge (white), shaded because it
   faces away from the light, darkest where it meets the wall */
.sp-canvas--3d .sp-side {
  background:
    repeating-linear-gradient(0deg, rgb(0 0 0 / 0.02) 0 1px, transparent 1px 3px),
    linear-gradient(to right, #e4e4e0 0%, #cfcfca 55%, #b9b9b3 100%);
  box-shadow: inset 1px 0 0 rgb(255 255 255 / 0.8);
}

/* Shadows on the wall: a tight contact shadow where the frame meets it, and a
   soft one cast down and to the right by the light. Both sit at the wall
   (just behind the canvas), so the canvas reads as hung, not floating. */
.sp-canvas--3d .sp-shadow {
  inset: 2% -2.5% -3.5% 2%;
  background: rgb(26 26 26 / 0.18);
  filter: blur(22px);
  transform: translateZ(-18px);
}

.sp-canvas--3d .sp-shadow::after {
  content: "";
  position: absolute;
  inset: 1% 1% 1.5% 0;
  background: rgb(26 26 26 / 0.22);
  filter: blur(5px);
}

/* A slow, barely-there sway instead of bobbing up and down: a hung canvas
   settles on its hook, it doesn't hover. `rotate` is separate from the
   scroll-driven `transform`, so the two combine. */
@keyframes sp-sway {
  0%, 100% { rotate: 0deg; }
  50%      { rotate: 0.25deg; }
}

@media (prefers-reduced-motion: no-preference) {
  .sp-canvas--3d {
    transform-origin: 50% 0;
    animation: sp-sway 9s ease-in-out infinite;
  }
}

/* ===== Wall stage: a spotlight on the gallery wall (custom.html C63, about.html GT3 RS) =====
   The same photographed wall and floor as the home page F8 section
   (images/lifestyle/backdrop-*). A full-screen sticky layer sits behind the
   spotlight, so the wall stays put while the captions scroll and leaves,
   floor and all, at the end of the section; the rest of the page is the
   plain wall grey. */
.wall-stage {
  position: relative;
  isolation: isolate;
  overflow: clip;
}

/* Full-height layer behind the section; the photo inside is sticky, so it
   stays put while scrolling and lands, floor and all, at the section's end */
.wall-stage-backdrop {
  position: absolute;
  inset: 0;
  z-index: -1;
  pointer-events: none;
}

.wall-stage-backdrop picture {
  position: sticky;
  top: 0;
  display: block;
  height: 100vh;
}

.wall-stage-backdrop img {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  object-position: 50% 100%;
}

/* White captions, as on the home page, with a soft halo for the paler wall */
.wall-stage .sp {
  --sp-ink: #f5f5f3;
  --sp-muted: rgb(245 245 243 / 0.82);
}

.wall-stage .sp-intro,
.wall-stage .sp-copy {
  text-shadow: 0 1px 2px rgb(0 0 0 / 0.22), 0 2px 18px rgb(20 22 24 / 0.5);
}

.wall-stage .cx-btn,
.wall-stage .btn {
  text-shadow: none;
}

/* The canvas hangs above the skirting board, not over the floor */
.wall-stage .sp-stage {
  height: calc(80vh - var(--sp-nav) - 2rem);
}

/* Phones: no white band behind the pinned canvas; inactive captions are
   hidden (data-dim="0"), so nothing slides under it */
@media (max-width: 860px) {
  .wall-stage .sp-stage {
    background: none;
    height: 46svh;
  }
  .wall-stage .sp--story .sp-stage {
    height: 40svh;
  }
}

/* ----- Wall stage choreography (js/wall-stage.js), as the home page F8 ----- */

/* The canvas appears on the wall: fades in, sharpens and settles */
.wall-stage .sp-stage {
  opacity: 0;
  transform: scale(0.94);
  filter: blur(12px);
  transition:
    opacity 1.2s ease,
    transform 1.9s cubic-bezier(0.16, 1, 0.3, 1),
    filter 1.5s cubic-bezier(0.16, 1, 0.3, 1);
}

.wall-stage .sp.is-on .sp-stage {
  opacity: 1;
  transform: none;
  filter: none;
}

/* Captions: only the one being read shows, and it resolves word by word */
.wall-stage .sp-copy {
  opacity: 0;
}

.wall-stage .sp-feature.is-reading .sp-copy {
  opacity: 1;
}

.wall-stage .sp-copy .w {
  display: inline-block;
  opacity: 0;
  filter: blur(10px);
  transform: translateY(0.35em);
  transition:
    opacity 0.5s ease,
    filter 0.5s ease,
    transform 0.5s ease;
}

.wall-stage .sp-feature.is-reading .sp-copy .w {
  opacity: 1;
  filter: blur(0);
  transform: none;
  transition:
    opacity 0.9s ease calc(120ms + var(--i) * 48ms),
    filter 1.1s cubic-bezier(0.16, 1, 0.3, 1) calc(120ms + var(--i) * 48ms),
    transform 1.1s cubic-bezier(0.16, 1, 0.3, 1) calc(120ms + var(--i) * 48ms);
}

/* Longer story captions read word by word a little faster */
.wall-stage .sp--story .sp-feature.is-reading .sp-copy .w {
  transition-delay: calc(80ms + var(--i) * 22ms);
}

/* Same caption spacing as the home page, with a long last step for the pause */
.wall-stage .sp-feature {
  min-height: 42svh;
}

.wall-stage .sp-feature:last-child {
  min-height: 80svh;
}

@media (max-width: 860px) {
  .wall-stage .sp-feature {
    min-height: 34svh;
  }
  .wall-stage .sp-feature:last-child {
    min-height: 70svh;
  }
  .wall-stage .sp--story .sp-feature {
    min-height: 52svh;
  }
}

@media (prefers-reduced-motion: reduce) {
  .wall-stage .sp-stage,
  .wall-stage .sp-copy .w {
    transition: none;
    filter: none;
    transform: none;
  }
}
