/* ==========================================================================
   motion-kinetic.css — additive motion layer for site-kinetic-lab
   --------------------------------------------------------------------------
   Loaded AFTER theme-kinetic.css. Contains no layout, colour or type rules;
   removing this file returns the site to its previous static behaviour.

   Contract (DESIGN-DIRECTION.md §2):
   - Motion only serves product switching, steps, structure, FAQ and video state.
   - Short, interruptible, and fully disabled under prefers-reduced-motion.
   - COMPLIANCE.md §5: motion must never simulate a physiological or medical
     effect, or imply an unproven output. The sheen below is a *specular
     photography highlight* on the object surface — not device light output.

   Every modern feature is wrapped in @supports so unsupported browsers keep
   the existing behaviour instead of losing content.
   ========================================================================== */

/* ---- shared easing tokens ------------------------------------------------ */
:root {
  --mk-ease-out: cubic-bezier(.16, 1, .3, 1);
  /* slight overshoot — reads as a part snapping into magnetic alignment */
  --mk-ease-magnet: cubic-bezier(.34, 1.56, .64, 1);
  --mk-fast: .28s;
  --mk-base: .52s;
}

/* ==========================================================================
   01 / CROSS-DOCUMENT VIEW TRANSITIONS
   Catalogue card -> PDP hero keeps the product image on screen instead of a
   white flash. Names are assigned at click time by motion-kinetic.js so only
   the pair being navigated between is ever tagged.
   ========================================================================== */
@view-transition { navigation: auto; }

@media (prefers-reduced-motion: no-preference) {
  /* The nav is identical on every page, so name it: the transition then carries
     it across as a shared element instead of cross-fading it away with the rest
     of the document. Combined with the nav shipping in each page's HTML, this is
     what keeps it visually static while navigating. */
  .nav { view-transition-name: bc-nav; }

  ::view-transition-old(bc-product),
  ::view-transition-new(bc-product) {
    animation-duration: .42s;
    animation-timing-function: var(--mk-ease-out);
  }
  /* Chrome/root cross-fade should not fight the shared element */
  ::view-transition-old(root),
  ::view-transition-new(root) { animation-duration: .3s; }
}

@media (prefers-reduced-motion: reduce) {
  ::view-transition-group(*),
  ::view-transition-old(*),
  ::view-transition-new(*) { animation: none !important; }
}

/* ==========================================================================
   02 / SCROLL-DRIVEN STRUCTURE DISASSEMBLY  (PDP · 03 / ANATOMY)
   The exploded render drifts apart and the callout list writes itself in as
   the section crosses the viewport. This is the one place where the motion
   *is* the explanation: DESIGN-DIRECTION.md §2 asks structure change to carry
   the product story rather than decoration.
   ========================================================================== */
/* Scroll-driven rules are gated on html[data-mk-sd="on"], which
   motion-kinetic.js sets only after confirming the document can actually
   scroll. Without that gate an inactive ViewTimeline would hold these
   elements in their `from` keyframe (opacity 0) and silently delete content. */
@supports (animation-timeline: view()) {
  @media (prefers-reduced-motion: no-preference) {

    html[data-mk-sd="on"] .product-anatomy-media {
      /* scale/settle tied to the section's own progress through the viewport */
      animation: mk-anatomy-settle linear both;
      animation-timeline: view();
      animation-range: entry 15% cover 55%;
      transform-origin: 50% 45%;
    }

    html[data-mk-sd="on"] .product-anatomy-list > li {
      animation: mk-callout-in linear both;
      animation-timeline: view();
      /* each callout resolves slightly later than the one above it */
      animation-range: entry 25% cover 45%;
    }
    html[data-mk-sd="on"] .product-anatomy-list > li:nth-child(2) { animation-range: entry 32% cover 52%; }
    html[data-mk-sd="on"] .product-anatomy-list > li:nth-child(3) { animation-range: entry 39% cover 59%; }
    html[data-mk-sd="on"] .product-anatomy-list > li:nth-child(4) { animation-range: entry 46% cover 66%; }
    html[data-mk-sd="on"] .product-anatomy-list > li:nth-child(n+5) { animation-range: entry 53% cover 73%; }
  }
}

@keyframes mk-anatomy-settle {
  from { transform: scale(1.06) translateY(14px); opacity: .55; }
  to   { transform: none; opacity: 1; }
}
@keyframes mk-callout-in {
  from { opacity: 0; transform: translateX(-14px); }
  to   { opacity: 1; transform: none; }
}

/* ==========================================================================
   03 / RELEASE-GATE TRACK  (lab.html · G1—G7)
   The rail fills as you scroll the section. It stops at the gate the project
   has actually reached, so the honest fact ("we are only at G1") becomes the
   visual statement instead of a footnote. The stop point is set by
   motion-kinetic.js from data-mk-gate-reached, never hardcoded here.
   ========================================================================== */
.lab-gates { position: relative; }

.lab-gates::before {
  content: "";
  position: absolute;
  inset-inline: 0;
  top: 0;
  height: 2px;
  background: currentColor;
  opacity: .12;
  pointer-events: none;
}

.lab-gates::after {
  content: "";
  position: absolute;
  inset-inline-start: 0;
  top: 0;
  height: 2px;
  /* --mk-gate-stop is the fraction of the rail that may ever light up */
  width: calc(var(--mk-gate-stop, 0) * 100%);
  background: var(--accent, #ff4f9a);
  transform-origin: 0 50%;
  /* Default is fully drawn: if the scroll-driven layer is off the rail must
     still show how far the project has actually got. The animation below only
     takes over when html[data-mk-sd="on"] is present. */
  transform: scaleX(var(--mk-gate-fill, 1));
  pointer-events: none;
}

@supports (animation-timeline: view()) {
  @media (prefers-reduced-motion: no-preference) {
    html[data-mk-sd="on"] .lab-gates::after {
      animation: mk-gate-fill linear both;
      animation-timeline: view();
      animation-range: entry 20% cover 60%;
    }
  }
}
@keyframes mk-gate-fill {
  from { transform: scaleX(0); }
  to   { transform: scaleX(1); }
}

/* Gates beyond the one actually reached stay visibly dormant. */
.lab-gates > article[data-mk-gate="pending"] { opacity: .48; }
.lab-gates > article[data-mk-gate="pending"] > span { opacity: .55; }
.lab-gates > article[data-mk-gate="reached"] > span { color: var(--accent, #ff4f9a); }

@media (prefers-reduced-motion: reduce) {
  .lab-gates::after { transform: scaleX(1); }
}

/* ==========================================================================
   04 / EVIDENCE STATE GRAMMAR
   A spec row that is still a design target must not read the same as a
   verified one. Unverified values breathe very slightly and carry a dashed
   underline; verified values are solid and static.

   This annotates CERTAINTY, not performance — it makes no claim about the
   product and is therefore compatible with COMPLIANCE.md §5.
   ========================================================================== */
.product-spec-row[data-mk-state="target"] dd {
  text-decoration: underline dashed currentColor 1px;
  text-underline-offset: .32em;
  opacity: .82;
}
.product-spec-row[data-mk-state="verified"] dd {
  text-decoration: none;
  opacity: 1;
}
.product-spec-row[data-mk-state="verified"] dt small {
  color: var(--accent, #ff4f9a);
}

@media (prefers-reduced-motion: no-preference) {
  .product-spec-row[data-mk-state="target"] dt small {
    animation: mk-unsettled 3.2s ease-in-out infinite;
  }
}
@keyframes mk-unsettled {
  0%, 100% { opacity: .58; }
  50%      { opacity: 1; }
}

/* ==========================================================================
   05 / SPECULAR SHEEN
   A slow highlight sweep across the object surface, driven by an animatable
   registered angle. Reads as studio lighting moving over a physical object.

   Deliberately NOT emissive and NOT coloured by the product's light mode:
   it must never be mistaken for the device's own optical output.
   ========================================================================== */
@property --mk-sheen {
  syntax: "<angle>";
  inherits: false;
  initial-value: 0deg;
}

@supports (background: conic-gradient(from var(--mk-sheen), #000, #fff)) {
  @media (prefers-reduced-motion: no-preference) and (hover: hover) {
    .pcard-media::after,
    .shop-feature-media::after,
    .object-card-media::after {
      content: "";
      position: absolute;
      inset: 0;
      pointer-events: none;
      border-radius: inherit;
      opacity: 0;
      transition: opacity var(--mk-base) var(--mk-ease-out);
      background: conic-gradient(
        from var(--mk-sheen),
        transparent 0deg,
        rgba(255, 255, 255, .34) 18deg,
        transparent 46deg,
        transparent 360deg
      );
      mix-blend-mode: soft-light;
    }
    .pcard:hover .pcard-media::after,
    .shop-feature-card:hover .shop-feature-media::after {
      opacity: 1;
      animation: mk-sheen-sweep 2.6s linear infinite;
    }
    .object-card:hover .object-card-media::after {
      opacity: 1;
      animation: mk-sheen-sweep 2.6s linear infinite;
    }
  }
}
@keyframes mk-sheen-sweep {
  to { --mk-sheen: 360deg; }
}

/* the ::after needs a positioned parent */
.pcard-media, .shop-feature-media, .object-card-media { position: relative; }

/* ==========================================================================
   06 / (removed) MAGNETIC PRODUCT SWITCHING
   Home section 03 is a two-up snap rail now, not a cross-fade stack, so the
   slide/tab transforms this block owned no longer have a target. Scroll snap
   provides the switching feel instead.
   ========================================================================== */


/* ==========================================================================
   07 / CATALOGUE STAGGER
   Cards resolve in reading order rather than all at once. motion-kinetic.js
   sets --i only on newly appended cards, so "load 12 more" animates the new
   batch and leaves already-read cards alone.
   ========================================================================== */
/* The hidden start state lives ONLY inside the keyframes, and motion-kinetic.js
   guarantees data-mk-enter is removed on a timer even if `animationend` never
   fires. A card must never depend on an animation completing to be readable. */
@media (prefers-reduced-motion: no-preference) {
  .pcard[data-mk-enter] {
    animation: mk-card-in var(--mk-base) var(--mk-ease-out) backwards;
    animation-delay: calc(var(--i, 0) * 42ms);
  }
}
@keyframes mk-card-in {
  from { opacity: 0; transform: translateY(16px); }
  to   { opacity: 1; transform: none; }
}

/* ==========================================================================
   08 / HERO FILM
   The campaign hero accepts a looping muted film behind the existing copy
   layers. The still image stays as the poster so there is no layout shift and
   no blank frame if the film is blocked or reduced motion is requested.
   ========================================================================== */
.campaign-hero-film {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  z-index: 0;
  opacity: 0;
  transition: opacity .9s var(--mk-ease-out);
}
.campaign-hero-film.is-ready { opacity: 1; }

@media (prefers-reduced-motion: reduce) {
  .campaign-hero-film { display: none; }
}

/* ==========================================================================
   09 / REVEAL — upgrade to scroll-driven where supported
   The existing .reveal / IntersectionObserver path stays as the fallback.
   ========================================================================== */
@supports (animation-timeline: view()) {
  @media (prefers-reduced-motion: no-preference) {
    html[data-mk-sd="on"] body[data-page="home"] .reveal:not(.in) {
      animation: mk-reveal linear both;
      animation-timeline: view();
      animation-range: entry 8% entry 62%;
    }
  }
}
@keyframes mk-reveal {
  from { opacity: 0; transform: translateY(22px); }
  to   { opacity: 1; transform: none; }
}

/* ==========================================================================
   10 / GLOBAL REDUCED-MOTION BACKSTOP
   ========================================================================== */
@media (prefers-reduced-motion: reduce) {
  .product-anatomy-media,
  .product-anatomy-list > li,
  .pcard[data-mk-enter],
  .object-card {
    animation: none !important;
    transition: none !important;
    opacity: 1 !important;
    transform: none !important;
  }
}

/* ==========================================================================
   11 / KX — MOTION PRIMITIVES FOR THE KINETIC-LAB SECTION PORT
   Interaction vocabulary lifted from the Stitch reference pages. Ambient loops
   are opacity-only so nothing here can read as a device emitting light or an
   output being produced (COMPLIANCE.md §5); they annotate UI liveness only.
   ========================================================================== */

/* --- ambient status loops -------------------------------------------------- */
@media (prefers-reduced-motion: no-preference) {
  .kx-pulse { animation: kx-pulse 2s cubic-bezier(.4, 0, .6, 1) infinite; }
}
@keyframes kx-pulse { 0%, 100% { opacity: 1; } 50% { opacity: .45; } }
/* --- media zoom (Ken-Burns on hover) --------------------------------------- */
.kx-zoom-media { overflow: hidden; }
.kx-zoom-media img { transition: transform .7s var(--mk-ease-out); will-change: transform; }
@media (prefers-reduced-motion: no-preference) and (hover: hover) {
  .kx-zoom-media:hover img,
  .kx-zoom-host:hover .kx-zoom-media img { transform: scale(1.06); }
}

/* --- step machine (join wizard) -------------------------------------------
   Panels fade+rise in. The site keeps its own [hidden] contract; this layer
   only animates the transition between visible panels.                     */
.kx-step { transition: opacity .5s cubic-bezier(.4, 0, .2, 1), transform .5s cubic-bezier(.4, 0, .2, 1); }
.kx-step[data-kx-enter] { opacity: 0; transform: translateY(28px); }

/* --- reduced-motion backstop ----------------------------------------------- */
@media (prefers-reduced-motion: reduce) {
  .kx-pulse,
  .kx-zoom-media img,
  .kx-step {
    animation: none !important;
    transition: none !important;
    transform: none !important;
  }
  .kx-step[data-kx-enter] { opacity: 1; }
}
