/* ==========================================================================
   DriveXos — motion layer

   Everything here is progressive enhancement. The `js` class is set by a tiny
   inline script in each page <head>, and removed again automatically if
   site.js fails to boot — so with broken or disabled JavaScript every page
   still renders fully visible, just without the animation.

   Load this AFTER site.css.
   ========================================================================== */

/* ---------- Keyframes ---------- */
@keyframes riseIn {
  from { opacity:0; transform:translateY(26px); }
  to   { opacity:1; transform:none; }
}
@keyframes riseInShot {
  from { opacity:0; transform:translateY(60px) scale(.97); }
  to   { opacity:1; transform:none; }
}
@keyframes orbDrift {
  0%,100% { transform:translate3d(0,0,0) scale(1); }
  33%     { transform:translate3d(3%,-2%,0) scale(1.06); }
  66%     { transform:translate3d(-2.5%,2%,0) scale(.96); }
}
@keyframes shimmer {
  from { transform:translateX(-120%) skewX(-18deg); }
  to   { transform:translateX(320%) skewX(-18deg); }
}
@keyframes pulseRing {
  0%   { box-shadow:0 0 0 0 rgba(79,124,255,.45); }
  70%  { box-shadow:0 0 0 16px rgba(79,124,255,0); }
  100% { box-shadow:0 0 0 0 rgba(79,124,255,0); }
}
@keyframes floatY {
  0%,100% { transform:translateY(0); }
  50%     { transform:translateY(-9px); }
}

/* ==========================================================================
   Scroll reveal — hidden up front, released by IntersectionObserver
   ========================================================================== */
.js .section-head,
.js .card,
.js .plan,
.js .dl-card,
.js .step,
.js .stat,
.js .split > div,
.js .faq details,
.js .faq > h2,
.js .panel,
.js .cta-band,
.js .table-scroll,
.js .section > .wrap > .notice,
.js .reveal {
  opacity:0;
  transform:translateY(24px);
  transition:opacity .72s var(--ease), transform .72s var(--ease);
  transition-delay:var(--d, 0ms);
  will-change:opacity, transform;
}
.js .is-in{ opacity:1 !important; transform:none !important; }

/* The two-column feature rows come in from their own side. */
.js .split > div:first-child{ transform:translateY(24px) translateX(-18px); }
.js .split > .split-media{ transform:translateY(24px) translateX(18px); }

/* ==========================================================================
   Hero / page-header entrance — CSS only, runs immediately on load
   ========================================================================== */
.js .hero > .wrap > *{ animation:riseIn .82s var(--ease) both; }
.js .hero > .wrap > .eyebrow  { animation-delay:.05s; }
.js .hero > .wrap > h1        { animation-delay:.14s; }
.js .hero > .wrap > .hero-sub { animation-delay:.24s; }
.js .hero > .wrap > .btn-row  { animation-delay:.33s; }
.js .hero > .wrap > .hero-note{ animation-delay:.41s; }
.js .hero > .wrap > .hero-meta{ animation-delay:.48s; }
.js .hero > .wrap > .hero-shot,
.js .hero > .wrap > .hero-stage{ animation:riseInShot 1.05s var(--ease) both; animation-delay:.56s; }

/* The glass readouts settle in after the screenshot, then breathe. */
.js .hero-chip{ animation:riseIn .7s var(--ease) both, floatY 6s var(--ease) 2.4s infinite; }
.js .hero-chip--a{ animation-delay:1.15s, 2.4s; }
.js .hero-chip--b{ animation-delay:1.3s, 3.2s; }

.js .page-head > .wrap > *{ animation:riseIn .7s var(--ease) both; }
.js .page-head > .wrap > .eyebrow{ animation-delay:.04s; }
.js .page-head > .wrap > h1{ animation-delay:.12s; }
.js .page-head > .wrap > p{ animation-delay:.2s; }

/* The ambient light behind the whole document drifts, very slowly. */
body::before{ animation:orbDrift 30s ease-in-out infinite; }
.cta-band::before{ animation:orbDrift 26s ease-in-out infinite reverse; }

/* ==========================================================================
   Rotating headline word

   The hero cycles through the things DriveXos actually finds. The container
   animates its width so the words after it slide rather than jump, and the
   word itself rises out and in with a short blur.
   ========================================================================== */
@keyframes wordOut {
  from { opacity:1; transform:none;                filter:blur(0); }
  to   { opacity:0; transform:translateY(-.42em);  filter:blur(5px); }
}
@keyframes wordIn {
  from { opacity:0; transform:translateY(.42em);   filter:blur(5px); }
  to   { opacity:1; transform:none;                filter:blur(0); }
}

.rotator{
  display:inline-flex; align-items:baseline; justify-content:flex-start;
  position:relative; overflow:hidden; vertical-align:bottom;
  max-width:100%;
}
/* No width transition on purpose. The box must clip vertically (overflow
   hidden) for the rise-in/out, so a width that animates independently of the
   text either leaves a gap before the following words or shaves the tail off
   the incoming one. Snapping the width at the exact moment the text swaps
   avoids both, and the fade carries the motion. */
.rotator-word{ display:inline-block; white-space:nowrap; will-change:opacity, transform, filter; }
.rotator-word.is-out{ animation:wordOut .34s var(--ease) forwards; }
.rotator-word.is-in { animation:wordIn  .44s var(--ease) forwards; }

/* The measuring clone never renders. */
.rotator-ghost{
  position:absolute; visibility:hidden; pointer-events:none;
  white-space:nowrap; left:0; top:0;
}

/* Cycling text is motion too — hold it still when asked. */
@media (prefers-reduced-motion:reduce){
  .rotator{ width:auto !important; transition:none !important; }
  .rotator-word{ animation:none !important; opacity:1 !important; transform:none !important; filter:none !important; }
}

/* ==========================================================================
   Header
   ========================================================================== */
.site-header{ transition:box-shadow .3s var(--ease), background .3s var(--ease); }
.site-header.is-scrolled{
  box-shadow:0 1px 0 rgba(255,255,255,.05), 0 14px 40px -24px rgba(0,0,0,1);
  background:rgba(7,11,20,.88);
}

/* Reading-progress bar, pinned to the very top. */
.scroll-progress{
  position:fixed; top:0; left:0; height:2.5px; width:100%; z-index:70;
  transform:scaleX(0); transform-origin:0 50%;
  background:var(--gradient); pointer-events:none;
  transition:transform .08s linear;
}

/* Nav links grow an underline on hover. */
.nav-links a{ position:relative; }
.nav-links a::after{
  content:''; position:absolute; left:14px; right:14px; bottom:4px; height:2px; border-radius:2px;
  background:var(--gradient); transform:scaleX(0); transform-origin:0 50%;
  transition:transform .26s var(--ease);
}
.nav-links a:hover::after{ transform:scaleX(1); }
.nav-links a.is-active::after{ display:none; }
@media (max-width:760px){ .nav-links a::after{ display:none; } }

/* ==========================================================================
   Buttons
   ========================================================================== */
.btn--primary{ position:relative; overflow:hidden; }
.btn--primary > *{ position:relative; z-index:1; }
.btn--primary::after{
  content:''; position:absolute; top:0; bottom:0; left:0; width:38%;
  background:linear-gradient(90deg, transparent, rgba(255,255,255,.34), transparent);
  transform:translateX(-120%) skewX(-18deg); pointer-events:none;
}
.btn--primary:hover::after{ animation:shimmer .85s var(--ease); }
.btn:active{ transform:translateY(1px) scale(.99); }

/* The hero's main call to action asks for attention — twice, then stops. */
.js .hero .btn--primary{ animation:pulseRing 2.6s var(--ease) 1.8s 2; }

/* ==========================================================================
   Cards
   ========================================================================== */
.card, .dl-card, .plan{ position:relative; }
.card-icon{ transition:transform .28s var(--ease), box-shadow .28s var(--ease); }
.card--hover:hover .card-icon{ transform:translateY(-2px) scale(1.07); box-shadow:0 12px 30px -10px rgba(88,120,255,.95); }
.card--hover::after{
  content:''; position:absolute; inset:0; border-radius:inherit; pointer-events:none;
  background:radial-gradient(70% 80% at 50% 0%, rgba(79,124,255,.10), transparent 72%);
  opacity:0; transition:opacity .3s var(--ease);
}
.card--hover:hover::after{ opacity:1; }
.dl-card:hover .dl-os{ animation:floatY 2.4s var(--ease) infinite; }

/* The featured plan sits slightly forward of the free one. */
.plan--featured{ transition:transform .3s var(--ease), box-shadow .3s var(--ease); }
@media (min-width:761px){
  .plan--featured{ transform:translateY(-10px); }
  .js .plan--featured.is-in{ transform:translateY(-10px) !important; }
  .plan--featured:hover{ transform:translateY(-14px) !important; }
}

/* Tick lists fill in line by line once their card arrives. */
.js .is-in .ticks li{ animation:riseIn .5s var(--ease) both; }
.js .is-in .ticks li:nth-child(1){ animation-delay:.04s; }
.js .is-in .ticks li:nth-child(2){ animation-delay:.08s; }
.js .is-in .ticks li:nth-child(3){ animation-delay:.12s; }
.js .is-in .ticks li:nth-child(4){ animation-delay:.16s; }
.js .is-in .ticks li:nth-child(5){ animation-delay:.20s; }
.js .is-in .ticks li:nth-child(6){ animation-delay:.24s; }
.js .is-in .ticks li:nth-child(7){ animation-delay:.28s; }
.js .is-in .ticks li:nth-child(8){ animation-delay:.32s; }
.js .is-in .ticks li:nth-child(n+9){ animation-delay:.36s; }

/* ==========================================================================
   Content elements
   ========================================================================== */
.faq details .faq-body{ animation:riseIn .38s var(--ease) both; }
.faq summary{ transition:background .18s var(--ease), color .18s var(--ease); }
.faq summary:hover{ background:rgba(79,124,255,.07); color:#fff; }

table.cmp tbody tr{ transition:background .18s var(--ease); }
/* `.yes` sits on the <td> itself, so never touch its `display` — anything but
   table-cell drops the cell out of the table and the columns collapse. Hover
   brightens the tick instead of scaling it. */
table.cmp .yes{ transition:color .2s var(--ease); }
table.cmp tbody tr:hover .yes{ color:#4FE0B8; }

.shot{ transition:transform .5s var(--ease), box-shadow .5s var(--ease); }
.split-media:hover .shot{ transform:translateY(-6px); box-shadow:0 36px 80px -30px rgba(0,0,0,1), 0 0 44px -16px rgba(79,124,255,.6); }

.field input, .field select, .field textarea{
  transition:border-color .16s var(--ease), box-shadow .16s var(--ease), transform .16s var(--ease);
}
.field input:focus, .field select:focus, .field textarea:focus{ transform:translateY(-1px); }

/* ==========================================================================
   Respect the reader's motion preference — everything above stands down
   ========================================================================== */
@media (prefers-reduced-motion:reduce){
  .js .section-head, .js .card, .js .plan, .js .dl-card, .js .step, .js .stat,
  .js .split > div, .js .faq details, .js .faq > h2, .js .panel, .js .cta-band,
  .js .table-scroll, .js .section > .wrap > .notice, .js .reveal{
    opacity:1 !important; transform:none !important;
  }
  body::before, .cta-band::before, .js .hero .btn--primary,
  .dl-card:hover .dl-os, .btn--primary:hover::after, .js .hero-chip{ animation:none !important; }
  .scroll-progress{ display:none; }
}
