/* ==========================================================================
   Yiertek v4 · motion.css — version B only (contract §6)
   ==========================================================================
   Loaded only when the build runs with MOTION=on, next to /js/motion.js.
   Three rules hold everywhere in this file:
   · every moving rule sits inside `prefers-reduced-motion: no-preference`;
   · nothing starts hidden unless JavaScript is running (html:not(.no-js)),
     and the one thing that does ([data-m-feature]) has a CSS failsafe that
     shows it after 3 s if motion.js never arrives;
   · only transform (and the individual scale/translate), opacity and, for the
     cover's single unmask, clip-path are animated. Colours come from tokens.
   motion.js adds html.m-js as it runs (CSS-driven reveals) and html.m-on once
   it has loaded GSAP (after `load` + idle): the pinned, scrubbed layer.
   ========================================================================== */

@media (prefers-reduced-motion: no-preference) {

  /* -- navigation: the card image becomes the product photo ---------------- */
  @view-transition { navigation: auto; }

  html[data-motion="on"] .product__media > .product__photo:first-child img { view-transition-name: m-photo; }

  ::view-transition-group(m-photo) {
    animation-duration: 560ms;
    animation-timing-function: var(--e-out);
  }
  ::view-transition-image-pair(m-photo) { overflow: clip; }
  ::view-transition-old(m-photo),
  ::view-transition-new(m-photo) { height: 100%; object-fit: cover; }
  ::view-transition-old(root),
  ::view-transition-new(root) { animation-duration: 280ms; }

  /* -- cover: the headline rises, the photograph unmasks from the right ----
     Pure CSS on purpose: it starts with the first paint, needs no script and
     ends visible even if motion.js never loads. motion.js adds the scroll
     parallax on the <img> transform; the settle uses the separate `scale`
     property so the two compose instead of fighting. */
  html[data-motion="on"] [data-m="cover"] [data-m-line] {
    animation: m-rise 950ms var(--e-out) 200ms both;
  }
  html[data-motion="on"] [data-m="cover"] [data-m-line]:nth-of-type(2) { animation-delay: 290ms; }
  html[data-motion="on"] [data-m="cover"] [data-m-line]:nth-of-type(3) { animation-delay: 380ms; }
  html[data-motion="on"] [data-m-photo] {
    overflow: clip;
    animation: m-unmask var(--d-hero) var(--e-move) both;
  }
  html[data-motion="on"] [data-m-photo] img {
    animation: m-settle 1800ms var(--e-out) both;
  }

  /* -- opening (section pages): room for the parallax ---------------------- */
  html[data-motion="on"] .opening__photo { overflow: clip; }

  /* -- product: the photograph settles; the feature rows wait for motion.js,
     which sets --m-i (row) and --m-len (each icon stroke) and adds .m-in when
     the first row is in view. No GSAP involved. */
  html[data-motion="on"] .product__photo { overflow: clip; }
  html[data-motion="on"] [data-m="spec"] .product__photo img {
    animation: m-settle-sm 1400ms var(--e-out) both;
  }
  html[data-motion="on"]:not(.no-js) [data-m-feature] {
    opacity: 0;
    animation: m-failsafe 1ms 3s forwards;
  }
  html[data-motion="on"].m-js [data-m-feature] { animation: none; }
  html[data-motion="on"] [data-m="spec"].m-in [data-m-feature] {
    animation: m-row 800ms var(--e-out) calc(var(--m-i, 0) * 70ms) both;
  }
  html[data-motion="on"] [data-m="spec"].m-in [data-m-icon] > * {
    stroke-dasharray: var(--m-len) calc(var(--m-len) + 2px);
    animation: m-draw 900ms var(--e-move) calc(100ms + var(--m-i, 0) * 70ms) both;
  }

  /* -- counters: tabular figures so the count does not jitter ------------- */
  html[data-motion="on"] [data-m="counter"] { font-variant-numeric: tabular-nums; }

  /* -- doors: the image leans in, the number lifts ------------------------- */
  html[data-motion="on"] .door__media { overflow: clip; }
  html[data-motion="on"] [data-m-door] .door__img { transition: transform 900ms var(--e-out); }
  html[data-motion="on"] [data-m-door] .door__num { transition: transform 500ms var(--e-out); }
  html[data-motion="on"] [data-m-door]:is(:hover, :focus-within) .door__img { transform: scale(1.02); }
  html[data-motion="on"] [data-m-door]:is(:hover, :focus-within) .door__num { transform: translateY(-4px); }

  /* -- rail: while a mouse drags, snapping steps aside --------------------- */
  html[data-motion="on"].m-on .rail__track.m-drag { scroll-snap-type: none; cursor: grabbing; user-select: none; }

  /* -- swatches: the ring springs (the ring itself is below, for everyone) - */
  html[data-motion="on"] [data-m="swatches"] .swatch::after {
    transform: scale(0.55);
    transition: transform 520ms cubic-bezier(0.34, 1.56, 0.64, 1), opacity var(--d-hover) linear;
  }
  html[data-motion="on"] [data-m="swatches"] .swatch:is(.m-active, :hover)::after { transform: none; }

  /* -- vinyl --------------------------------------------------------------- */
  html[data-motion="on"].m-on [data-m-record] svg { will-change: transform; }

  /* -- powers of ten -------------------------------------------------------
     One window, the same size in both states, so switching to the pinned
     layer moves nothing: 4 × window + 8 × pad = 400svh either way.
     Before motion.js (or if it never comes) the four windows simply follow
     each other down the page. With html.m-on they stack in one sticky
     viewport and motion.js scrubs the zoom from each into the next. */
  html[data-motion="on"]:not(.no-js) .powers {
    --m-win: min(72svh, calc((100vw - 2 * var(--gutter)) * 1.25));
    --m-pad: calc((100svh - var(--m-win)) / 2);
    padding: 0;
    height: auto;
    overflow: visible;
  }
  html[data-motion="on"]:not(.no-js) .powers__frames {
    display: flex;
    flex-direction: column;
    gap: calc(2 * var(--m-pad));
    margin: 0;
    padding: var(--m-pad) var(--gutter);
    max-width: none;
    overflow: visible;
    list-style: none;
  }
  html[data-motion="on"]:not(.no-js) .powers__frame {
    position: relative;
    flex: none;
    width: min(100%, 1120px);
    height: var(--m-win);
    margin: 0 auto;
    overflow: clip;
    background: var(--paper-2);
  }
  html[data-motion="on"]:not(.no-js) .powers__frame img {
    position: absolute;
    inset: 0;
    width: 100%;
    height: 100%;
    object-fit: cover;
    transform-origin: 50% 50%;
  }
  html[data-motion="on"]:not(.no-js) .powers__cap {
    position: absolute;
    left: var(--s-4);
    bottom: var(--s-4);
    margin: 0;
    padding: var(--s-2) var(--s-3);
    background: var(--paper);
    color: var(--ink-2);
  }
  html[data-motion="on"].m-on .powers { height: 400svh; }
  html[data-motion="on"].m-on .powers__frames {
    position: sticky;
    top: 0;
    display: block;
    height: 100svh;
    padding: 0;
  }
  html[data-motion="on"].m-on .powers__frame {
    position: absolute;
    top: var(--m-pad);
    left: 0;
    right: 0;
    width: min(100% - 2 * var(--gutter), 1120px);
    transform-origin: 50% 50%;
  }
  html[data-motion="on"].m-on .powers__frame:not(:first-child) { visibility: hidden; }

  /* -- enquiry tray: the flying copy and the count's CSS bump ------------- */
  .m-fly {
    position: fixed;
    z-index: 1000;
    margin: 0;
    object-fit: cover;
    border-radius: var(--r-card);
    box-shadow: var(--shadow-overlay);
    pointer-events: none;
    transform-origin: 50% 50%;
    will-change: transform, opacity;
  }
  /* Used when an item is added before GSAP has arrived. */
  html[data-motion="on"] [data-tray-count].m-bump { animation: m-bump 700ms cubic-bezier(0.34, 1.8, 0.64, 1); }
}

/* -- the swatch ring exists with or without motion (reduced: no scale) ---- */
html[data-motion="on"] [data-m="swatches"] .swatch { position: relative; }
html[data-motion="on"] [data-m="swatches"] .swatch::after {
  content: "";
  position: absolute;
  inset: -4px;
  border: 1.5px solid var(--copper-line);
  border-radius: var(--r-pill);
  opacity: 0;
  transition: opacity 150ms linear;
  pointer-events: none;
}
html[data-motion="on"] [data-m="swatches"] .swatch:is(.m-active, :hover)::after { opacity: 1; }

/* -- the check motion.js draws in [data-form-status] --------------------- */
@media (prefers-reduced-motion: no-preference) {
  .m-check { animation: m-pop 900ms cubic-bezier(0.34, 1.56, 0.64, 1) both; }
  .m-check > * { stroke-dasharray: var(--m-len) calc(var(--m-len) + 2px); animation: m-draw 550ms var(--e-move) both; }
  .m-check > :nth-child(2) { animation-delay: 300ms; }
}
.m-check {
  width: 1.25em;
  height: 1.25em;
  margin-right: 0.45em;
  vertical-align: -0.28em;
  fill: none;
  stroke: var(--copper-line);
  stroke-width: 2;
  stroke-linecap: round;
  stroke-linejoin: round;
}

/* -- the real figure, for screen readers, while a counter's digits roll -- */
.m-sr {
  position: absolute;
  width: 1px;
  height: 1px;
  margin: -1px;
  padding: 0;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
  border: 0;
}

@keyframes m-rise { from { opacity: 0; translate: 0 0.42em; } }
@keyframes m-row { from { opacity: 0; translate: 0 14px; } to { opacity: 1; translate: none; } }
@keyframes m-draw { from { stroke-dashoffset: calc(var(--m-len) + 1px); } to { stroke-dashoffset: 0px; } }
@keyframes m-pop { from { scale: 0.6; } }
@keyframes m-unmask { from { clip-path: inset(0 0 0 100%); } to { clip-path: inset(0); } }
@keyframes m-settle { from { scale: 1.06; } }
@keyframes m-settle-sm { from { scale: 1.035; } }
@keyframes m-failsafe { to { opacity: 1; } }
@keyframes m-bump { from { scale: 1.5; } }
