/* ==========================================================================
   KinRidge — Motion
   Every scroll- or pointer-driven effect lives here so the whole layer can be
   rolled back by removing one <link>. Tokens come from css/main.css.

   Contract
   --------
   .js-motion          set by an inline script in <head>; without it nothing
                       in this file hides content, so a JS failure can only
                       cost animation — never the page.
   [data-motion]       scroll-triggered variants, toggled by KR.motion (IO)
   [data-hero]         first-paint entrance, pure CSS animation so it plays
                       even if motion.js never loads
   prefers-reduced-motion  → everything jumps to its end state
   ========================================================================== */

/* --------------------------------------------------------------------------
   0. Reduced motion — declare first so later rules can be overridden by it
   -------------------------------------------------------------------------- */
@media (prefers-reduced-motion: reduce) {
  .js-motion [data-motion],
  .js-motion [data-hero],
  .js-motion .hero__title .line {
    opacity: 1 !important;
    transform: none !important;
    clip-path: none !important;
    filter: none !important;
    animation: none !important;
    transition: none !important;
  }
  .hero__media { animation: none !important; }
}

/* --------------------------------------------------------------------------
   1. Entrance primitives
   -------------------------------------------------------------------------- */

/* fade-rise — the workhorse for grids and lists.
   --i is the item index; the delay is capped at 5 steps so long lists never
   take longer than ~300ms to finish arriving. */
.js-motion [data-motion="fade-rise"] {
  opacity: 0;
  transform: translateY(var(--rise));
  transition: opacity var(--t-scene) var(--ease-soft),
              transform var(--t-scene) var(--ease-soft);
  transition-delay: calc(var(--i, 0) * var(--stagger));
}
.js-motion [data-motion="fade-rise"].is-in {
  opacity: 1;
  transform: none;
}

/* Blur is desktop-only: on a phone it costs a compositor layer per tile.
   Note the explicit delay on every property — using the `transition` shorthand
   here would reset the stagger that the rule above just set. */
@media (hover: hover) and (min-width: 861px) {
  .js-motion [data-motion="fade-rise"][data-blur] {
    filter: blur(8px);
    transition:
      opacity var(--t-scene) var(--ease-soft) calc(var(--i, 0) * var(--stagger)),
      transform var(--t-scene) var(--ease-soft) calc(var(--i, 0) * var(--stagger)),
      filter var(--t-scene) var(--ease-soft) calc(var(--i, 0) * var(--stagger));
  }
  .js-motion [data-motion="fade-rise"][data-blur].is-in { filter: blur(0); }
}

/* line — masked line reveal for display type (hero + statement) */
.js-motion [data-motion="line"] {
  display: block;
  clip-path: inset(-10% 0 100% 0);
  transform: translateY(0.35em);
  transition: clip-path var(--t-scene) var(--ease-soft),
              transform var(--t-scene) var(--ease-soft);
  transition-delay: var(--d, 0ms);
}
.js-motion [data-motion="line"].is-in {
  clip-path: inset(-10% 0 -10% 0);
  transform: none;
}

/* line-grow — 1px rules that draw themselves from the centre */
.js-motion [data-motion="line-grow"] {
  transform: scaleX(0);
  transform-origin: center;
  transition: transform var(--t-scene) var(--ease-soft);
}
.js-motion [data-motion="line-grow"].is-in { transform: scaleX(1); }

/* --------------------------------------------------------------------------
   2. Hero — first paint (I1) + scroll drift (I2)
   -------------------------------------------------------------------------- */

.js-motion .hero [data-hero] {
  animation: krHeroIn var(--t-scene) var(--ease-soft) both;
  animation-delay: var(--d, 0ms);
}
@keyframes krHeroIn {
  from { opacity: 0; transform: translateY(12px); }
  to   { opacity: 1; transform: none; }
}

/* Title lines mask upward. Kept separate from [data-hero] so the clip and the
   travel can compose without fighting a single keyframe list. */
.js-motion .hero__title .line {
  clip-path: inset(-12% 0 100% 0);
  transform: translateY(0.32em);
  animation: krLineUp var(--t-scene) var(--ease-soft) both;
  animation-delay: var(--d, 0ms);
}
@keyframes krLineUp {
  from { clip-path: inset(-12% 0 100% 0); transform: translateY(0.32em); }
  to   { clip-path: inset(-12% 0 -12% 0); transform: none; }
}

/* The media box is inset beyond the section so a 40px drift never exposes an
   edge. Push-in on the <img>, drift on the wrapper — two elements, two jobs. */
.hero__media { inset: -8% 0; }
.js-motion .hero__media img {
  transform: scale(1.04);
  transition: transform var(--t-hero) var(--ease-soft);
}
.js-motion body.is-ready .hero__media img { transform: scale(1); }

@supports (animation-timeline: scroll()) {
  @media (prefers-reduced-motion: no-preference) {
    .js-motion .hero__media {
      animation: krHeroDrift linear both;
      animation-timeline: scroll(root block);
      animation-range: 0 90vh;
    }
  }
}
@keyframes krHeroDrift {
  from { transform: translateY(0); }
  to   { transform: translateY(40px); }
}

/* --------------------------------------------------------------------------
   3. Buttons — press (P0), magnetic glow (I6), copy feedback (C1)
   -------------------------------------------------------------------------- */
.btn,
.icon-btn,
.lang__btn,
.menu-toggle,
.faq__cat,
.accordion__btn {
  transition: background var(--t-fast) var(--ease),
              color var(--t-fast) var(--ease),
              border-color var(--t-fast) var(--ease),
              transform var(--t-fast) var(--spring);
}
.btn:active,
.icon-btn:active,
.lang__btn:active,
.faq__cat:active { transform: scale(0.97); }

/* Magnetic: JS writes --mx/--my (glow centre) and translate via --tx/--ty. */
.btn--magnetic {
  position: relative;
  isolation: isolate;
  transform: translate(var(--tx, 0), var(--ty, 0));
  transition: transform var(--t-comp) var(--ease),
              background var(--t-fast) var(--ease),
              color var(--t-fast) var(--ease),
              border-color var(--t-fast) var(--ease);
}
.btn--magnetic::before {
  content: "";
  position: absolute;
  inset: 0;
  border-radius: inherit;
  background: radial-gradient(circle 110px at var(--mx, 50%) var(--my, 50%),
              var(--glow, rgba(247, 245, 239, 0.16)), transparent 72%);
  opacity: 0;
  transition: opacity var(--t-comp) var(--ease);
  pointer-events: none;
  z-index: -1;
}
.btn--magnetic:hover::before { opacity: 1; }
/* Light-background buttons need a dark glow or it reads as haze. */
.btn--magnetic[data-glow="ink"] { --glow: rgba(28, 27, 24, 0.10); }

/* Copy button: icon swaps to a check, then back after 1.2s. */
.copy-btn {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 4px 0;
  font-size: var(--fs-eyebrow);
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--c-stone);
  transition: color var(--t-fast) var(--ease);
}
.copy-btn:hover { color: var(--c-ink); }
.copy-btn__box {
  position: relative;
  width: 15px;
  height: 15px;
  flex-shrink: 0;
}
.copy-btn__box svg {
  position: absolute;
  inset: 0;
  width: 15px;
  height: 15px;
  transition: opacity var(--t-state) var(--ease),
              transform var(--t-state) var(--spring);
}
.copy-btn__check {
  opacity: 0;
  transform: scale(0.5);
  color: var(--c-moss);
}
.copy-btn.is-done { color: var(--c-moss); }
.copy-btn.is-done .copy-btn__mail { opacity: 0; transform: scale(0.5); }
.copy-btn.is-done .copy-btn__check { opacity: 1; transform: none; }

/* --------------------------------------------------------------------------
   4. Cards — neighbour give-way (I3), pointer tilt (L1)
   -------------------------------------------------------------------------- */
.card-shift {
  transition: transform var(--t-comp) var(--ease),
              box-shadow var(--t-comp) var(--ease);
  will-change: transform;
}
@media (min-width: 961px) {
  /* Scoped under .js-motion so these beat [data-motion="fade-rise"].is-in,
     which also owns `transform` on the same element. */
  .js-motion .card-shift.is-hover { transform: translateY(calc(var(--lift) * -1)); }
  .js-motion .card-shift.is-near { transform: translateX(var(--give, 0px)); }
  /* Product imagery settles faster than the card travels — the image should
     feel responsive, the card should feel weighted. */
  .card-shift:hover .product-card__img,
  .card-shift:hover .cat-card__img { transform: scale(1.04); }
}
.card-shift:hover { z-index: 2; }

/* Tilt: pointer-driven, desktop only, capped at 2.5deg by JS. */
[data-tilt] {
  transition: transform var(--t-comp) var(--ease);
  transform-style: preserve-3d;
}
@media (hover: none) {
  [data-tilt] { transform: none !important; }
}

/* --------------------------------------------------------------------------
   5. Header — hide on scroll down (G1)
   -------------------------------------------------------------------------- */
.site-header {
  transition: transform var(--t-state) var(--ease),
              border-color var(--t-fast) var(--ease);
}
@media (prefers-reduced-motion: no-preference) {
  .site-header.is-hidden { transform: translateY(-100%); }
}

/* Language swap veil (G2) — hides the DOM rebuild behind an intentional beat */
body.is-swapping main,
body.is-swapping .site-footer {
  opacity: 0;
  transform: translateY(6px);
}
main,
.site-footer {
  transition: opacity 120ms linear, transform 120ms var(--ease);
}

/* --------------------------------------------------------------------------
   6. Footer top rule (I7)
   -------------------------------------------------------------------------- */
.site-footer { position: relative; }
.site-footer::before {
  content: "";
  position: absolute;
  left: 0;
  right: 0;
  top: 0;
  height: 1px;
  background: rgba(247, 245, 239, 0.24);
  transform: scaleX(0);
  transform-origin: center;
  transition: transform var(--t-scene) var(--ease-soft);
}
.site-footer.is-in::before { transform: scaleX(1); }

/* --------------------------------------------------------------------------
   7. Accordion — grid-template-rows instead of max-height (F1)
   Animating max-height from a measured pixel value reflows the page and makes
   long answers jump. 0fr → 1fr is one interpolatable value with no reflow.
   -------------------------------------------------------------------------- */
.accordion__panel {
  display: grid;
  grid-template-rows: 0fr;
  transition: grid-template-rows var(--t-med) var(--ease);
}
.accordion__panel > .accordion__panel-inner {
  overflow: hidden;
  min-height: 0;
}
.accordion__item.is-open .accordion__panel { grid-template-rows: 1fr; }

/* Category cross-fade (F2) — list fades out, rebuilds, fades in */
.faq__list {
  transition: opacity var(--t-fast) var(--ease);
}
.faq__list.is-swapping { opacity: 0; }

/* --------------------------------------------------------------------------
   8. Product detail — cross-fade gallery (D1), thumb indicator, lightbox (D2),
   sticky enquiry bar (D3), colour swatch (D4)
   -------------------------------------------------------------------------- */
.product-hero__media .media--main { position: relative; }
.product-hero__media .media--main .media__img {
  position: absolute;
  inset: 0;
  transition: opacity var(--t-state) var(--ease);
}
.product-hero__media .media--main .media__img.is-visible { opacity: 1; }
.product-hero__media .media--main .media__img:not(.is-visible) { opacity: 0; }

/* Thumbnails: the border becomes a sliding underline, so the active state
   reads as one object moving rather than three boxes blinking. */
.product-hero__thumbs { position: relative; padding-bottom: 12px; }
.product-hero__thumbs::before {
  content: "";
  position: absolute;
  left: 0;
  bottom: 0;
  width: calc((100% - var(--sp-2) * 2) / 3);
  height: 1px;
  background: var(--c-ink);
  transform: translateX(calc(var(--idx, 0) * (100% + var(--sp-2))));
  transition: transform var(--t-state) var(--ease);
}
.product-hero__thumb {
  border: 0 !important;
  opacity: 0.55;
  transition: opacity var(--t-state) var(--ease);
}
.product-hero__thumb.is-active { opacity: 1; }

/* Clickable crops open the lightbox */
.gallery-1plus4 .media,
.product-hero__media .media--main { cursor: zoom-in; }

.lightbox {
  position: fixed;
  inset: 0;
  z-index: 200;
  display: grid;
  place-items: center;
  padding: clamp(16px, 4vw, 48px);
}
.lightbox[hidden] { display: none; }
.lightbox__backdrop {
  position: absolute;
  inset: 0;
  background: rgba(28, 27, 24, 0.92);
  opacity: 0;
  transition: opacity var(--t-comp) var(--ease);
}
.lightbox.is-open .lightbox__backdrop { opacity: 1; }
.lightbox__img {
  position: relative;
  max-width: 92vw;
  max-height: 86vh;
  width: auto;
  height: auto;
  object-fit: contain;
  transition: opacity var(--t-comp) var(--ease);
}
.lightbox__close {
  position: absolute;
  top: clamp(12px, 3vw, 28px);
  right: clamp(12px, 3vw, 28px);
  width: 44px;
  height: 44px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  border-radius: 999px;
  color: var(--c-paper);
  background: rgba(247, 245, 239, 0.12);
  transition: background var(--t-fast) var(--ease), transform var(--t-fast) var(--spring);
}
.lightbox__close:hover { background: rgba(247, 245, 239, 0.24); }
.lightbox__close:active { transform: scale(0.94); }
.lightbox__close svg { width: 20px; height: 20px; }
body.is-locked { overflow: hidden; }

/* Sticky enquiry bar — the only motion on the site that changes conversion,
   so it gets the least decoration: it rises, it stays, it can be dismissed. */
.enquiry-bar {
  position: fixed;
  left: 0;
  right: 0;
  bottom: 0;
  z-index: 60;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--sp-4);
  min-height: 64px;
  padding: 10px clamp(16px, 4vw, 40px);
  background: var(--c-ink);
  color: var(--c-paper);
  transform: translateY(100%);
  transition: transform var(--t-med) var(--ease);
  box-shadow: 0 -1px 0 rgba(247, 245, 239, 0.12);
}
.enquiry-bar.is-in { transform: none; }
.enquiry-bar__name {
  font-family: var(--font-display);
  font-size: var(--fs-h5);
  letter-spacing: -0.01em;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.enquiry-bar__actions { display: flex; align-items: center; gap: var(--sp-3); }
.enquiry-bar .btn { color: var(--c-paper); border-color: rgba(247, 245, 239, 0.5); }
.enquiry-bar .btn:hover { background: var(--c-paper); color: var(--c-ink); }
.enquiry-bar__close {
  width: 36px;
  height: 36px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  border-radius: 999px;
  color: rgba(247, 245, 239, 0.8);
  transition: background var(--t-fast) var(--ease);
}
.enquiry-bar__close:hover { background: rgba(247, 245, 239, 0.12); }
.enquiry-bar__close svg { width: 16px; height: 16px; }
@media (max-width: 640px) {
  .enquiry-bar__name { font-size: 15px; }
  .enquiry-bar { min-height: 60px; }
}
/* Never let the bar sit on top of the footer */
body.has-enquiry-bar .site-footer { padding-bottom: 72px; }

/* Colour swatch (D4) — selectable, no fake image swaps */
.color-swatch {
  cursor: pointer;
  padding: 4px 8px 4px 4px;
  border-radius: 999px;
  transition: background var(--t-state) var(--ease);
}
.color-swatch:hover { background: rgba(28, 27, 24, 0.05); }
.color-swatch__dot {
  transition: transform var(--t-state) var(--spring);
}
.color-swatch.is-active .color-swatch__dot { transform: scale(1.3); }
.color-swatch.is-active { color: var(--c-ink); }
.color-swatch.is-active .color-swatch__dot {
  box-shadow: 0 0 0 2px var(--c-paper), 0 0 0 3px var(--c-ink);
}

/* --------------------------------------------------------------------------
   9. Wholesale process (W1) — rules draw, numbers come up to full strength
   -------------------------------------------------------------------------- */
.process__step {
  border-top-color: transparent;
  position: relative;
}
.process__step::after {
  content: "";
  position: absolute;
  left: 0;
  right: 0;
  top: -1px;
  height: 1px;
  background: var(--c-line);
  transform: scaleX(0);
  transform-origin: left;
  transition: transform var(--t-scene) var(--ease-soft);
  transition-delay: calc(var(--i, 0) * 120ms);
}
.process.is-in .process__step::after { transform: scaleX(1); }
.process__step::before {
  opacity: 0.55;
  transition: opacity var(--t-state) var(--ease);
  transition-delay: calc(var(--i, 0) * 120ms + 220ms);
}
.process.is-in .process__step::before { opacity: 1; }
