/* ============================================================
   scroll-fx.css — companion styles for scroll-fx.js
   Inline Dental.

   Everything here defaults to VISIBLE. The JS is what hides and
   animates. That ordering matters: if the CSS hid things instead,
   anyone with reduced motion enabled (where the JS bails out) or
   with JavaScript blocked would be served a blank page.
   ============================================================ */

/* Transforms on inline SVG are unreliable without an explicit transform-box —
   rotateX can silently compute to identity even when the JS is correct. */
[data-scroll-fx="tilt3d"],
[data-scroll-fx="scale-in"] {
  transform-box: fill-box;
}
[data-scroll-fx="tilt3d"] { transform-origin: 50% 100%; }

[data-scroll-fx="clip-reveal"] { will-change: clip-path, opacity; }
[data-scroll-fx="split-lines"] { overflow: hidden; }
[data-scroll-fx="split-lines"] > span { display: inline-block; will-change: transform, opacity; }

/* Word-level stagger (21st.dev "Blur Out Up" pattern) */
.fx-word {
  display: inline-block;
  white-space: pre;
  will-change: transform, opacity, filter;
}

/* mono-color needs a compositing context so the filter does not repaint
   the whole stacking context on every frame. */
[data-scroll-fx="mono-color"] { will-change: filter, transform; backface-visibility: hidden; }

/* Pre-hide, but ONLY when JS is present and reduced motion is off. `fx-ready` is
   set by a tiny inline script in <head> and removed by the engine the instant it
   has written its first frame of inline styles, so nothing can be left stranded
   invisible if the engine fails to start. */
.fx-ready [data-scroll-fx="rise"],
.fx-ready [data-scroll-fx="tilt3d"],
.fx-ready [data-scroll-fx="scale-in"],
.fx-ready [data-scroll-fx="blur-stagger"],
.fx-ready [data-scroll-fx="word-stagger"],
.fx-ready [data-scroll-fx="split-lines"] { opacity: 0; }
.fx-ready [data-scroll-fx="clip-reveal"] { clip-path: inset(0 0 100% 0); opacity: .25; }

/* ---------- Page-load hero intro ---------- */
[data-fx-step] {
  transition:
    opacity .95s cubic-bezier(.16, 1, .3, 1),
    transform 1.05s cubic-bezier(.16, 1, .3, 1),
    filter .95s cubic-bezier(.16, 1, .3, 1);
}
.fx-intro-armed [data-fx-step] {
  opacity: 0;
  transform: translate3d(0, 26px, 0);
  filter: blur(12px);
}
.fx-intro-armed [data-fx-step="media"] {
  transform: translate3d(0, 34px, 0) scale(.965);
  filter: blur(16px);
}
.fx-intro-play [data-fx-step] {
  opacity: 1;
  transform: none;
  filter: none;
}

/* Once the intro has played, drop the hints so the compositor can
   release the layers. */
.fx-intro-play [data-fx-step] { will-change: auto; }

@media (max-width: 767.98px) {
  /* No blur at all on phones. A blur filter on the hero is the single most
     expensive thing on a mobile GPU, and because the reveal used to wait for
     window.load it left the hero visibly blurred for seconds on a slow
     connection. Opacity and a short rise give the same feel for free. */
  [data-fx-step] {
    transition:
      opacity .5s cubic-bezier(.16, 1, .3, 1),
      transform .55s cubic-bezier(.16, 1, .3, 1);
  }
  .fx-intro-armed [data-fx-step] { transform: translate3d(0, 14px, 0); filter: none; }
  .fx-intro-armed [data-fx-step="media"] { transform: translate3d(0, 18px, 0) scale(.99); filter: none; }
}

/* ---------- Reduced motion: everything visible, nothing moves ---------- */
@media (prefers-reduced-motion: reduce) {
  [data-scroll-fx],
  [data-scroll-fx="split-lines"] > span,
  .fx-word,
  [data-fx-step] {
    opacity: 1 !important;
    transform: none !important;
    filter: none !important;
    clip-path: none !important;
    transition: none !important;
  }
  .fx-intro-armed [data-fx-step] { opacity: 1 !important; transform: none !important; filter: none !important; }
}
