/* ==========================================================================
   motion.css — the site's motion and micro-detail layer (2026-08-11)
   --------------------------------------------------------------------------
   ONE file, loaded last, so the whole "feel" of the site can be read, tuned or
   switched off in a single place. It deliberately does NOT restyle anything:
   no new colours, no new spacing, no layout. It only changes how things MOVE
   and adds the small finishes that make a page feel considered.

   Rules it follows, so it can never become the next white box:
     • nothing here paints a background on a surface that did not have one
     • every transition names its properties; no `transition: all`
     • only transform / opacity / filter / colour animate, never width, height,
       top or left, so nothing can reflow mid-animation
     • everything collapses under prefers-reduced-motion at the bottom
     • no !important except where it has to beat an inline page style

   Tuning: change the four --mo-* values and the whole site changes together.
   ========================================================================== */

:root{
  /* one easing family. --mo-out is the workhorse: fast start, soft landing.
     --mo-spring overshoots very slightly, only for things that "arrive". */
  --mo-out:cubic-bezier(.22,.61,.24,1);
  --mo-spring:cubic-bezier(.34,1.26,.52,1);
  --mo-fast:.18s;
  --mo-base:.32s;
  --mo-slow:.55s;
}

/* ══════════════════════════════════════════════════════════════════════════
   0. PAGE TRANSITIONS
   --------------------------------------------------------------------------
   Cross-document view transitions. Clicking a link no longer flashes white:
   the old page fades out as the new one fades in, and any element the two
   pages share MORPHS between them.

   This is CSS only. No framework, no build step, no JS. A browser that does
   not support it simply navigates the old way, so there is nothing to detect
   and no fallback to maintain.

   ⚠️ LIGHT MODE IS DELIBERATELY QUIETER. The user asked for light to stay
   restrained until a bigger change is approved, and for dark to get the full
   treatment. So light gets the smoothness (a clean cross-fade and the morph,
   which is continuity rather than decoration) and dark additionally gets a
   slight lift and a longer settle.
   ══════════════════════════════════════════════════════════════════════════ */
@view-transition{ navigation:auto }

::view-transition-group(root){ animation-duration:var(--mo-base) }
::view-transition-old(root),
::view-transition-new(root){
  animation-duration:var(--mo-base);
  animation-timing-function:var(--mo-out);
}
/* the outgoing page must not sit on top of the incoming one, or the fade
   reads as a flicker */
::view-transition-old(root){ z-index:0 }
::view-transition-new(root){ z-index:1 }

/* DARK gets the extra: the incoming page rises a few pixels as it arrives. */
html[data-theme="dark"]::view-transition-new(root){
  animation:moPageIn var(--mo-slow) var(--mo-out) both;
}
html[data-theme="dark"]::view-transition-old(root){
  animation:moPageOut var(--mo-base) var(--mo-out) both;
}
@keyframes moPageIn{ from{opacity:0;transform:translate3d(0,10px,0)} to{opacity:1;transform:none} }
@keyframes moPageOut{ from{opacity:1} to{opacity:0} }

/* Shared elements. A card image and the hero it opens into carry the same
   view-transition-name, so the thumbnail grows into the hero instead of the
   page blinking. The names are generated per post, never hand-written, since
   two elements sharing a name on ONE page cancels the whole transition. */
::view-transition-group(*){ animation-timing-function:var(--mo-out) }
::view-transition-old(.eco-morph),
::view-transition-new(.eco-morph){ mix-blend-mode:normal }

/* ── Typography: the cheapest premium detail there is ────────────────────
   balance stops a heading leaving one lonely word on its last line; pretty
   stops a paragraph ending in a single-word orphan. Both are ignored by
   browsers that do not know them. */
h1,h2,h3,h4,
.page-title h1,.page-title h2,
.ct-tag,.st-tag,.xg-tag,.hub-lead,.blog-lead,.ct-lead,.st-lead{
  text-wrap:balance;
}
p,li,figcaption,.cfaq__a,.bvc-row__v > div{ text-wrap:pretty }

/* ── 1. Anchor scrolling ─────────────────────────────────────────────────
   Jumping to #section used to teleport and land with the heading jammed under
   the sticky nav. scroll-margin-top uses the nav height the bar already
   measures for the hero, so the two can never disagree. */
html{scroll-behavior:smooth}
:target,
[id]{scroll-margin-top:calc(var(--eco-nav-h,84px) + 18px)}

/* ── 2. Text selection, in brand colour rather than the browser's blue ──── */
::selection{background:rgba(19,194,168,.24);color:inherit}
html[data-theme="dark"] ::selection{background:rgba(126,200,255,.30)}

/* ── 3. One focus ring for the whole site ────────────────────────────────
   :focus-visible only, so a mouse click never draws it but a Tab key always
   does. Previously each component invented its own, and several drew none. */
a:focus-visible,button:focus-visible,input:focus-visible,
select:focus-visible,textarea:focus-visible,summary:focus-visible,
[tabindex]:focus-visible{
  outline:2px solid #13c2a8;outline-offset:3px;border-radius:6px;
}
html[data-theme="dark"] a:focus-visible,
html[data-theme="dark"] button:focus-visible,
html[data-theme="dark"] input:focus-visible,
html[data-theme="dark"] select:focus-visible,
html[data-theme="dark"] textarea:focus-visible,
html[data-theme="dark"] summary:focus-visible,
html[data-theme="dark"] [tabindex]:focus-visible{outline-color:#7ec8ff}

/* ── 4. Cards: one lift, everywhere ──────────────────────────────────────
   The site had four different card hovers with four different speeds. This is
   the shared one. Individual components keep their own shadow colour; only the
   timing and the distance are unified. */
.hub-card,.fac-card,.ct-card,.st-item,.rp-card,.blog-card,
.xg-card,.ac-card,.ti-card,.cfaq__q,.benefit-item{
  transition:transform var(--mo-base) var(--mo-out),
             box-shadow var(--mo-base) var(--mo-out),
             border-color var(--mo-base) ease;
}
.hub-card:hover,.fac-card:hover,.ct-card:hover,.st-item:hover,
.rp-card:hover,.blog-card:hover,.xg-card:hover,.ac-card:hover,
.ti-card:hover{transform:translateY(-4px)}

/* ── 5. Media: a slow, quiet zoom inside its own frame ──────────────────
   Only where the frame already clips (overflow:hidden), so nothing can spill.
   scale on the IMAGE, never on the card, so text stays pin sharp. */
.blog-card__img,.rp-img,.fac-card__img,.article-figure,.ct-card__media,
.gal-item,.ti-card__img{overflow:hidden}
.blog-card__img img,.rp-img img,.fac-card__img img,.gal-item img{
  transition:transform var(--mo-slow) var(--mo-out);
}
.blog-card:hover .blog-card__img img,
.rp-card:hover .rp-img img,
.fac-card:hover .fac-card__img img,
.gal-item:hover img{transform:scale(1.045)}

/* ── 6. In-text links: an underline that wipes in from the left ─────────
   Replaces the static 1px border several blocks were drawing. Uses
   background-size, which animates on the compositor, rather than width. */
.article-body p > a,.cfaq__a a,.st-src a,.ct-wrap p > a,.crumbs__i a{
  text-decoration:none;
  background-image:linear-gradient(currentColor,currentColor);
  background-repeat:no-repeat;
  background-position:0 100%;
  background-size:0% 1px;
  transition:background-size var(--mo-base) var(--mo-out),color var(--mo-fast) ease;
}
.article-body p > a:hover,.cfaq__a a:hover,.st-src a:hover,
.ct-wrap p > a:hover,.crumbs__i a:hover{background-size:100% 1px}

/* ── 7. Buttons: a real press ───────────────────────────────────────────
   Hover lifts, ACTIVE presses back down. The missing press is why buttons on
   most sites feel like pictures of buttons. */
.btn-apply,.xg-btn,.st-btn,.ct-btn,.ac-btn,.footer-apply,.eco-fab__btn,.cf-submit{
  transition:transform var(--mo-fast) var(--mo-out),
             box-shadow var(--mo-base) var(--mo-out),filter var(--mo-fast) ease;
}
.btn-apply:active,.xg-btn:active,.st-btn:active,.ct-btn:active,
.ac-btn:active,.footer-apply:active,.eco-fab__btn:active,.cf-submit:active{
  transform:translateY(1px) scale(.985);
}

/* ── 8. Details/summary: the marker turns instead of jumping ───────────── */
details summary{transition:color var(--mo-fast) ease}
details .cfaq__plus,details .xg-plus,details .ac-plus,details .faq-chev{
  transition:transform var(--mo-base) var(--mo-spring);
}

/* ── 9. Section headings: a hairline that draws itself in ───────────────
   Only on headings that ALREADY had a rule under them, so nothing new appears.
   transform:scaleX is free; animating width is not. */
.ct-sechead h2,.st-sechead h2,.cmp-h,.cfaq__h{position:relative}
.ct-sechead h2::after,.st-sechead h2::after,.cmp-h::after,.cfaq__h::after{
  content:"";position:absolute;left:0;bottom:-8px;width:52px;height:2px;border-radius:2px;
  background:linear-gradient(90deg,#13c2a8,rgba(19,194,168,0));
  transform:scaleX(0);transform-origin:left;
  transition:transform var(--mo-slow) var(--mo-out);
}
[data-aos].aos-animate .ct-sechead h2::after,
[data-aos].aos-animate .st-sechead h2::after,
[data-aos].aos-animate .cmp-h::after,
[data-aos].aos-animate .cfaq__h::after,
.ct-sechead:hover h2::after,.st-sechead:hover h2::after{transform:scaleX(1)}
html[data-theme="dark"] .ct-sechead h2::after,
html[data-theme="dark"] .st-sechead h2::after,
html[data-theme="dark"] .cmp-h::after,
html[data-theme="dark"] .cfaq__h::after{
  background:linear-gradient(90deg,#7ec8ff,rgba(126,200,255,0));
}

/* ── 10. Scroll reveal, softened ────────────────────────────────────────
   AOS ships a 100px travel with ease-in-out, which reads as "sliding
   furniture". 14px with a decelerating curve reads as "settling". Overriding
   AOS's own variables rather than its keyframes keeps its JS in charge. */
[data-aos]{transition-timing-function:var(--mo-out)!important}
[data-aos^="fade"][data-aos^="fade"]{transform:translate3d(0,14px,0)}
[data-aos^="fade"][data-aos^="fade"].aos-animate{transform:translate3d(0,0,0)}
[data-aos="fade-up"]{transform:translate3d(0,16px,0)}
[data-aos="fade-down"]{transform:translate3d(0,-16px,0)}
[data-aos="fade-left"]{transform:translate3d(16px,0,0)}
[data-aos="fade-right"]{transform:translate3d(-16px,0,0)}
[data-aos="zoom-in"]{transform:scale(.985)}

/* ── 11. Sticky table headers on the wide comparison tables ─────────────
   A small thing that makes a long table feel built rather than dumped. */
.cmp-wrap{scroll-behavior:smooth}
table.cmp thead th{position:sticky;top:0;z-index:2}

/* ── 12. Scrollbars, only where a block scrolls on its own ─────────────── */
.cmp-wrap,.eco-nav__sub,.gal-scroll{scrollbar-width:thin;scrollbar-color:rgba(19,194,168,.5) transparent}
.cmp-wrap::-webkit-scrollbar,.gal-scroll::-webkit-scrollbar{height:8px;width:8px}
.cmp-wrap::-webkit-scrollbar-thumb,.gal-scroll::-webkit-scrollbar-thumb{
  background:rgba(19,194,168,.45);border-radius:8px}
.cmp-wrap::-webkit-scrollbar-track,.gal-scroll::-webkit-scrollbar-track{background:transparent}
html[data-theme="dark"] .cmp-wrap,html[data-theme="dark"] .gal-scroll{scrollbar-color:rgba(126,200,255,.5) transparent}
html[data-theme="dark"] .cmp-wrap::-webkit-scrollbar-thumb,
html[data-theme="dark"] .gal-scroll::-webkit-scrollbar-thumb{background:rgba(126,200,255,.45)}

/* ── 13. NOT DONE ON PURPOSE: fading lazy images in ─────────────────────
   The obvious version is
       img[loading="lazy"]{animation:fade .5s both}
   and it is a trap. `both` fill applies the 0% frame BEFORE the animation
   starts, so anything that stops the animation from ever starting leaves every
   lazy image at opacity 0. A decorative fade must never be able to hide
   content. Doing it safely needs JS to add a class on the decode event, which
   is more machinery than the effect is worth. */

/* ── EVERYTHING OFF ──────────────────────────────────────────────────────
   One block, last, so there is a single answer to "does this site respect
   reduced motion". Animation duration is not zeroed but set to 1ms, which
   keeps animationend listeners firing. */
@media (prefers-reduced-motion:reduce){
  html{scroll-behavior:auto}
  /* ⚠️ 1ms, not `animation:none`. Removing the animation outright can leave
     the outgoing and incoming snapshots both painted; a 1ms duration ends the
     transition immediately AND cleanly. */
  ::view-transition-group(*),::view-transition-old(*),::view-transition-new(*){
    animation-duration:1ms!important;
  }
  *,*::before,*::after{
    animation-duration:1ms!important;animation-iteration-count:1!important;
    transition-duration:1ms!important;scroll-behavior:auto!important;
  }
  .hub-card:hover,.fac-card:hover,.ct-card:hover,.st-item:hover,
  .rp-card:hover,.blog-card:hover,.xg-card:hover,.ac-card:hover,
  .ti-card:hover{transform:none}
  .blog-card:hover .blog-card__img img,.rp-card:hover .rp-img img,
  .fac-card:hover .fac-card__img img,.gal-item:hover img{transform:none}
  img[loading="lazy"]{animation:none}
  [data-aos]{transform:none!important;opacity:1!important}
}
