/*!
 * CloudyWP Motion System — presentation layer for js/cwp-motion.js
 * ─────────────────────────────────────────────────────────────────────────
 * Every effect is DECLARATIVE: markup opts in with a data-attribute, this
 * file owns the visual states, and the engine only flips a class or writes a
 * single CSS custom property per frame. Nothing here hardcodes a colour —
 * the page-aware accent chain (--sp-A → --h-accent → --cwp-accent) is used
 * everywhere, exactly like the rest of the theme.
 *
 * NO-JS / FAIL-SAFE CONTRACT
 *   Hidden states are scoped to `html.cwp-js`. That class is added by a tiny
 *   inline head script and REMOVED again after a timeout if the engine never
 *   boots — so a JS failure can never leave the page permanently blank.
 *
 * PERFORMANCE CONTRACT
 *   Only `opacity`, `transform`, `filter` and custom properties animate —
 *   never width/height/top/left. Word-scrub and progress bars are driven by a
 *   SINGLE custom property write per frame per container, with the per-item
 *   maths done in CSS via clamp(), so a 60-word paragraph costs one write.
 */

/* ═══════════════════════════════════════════════════════════════════════
   0. TOKENS
═══════════════════════════════════════════════════════════════════════ */
:root{
    --cwp-m-ease:      cubic-bezier(.22,1,.36,1);   /* Apple-ish overshoot-free out */
    --cwp-m-ease-soft: cubic-bezier(.4,0,.2,1);
    --cwp-m-dur:       .9s;
    --cwp-m-accent:    var(--sp-A, var(--h-accent, var(--cwp-accent, #c9f163)));
}

/* In-page anchors must clear the fixed header. Doing this in CSS also fixes
   native hash navigation and browser find-on-page, which a JS-only smooth
   scroll handler would miss. --cwp-nav-h is defined by the navigation part. */
html{ scroll-padding-top:calc(var(--cwp-nav-h,80px) + 18px); }

/* ═══════════════════════════════════════════════════════════════════════
   1. REVEAL — one-shot entrance, opt in with data-cwp-reveal="<variant>"
   Variants: up (default) · fade · scale · clip · left · right · blur
   Optional: data-cwp-delay="120"  (ms, read by the engine → --cwp-m-delay)
═══════════════════════════════════════════════════════════════════════ */
.cwp-js [data-cwp-reveal]{
    opacity:0;
    transition:opacity var(--cwp-m-dur) var(--cwp-m-ease-soft),
               transform var(--cwp-m-dur) var(--cwp-m-ease),
               clip-path var(--cwp-m-dur) var(--cwp-m-ease),
               filter .8s var(--cwp-m-ease-soft);
    transition-delay:var(--cwp-m-delay,0ms);
    will-change:opacity,transform;
}
.cwp-js [data-cwp-reveal="up"],
.cwp-js [data-cwp-reveal=""]        { transform:translate3d(0,34px,0); }
.cwp-js [data-cwp-reveal="down"]    { transform:translate3d(0,-28px,0); }
.cwp-js [data-cwp-reveal="left"]    { transform:translate3d(-36px,0,0); }
.cwp-js [data-cwp-reveal="right"]   { transform:translate3d(36px,0,0); }
.cwp-js [data-cwp-reveal="scale"]   { transform:scale(.94) translate3d(0,18px,0); }
.cwp-js [data-cwp-reveal="blur"]    { transform:translate3d(0,20px,0); filter:blur(12px); }
/* Starts part-open, not fully clipped: Chrome's IntersectionObserver counts
   the target's own clip-path, so a heading clipped to zero area never reaches
   the 0.12 threshold and stays invisible for good. */
.cwp-js [data-cwp-reveal="clip"]    { transform:translate3d(0,26px,0); clip-path:inset(0 0 55% 0); }
.cwp-js [data-cwp-reveal="fade"]    { transform:none; }

/* Settled state — also releases will-change so long pages stay cheap. */
.cwp-js [data-cwp-reveal].is-in{
    opacity:1;
    transform:none;
    filter:none;
    clip-path:inset(0 0 0 0);
    will-change:auto;
}

/* Stagger: put data-cwp-stagger="70" on a parent; the engine assigns each
   revealable child an incremental --cwp-m-delay. */
[data-cwp-stagger]{ --cwp-m-step:70ms; }

/* ═══════════════════════════════════════════════════════════════════════
   2. SCRUB — the engine writes --cwp-p (0→1) as the element crosses the
   viewport. Everything below is pure CSS maths off that one value.
═══════════════════════════════════════════════════════════════════════ */
[data-cwp-scrub]{ --cwp-p:0; }

/* Generic progress bar: <i class="cwp-bar"><i class="cwp-bar__fill"></i></i> */
.cwp-bar{
    display:block;position:relative;overflow:hidden;
    height:2px;background:var(--cwp-line,rgba(0,0,0,.1));
}
.cwp-bar__fill{
    display:block;height:100%;width:100%;
    background:var(--cwp-m-accent);
    transform:scaleX(var(--cwp-p,0));
    transform-origin:0 50%;
}

/* ═══════════════════════════════════════════════════════════════════════
   3. WORD SCRUB — Apple's signature "copy lights up as you scroll".
   The engine wraps words in .cwp-w (each carrying --i) and writes --cwp-p.
   Per-word opacity is resolved by clamp() so one write drives every word.
═══════════════════════════════════════════════════════════════════════ */
[data-cwp-words]{
    --cwp-p:0;
    --cwp-lead:6;      /* how many words are mid-fade at once */
    --cwp-span:1;      /* word count + lead — set by the engine */
    --cwp-dim:.48;     /* resting opacity of un-read words; below ~.45 large text fails 3:1 */
}
/* --wp is this word's own 0→1 progress, derived from the container's single
   --cwp-p. Declaring it once and reusing it keeps opacity and transform
   perfectly in step, and means adding another property later costs nothing. */
.cwp-w{
    --wp:clamp(
        0,
        calc((var(--cwp-p) * var(--cwp-span) - var(--i,0)) / var(--cwp-lead)),
        1
    );
    /* inline-block so transform applies — it is ignored on plain inline
       elements. Words still wrap normally. */
    display:inline-block;
    opacity:calc(var(--cwp-dim) + (1 - var(--cwp-dim)) * var(--wp));
    transform:translateY(calc((1 - var(--wp)) * 0.36em));
    will-change:opacity,transform;
}
/* Before the engine measures, show the copy at full strength (no flash of
   near-invisible text if the script is slow or blocked). */
.cwp-js [data-cwp-words]:not(.is-measured) .cwp-w{
    opacity:1;transform:none;
}

/* ═══════════════════════════════════════════════════════════════════════
   4. TYPEWRITER — data-cwp-type. Real text stays in the DOM for SEO and
   assistive tech; only the visible node is re-typed.
═══════════════════════════════════════════════════════════════════════ */
[data-cwp-type]{ white-space:pre-wrap; }
.cwp-type__caret{
    display:inline-block;
    width:.055em;min-width:2px;height:.86em;
    margin-left:.06em;vertical-align:-.06em;
    background:var(--cwp-m-accent);
    animation:cwp-caret 1.05s steps(1,end) infinite;
}
@keyframes cwp-caret{ 0%,45%{opacity:1;} 46%,100%{opacity:0;} }
/* Once typing finishes the caret fades out instead of blinking forever. */
.cwp-typed .cwp-type__caret{
    animation:none;opacity:0;
    transition:opacity .45s ease .35s;
}

/* ═══════════════════════════════════════════════════════════════════════
   5. PARALLAX — engine writes the transform (needs px maths).
═══════════════════════════════════════════════════════════════════════ */
[data-cwp-parallax]{ will-change:transform; }

/* ═══════════════════════════════════════════════════════════════════════
   6. PIN — the "text scrolls, visual stays still" primitive.
   Structure:
     <div data-cwp-pin>              ← tall track (height sets scroll length)
       <div class="cwp-pin__stage">  ← position:sticky, 100vh, stays put
       <div class="cwp-pin__steps">  ← normal flow, scrolls past the stage
═══════════════════════════════════════════════════════════════════════ */
/* The track's height comes from the steps column being tall — never from a
   min-height on the track itself. A min-height here would add dead scroll
   below the last step whenever the two disagree. */
[data-cwp-pin]{ --cwp-p:0; position:relative; }
.cwp-pin__stage{
    position:sticky;top:0;
    height:100vh;height:100svh;
    display:flex;align-items:center;
    overflow:hidden;
}
/* Layers inside the stage crossfade as the active step changes. */
.cwp-pin__layer{
    position:absolute;inset:0;
    opacity:0;transform:scale(1.06);
    transition:opacity .85s var(--cwp-m-ease-soft),
               transform 1.4s var(--cwp-m-ease);
    will-change:opacity,transform;
}
.cwp-pin__layer.is-active{ opacity:1;transform:scale(1); }

/* ═══════════════════════════════════════════════════════════════════════
   7. HORIZONTAL RAIL — scroll-snap gallery with pointer drag.
═══════════════════════════════════════════════════════════════════════ */
[data-cwp-rail]{
    display:flex;
    overflow-x:auto;overflow-y:hidden;
    scroll-snap-type:x mandatory;
    scroll-behavior:smooth;
    overscroll-behavior-x:contain;
    -webkit-overflow-scrolling:touch;
    scrollbar-width:none;
    cursor:grab;
}
[data-cwp-rail]::-webkit-scrollbar{ display:none; }
[data-cwp-rail] > *{ flex:0 0 auto;scroll-snap-align:center; }
[data-cwp-rail].is-dragging{
    cursor:grabbing;
    scroll-snap-type:none;
    scroll-behavior:auto;
    user-select:none;
}
[data-cwp-rail].is-dragging *{ pointer-events:none; }

/* ═══════════════════════════════════════════════════════════════════════
   8. POINTER AFFORDANCES — magnetic buttons + card tilt (fine pointers only)
═══════════════════════════════════════════════════════════════════════ */
[data-cwp-magnetic]{ will-change:transform; }
[data-cwp-tilt]{ transform-style:preserve-3d;will-change:transform; }

/* ═══════════════════════════════════════════════════════════════════════
   9. SECTION PROGRESS RAIL — the fixed chapter dots (Apple/editorial cue)
═══════════════════════════════════════════════════════════════════════ */
/* Section titles are ALWAYS readable — the rail is a table of contents you
   can jump from, not a hover-to-discover control. Only colour and weight
   change as you scroll, which is enough to say "you are here" without the
   labels appearing and disappearing under the reader. */
.cwp-chapters{
    position:fixed;left:26px;top:50%;transform:translateY(-50%);
    z-index:9500;
    padding:0;margin:0;
    /* Hairline the markers sit on, so the list reads as one object. */
    border-left:1px solid var(--cwp-line,rgba(0,0,0,.1));
    opacity:0;pointer-events:none;
    transition:opacity .5s var(--cwp-m-ease-soft);
}
.cwp-chapters.is-visible{ opacity:1;pointer-events:auto; }
/* Content under the rail (a full-bleed section): fade out, never overlap. */
.cwp-chapters.is-visible.is-blocked{ opacity:0;pointer-events:none; }
.cwp-chapters__item{ display:block; }
.cwp-chapters__link{
    display:flex;align-items:center;gap:11px;
    text-decoration:none;color:inherit;
    padding:5px 0 5px 13px;
    position:relative;
}
/* Marker grows into an accent bar for the section you are in. */
.cwp-chapters__dot{
    position:absolute;left:-1px;top:50%;
    width:2px;height:11px;margin-top:-5.5px;
    background:var(--cwp-ink-3,#888);
    opacity:.35;
    transition:opacity .3s ease,height .35s var(--cwp-m-ease),
               margin-top .35s var(--cwp-m-ease),background .3s ease;
}
.cwp-chapters__label{
    font-size:12px;font-weight:500;letter-spacing:-.005em;line-height:1.3;
    color:var(--cwp-ink-3,#888);
    white-space:nowrap;
    transition:color .3s ease,font-weight .3s ease,opacity .3s ease;
}
.cwp-chapters__link:hover .cwp-chapters__label{ color:var(--cwp-ink,#0f0f0f); }
.cwp-chapters__link:hover .cwp-chapters__dot{ opacity:.7; }

.cwp-chapters__item.is-current .cwp-chapters__dot{
    opacity:1;background:var(--cwp-m-accent);
    height:22px;margin-top:-11px;
}
.cwp-chapters__item.is-current .cwp-chapters__label{
    color:var(--cwp-ink,#0f0f0f);font-weight:700;
}
.cwp-chapters__link:focus-visible{ outline:2px solid var(--cwp-m-accent);outline-offset:3px; }
/* Labels need real width — below this the rail would sit over the content. */
@media (max-width:1280px){ .cwp-chapters{ display:none; } }

/* Top-of-page reading progress line */
.cwp-readbar{
    position:fixed;inset:0 0 auto;height:3px;z-index:9998;
    /* --cwp-readbar-color is written on :root per page by cwp_page_accent().
       The old value chained to --sp-A, which is declared on #cwp-sp — but this
       element is appended to <body>, outside it, so the accent never reached
       here and every page drew the same default. The old chain stays as the
       fallback for any view that does not set the variable. */
    background:var(--cwp-readbar-color, var(--cwp-m-accent));
    transform:scaleX(var(--cwp-p,0));transform-origin:0 50%;
    pointer-events:none;opacity:0;
    transition:opacity .35s ease;
    will-change:transform;
}
.cwp-readbar.is-visible{ opacity:1; }
/* Logged-in views put the WP admin bar at top:0 with a far higher z-index, so
   a bar pinned to 0 is simply hidden behind it — and the people most likely to
   check the site are logged in. Sits below the admin bar instead; the height
   changes at 782px, which is where WP switches the bar to its taller layout. */
body.admin-bar .cwp-readbar{ top:32px; }
@media screen and (max-width:782px){
    body.admin-bar .cwp-readbar{ top:46px; }
}

/* ═══════════════════════════════════════════════════════════════════════
   10. ACCESSIBILITY — reduced motion resolves every effect to its END state.
   Nothing is left hidden, mid-fade, or mid-type.
═══════════════════════════════════════════════════════════════════════ */
@media (prefers-reduced-motion:reduce){
    .cwp-js [data-cwp-reveal],
    .cwp-js [data-cwp-reveal].is-in{
        opacity:1 !important;transform:none !important;
        filter:none !important;clip-path:none !important;
        transition:none !important;
    }
    .cwp-w{ opacity:1 !important;transform:none !important; }
    .cwp-pin__layer{ transition:none; }
    .cwp-type__caret{ display:none; }
    [data-cwp-rail]{ scroll-behavior:auto; }
    .cwp-chapters__dot,.cwp-chapters__label{ transition:none; }
}

/* ═══════════════════════════════════════════════════════════════════════
   11. PRINT — motion states must never hide content on paper.
═══════════════════════════════════════════════════════════════════════ */
@media print{
    .cwp-js [data-cwp-reveal]{ opacity:1 !important;transform:none !important;clip-path:none !important; }
    .cwp-w{ opacity:1 !important;transform:none !important; }
    .cwp-chapters,.cwp-readbar,.cwp-type__caret{ display:none !important; }
    .cwp-pin__stage{ position:static;height:auto; }
}
