/* ===========================================================================
   motion.css — tech-seed.online

   WHY THIS FILE EXISTS, AND WHY IT IS SEPARATE FROM site.css
   ----------------------------------------------------------
   The demo sites we build for clients are deliberately near-static: one 18px
   rise on entry and nothing else. That restraint is correct THERE — a plumber's
   customer is looking for a phone number at 2am and every moving thing is a tax
   on that.

   This is our OWN site, and it has a different job. A prospect who was just
   cold-called is here to answer one question: "can these people actually
   build?" Motion is the cheapest honest evidence available. So this site moves,
   and the demo sites do not, and that difference is the argument.

   THE RULES IT STILL OBEYS
     1. Transform and opacity only. Never width/height/top/left in a running
        animation — those hit layout and jank on a mid-range Android.
     2. Nothing translates on the X axis at any breakpoint. A 20px slide-in from
        the right is the classic way a page picks up 20px of horizontal scroll
        on a phone, and this site is measured at a true 390px.
     3. Any scale lives inside an overflow-clipped mask.
     4. prefers-reduced-motion turns ALL of it off — not "reduces" it. Every
        animated element's resting state is its final state, so disabling the
        motion leaves a correct page rather than an invisible one.
     5. Entry animations run once. Nothing loops, nothing pulses, nothing
        demands attention after you have read it.
   =========================================================================== */

/* ---- staged hero entrance ------------------------------------------------
   The hero is the only place with a scripted sequence. Each element comes in
   70ms after the last, so the eye is walked: what we do -> the claim -> the
   price -> the action. It reads as pacing rather than as an effect.          */
html.jsr .hero .eyebrow,
html.jsr .hero h1,
html.jsr .hero .lede,
html.jsr .hero .pricecard,
html.jsr .hero .btnrow,
html.jsr .hero .trust{
  opacity:0;transform:translateY(16px);
  animation:heroIn .82s var(--ease) forwards;
}
html.jsr .hero .eyebrow  {animation-delay:.06s}
html.jsr .hero h1        {animation-delay:.13s}
html.jsr .hero .lede     {animation-delay:.22s}
html.jsr .hero .pricecard{animation-delay:.31s}
html.jsr .hero .btnrow   {animation-delay:.40s}
html.jsr .hero .trust    {animation-delay:.48s}
@keyframes heroIn{to{opacity:1;transform:none}}

/* ---- the price, revealed from behind a mask ------------------------------
   Considered a count-up here and rejected it. A ticking number reads as a
   dashboard metric, and it also changes width as it passes 1 -> 2 -> 3 digits,
   which shifts the layout on the most important element on the page. A clip
   reveal says the same thing typographically and cannot reflow anything.     */
.pricecard .fig,.retbox .fig,.tier .fig{overflow:hidden;display:block}
html.jsr .pricecard .fig > span{display:inline-block;transform:translateY(105%);
  animation:figIn .9s cubic-bezier(.16,.84,.3,1) forwards;animation-delay:.38s}
@keyframes figIn{to{transform:none}}

/* ---- section reveals, with stagger ---------------------------------------
   .rv reveals a block. .rvs reveals its CHILDREN in sequence — used where the
   content is a list making a cumulative point, so the order is the argument.  */
html.jsr .rvs > *{opacity:0;transform:translateY(18px)}
html.jsr .rvs.in > *{opacity:1;transform:none;
  transition:opacity .62s var(--ease),transform .62s var(--ease)}
html.jsr .rvs.in > *:nth-child(1){transition-delay:.02s}
html.jsr .rvs.in > *:nth-child(2){transition-delay:.09s}
html.jsr .rvs.in > *:nth-child(3){transition-delay:.16s}
html.jsr .rvs.in > *:nth-child(4){transition-delay:.23s}
html.jsr .rvs.in > *:nth-child(5){transition-delay:.30s}
html.jsr .rvs.in > *:nth-child(6){transition-delay:.37s}
html.jsr .rvs.in > *:nth-child(7){transition-delay:.44s}
html.jsr .rvs.in > *:nth-child(8){transition-delay:.51s}
html.jsr .rvs.in > *:nth-child(9){transition-delay:.58s}
html.jsr .rvs.in > *:nth-child(10){transition-delay:.65s}

/* ---- portfolio screenshots settle into their frame -----------------------
   The scale is inside .shot, which is overflow:hidden — so it can never add
   scroll width. 1.06 -> 1 is small enough to read as the image settling rather
   than as a zoom.                                                            */
.work .shot img{will-change:transform}
html.jsr .work .shot img{transform:scale(1.06)}
html.jsr .rv.in .work .shot img,
html.jsr .work.in .shot img{transform:none;transition:transform 1.15s cubic-bezier(.16,.84,.3,1)}
.work:hover .shot img{transform:scale(1.02);transition:transform .6s var(--ease)}

/* ---- header condense on scroll ------------------------------------------ */
header{transition:box-shadow .3s var(--ease),background .3s var(--ease)}
header.stuck{box-shadow:0 1px 0 rgba(17,19,23,.06),0 10px 30px -22px rgba(17,19,23,.5);
  background:rgba(255,255,255,.92)}
header .wrap{transition:min-height .32s var(--ease)}
header.stuck .wrap{min-height:58px}
.mark .glyph{transition:transform .32s var(--ease)}
header.stuck .mark .glyph{transform:scale(.9)}

/* ---- the seed mark draws itself, once ------------------------------------
   pathLength="1" normalises every path to a length of 1, so one dasharray
   value animates all three strokes evenly regardless of their real geometry.
   Header only — the footer copy is static, because a mark that redraws every
   time it scrolls into view stops being a detail and becomes a tic.          */
html.jsr header .mark .glyph path{stroke-dasharray:1;stroke-dashoffset:1;
  animation:draw 1.15s cubic-bezier(.5,0,.2,1) forwards}
html.jsr header .mark .glyph path:nth-child(1){animation-delay:.18s}
html.jsr header .mark .glyph path:nth-child(2){animation-delay:.34s}
html.jsr header .mark .glyph path:nth-child(3){animation-delay:.48s}
@keyframes draw{to{stroke-dashoffset:0}}

/* ---- nav underline sweep ------------------------------------------------- */
nav.links a{position:relative}
nav.links a::after{content:"";position:absolute;left:0;right:0;bottom:-5px;height:2px;
  background:var(--accent);transform:scaleX(0);transform-origin:right;
  transition:transform .34s var(--ease)}
nav.links a:hover::after,nav.links a[aria-current=page]::after{transform:scaleX(1);
  transform-origin:left}

/* ---- buttons: the arrow leads ------------------------------------------- */
.btn svg{transition:transform .3s var(--ease)}
.btn:hover svg:last-child{transform:translateX(3px)}
.work .go svg{transition:transform .3s var(--ease)}
.work:hover .go svg{transform:translateX(5px)}

/* ---- FAQ: height is animated by JS, the marker by CSS -------------------- */
.faq details{overflow:hidden}
.faq .a{will-change:opacity}
.faq details.animating{pointer-events:none}
.faq summary::after{transition:transform .34s var(--ease),margin-top .34s var(--ease)}

/* ---- form feedback ------------------------------------------------------- */
.msg.ok,.msg.err{animation:msgIn .42s var(--ease)}
@keyframes msgIn{from{opacity:0;transform:translateY(-6px)}to{opacity:1;transform:none}}
.field input,.field select,.field textarea{transition:border-color .18s var(--ease),
  box-shadow .18s var(--ease),transform .18s var(--ease)}

/* ===========================================================================
   The switch. Everything above resolves to its resting state, which is also
   its final state — so this is a true off, not a degraded animation.
   =========================================================================== */
@media (prefers-reduced-motion:reduce){
  html.jsr .hero .eyebrow,html.jsr .hero h1,html.jsr .hero .lede,
  html.jsr .hero .pricecard,html.jsr .hero .btnrow,html.jsr .hero .trust,
  html.jsr .pricecard .fig > span,
  html.jsr header .mark .glyph path{animation:none;opacity:1;transform:none;
    stroke-dashoffset:0}
  html.jsr .rvs > *,html.jsr .rvs.in > *{opacity:1;transform:none;transition:none}
  html.jsr .work .shot img,.work:hover .shot img{transform:none;transition:none}
  header,header .wrap,.mark .glyph,nav.links a::after,.btn svg,.work .go svg,
  .faq summary::after{transition:none}
  header.stuck .wrap{min-height:var(--navh)}
  header.stuck .mark .glyph{transform:none}
  .msg.ok,.msg.err{animation:none}
}

/* ---- hero media settles in last ------------------------------------------
   It arrives after the price, so the sequence reads: claim -> price -> proof.
   Scale lives inside the clipped frame, so it cannot add scroll width.        */
html.jsr .hmedia{opacity:0;transform:translateY(22px);
  animation:heroIn .95s var(--ease) forwards;animation-delay:.30s}
html.jsr .hmedia img{transform:scale(1.05);
  animation:mediaSettle 1.5s cubic-bezier(.16,.84,.3,1) forwards;animation-delay:.30s}
@keyframes mediaSettle{to{transform:none}}
@media (prefers-reduced-motion:reduce){
  html.jsr .hmedia,html.jsr .hmedia img{animation:none;opacity:1;transform:none}
}
