/* Boostonic scroll-motion layer. Everything below only applies when JS adds .js-motion
   (i.e. not for prefers-reduced-motion), so the base site works unchanged without it. */

/* ---------- stars (hero, orbit, contact) ---------- */
.stars { position: absolute; inset: 0; pointer-events: none; overflow: hidden; }
.stars i {
  position: absolute; width: var(--s, 2px); height: var(--s, 2px); border-radius: 50%; background: #fff;
  opacity: .1; animation: twinkle var(--d, 4s) ease-in-out var(--delay, 0s) infinite;
}
@keyframes twinkle { 50% { opacity: var(--o, .9); } }
.hero .stars { z-index: 1; }

/* ---------- hero ---------- */
.js-motion .hero-art img {
  transform: translate3d(calc(var(--mx, 0) * -14px), calc(var(--hero-y, 0px) + var(--my, 0) * -10px), 0) scale(var(--hero-zoom, 1.06));
  transition: transform .25s linear;
}
.js-motion .hero-copy { transform: translate3d(0, var(--copy-y, 0px), 0); opacity: var(--copy-o, 1); will-change: transform, opacity; }
.js-motion .art-label { transform: translate3d(0, calc(var(--copy-y, 0px) * -.5), 0); }
.js-motion.hero-in .hero h1 { animation: rise 1.1s cubic-bezier(.2,.8,.2,1) both; }
.js-motion.hero-in .hero .eyebrow { animation: rise .9s cubic-bezier(.2,.8,.2,1) .05s both; }
.js-motion.hero-in .hero-copy > p:not(.eyebrow) { animation: rise .9s cubic-bezier(.2,.8,.2,1) .25s both; }
.js-motion.hero-in .hero-actions { animation: rise .9s cubic-bezier(.2,.8,.2,1) .4s both; }
.js-motion.hero-in .hero-note { animation: rise .9s cubic-bezier(.2,.8,.2,1) .55s both; }
.js-motion.hero-in .art-label { animation: rise 1s ease .8s both; }
@keyframes rise { from { opacity: 0; transform: translateY(46px); filter: blur(8px); } to { opacity: 1; transform: none; filter: none; } }
.scroll-cue::before { transform-origin: left; animation: cue 2.2s ease-in-out infinite; }
@keyframes cue { 0%,100% { transform: scaleX(.3); } 50% { transform: scaleX(1); } }

/* marquee speeds up with scroll velocity (JS sets --mq-skew) */
.js-motion .marquee-track { animation-duration: 34s; }
.js-motion .marquee { transform: skewY(var(--mq-skew, 0deg)); transition: transform .3s ease-out; }

/* ---------- intro: word-by-word fill ---------- */
.js-motion .intro-main h2 .mw { opacity: .14; transition: opacity .35s ease; }
.js-motion .intro-main h2 .mw.on { opacity: 1; }
.js-motion .intro-main h2 span[data-i18n="intro.accent"] .mw { transition-duration: .5s; }

/* ---------- services: active row follows scroll ---------- */
.js-motion .service-item { position: relative; }
.js-motion .service-item::after {
  content: ''; position: absolute; left: 0; right: 0; bottom: -1px; height: 2px; background: var(--lime);
  transform: scaleX(var(--line, 0)); transform-origin: left;
}
.js-motion .service-item h3, .js-motion .service-item p, .js-motion .service-num { transition: color .4s ease, transform .5s cubic-bezier(.2,.8,.2,1), opacity .4s ease; }
.js-motion .service-item:not(.is-active) h3 { color: #6f7b76; }
.js-motion .service-item:not(.is-active) p { opacity: .45; }
.js-motion .service-item.is-active h3 { color: #fff; transform: translateX(14px); }
.js-motion .service-item.is-active .service-num { color: var(--lime); }
.js-motion .service-item.is-active .service-mark { border-color: var(--lime); }
.js-motion .service-item.is-active .service-mark::after { transform: rotate(90deg); }

/* ---------- orbit: pinned astronaut zoom ---------- */
.orbit { position: relative; height: 260vh; background: #02050c; color: #fff; }
.orbit-sticky { position: sticky; top: 0; height: 100svh; overflow: hidden; isolation: isolate; }
.orbit-img { position: absolute; inset: 0; }
.orbit-img img {
  width: 100%; height: 100%; object-fit: cover; object-position: 70% 50%;
  transform-origin: 77% 58%; transform: scale(var(--oz, 1)); will-change: transform;
}
.orbit-sticky::after {
  content: ''; position: absolute; inset: 0; z-index: 2; pointer-events: none;
  background: radial-gradient(ellipse at 77% 58%, transparent calc(var(--clear, 45) * 1%), rgba(2,5,12,var(--dim, .85)) 85%),
              linear-gradient(90deg, rgba(2,5,12,.82), rgba(2,5,12,.15) 70%);
}
.orbit .stars { z-index: 1; }
.orbit-copy {
  position: absolute; z-index: 3; left: var(--gutter); right: var(--gutter); bottom: 14vh; max-width: 760px;
  transform: translate3d(0, var(--oy, 0px), 0); opacity: var(--oo, 1);
}
.orbit-copy h2 { margin: 20px 0 0; font-size: clamp(44px, 7.4vw, 108px); font-weight: 600; letter-spacing: -.085em; line-height: .93; }
.orbit-copy h2 em { font-style: normal; color: var(--lime); }
.orbit-copy p.sub { margin: 22px 0 0; max-width: 430px; color: #b5c0cc; font-size: clamp(16px, 1.4vw, 19px); }
.orbit-hud {
  position: absolute; z-index: 3; right: var(--gutter); top: 120px; display: grid; gap: 8px; text-align: right;
  font-size: 11px; font-weight: 700; letter-spacing: .16em; text-transform: uppercase; color: #9fb0c6;
}
.orbit-hud b { display: block; color: var(--lime); font-size: clamp(26px, 3vw, 40px); letter-spacing: -.04em; font-variant-numeric: tabular-nums; }

/* ---------- work: cards stay aligned; screenshots zoom in as each card arrives ---------- */
.js-motion .project-grid { align-items: stretch; }
.js-motion .project-card .site-screenshot { transform: scale(1.14); transition: transform 1.4s cubic-bezier(.2,.8,.2,1); }
.js-motion .project-card.visible .site-screenshot { transform: scale(1); }
.js-motion .project-card.visible:hover .site-screenshot { transform: scale(1.035); transition-duration: .8s; }
.js-motion .project-card .phone-mock { opacity: 0; translate: 0 40px; transition: opacity .8s ease .35s, translate .9s cubic-bezier(.2,.8,.2,1) .35s; }
.js-motion .project-card.visible .phone-mock { opacity: 1; translate: 0 0; }

/* ---------- pricing ---------- */
.js-motion .pricing-card { transition: transform .7s cubic-bezier(.2,.8,.2,1), box-shadow .35s ease, opacity .7s ease, filter .7s ease; }

/* ---------- approach: pinned horizontal journey (desktop only) ---------- */
.approach.is-pinned { padding-block: 0; }
.approach.is-pinned .approach-sticky {
  position: sticky; top: 0; height: 100svh; overflow: hidden; display: flex; align-items: center;
}
.approach.is-pinned .approach-sticky > .wrap { width: 100%; max-width: none; margin: 0; padding-left: var(--gutter); }
.approach.is-pinned .approach-head { max-width: 1320px; padding-right: var(--gutter); }
.approach.is-pinned .section-title { font-size: clamp(36px, 4.6vw, 62px); }
.approach.is-pinned .approach-steps {
  display: flex; gap: 20px; width: max-content; margin-top: 44px; padding-right: var(--gutter);
  border-top: 0; will-change: transform; transform: translate3d(var(--hx, 0px), 0, 0);
}
.approach.is-pinned .approach-step {
  position: relative; width: min(400px, 36vw); min-height: 290px; display: flex; flex-direction: column; justify-content: space-between;
  gap: 18px; padding: 26px 26px 28px; border: 1px solid rgba(11,14,17,.16); border-radius: 8px 8px 26px 8px; background: var(--paper-white);
  transition: background .4s ease, color .4s ease, transform .5s cubic-bezier(.2,.8,.2,1);
}
.approach.is-pinned .approach-step.is-lit { background: var(--ink); color: #fff; transform: translateY(-10px); }
.approach.is-pinned .approach-step.is-lit p { color: #aab4af; }
.approach.is-pinned .approach-step.is-lit .step-index { color: var(--lime); }
.approach.is-pinned .approach-step .step-index { font-size: 46px; font-weight: 700; letter-spacing: -.07em; line-height: 1; }
.approach-rail {
  display: none; position: absolute; left: var(--gutter); right: var(--gutter); bottom: 9vh; height: 2px; background: rgba(11,14,17,.16);
}
.approach.is-pinned .approach-rail { display: block; }
.approach-rail i { display: block; height: 100%; background: var(--blue); transform-origin: left; transform: scaleX(var(--rail, 0)); }

/* ---------- contact ---------- */
.contact-art { position: absolute; inset: 0; z-index: 0; overflow: hidden; pointer-events: none; }
.contact-art img {
  position: absolute; right: -6%; top: -8%; width: 70%; height: 116%; object-fit: cover; object-position: 75% 50%;
  mix-blend-mode: luminosity; opacity: .42;
  -webkit-mask-image: linear-gradient(90deg, transparent 0%, #000 55%); mask-image: linear-gradient(90deg, transparent 0%, #000 55%);
  transform: translate3d(0, var(--cy, 0px), 0) scale(var(--cz, 1.05));
}
.contact .contact-inner { z-index: 1; }
.js-motion .contact h2 { transform: scale(var(--ch, 1)); transform-origin: left bottom; }

/* ---------- mobile / reduced motion ---------- */
@media (max-width: 900px) {
  .orbit { height: 220vh; }
  .orbit-hud { top: 96px; }
  .orbit-copy { bottom: 10vh; }
  .orbit-img img { object-position: 78% 50%; transform-origin: 80% 60%; }
  .contact-art img { width: 100%; right: -20%; opacity: .3; }
}
@media (prefers-reduced-motion: reduce) {
  .orbit { height: auto; }
  .orbit-sticky { position: relative; height: 80svh; }
  .stars i, .scroll-cue::before { animation: none; }
}

/* ---------- portfolio mockups: clean single-frame phone, cropped captures ----------
   The mobile captures are full iPhone screenshots (status bar, notch, Safari bar). We crop to the page
   content only: --pt = px cropped from top, --ph = content height, --pf = full image height. */
.device-stage { --pt: 48; --ph: 656; --pf: 800; }
.device-stage--blerdon { --pt: 48; --ph: 612; --pf: 756; }

.site-screenshot { width: 102%; max-width: none; object-fit: cover; object-position: left top; }

.phone-mock {
  width: auto; height: 76%; max-height: none; aspect-ratio: 370 / var(--ph);
  right: 5%; bottom: 9%; padding: 3px; border: 0; border-radius: 17px; background: #0b0d0f;
  box-shadow: 0 0 0 1px rgba(255,255,255,.3), 0 2px 0 rgba(255,255,255,.12) inset, 0 20px 40px rgba(0,0,0,.5);
  transform: none;
}
.project-card:hover .phone-mock { transform: translate3d(0, -5px, 0); }
.phone-screen { border-radius: 14px; background: #000; }
.phone-screenshot {
  position: absolute; left: 0; display: block; width: 100%; max-width: none; object-fit: fill;
  top: calc(var(--pt) / var(--ph) * -100%); height: calc(var(--pf) / var(--ph) * 100%);
}

/* ---------- "Ask Boostonic" launcher: pop-in, soft pulse ring, light sweep, twinkling icon ----------
   Uses the individual scale/translate properties and box-shadow only, so it never fights the
   magnetic-hover transform set elsewhere. Calms down on hover and while the assistant is open. */
.assistant-launcher {
  overflow: hidden;
  animation: ask-pop .8s cubic-bezier(.2,1.5,.4,1) 1.2s both, ask-ring 3.4s ease-out 2.4s infinite;
}
.assistant-launcher > * { position: relative; z-index: 1; }
.assistant-launcher::after {
  content: ''; position: absolute; z-index: 0; top: 0; bottom: 0; left: -60%; width: 38%; pointer-events: none;
  background: linear-gradient(100deg, transparent, rgba(255,255,255,.7), transparent); transform: skewX(-20deg);
  animation: ask-sweep 4.6s ease-in-out 3s infinite;
}
.assistant-launcher-icon { animation: ask-spark 3.6s ease-in-out infinite; }
.assistant-launcher:hover, .assistant-launcher[aria-expanded="true"] { animation: ask-pop .8s cubic-bezier(.2,1.5,.4,1) 1.2s both; }
.assistant-launcher:hover::after, .assistant-launcher[aria-expanded="true"]::after { animation: none; opacity: 0; }

@keyframes ask-pop { from { opacity: 0; scale: .55; translate: 0 26px; } to { opacity: 1; scale: 1; translate: 0 0; } }
@keyframes ask-ring {
  0%   { box-shadow: 0 12px 34px rgba(0,0,0,.26), 0 0 0 0 rgba(201,243,29,.6); }
  65%  { box-shadow: 0 12px 34px rgba(0,0,0,.26), 0 0 0 20px rgba(201,243,29,0); }
  100% { box-shadow: 0 12px 34px rgba(0,0,0,.26), 0 0 0 20px rgba(201,243,29,0); }
}
@keyframes ask-sweep { 0% { left: -60%; } 28%, 100% { left: 140%; } }
@keyframes ask-spark { 0%, 100% { transform: rotate(0) scale(1); } 50% { transform: rotate(90deg) scale(1.18); } }
@media (prefers-reduced-motion: reduce) {
  .assistant-launcher, .assistant-launcher::after, .assistant-launcher-icon { animation: none; }
}
