/* ============================================================
   JoulesCloud — motion layer
   Loaded after the inline <style>.

   Safety model: this file never hides an element that would
   stay hidden if a script failed. Entrances are keyframe
   animations with `both` fill, triggered either by html.js-anim
   alone (set by a pre-paint guard when motion is welcome) or by
   a trigger class that JS adds when the element scrolls in. If
   the trigger never lands, the element simply stays in its
   normal, visible state.
   ============================================================ */

:root{
  --ease-out:cubic-bezier(.16,.84,.44,1);
  --ease-spring:cubic-bezier(.22,1.15,.36,1);
  --m-fast:.4s;
  --m-mid:.6s;
  --m-slow:.9s;
}

/* ------------------------------------------------------------
   Shared keyframes
   ------------------------------------------------------------ */
@keyframes jc-rise{from{opacity:0;transform:translateY(18px)}to{opacity:1;transform:none}}
@keyframes jc-rise-sm{from{opacity:0;transform:translateY(12px)}to{opacity:1;transform:none}}
@keyframes jc-pop{from{opacity:0;transform:scale(.55)}to{opacity:1;transform:none}}
@keyframes jc-grow-y{from{transform:scaleY(0)}to{transform:scaleY(1)}}
@keyframes jc-grow-x{from{transform:scaleX(0)}to{transform:scaleX(1)}}
@keyframes jc-clip{from{opacity:0;clip-path:inset(0 0 110% 0)}to{opacity:1;clip-path:inset(0 0 -22% 0)}}
@keyframes jc-spin{to{transform:rotate(360deg)}}

/* ------------------------------------------------------------
   1. Scroll-progress rail
   ------------------------------------------------------------ */
.scroll-rail{
  position:fixed;left:0;top:0;height:3px;width:100%;z-index:100;
  background:linear-gradient(90deg,var(--accent),var(--accent-deep));
  transform:scaleX(0);transform-origin:0 50%;
  transition:transform .12s linear;
  pointer-events:none;
}

/* ------------------------------------------------------------
   2. Top bar: condense + blur once the page has scrolled
   ------------------------------------------------------------ */
.topbar{transition:height .25s var(--ease-out),background .25s ease,box-shadow .25s ease,border-color .25s ease}
.topbar .wrap{transition:height .25s var(--ease-out)}
.topbar.scrolled{
  background:color-mix(in srgb,var(--ground) 82%,transparent);
  -webkit-backdrop-filter:blur(10px);backdrop-filter:blur(10px);
  box-shadow:0 8px 24px -16px rgba(15,25,23,.4);
}
.topbar.scrolled .wrap{height:54px}
.topbar .brand .glyph{transition:transform .25s var(--ease-spring)}
.topbar.scrolled .brand .glyph{transform:scale(.9)}

/* brand glyph: tiny equalizer tick on hover */
.brand .glyph::after{transform-origin:50% 100%}
.brand:hover .glyph::after{animation:eq .55s ease-in-out infinite alternate}
@keyframes eq{from{transform:scaleY(.55)}to{transform:scaleY(1)}}

/* ------------------------------------------------------------
   3. Nav scroll-spy underline
   ------------------------------------------------------------ */
.nav-links a{position:relative}
.nav-links a::after{
  content:"";position:absolute;left:0;right:0;bottom:-7px;height:2px;
  background:var(--accent);border-radius:2px;
  transform:scaleX(0);transform-origin:0 50%;
  transition:transform .3s var(--ease-out);
}
.nav-links a:hover::after,.nav-links a.active::after{transform:scaleX(1)}
.nav-links a.active{color:var(--ink)}

/* theme toggle: icon rotates with the theme, press nudges it */
#themeToggle svg{transition:transform .5s var(--ease-spring)}
:root[data-theme="dark"] #themeToggle svg{transform:rotate(180deg)}
#themeToggle:active svg{transform:rotate(180deg) scale(.85)}
:root[data-theme="dark"] #themeToggle:active svg{transform:rotate(360deg) scale(.85)}

/* full-page tint crossfade while switching theme */
html.theme-anim,html.theme-anim *{
  transition:background-color .35s ease,border-color .35s ease,color .3s ease,fill .3s ease !important;
}

/* ------------------------------------------------------------
   4. Buttons: sheen sweep + press feedback
   ------------------------------------------------------------ */
.btn-primary{position:relative;overflow:hidden;isolation:isolate}
.btn-primary::after{
  content:"";position:absolute;top:0;bottom:0;left:-140%;width:55%;
  background:linear-gradient(100deg,transparent,rgba(255,255,255,.4),transparent);
  transform:skewX(-18deg);pointer-events:none;
}
.btn-primary:hover::after{animation:sheen .75s var(--ease-out)}
@keyframes sheen{to{left:150%}}
.btn:active{transform:translateY(0) scale(.97)}

/* ------------------------------------------------------------
   5. Hero — boot sequence (pure CSS, `both` fill = ends visible)
   ------------------------------------------------------------ */
.hero{position:relative}
.hero::before{
  content:"";position:absolute;top:-24%;right:-8%;width:54vw;height:54vw;
  max-width:760px;max-height:760px;z-index:0;pointer-events:none;
  background:radial-gradient(circle at 65% 35%,color-mix(in srgb,var(--accent) 16%,transparent),transparent 62%);
  filter:blur(14px);
  animation:heroGlow 16s ease-in-out infinite alternate;
}
@keyframes heroGlow{
  from{transform:translate(0,0) scale(1);opacity:.7}
  to{transform:translate(-46px,34px) scale(1.16);opacity:1}
}
.hero .wrap{position:relative;z-index:1}

html.js-anim .hero .eyebrow{animation:jc-rise var(--m-mid) var(--ease-out) both}
html.js-anim .hero h1{animation:jc-clip var(--m-slow) var(--ease-out) .12s both}
html.js-anim .hero .lede{animation:jc-rise var(--m-mid) var(--ease-out) .24s both}
html.js-anim .hero .hero-cta{animation:jc-rise var(--m-mid) var(--ease-spring) .36s both}
html.js-anim .hero .tagline-strip{animation:jc-rise var(--m-mid) var(--ease-out) .46s both}
html.js-anim .hero .console{animation:jc-rise var(--m-slow) var(--ease-spring) .5s both}

html.js-anim .hero .tagline-strip .tag{
  animation:jc-pop .5s var(--ease-spring) both;
  animation-delay:calc(.6s + var(--i,0) * .05s);
}
.tag{transition:transform .18s var(--ease-out),border-color .18s ease,color .18s ease}
.tag:hover{transform:translateY(-2px);border-color:var(--accent);color:var(--accent)}

/* animated underline on the accent phrase — a gradient on the text
   itself so it tracks line wrapping; grows in, then stays. */
.hero h1 em{
  background-image:linear-gradient(color-mix(in srgb,var(--accent) 50%,transparent),
                                   color-mix(in srgb,var(--accent) 50%,transparent));
  background-repeat:no-repeat;background-position:0 100%;background-size:0 .07em;
}
html.js-anim .hero h1 em{animation:emUnderline .7s var(--ease-out) 1s forwards}
@keyframes emUnderline{to{background-size:100% .07em}}

/* gentle attention ring on the hero primary CTA */
html.js-anim .hero-cta .btn-primary{animation:ctaBreath 3.4s ease-in-out 2s infinite}
@keyframes ctaBreath{
  0%,100%{box-shadow:0 0 0 0 color-mix(in srgb,var(--accent) 40%,transparent)}
  50%{box-shadow:0 0 0 9px transparent}
}

/* scroll hint under the hero */
.scroll-hint{display:flex;justify-content:center;margin-top:30px;color:var(--ink-faint)}
.scroll-hint svg{animation:bob 1.8s ease-in-out infinite}
@keyframes bob{0%,100%{transform:translateY(0);opacity:.5}50%{transform:translateY(5px);opacity:1}}
@media(max-width:980px){.scroll-hint{display:none}}

/* ------------------------------------------------------------
   6. Hero console: parallax + live booking cycle + row hover
   ------------------------------------------------------------ */
.console{will-change:transform}
.sched-row .rlab{transition:color .2s ease}
.sched-row:hover .rlab{color:var(--accent)}
.sched-row .blk{transition:filter .2s ease}
.sched-row:hover .blk{filter:brightness(1.06)}
.blk.pending{transition:background .5s ease,color .5s ease,border-color .5s ease}
.blk.pending.resolved{background:var(--ch-blue);color:#fff;border:1px solid transparent}

/* ------------------------------------------------------------
   7. Section headline reveals — animate when app.js adds .in,
   otherwise the heading is just its normal self.
   ------------------------------------------------------------ */
html.js-anim .band-head.in h2{animation:jc-clip var(--m-slow) var(--ease-out) .06s both}
@keyframes lineGrow{from{width:0}to{width:26px}}
html.js-anim .band-head.in .eyebrow::before{animation:lineGrow var(--m-mid) var(--ease-out) both}

/* dividers draw left-to-right when JS adds .drawn */
.rule,.rule-ticks{transform-origin:0 50%}
html.js-anim .rule.drawn,html.js-anim .rule-ticks.drawn{animation:jc-grow-x var(--m-slow) var(--ease-out) both}

/* deep-dive panels slide in from the outer edge (app.js adds .in) */
@keyframes slideFromR{from{opacity:0;transform:translateX(42px)}to{opacity:1;transform:none}}
@keyframes slideFromL{from{opacity:0;transform:translateX(-42px)}to{opacity:1;transform:none}}
html.js-anim .deep .panel.in{animation:slideFromR var(--m-slow) var(--ease-spring) both}
html.js-anim .deep.flip .panel.in{animation:slideFromL var(--m-slow) var(--ease-spring) both}

/* ------------------------------------------------------------
   8. Generic staggered group entrances.
   .anim-item is added by JS immediately before it starts
   watching the container; .anim-go is added on scroll-in (or by
   the JS safety net). No trigger -> element stays visible.
   ------------------------------------------------------------ */
html.js-anim .anim-go .anim-item{
  animation:jc-rise-sm .55s var(--ease-spring) both;
  animation-delay:calc(var(--i,0) * .055s);
}

/* sibling-staggered .reveal cards (delay set by JS via --i) */
html.js-anim .reveal[style*="--i"]{transition-delay:calc(var(--i,0) * .07s)}

/* ------------------------------------------------------------
   9. Data-viz motion
   ------------------------------------------------------------ */
.kpi .kv,.rc-num{font-variant-numeric:tabular-nums}
html.js-anim .kpi.alert.anim-go .kv{animation:alertPulse 2.4s ease-in-out .6s infinite}
@keyframes alertPulse{
  0%,100%{text-shadow:0 0 0 transparent}
  50%{text-shadow:0 0 14px color-mix(in srgb,var(--sev-crit) 55%,transparent)}
}

/* dashboard bars grow from the baseline */
.chart-bars .cb i{transform-origin:50% 100%}
html.js-anim .chart-bars.anim-go .cb i{
  animation:jc-grow-y .7s var(--ease-spring) both;
  animation-delay:calc(.1s + var(--i,0) * .06s);
}

/* incident severity: critical keeps pulsing once in view */
html.js-anim .inc-list.anim-go .inc .sev.c{animation:sevPulse 1.8s ease-in-out infinite}
@keyframes sevPulse{
  0%,100%{box-shadow:0 0 0 0 color-mix(in srgb,var(--sev-crit) 60%,transparent)}
  70%{box-shadow:0 0 0 7px transparent}
}

/* matrix level chips pop after their row arrives */
html.js-anim .matrix tbody.anim-go .lvl{
  animation:jc-pop .4s var(--ease-spring) both;
  animation-delay:calc(.15s + var(--i,0) * .05s);
}

/* progress bars: moving shimmer once filled */
.pbar .track,.pbar .track i{position:relative;overflow:hidden}
html.js-anim .pbar .track i::after{
  content:"";position:absolute;inset:0;transform:translateX(-100%);
  background:linear-gradient(90deg,transparent,rgba(255,255,255,.45),transparent);
}
html.js-anim [data-progress].in .pbar .track i::after{animation:barShimmer 2.4s ease-in-out 1s infinite}
@keyframes barShimmer{to{transform:translateX(100%)}}

/* module mini-vizzes: children animate in sequence on .viz-go
   (--i set by JS). Transform-origins set unconditionally so a
   missing --i still animates cleanly. */
.mviz-steps i.fill,.mviz-sev i{transform-origin:0 50%}
.mviz-bars i,.mviz-gauge i::after{transform-origin:50% 100%}
.mviz-cal i,.mviz-steps b{transform-origin:50% 50%}

/* Each viz plays its entrance once, then keeps a quiet idle loop
   running so the module cards read as live dashboards rather than
   frozen thumbnails. Loops are slow, low-amplitude and phase-shifted
   by --i; the reduced-motion block at the end forces them to a
   single iteration. */

/* 01 — calendar: colour surfaces cell by cell, on a diagonal drift */
@keyframes vizCalIdle{
  0%,100%{opacity:.45;transform:scale(.86)}
  45%{opacity:1;transform:scale(1)}
}
html.js-anim .mviz.viz-go .mviz-cal i{
  animation:
    jc-pop .45s var(--ease-spring) calc(var(--i,0)*.04s) both,
    vizCalIdle 4.6s ease-in-out calc(1s + var(--i,0)*.11s) infinite;
}

/* 02 — steps: a fill wipe travels the track and restarts */
.mviz-steps i,.mviz-steps i.fill{
  background:linear-gradient(90deg,var(--accent) 50%,var(--line) 50%);
  background-size:220% 100%;background-position:100% 0;
}
@keyframes vizStepsIdle{
  0%,10%{background-position:100% 0}
  55%,100%{background-position:0 0}
}
@keyframes vizStepsDot{
  0%,14%{border-color:var(--line);background:var(--paper)}
  58%,100%{border-color:var(--accent);background:var(--accent)}
}
html.js-anim .mviz.viz-go .mviz-steps i{
  animation:
    jc-grow-x .5s var(--ease-spring) calc(var(--i,0)*.05s) both,
    vizStepsIdle 5s ease-in-out calc(1s + var(--i,0)*.22s) infinite;
}
html.js-anim .mviz.viz-go .mviz-steps b{
  animation:
    jc-pop .4s var(--ease-spring) calc(var(--i,0)*.05s) both,
    vizStepsDot 5s ease-in-out calc(1s + var(--i,0)*.22s) infinite;
}

/* 03 — equipment: vertical bars rise and fall like an equaliser */
@keyframes vizBarsIdle{
  0%,100%{transform:scaleY(.32)}
  50%{transform:scaleY(1)}
}
html.js-anim .mviz.viz-go .mviz-bars i{
  animation:
    jc-grow-y .55s var(--ease-spring) calc(var(--i,0)*.05s) both,
    vizBarsIdle 3.4s ease-in-out calc(.9s + var(--i,0)*.19s) infinite;
}

/* 04 — incidents: severity bars stretch out and ease back */
@keyframes vizSevIdle{
  0%,100%{transform:scaleX(.5)}
  50%{transform:scaleX(1)}
}
html.js-anim .mviz.viz-go .mviz-sev i{
  animation:
    jc-grow-x .55s var(--ease-spring) calc(var(--i,0)*.06s) both,
    vizSevIdle 4.1s ease-in-out calc(1s + var(--i,0)*.16s) infinite;
}

/* 05 — inventory: fill levels pump up and down inside each column */
@keyframes vizGaugeIdle{
  0%,100%{transform:scaleY(.62)}
  46%{transform:scaleY(1.28)}
}
html.js-anim .mviz.viz-go .mviz-gauge i::after{
  animation:
    jc-grow-y .55s var(--ease-spring) .1s both,
    vizGaugeIdle 3.7s ease-in-out calc(1.1s + var(--i,0)*.14s) infinite;
}

/* 06 — dashboard: the donut redistributes its segments and turns */
@property --seg-a{syntax:"<percentage>";inherits:false;initial-value:62%}
@property --seg-b{syntax:"<percentage>";inherits:false;initial-value:82%}
@property --seg-c{syntax:"<percentage>";inherits:false;initial-value:93%}
@keyframes donutIn{from{opacity:0;transform:rotate(-140deg) scale(.55)}to{opacity:1;transform:rotate(0) scale(1)}}
@keyframes vizDonutIdle{
  0%{--seg-a:62%;--seg-b:82%;--seg-c:93%;transform:rotate(0)}
  25%{--seg-a:47%;--seg-b:71%;--seg-c:87%}
  50%{--seg-a:73%;--seg-b:88%;--seg-c:96%;transform:rotate(180deg)}
  75%{--seg-a:55%;--seg-b:79%;--seg-c:90%}
  100%{--seg-a:62%;--seg-b:82%;--seg-c:93%;transform:rotate(360deg)}
}
html.js-anim .mviz.viz-go .mviz-donut i{
  background:conic-gradient(var(--ch-green) 0 var(--seg-a),
                            var(--ch-amber) var(--seg-a) var(--seg-b),
                            var(--ch-blue) var(--seg-b) var(--seg-c),
                            var(--sev-crit) var(--seg-c) 100%);
  animation:
    donutIn .7s var(--ease-spring) both,
    vizDonutIdle 15s ease-in-out 1.1s infinite;
}

/* sheen sweep across a mini-viz on card hover */
.mviz::after{
  content:"";position:absolute;inset:0;transform:translateX(-100%);
  background:linear-gradient(100deg,transparent,color-mix(in srgb,var(--accent) 16%,transparent),transparent);
  pointer-events:none;
}
.mod:hover .mviz::after{animation:sheen2 .9s var(--ease-out)}
@keyframes sheen2{to{transform:translateX(100%)}}

/* ------------------------------------------------------------
   10. Workflow loop: sequential light-up + cycling pulse
   ------------------------------------------------------------ */
.loop .step{transition:background .45s ease}
.loop .step .n{transition:color .45s ease,letter-spacing .45s ease}
.loop .step::after{transition:color .3s ease,transform .3s ease}
html.js-anim .loop.anim-go .step{
  animation:jc-rise-sm .5s var(--ease-spring) both;
  animation-delay:calc(var(--i,0) * .1s);
}
.loop .step.pulse{background:var(--accent-tint)}
.loop .step.pulse .n{color:var(--accent-deep);letter-spacing:.14em}
.loop .step.pulse::after{color:var(--accent);transform:translateX(3px)}
.loop .step:last-child.pulse::after{transform:rotate(-40deg)}

/* ------------------------------------------------------------
   11. Cursor-following highlight on cards
   ------------------------------------------------------------ */
.mod,.who-card,.pitem{position:relative;overflow:hidden}
.mod::before,.who-card::before,.pitem::before{
  content:"";position:absolute;inset:0;pointer-events:none;z-index:0;
  background:radial-gradient(220px circle at var(--mx,50%) var(--my,-20%),
            color-mix(in srgb,var(--accent) 13%,transparent),transparent 70%);
  opacity:0;transition:opacity .3s ease;
}
.mod:hover::before,.who-card:hover::before,.pitem:hover::before{opacity:1}
.mod>*,.who-card>*,.pitem>*{position:relative;z-index:1}

/* ------------------------------------------------------------
   12. FAQ: height-animated accordion + rotating marker
   ------------------------------------------------------------ */
.faq .faq-a{overflow:hidden;transition:height .34s var(--ease-out),opacity .28s ease}
.faq summary::after{transition:transform .28s var(--ease-spring),color .2s ease}
.faq details[open] summary::after{content:"+";transform:rotate(45deg)}

/* ------------------------------------------------------------
   13. Contact: drifting glow + field focus + submit spinner
   ------------------------------------------------------------ */
html.js-anim .contact::after{animation:drift 22s ease-in-out infinite alternate}
@keyframes drift{
  from{transform:translate(0,0) scale(1)}
  to{transform:translate(-38px,30px) scale(1.18)}
}
.jc-form label{transition:color .2s ease,transform .2s ease}
.jc-form .fg:focus-within label{color:#7FDCCB;transform:translateX(2px)}
.jc-form .btn-primary:disabled::before{
  content:"";width:14px;height:14px;margin-right:2px;display:inline-block;
  border:2px solid rgba(6,32,28,.35);border-top-color:#06201C;border-radius:50%;
  animation:jc-spin .7s linear infinite;vertical-align:-2px;
}
.consent:not([hidden]){animation:consentIn .42s var(--ease-spring)}
@keyframes consentIn{from{transform:translateY(100%)}to{transform:translateY(0)}}

/* ------------------------------------------------------------
   14. Carousel: soft zoom-in when it enters view + hover
   ------------------------------------------------------------ */
@keyframes frameIn{from{opacity:0;transform:scale(.96)}to{opacity:1;transform:none}}
html.js-anim .carousel.viz-go .shot-frame{animation:frameIn .7s var(--ease-spring) both}
.shot-frame{transition:box-shadow .3s ease}

/* ------------------------------------------------------------
   15. Reduced-motion hard stop (defensive; html.js-anim is also
   withheld entirely when the visitor asks to reduce motion)
   ------------------------------------------------------------ */
@media(prefers-reduced-motion:reduce){
  .scroll-rail{display:none}
  *,*::before,*::after{
    animation-duration:.001s!important;animation-iteration-count:1!important;
    transition-duration:.001s!important;
  }
}
