/* ============================================================================
   ChromaLayer — motion layer
   ----------------------------------------------------------------------------
   Loaded after home.css. Everything here is presentation-only: strip this file
   and motion.js out and the site still renders correctly, just static.

   The system is four things:
     1. Tokens      — one set of durations and curves, used everywhere
     2. Reveal      — scroll-triggered entrances, with variants per section
     3. Ambient     — slow light movement behind the hero
     4. Micro       — hover / press / drag feedback

   Rules the whole file follows:
     - animate transform, opacity, filter, clip-path. Nothing else.
     - no continuous loop costs more than two composited layers
     - every ambient loop and every parallax stops under reduced motion
   ========================================================================== */

:root {
  /* Durations. Reveals stay under ~700ms so reading is never gated on motion. */
  --m-press:   110ms;
  --m-micro:   180ms;
  --m-hover:   240ms;
  --m-element: 480ms;
  --m-reveal:  660ms;

  /* Curves. --m-out is the house curve: a firm, decisive settle with no
     overshoot. --m-spring adds a trace of overshoot, used only on press
     release and the compare grip, where a physical feel is the point. */
  --m-out:      cubic-bezier(.16, 1, .3, 1);
  --m-out-soft: cubic-bezier(.22, .61, .36, 1);
  --m-inout:    cubic-bezier(.65, 0, .35, 1);
  --m-spring:   cubic-bezier(.34, 1.32, .64, 1);

  /* Stagger step for grouped children. */
  --m-stagger: 68ms;
}

/* ---------------------------------------------------------------- 1. REVEAL */
/*
   Replaces the single fade-and-rise that every block used to share. Each
   variant belongs to a kind of content:
     rise    body copy, list items          — the default
     mask    headings, images               — wipes up from a clipped edge
     lift    cards, product surfaces        — settles down onto the page
     slide   feature groups                 — direction carries meaning
     fade    dense blocks (FAQ, footer)     — no movement, just presence
*/

/* Gated on .has-motion, which the head script sets and a watchdog removes if
   motion.js never reports in. Without the gate, a failed or throwing script
   would leave every section on the page at opacity 0 — the motion layer must
   not be able to take the content down with it. */
/*
   Reveals are ANIMATIONS, not transitions. A `transition` shorthand here would
   replace whatever transition the component already declares, and several of
   these elements are cards that also respond to hover — .cl-step, .cl-note,
   .cl-compat__col all silently lost their border/shadow hover feedback to it.
   An animation is a separate cascade origin, so the two never collide, and
   fill-mode holds the final frame without needing a second rule to win a
   specificity fight against the hidden state.
*/
html.has-motion .cl-page [data-anim] { opacity: 0; }

.cl-page [data-anim].is-in {
  animation-duration: var(--m-reveal);
  animation-timing-function: var(--m-out);
  animation-delay: var(--m-delay, 0ms);
  animation-fill-mode: both;
}

.cl-page [data-anim="rise"].is-in    { animation-name: m-rise; }
.cl-page [data-anim="fade"].is-in    { animation-name: m-fade; }
.cl-page [data-anim="lift"].is-in    { animation-name: m-lift; }
.cl-page [data-anim="slide-l"].is-in { animation-name: m-slide-l; }
.cl-page [data-anim="slide-r"].is-in { animation-name: m-slide-r; }
.cl-page [data-anim="mask"].is-in    { animation-name: m-mask; }
.cl-page [data-anim="focus"].is-in   { animation-name: m-focus; }

@keyframes m-rise    { from { opacity: 0; transform: translate3d(0, 14px, 0); }            to { opacity: 1; transform: none; } }
@keyframes m-fade    { from { opacity: 0; }                                                to { opacity: 1; } }
@keyframes m-lift    { from { opacity: 0; transform: translate3d(0, 22px, 0) scale(.982); } to { opacity: 1; transform: none; } }
@keyframes m-slide-l { from { opacity: 0; transform: translate3d(-20px, 0, 0); }           to { opacity: 1; transform: none; } }
@keyframes m-slide-r { from { opacity: 0; transform: translate3d(20px, 0, 0); }            to { opacity: 1; transform: none; } }
/* A wipe rather than a move: the element is already in place and is uncovered
   from the bottom edge. Reads as precision rather than arrival. */
@keyframes m-mask    { from { opacity: 0; clip-path: inset(0 0 100% 0); transform: translate3d(0, 8px, 0); }
                       to   { opacity: 1; clip-path: inset(0 0 0 0);    transform: none; } }
/* Blur is the most expensive property here, so it is used on one surface. */
@keyframes m-focus   { from { opacity: 0; transform: translate3d(0, 16px, 0) scale(.99); filter: blur(7px); }
                       to   { opacity: 1; transform: none; filter: none; } }

/* Children of a staggered container get their index from motion.js. */
.cl-page [data-stagger] > * { --m-delay: calc(var(--m-i, 0) * var(--m-stagger)); }

/* ------------------------------------------------------------- 2. LIGHT SWEEP */
/*
   One pass of light across a surface as it arrives. Used on exactly four
   things: the hero product frame, the app screenshots, the comparison, and
   the pricing card. It runs once and is then removed from the DOM's concern.
*/
.cl-sweep { position: relative; overflow: hidden; }
.cl-sweep::after {
  content: "";
  position: absolute; inset: 0;
  border-radius: inherit;
  pointer-events: none;
  /* Above the comparison's own stacking (tags 3, divider 3, range 4) so the
     light passes over the whole surface. pointer-events:none keeps the
     divider fully draggable underneath it. */
  z-index: 6;
  opacity: 0;
  background: linear-gradient(
    100deg,
    transparent 38%,
    rgba(255, 255, 255, .10) 47%,
    rgba(255, 255, 255, .18) 50%,
    rgba(255, 255, 255, .10) 53%,
    transparent 62%
  );
  transform: translate3d(-120%, 0, 0);
}
.cl-sweep.is-in::after { animation: m-sweep 1150ms var(--m-out-soft) 220ms 1; }
@keyframes m-sweep {
  0%   { opacity: 0; transform: translate3d(-120%, 0, 0); }
  12%  { opacity: 1; }
  88%  { opacity: 1; }
  100% { opacity: 0; transform: translate3d(120%, 0, 0); }
}

/* ----------------------------------------------------------------- 3. AMBIENT */
/*
   Two very slow light fields behind the hero, plus a static fine grid. The
   fields are pseudo-elements moved by transform only, so the whole effect is
   two composited layers and no per-frame JavaScript.
*/
.cl-aura {
  position: absolute; inset: -25% -10% auto -10%; height: 780px;
  pointer-events: none; z-index: 0;
  contain: strict;
}
.cl-aura::before, .cl-aura::after {
  content: ""; position: absolute; border-radius: 50%;
  filter: blur(20px);
  will-change: transform;
}
.cl-aura::before {
  width: 58%; height: 62%; top: 2%; left: 8%;
  background: radial-gradient(closest-side, var(--cl-glow), transparent 72%);
  animation: m-drift-a 44s var(--m-inout) infinite alternate;
}
.cl-aura::after {
  width: 46%; height: 54%; top: 12%; right: 6%;
  background: radial-gradient(closest-side, color-mix(in srgb, var(--cl-violet) 20%, transparent), transparent 72%);
  animation: m-drift-b 58s var(--m-inout) infinite alternate;
}
@keyframes m-drift-a {
  from { transform: translate3d(-4%, 0, 0) scale(1); }
  to   { transform: translate3d(7%, 5%, 0) scale(1.14); }
}
@keyframes m-drift-b {
  from { transform: translate3d(5%, 3%, 0) scale(1.08); }
  to   { transform: translate3d(-6%, -4%, 0) scale(.94); }
}

/* Fine engineering grid, faded out at the edges so it never reads as a border. */
.cl-hero::after {
  content: ""; position: absolute; inset: 0; z-index: 0; pointer-events: none;
  background-image:
    linear-gradient(to right, var(--cl-line) 1px, transparent 1px),
    linear-gradient(to bottom, var(--cl-line) 1px, transparent 1px);
  background-size: 72px 72px;
  opacity: .34;
  -webkit-mask-image: radial-gradient(120% 80% at 50% 0%, #000 0%, transparent 72%);
          mask-image: radial-gradient(120% 80% at 50% 0%, #000 0%, transparent 72%);
}

/* ------------------------------------------------------- 4. HERO LOAD SEQUENCE */
/*
   Runs on load, not on scroll — the hero is already in view. Total length is
   ~740ms to the product frame landing, so the app is on screen almost at once.
*/
html.has-motion .cl-hero [data-hero] { opacity: 0; }
.cl-hero.is-ready [data-hero] {
  animation: m-hero-in var(--m-element) var(--m-out) both;
  animation-delay: calc(var(--m-hero-i, 0) * 80ms);
}
@keyframes m-hero-in {
  from { opacity: 0; transform: translate3d(0, 12px, 0); }
  to   { opacity: 1; transform: none; }
}

/* The headline gets the wipe instead, so it resolves rather than travels. */
.cl-hero.is-ready [data-hero="1"] {
  animation: m-hero-head 620ms var(--m-out) both;
  animation-delay: 80ms;
}
@keyframes m-hero-head {
  from { opacity: 0; clip-path: inset(0 0 100% 0); transform: translate3d(0, 10px, 0); }
  to   { opacity: 1; clip-path: inset(0 0 0 0);   transform: none; }
}

/* The product frame arrives last and from slightly further back. */
.cl-hero.is-ready [data-hero="5"] {
  animation: m-hero-product 760ms var(--m-out) both;
  animation-delay: 300ms;
}
@keyframes m-hero-product {
  from { opacity: 0; transform: translate3d(0, 26px, 0) scale(.965); }
  to   { opacity: 1; transform: none; }
}

/* ---------------------------------------------------- 5. POINTER PARALLAX */
/*
   motion.js writes --m-px / --m-py in the range -1..1. Applied only where it
   adds depth to a product surface, and only for a fine pointer.
*/
@media (hover: hover) and (pointer: fine) {
  .cl-tilt {
    transform:
      perspective(1400px)
      rotateX(calc(var(--m-py, 0) * -1.1deg))
      rotateY(calc(var(--m-px, 0) * 1.5deg))
      translate3d(calc(var(--m-px, 0) * 5px), calc(var(--m-py, 0) * 4px), 0);
    transition: transform 420ms var(--m-out-soft);
  }
  .cl-tilt.is-tracking { transition: none; }
}

/* A soft highlight that follows the cursor across a primary button. */
@media (hover: hover) and (pointer: fine) {
  .cl-btn--primary { position: relative; overflow: hidden; isolation: isolate; }
  .cl-btn--primary::before {
    content: ""; position: absolute; inset: 0; z-index: -1;
    opacity: 0; transition: opacity var(--m-hover) var(--m-out);
    background: radial-gradient(
      120px circle at var(--m-mx, 50%) var(--m-my, 50%),
      rgba(255, 255, 255, .28), transparent 68%);
  }
  .cl-btn--primary:hover::before { opacity: 1; }
}

/* ------------------------------------------------------------------ 6. MICRO */

/* Buttons: lift and settle. Press is a real displacement, not a scale toy. */
.cl-btn {
  transition:
    transform var(--m-micro) var(--m-out),
    background-color var(--m-hover) var(--m-out),
    border-color var(--m-hover) var(--m-out),
    box-shadow var(--m-hover) var(--m-out),
    color var(--m-hover) var(--m-out);
}
.cl-btn--primary:hover, .cl-btn--ghost:hover { transform: translate3d(0, -1px, 0); }
.cl-btn:active { transform: translate3d(0, 1px, 0); transition-duration: var(--m-press); }

/* Arrow nudge on the buy CTA. */
.cl-btn__arrow { transition: transform var(--m-hover) var(--m-out); }
.cl-btn:hover .cl-btn__arrow { transform: translate3d(3px, 0, 0); }

/* Nav: an indicator that grows from the centre, and a persistent one for the
   section you are actually in. */
.cl-nav__links a { position: relative; }
.cl-nav__links a::after {
  content: ""; position: absolute; left: 11px; right: 11px; bottom: 3px; height: 1.5px;
  border-radius: 2px; background: var(--cl-brand);
  transform: scaleX(0); transform-origin: 50% 50%;
  transition: transform var(--m-hover) var(--m-out);
}
.cl-nav__links a:hover::after { transform: scaleX(1); }
.cl-nav__links a[aria-current="true"]::after { transform: scaleX(1); opacity: .55; }

/* Header compaction on scroll. */
.cl-nav { transition: height var(--m-hover) var(--m-out); }
.cl-header.is-stuck .cl-nav { height: 56px; }
.cl-brand img { transition: transform var(--m-hover) var(--m-out); }
.cl-header.is-stuck .cl-brand img { transform: scale(.92); }

/* Mobile drawer: a real open, not a display flip. Scoped to the breakpoint
   where the drawer exists — applying `display: grid` unconditionally put an
   expanded, invisible drawer inside the desktop header. */
@media (max-width: 1080px) {
  .cl-drawer {
    display: grid; grid-template-rows: 0fr;
    transition: grid-template-rows var(--m-element) var(--m-out),
                opacity var(--m-hover) var(--m-out);
    opacity: 0;
  }
  .cl-drawer__inner { overflow: hidden; min-height: 0; }
  .cl-drawer[data-open="true"] { grid-template-rows: 1fr; opacity: 1; }
  .cl-drawer[data-open="false"] { pointer-events: none; }
}
.cl-nav__toggle svg { transition: transform var(--m-hover) var(--m-spring); }
.cl-nav__toggle[aria-expanded="true"] .cl-x { transform: rotate(90deg); }

/* Cards: a firmer, quicker lift than the old 300ms. */
.cl-step, .cl-shot, .cl-note {
  transition: transform var(--m-hover) var(--m-out),
              border-color var(--m-hover) var(--m-out),
              box-shadow var(--m-hover) var(--m-out);
}
.cl-step:hover { transform: translate3d(0, -3px, 0); }
.cl-shot:hover { transform: translate3d(0, -2px, 0); box-shadow: var(--cl-shadow-lg); }

/* Feature rows illuminate their icon rather than moving. */
.cl-feat h4 svg { transition: transform var(--m-hover) var(--m-spring), filter var(--m-hover) var(--m-out); }
.cl-feat:hover h4 svg { transform: scale(1.12); filter: drop-shadow(0 0 6px var(--cl-glow)); }

/* Presets and chips get a quick, tactile state change. */
.cl-preset, .cl-chip {
  transition: transform var(--m-micro) var(--m-out),
              background-color var(--m-hover) var(--m-out),
              border-color var(--m-hover) var(--m-out),
              color var(--m-hover) var(--m-out);
}
.cl-preset:active, .cl-chip:active { transform: scale(.97); }
.cl-preset svg { transition: transform var(--m-hover) var(--m-spring); }
.cl-preset:hover svg { transform: scale(1.14); }

/* FAQ marker. */
.cl-q > summary { transition: color var(--m-hover) var(--m-out); }

/* Ticker. Two identical sequences translated by exactly half the track width,
   so the loop point is seamless. Linear easing — any curve would read as the
   belt speeding up and slowing down. */
.cl-ticker__track { animation: m-ticker 46s linear infinite; }
.cl-ticker:hover .cl-ticker__track { animation-play-state: paused; }
@keyframes m-ticker {
  from { transform: translate3d(0, 0, 0); }
  to   { transform: translate3d(-50%, 0, 0); }
}

/* The live indicator. The one continuous loop on the page, and it earns its
   place: it marks the preview as actually live. */
.cl-live::before { animation: m-live 2.6s var(--m-inout) infinite; }
@keyframes m-live {
  0%, 100% { opacity: 1;  transform: scale(1); }
  50%      { opacity: .45; transform: scale(.82); }
}

/* --------------------------------------------------------- 7. COMPARE SLIDER */

/* Grip: grows on hover, compresses on grab. */
.cl-compare__grip {
  transition: transform var(--m-hover) var(--m-spring), box-shadow var(--m-hover) var(--m-out);
}
.cl-compare:hover .cl-compare__grip { transform: translate(50%, -50%) scale(1.08); }
.cl-compare.is-dragging .cl-compare__grip { transform: translate(50%, -50%) scale(.94); }
.cl-compare__divider::before { transition: box-shadow var(--m-hover) var(--m-out); }
.cl-compare.is-dragging .cl-compare__divider::before { box-shadow: 0 0 0 1px rgba(0,0,0,.28), 0 0 16px 2px var(--cl-glow); }

/* The intro glide is driven per-frame from motion.js, not by a transition:
   both the divider transform and the after-layer clip-path resolve through
   `var(--_split)`, and a transition across a custom-property change never
   committed the intermediate values. `.is-intro` is kept purely as a state
   hook — adding a transition here would fight the tween. */
.cl-compare__tag {
  transition: opacity var(--m-element) var(--m-out) 260ms,
              transform var(--m-element) var(--m-out) 260ms;
}
.cl-compare:not(.is-in) .cl-compare__tag { opacity: 0; transform: translate3d(0, -6px, 0); }

/* ------------------------------------------------------------- 8. STUDIO DEMO */

/* Value readouts tick over rather than snapping. */
.cl-ctrl__val output { transition: color var(--m-micro) var(--m-out); }
.cl-panel.is-tweening .cl-ctrl__val output { color: var(--cl-ink); }

/* The row you are touching lifts out of the stack very slightly. */
.cl-ctrl {
  transition: border-color var(--m-hover) var(--m-out),
              background-color var(--m-hover) var(--m-out);
}
.cl-ctrl:focus-within { border-color: var(--cl-brand-line); background: var(--cl-surface); }
.cl-ctrl:focus-within .cl-ctrl__ico { color: var(--cl-brand); border-color: var(--cl-brand-line); }
.cl-ctrl__ico { transition: color var(--m-hover) var(--m-out), border-color var(--m-hover) var(--m-out); }

.cl-step-btn { transition: background-color var(--m-micro) var(--m-out), transform var(--m-press) var(--m-out); }
.cl-step-btn:active { transform: scale(.88); }

/* ------------------------------------------------------------ 9. TECHNICAL */

/* A hairline that draws itself across the spec strip as it arrives. */
.cl-specs { position: relative; }
.cl-specs::before {
  content: ""; position: absolute; top: -1px; left: 0; right: 0; height: 1px;
  background: linear-gradient(90deg, transparent, var(--cl-brand), transparent);
  transform: scaleX(0); transform-origin: 0 50%;
  transition: transform 900ms var(--m-out) 180ms;
}
.cl-specs.is-in::before { transform: scaleX(1); }
.cl-spec b { font-variant-numeric: tabular-nums; }

/* ------------------------------------------------------------- 10. PRICING */

.cl-price__amount b { display: inline-block; }
.cl-price.is-in .cl-price__amount b { animation: m-price 700ms var(--m-out) 200ms both; }
@keyframes m-price {
  from { opacity: 0; clip-path: inset(0 0 100% 0); transform: translate3d(0, 14px, 0); }
  to   { opacity: 1; clip-path: inset(0 0 0 0);    transform: none; }
}

/* Border illumination on the buy card's CTA, rather than a pulse. */
.cl-price .cl-btn--primary:hover {
  box-shadow: 0 2px 4px rgba(0,0,0,.2), 0 12px 34px var(--cl-glow), 0 0 0 1px var(--cl-brand-line);
}

/* Motion forced on for all environments */

/* On true touch-only screens (phones/tablets without hover), tone down depth effects. */
@media (hover: none) and (pointer: coarse) {
  .cl-tilt { transform: none !important; }
  .cl-aura::after { display: none; }
  .cl-hero::after { opacity: .2; }
}
