/* ==========================================================================
   AXIS PREMIUM DESIGN SYSTEM — motion.css
   --------------------------------------------------------------------------
   Library-free motion. Both Stripe and Linear ship ZERO animation libraries
   — that is the evidence-based standard. Every pattern here is:
     (a) a CSS transition/keyframe on the tokenized speeds + easings, or
     (b) a CSS custom property written by axis-motion.js (~5KB vanilla).
   No GSAP, no framer-motion, no Lenis, no CDN. All motion gated behind
   html.js (JS present) and prefers-reduced-motion.

   Reveal states only hide content when html.js is set, so a no-JS visitor
   always gets a fully readable page.
   ========================================================================== */

/* ==========================================================================
   1. REVEAL ENGINE (IntersectionObserver, driven by axis-motion.js)
   Variants:
     blur-rise   — Linear's signature: opacity 0 + blur(10px) + translateY(20%)
                   → clear on --ease-rise. For headlines and hero statements.
     fade-rise   — opacity 0 + translateY(24px). For cards, rows, images.
     scale-settle— opacity 0 + scale(.965). For framed visuals, mockups.
     clip-rise   — vertical wipe via clip-path (our original: curtain-lift
                   from the bottom). For long display statements.
   Use .reveal-guard on blur-rise display text whose parent clips overflow
   (the 30px padding/margin guard — Linear's "precision" trick).
   ========================================================================== */
html.js [data-reveal] {
  opacity: 0;
  transition:
    opacity var(--speed-slow) var(--ease-rise),
    transform var(--speed-slow) var(--ease-rise),
    filter var(--speed-slow) var(--ease-rise),
    clip-path var(--speed-slow) var(--ease-slip);
  will-change: opacity, transform, filter;
}

html.js [data-reveal="blur-rise"] {
  filter: blur(var(--reveal-blur));
  transform: translateY(var(--reveal-distance));
}

html.js [data-reveal="fade-rise"] { transform: translateY(24px); }

html.js [data-reveal="scale-settle"] { transform: scale(.965) translateY(10px); }

html.js [data-reveal="clip-rise"] {
  opacity: 1;                     /* wipe, not fade */
  transform: none;
  clip-path: inset(0 0 100% 0);
}

html.js [data-reveal].is-revealed {
  opacity: 1;
  filter: none;
  transform: none;
  clip-path: inset(0 0 0% 0);
  transition-delay: var(--reveal-delay, 0ms);
}

/* Blur clipping guard — pad the element so blur(10px) + translateY(20%)
   never clips against an overflow-hidden parent. */
.reveal-guard {
  padding: var(--reveal-guard);
  margin: calc(var(--reveal-guard) * -1);
}

/* Stagger: axis-motion.js writes --i on each direct child of [data-stagger];
   --reveal-delay inherits into the child's own reveal. */
[data-stagger] > * { --reveal-delay: calc(var(--i, 0) * var(--stagger, var(--stagger-2))); }

/* ==========================================================================
   2. SCROLL-PROGRESS CONSUMERS (axis-motion.js writes --progress / --page-progress)
   Scroll drives geometry, not just opacity (Locomotive evidence).
   ========================================================================== */
[data-progress] { --progress: 0; }

/* Horizontal hairline progress across a section */
.axis-progress-rail {
  transform: scaleX(var(--progress, 0));
  transform-origin: 0 50%;
  background: var(--sig-500);
  height: 1px;
  width: 100%;
}

/* Vertical progress (fixed page rail) */
.axis-progress-vertical {
  transform: scaleY(var(--progress, 0));
  transform-origin: 50% 0;
}

/* Curtain-lift wipe bound to scroll progress */
[data-clip="wipe-up"] {
  clip-path: inset(0 0 calc((1 - var(--progress, 0)) * 100%) 0);
}

/* Parallax layers — axis-motion.js writes --shift (px offset of the element
   center from viewport center); --parallax-amount sets depth. Apply to
   WRAPPER layers only — never combine with [data-reveal] on the same
   element (transform conflict). */
[data-parallax] {
  transform: translate3d(0, calc(var(--shift, 0px) * var(--parallax-amount, .2)), 0);
  will-change: transform;
}

/* ==========================================================================
   3. EDGE-HIGHLIGHT CARDS (Linear's signature "expensive" detail,
   re-implemented originally)
   Their mechanism: an ellipse mask that follows the pointer. Ours:
   (a) an inner lamp — a soft radial spotlight at --mx/--my;
   (b) a border signal — a 1px accent border whose ALPHA rises as the
       pointer approaches any edge (--edge-int, computed by axis-motion.js).
   Same premium feel, different mechanism, ~12 lines of CSS.
   ========================================================================== */
[data-edge] {
  position: relative;
  border: 1px solid var(--border-thin);
  transition: border-color var(--speed-fast) var(--ease-settle),
              background var(--speed-fast) var(--ease-settle),
              box-shadow var(--speed-fast) var(--ease-settle);
}

[data-edge] > * { position: relative; z-index: 1; }

/* inner lamp */
[data-edge]::before {
  content: "";
  position: absolute;
  inset: 0;
  border-radius: inherit;
  pointer-events: none;
  z-index: 0;
  opacity: 0;
  transition: opacity var(--speed-regular) var(--ease-settle);
  background: radial-gradient(320px circle at var(--mx, 50%) var(--my, 50%),
              var(--edge-lamp), transparent 70%);
}

/* border signal — proximity-driven alpha */
[data-edge]::after {
  content: "";
  position: absolute;
  inset: 0;
  border-radius: inherit;
  pointer-events: none;
  z-index: 2;
  border: 1px solid var(--sig-500);
  opacity: calc(var(--edge-int, 0) * .55);
  transition: opacity var(--speed-quick) var(--ease-leave);
}

@media (hover: hover) and (pointer: fine) {
  [data-edge]:hover::before { opacity: 1; }
  [data-edge]:hover { box-shadow: var(--shadow-medium); }
}

/* ==========================================================================
   4. PILL CTA — dot-wipe ("iris") hover
   Lusion evidence: a dot inside the pill scales into a full color wipe.
   Ours: the iris starts at the label's left (18%), grows radially to fill,
   text rides above it. One ::after, one transition.
   ========================================================================== */
.pill {
  position: relative;
  isolation: isolate;
  overflow: hidden;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: .55rem;
  border-radius: var(--radius-pill);
  border: 1px solid transparent;
  font-family: var(--font-sans);
  font-weight: var(--w-medium);
  font-size: var(--text-small);
  letter-spacing: -.006em;
  line-height: 1;
  white-space: nowrap;
  cursor: pointer;
  text-decoration: none;
  transition: background-color var(--speed-regular) var(--ease-settle),
              color var(--speed-regular) var(--ease-settle),
              border-color var(--speed-regular) var(--ease-settle),
              box-shadow var(--speed-regular) var(--ease-settle);
}

.pill > * { position: relative; z-index: 1; }
.pill-sm { height: var(--pill-sm); padding: 0 1.05rem; font-size: var(--text-mini); }
.pill-md { height: var(--pill-md); padding: 0 1.5rem; }
.pill-lg { height: var(--pill-lg); padding: 0 2rem; font-size: .9375rem; }

.pill-primary { background: var(--cta-bg); color: var(--cta-text); box-shadow: var(--shadow-stack); }
.pill-primary::after {
  content: "";
  position: absolute;
  left: 18%;
  top: 50%;
  width: 10px;
  height: 10px;
  margin: -5px 0 0 -5px;
  border-radius: var(--radius-circle);
  background: var(--cta-wipe);
  transform: scale(0);
  transition: transform var(--speed-slow) var(--ease-settle);
  z-index: 0;
}
.pill-primary:hover { background: var(--cta-hover); }
.pill-primary:active { transform: translateY(1px); }
@media (hover: hover) and (pointer: fine) {
  .pill-primary:hover::after { transform: scale(48); }
}

.pill-ghost {
  background: var(--cta-secondary-bg);
  border-color: var(--cta-secondary-border);
  color: var(--cta-secondary-text);
}
.pill-ghost:hover { border-color: var(--border-strong); background: var(--lift-1); }

/* ==========================================================================
   5. THIN-UNDERLINE LINK (Locomotive evidence: .05em thickness, .1em offset)
   Ours: a gradient underline that GROWS from the left on hover/focus —
   keeps the premium .05em hairline, adds Stripe-snap motion.
   ========================================================================== */
.u-link {
  background-image: linear-gradient(currentColor, currentColor);
  background-repeat: no-repeat;
  background-size: 0% 0.05em;
  background-position: 0 96%;
  text-decoration: none;
  padding-bottom: .12em;
  transition: background-size var(--speed-regular) var(--ease-settle);
}
.u-link:hover,
.u-link:focus-visible { background-size: 100% 0.05em; }
.u-link-static { background-size: 100% .05em; } /* already-underlined variant */

/* ==========================================================================
   6. MARQUEE — proof strip (Linear evidence: 30s linear infinite)
   axis-motion.js duplicates the track content to exactly two identical
   halves for a seamless -50% loop. Pauses on hover. Fades at both edges.
   ========================================================================== */
.marquee {
  overflow: hidden;
  -webkit-mask-image: linear-gradient(90deg, transparent, #000 12%, #000 88%, transparent);
  mask-image: linear-gradient(90deg, transparent, #000 12%, #000 88%, transparent);
}
.marquee-track {
  display: flex;
  align-items: center;
  gap: 3rem;
  width: max-content;
  animation: axis-marquee var(--marquee-duration, 30s) linear infinite;
  will-change: transform;
}
@keyframes axis-marquee { to { transform: translateX(-50%); } }
.marquee:hover .marquee-track { animation-play-state: paused; }

/* ==========================================================================
   7. PRELOADER — simplified odometer (Lusion evidence: rolling percentage
   over the transition layer). Ours: mono counter 000→100 over ~750ms with a
   hairline fill, then a curtain lift. Hidden without JS; skipped entirely
   under prefers-reduced-motion. One second maximum, once per load.
   ========================================================================== */
.preloader { display: none; }
html.js .preloader {
  display: flex;
  position: fixed;
  inset: 0;
  z-index: 9999;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 1.25rem;
  background: var(--bg-canvas);
  transition: transform .55s var(--ease-slip);
}
html.js.preloader-done .preloader { transform: translateY(-100%); }
.preloader-counter {
  font-family: var(--font-mono);
  font-size: clamp(1.75rem, 3.5vw, 2.75rem);
  font-weight: var(--w-regular);
  color: var(--text-1);
  font-variant-numeric: tabular-nums;
  letter-spacing: .04em;
}
.preloader-hairline {
  width: 220px;
  height: 1px;
  background: var(--border-solid);
  overflow: hidden;
}
.preloader-fill {
  display: block;
  width: 100%;
  height: 100%;
  background: var(--sig-500);
  transform: scaleX(0);
  transform-origin: 0 50%;
}
.preloader-word {
  font-family: var(--font-mono);
  font-size: var(--text-micro);
  letter-spacing: var(--label-ls);
  text-transform: uppercase;
  color: var(--text-4);
}

/* ==========================================================================
   8. HEADER — fixed, transparent at top, blur + hairline after scroll
   (Linear evidence: blur(20px), bg only after scroll, border-color
   transition on the quick speed).
   ========================================================================== */
.site-header {
  position: fixed;
  top: 0;
  left: 0;
  right: 0;
  z-index: 100;
  height: var(--header-height);
  display: flex;
  align-items: center;
  backdrop-filter: blur(var(--header-blur));
  -webkit-backdrop-filter: blur(var(--header-blur));
  background: transparent;
  border-bottom: 1px solid transparent;
  transition: background-color var(--speed-quick) var(--ease-settle),
              border-color var(--speed-quick) var(--ease-settle);
}
.site-header.is-scrolled {
  background: color-mix(in srgb, var(--bg-0) 82%, transparent);
  border-bottom-color: var(--border-thin);
}

/* ==========================================================================
   9. FIXED SCROLL RAIL (Lusion evidence: fixed right progress rail)
   ========================================================================== */
.scroll-rail {
  position: fixed;
  right: 12px;
  top: 50%;
  width: var(--rail-width);
  height: 20vh;
  transform: translateY(-50%);
  background: var(--border-thin);
  border-radius: var(--radius-pill);
  overflow: hidden;
  z-index: 90;
}
.scroll-rail-fill {
  display: block;
  width: 100%;
  height: 100%;
  background: var(--sig-500);
  transform: scaleY(var(--page-progress, 0));
  transform-origin: 50% 0;
}

/* ==========================================================================
   10. SCROLL CUE — restrained nudge (Lusion's two-keyframe discipline:
   one 3s loop, transform only)
   ========================================================================== */
@keyframes axis-nudge {
  0%, 100% { transform: translateY(0); opacity: .55; }
  50% { transform: translateY(5px); opacity: 1; }
}
.scroll-cue-arrow { animation: axis-nudge 3s cubic-bezier(.1, 0, .1, 1) infinite; }

/* ==========================================================================
   11. HONEST DEFAULTS — reduced motion + no hover + no JS
   ========================================================================== */
@media (prefers-reduced-motion: reduce) {
  html.js .preloader { display: none; }
  html.js [data-reveal] {
    opacity: 1 !important;
    filter: none !important;
    transform: none !important;
    clip-path: none !important;
    transition: none !important;
  }
  .marquee-track { animation: none; }
  .scroll-cue-arrow { animation: none; }
  [data-parallax] { transform: none !important; }
}

@media (hover: none) {
  [data-edge]::before,
  [data-edge]::after { display: none; }
}
