.scene-layer {
  transition: none;
  contain: layout paint style;
}

.scene-copy,
.scene-visual {
  will-change: clip-path;
}

/*
 * Motion language — five deliberate families, selected via data-motion.
 * Every family consumes the same three JS-driven scalars per element:
 *   --reveal / --item   0→1 entrance progress (already eased per-family in JS)
 *   --exit               0→1 handoff-recede progress, final ~10-14% of a scene
 *   --idle-x / --idle-y  a few px of settled, non-unison drift (cards/windows only)
 * Nothing here is a plain fade, plain slide or plain scale in isolation — each
 * family composes translate + clip-path/mask + filter + (where relevant)
 * rotation or shadow into one considered gesture.
 */

[data-reveal],
[data-seq] {
  --reveal: 0;
  --item: 0;
  --exit: 0;
  --idle-x: 0px;
  --idle-y: 0px;
  will-change: opacity, transform, clip-path, filter;
}

/* type-confident — eyebrow / headline: a confident overshoot-settle with a
   directional mask that resolves left-to-right, like light gathering into
   focus, plus a soft blur-in. Exit lifts the line away and lets it dim,
   never vanish outright — the wipe finishes the job. */
[data-motion="type-confident"] {
  opacity: calc(.001 + min(1, var(--reveal)) * .999 * (1 - var(--exit) * .55));
  transform: translate3d(0, calc((1 - min(1.06, var(--reveal))) * 15px - var(--exit) * 9px), 0)
    scale(calc(0.985 + min(1, var(--reveal)) * 0.015 - var(--exit) * 0.016));
  clip-path: inset(0 calc(max(0%, (1 - var(--reveal)) * 62%)) 0 0);
  filter: blur(calc((1 - min(1, var(--reveal))) * 5px + var(--exit) * 4px));
}

/* type-calm — summary / tags: gentle rise, soft blur-in, no overshoot, no
   wipe edge. Recedes quietly on exit rather than lifting. */
[data-motion="type-calm"] {
  opacity: calc(.001 + var(--reveal) * .999 * (1 - var(--exit) * .5));
  transform: translate3d(0, calc((1 - var(--reveal)) * 12px + var(--exit) * 6px), 0);
  filter: blur(calc((1 - var(--reveal)) * 3px + var(--exit) * 3px));
}

/* depth-window — product windows, app shells, panels, the scene-visual pane
   itself: real depth on arrival (scale + blur-in + shadow growing in), and a
   quiet recede-into-the-surface on exit (shadow flattens, doesn't vanish).
   This family is used both on [data-reveal] elements (the scene-visual pane
   itself, which gets --reveal from JS) and on [data-seq] elements (product
   windows, app shells, panels nested inside it, which get --item instead) —
   read whichever one the element actually has via max(), never --reveal alone. */
[data-motion="depth-window"] {
  opacity: calc(.001 + min(1, max(var(--reveal), var(--item))) * .999);
  transform: translate3d(var(--idle-x), calc((1 - min(1, max(var(--reveal), var(--item)))) * 24px + var(--idle-y)), 0)
    scale(calc(0.945 + min(1, max(var(--reveal), var(--item))) * 0.055 - var(--exit) * 0.028));
  filter: blur(calc((1 - min(1, max(var(--reveal), var(--item)))) * 8px + var(--exit) * 2.5px));
}

/* clip-path for .scene-visual is owned entirely by SceneRenderer.js (it must
   compose the entrance wipe with the crossfade handoff wipe using the same
   inline style — a CSS rule here would always lose to that inline write). */
.scene-visual[data-motion="depth-window"] {
  transform: translate3d(calc((1 - min(1, var(--reveal))) * 30px + var(--idle-x)), var(--idle-y), 0)
    scale(calc(0.965 + min(1, var(--reveal)) * 0.035 - var(--exit) * 0.02));
}
.scene--center .scene-visual[data-motion="depth-window"] {
  transform: translate3d(var(--idle-x), calc((1 - min(1, var(--reveal))) * 20px + var(--idle-y)), 0)
    scale(calc(0.965 + min(1, var(--reveal)) * 0.035 - var(--exit) * 0.02));
}

.product-window[data-motion="depth-window"],
.app-shell[data-motion="depth-window"],
.property-app[data-motion="depth-window"],
.property-detail[data-motion="depth-window"],
.consultant-app[data-motion="depth-window"],
.procurement-brief[data-motion="depth-window"],
.demand-panel[data-motion="depth-window"] {
  box-shadow: 0 calc((var(--item) - var(--exit) * .3) * 46px) calc((var(--item) - var(--exit) * .3) * 70px) rgba(0, 0, 0, calc((var(--item) - var(--exit) * .2) * .38));
}

/* grid-card — logo/company grid tiles, cards, rows: diagonal micro-entrance
   with a slight rotation settle, off-center pivot so it reads as considered
   rather than mechanical. Settles into a few px of idle drift once fully in.
   Exit compresses inward toward the grid, like tidying up, not leaving.
   No clip-path here deliberately — this family covers text-bearing bars
   (labels, captions, table rows) as well as cards, and a mask edge risks
   cropping glyphs mid-transition; translate+rotate+opacity carries it. */
[data-motion="grid-card"] {
  transform-origin: 30% 70%;
  opacity: calc(.001 + min(1, var(--item)) * .999 * (1 - var(--exit) * .4));
  transform: translate3d(calc((1 - min(1.1, var(--item))) * 20px - var(--exit) * 7px + var(--idle-x)), calc((1 - min(1.1, var(--item))) * 11px + var(--idle-y)), 0)
    rotate(calc((1 - min(1, var(--item))) * -3.4deg))
    scale(calc(1 - var(--exit) * 0.045));
}

/* doc-leaf — document pages: settle from a fanned angle into rest, per-page
   angle supplied via --doc-rest-angle/--doc-fan-angle custom properties. */
[data-motion="doc-leaf"] {
  opacity: calc(.001 + min(1, var(--item)) * .999 * (1 - var(--exit) * .45));
  transform: translate3d(0, calc((1 - min(1, var(--item))) * 22px - var(--exit) * 8px), 0)
    rotate(calc(var(--doc-rest-angle, 0deg) + (1 - min(1, var(--item))) * var(--doc-fan-angle, -6deg)));
}

.chart-line {
  stroke-dasharray: 1200;
  stroke-dashoffset: calc(1200 * (1 - var(--item, 0)));
}
.chart-area { opacity: calc(.08 + var(--item, 0) * .24); }

.scene-brand {
  opacity: 1;
  transform: none;
}

.is-paused::after {
  content: "Paused";
  position: absolute;
  z-index: 80;
  right: var(--safe-x);
  bottom: 46px;
  color: var(--quiet);
  font-size: 11px;
  letter-spacing: .18em;
  text-transform: uppercase;
}

@media (prefers-reduced-motion: reduce) {
  [data-reveal],
  [data-seq] {
    transform: none !important;
    filter: none !important;
    clip-path: none !important;
    opacity: calc(.001 + min(1, max(var(--reveal, 0), var(--item, 0))) * .999) !important;
  }
}
