/* Motion layer, loaded after type-scale.css on every page.
   Easing palette (unchanged from the rest of the site):
     reveal  cubic-bezier(.22, 1, .36, 1)   fast out, soft settle
     travel  cubic-bezier(.65, 0, .35, 1)   in-out for wipes and carousels
   Everything here is skipped or flattened for prefers-reduced-motion. */

/* ---------- Page transitions (cross-document View Transitions, progressive enhancement) ---------- */
@view-transition { navigation: auto; }
::view-transition-old(root) { animation: 480ms cubic-bezier(.65, 0, .35, 1) both page-leave; }
::view-transition-new(root) { animation: 800ms cubic-bezier(.65, 0, .35, 1) both page-enter; }
@keyframes page-leave { to { opacity: .2; transform: translateY(-48px); } }
@keyframes page-enter { from { clip-path: inset(100% 0 0 0); } to { clip-path: inset(0 0 0 0); } }
/* Phones: a quick crossfade (GPU-only) instead of the full-screen clip-path wipe */
@media (max-width: 760px) {
  ::view-transition-old(root) { animation: 240ms ease-out both page-fade-out; }
  ::view-transition-new(root) { animation: 360ms ease-out both page-fade-in; }
}
@keyframes page-fade-out { to { opacity: 0; } }
@keyframes page-fade-in { from { opacity: 0; } }

/* ---------- Kinetic headline words: rise and un-rotate out of their masks ---------- */
.hero-word { transform: translateY(110%) rotate(6deg); transform-origin: 0% 100%; }
.boltwagon-page .bw-introduction h1 .hero-word-mask { padding-bottom: .16em; margin-bottom: -.16em; }

/* ---------- Line-by-line paragraph reveal (motion.js splits words) ---------- */
.is-split { animation: none !important; opacity: 1 !important; transform: none !important; }
.is-split .lw { display: inline-block; opacity: 0; transform: translateY(.4em); }

/* ---------- Image reveal: wipe up while the image settles from 1.12x ---------- */
/* Phones: the transform-only wipe mask motion.js wraps around each image */
.reveal-mask { display: block; height: 100%; overflow: hidden; border-radius: inherit; }
.reveal-mask--fill { position: absolute; inset: 0; }
.reveal-media { will-change: clip-path; }
.reveal-media.is-revealed { will-change: auto; }

/* Home project images zoom gently inside their mask on hover */
@media (hover: hover) and (pointer: fine) {
  .case .reveal-media.is-revealed :is(img, video) { transition: transform 900ms cubic-bezier(.22, 1, .36, 1); }
  .case .reveal-media.is-revealed:hover :is(img, video) { transform: scale(1.04); }
}

/* ---------- Links: the underline retracts right and redraws from the left ---------- */
.footer-socials a,
.footer-cta-link {
  text-decoration: none;
  background-image: linear-gradient(currentColor, currentColor);
  background-repeat: no-repeat;
  background-position: 0 100%;
  background-size: 100% 2px;
}
.footer-cta-link { background-size: 100% .06em; }
@media (hover: hover) and (pointer: fine) {
  .footer-socials a:hover,
  .footer-cta-link:hover { animation: underline-redraw 720ms cubic-bezier(.65, 0, .35, 1); }
}
@keyframes underline-redraw {
  0%   { background-position: 100% 100%; background-size: 100% var(--underline, 2px); }
  45%  { background-position: 100% 100%; background-size: 0% var(--underline, 2px); }
  46%  { background-position: 0 100%;    background-size: 0% var(--underline, 2px); }
  100% { background-position: 0 100%;    background-size: 100% var(--underline, 2px); }
}
.footer-cta-link { --underline: .06em; }

/* Magnetic elements are moved by motion.js; keep them on their own layer while active */
.is-magnetic { will-change: transform; }

/* ---------- Boltwagon dashboard carousel: transform-only (was left/width/height) ---------- */
/* The old positions were the same proportions at every size: 951px of 1360px = 69.93%. */
.bolt-dashboard-final { container-type: inline-size; }
.bolt-dashboard-final__screen,
.bolt-dashboard-final__screen:is(.is-left, .is-center, .is-right, .is-exit-left, .is-enter-right) {
  left: 50%;
  width: 61.62%;
  height: 75.38%;
  transform: translate3d(calc(-50% + var(--screen-x, 0px)), -50%, 0) scale(var(--screen-scale, 1));
  transition: transform 1200ms cubic-bezier(.65, 0, .35, 1), opacity 700ms cubic-bezier(.65, 0, .35, 1);
  will-change: transform, opacity;
}
.bolt-dashboard-final__screen.is-left { --screen-x: -69.93cqw; }
.bolt-dashboard-final__screen.is-center { --screen-scale: 1.1277; z-index: 2; }
.bolt-dashboard-final__screen.is-right { --screen-x: 69.93cqw; }
.bolt-dashboard-final__screen.is-exit-left { --screen-x: -110.29cqw; }
.bolt-dashboard-final__screen.is-enter-right { --screen-x: 110.29cqw; }

/* ---------- Hero cards: physics in the style of spencergabor.com ---------- */
/* motion.js writes --px/--py (spring offset in px) and --pv (spring velocity) every frame.
   They move the image and its shadow layer with the individual translate/rotate properties,
   which compose with the card's scroll-spread transform instead of replacing it. */
:root {
  /* Spring-shaped easing: ~14% overshoot, settles within the duration */
  --ease-out-elastic: linear(0, .5737 7.6%, .8382 11.87%, .9463 14.19%, 1.0292 16.54%, 1.0886 18.97%, 1.1258 21.53%, 1.137 22.97%, 1.1424 24.48%, 1.1423 26.1%, 1.1366 27.86%, 1.1165 31.01%, 1.0507 38.62%, 1.0219 42.57%, .9995 46.99%, .9872 51.63%, .9842 58.77%, 1.0011 81.26%, 1);
}
@media (prefers-reduced-motion: no-preference) {
  .hero-cards .hero-card { box-shadow: none; }
  .hero-cards .hero-card::before {
    content: "";
    position: absolute;
    inset: 0;
    z-index: -1;
    box-shadow: 0 9px 21px rgba(0, 0, 0, .1), 0 38px 38px rgba(0, 0, 0, .09), 0 85px 51px rgba(0, 0, 0, .05);
    pointer-events: none;
  }
  .hero-cards .hero-card > img,
  .hero-cards .hero-card::before {
    translate: calc(var(--px, 0) * 1px) calc(var(--py, 0) * 1px);
    rotate: calc(var(--pv, 0) * -.25deg);
    transition: scale .8s var(--ease-out-elastic);
  }
  /* Load-in: armed at scale 0, then each card pops in on its own delay */
  .hero-cards.is-armed .hero-card > img,
  .hero-cards.is-armed .hero-card::before { scale: 0; }
  .hero-cards.is-popping .hero-card > img,
  .hero-cards.is-popping .hero-card::before { transition: scale 1s var(--ease-out-elastic) var(--pop-delay, 0ms); }
}
@media (max-width: 760px) and (prefers-reduced-motion: no-preference) {
  /* The same three-layer shadow, scaled to 84px cards */
  .hero-cards .hero-card::before { box-shadow: 0 5px 12px rgba(0, 0, 0, .1), 0 18px 20px rgba(0, 0, 0, .08), 0 36px 28px rgba(0, 0, 0, .05); }
}
@media (min-width: 761px) and (hover: hover) and (pointer: fine) and (prefers-reduced-motion: no-preference) {
  /* The old hover lift is replaced by a subtle elastic scale; pressing pushes the card in */
  .hero-cards .hero-card:hover { transform: translateX(var(--spread-x)) translateY(var(--card-offset)) rotate(var(--card-tilt)); box-shadow: none; }
  .hero-cards:not(.is-armed) .hero-card:hover > img,
  .hero-cards:not(.is-armed) .hero-card:hover::before { scale: 1.04; }
  .hero-cards:not(.is-armed) .hero-card:active > img,
  .hero-cards:not(.is-armed) .hero-card:active::before { scale: .97; }
}

/* The hero clipped everything at its bottom edge, slicing the lifted card's shadow.
   Clip sideways only (the phone carousel slides cards off-screen horizontally). */
@supports (overflow: clip) {
  .hero { overflow-x: clip; overflow-y: visible; }
}

/* ---------- "View Work" cursor bubble ---------- */
/* script.js sets --bubble-bg/--bubble-fg per project and --bubble-angle/--bubble-sx/--bubble-sy
   from the bubble's velocity. The colour circle is a separate layer so only it squashes. */
.work-preview__cursor > .work-preview__overlay {
  width: 144px;
  height: 144px;
  background: none;
  border: 0;
  box-shadow: none;
  -webkit-backdrop-filter: none;
  backdrop-filter: none;
  color: var(--bubble-fg, #fff);
  font-size: 16px;
  font-weight: 600;
  letter-spacing: -.01em;
  opacity: 1;
  isolation: isolate;
  /* Exit: a quick shrink with a twist */
  transform: translate(-50%, -50%) scale(0) rotate(-20deg);
  transition: transform 280ms cubic-bezier(.65, 0, .35, 1);
}
.work-preview__cursor > .work-preview__overlay::before {
  content: "";
  position: absolute;
  inset: 0;
  z-index: -1;
  border-radius: 50%;
  background: var(--bubble-bg, #171717);
  box-shadow: 0 12px 32px rgba(0, 0, 0, .18);
  transform: rotate(var(--bubble-angle, 0deg)) scale(var(--bubble-sx, 1), var(--bubble-sy, 1));
  transition: background-color 300ms ease;
}
/* Enter: an elastic pop that rotates upright */
.work-preview__cursor.is-visible > .work-preview__overlay {
  opacity: 1;
  transform: translate(-50%, -50%) scale(1) rotate(0deg);
  transition: transform 700ms var(--ease-out-elastic);
}
.work-preview__label {
  display: block;
  transform: translateY(16px);
  opacity: 0;
  transition: transform 200ms cubic-bezier(.65, 0, .35, 1), opacity 160ms linear;
}
.work-preview__cursor.is-visible .work-preview__label {
  transform: translateY(0);
  opacity: 1;
  transition: transform 520ms cubic-bezier(.22, 1, .36, 1) 90ms, opacity 240ms linear 90ms;
}

/* ---------- Nav: liquid active-tab marker ---------- */
/* .nav-indicator becomes a full-size layer holding two blobs; script.js moves them and the
   #nav-goo SVG filter melts them into one drop of liquid. */
.desktop-nav .nav-indicator {
  inset: 0;
  width: auto !important;
  height: auto !important;
  transform: none !important;
  background: none;
  border-radius: 0;
  filter: url(#nav-goo);
  will-change: auto;
}
.nav-blob {
  position: absolute;
  top: 0;
  left: 0;
  background: #000;
  border-radius: 6px;
  will-change: transform, width, height;
}

/* Layering: pill backgrounds, then the liquid, then the text. The active tab no longer turns
   black on its own (that happened before the liquid arrived); tabs light up as it covers them. */
@media (min-width: 761px) {
  .desktop-nav a { z-index: auto; }
  .desktop-nav .nav-indicator { z-index: 1; }
  .desktop-nav .nav-link__text { position: relative; z-index: 2; transition: color 140ms linear; }
  #home .topbar .desktop-nav a:is(.is-active, [aria-current="page"], :active) { background: transparent; color: var(--ink); }
  .desktop-nav a.is-lit .nav-link__text { color: #fff; }
}

/* ---------- Home footer reveal (motion.js adds .is-armed, then .is-revealed in view) ---------- */
.portfolio-footer--home.is-armed .footer-top h2 > span { overflow: hidden; padding: 0 .1em .1em; margin: 0 -.1em -.1em; }
.portfolio-footer--home.is-armed .footer-line {
  display: block;
  transform: translateY(110%) rotate(4deg);
  transform-origin: 0% 100%;
  transition: transform 1000ms cubic-bezier(.22, 1, .36, 1) calc(var(--i, 0) * 100ms);
}
.portfolio-footer--home.is-armed .footer-smile svg {
  rotate: -60deg;
  scale: .7;
  transition: rotate 1200ms cubic-bezier(.22, 1, .36, 1) 300ms, scale 1200ms cubic-bezier(.34, 1.56, .64, 1) 300ms;
}
.portfolio-footer--home.is-armed .footer-smile :is(circle, path) { stroke-dasharray: 1 1; stroke-dashoffset: 1.001; }
.portfolio-footer--home.is-armed .footer-smile circle { transition: stroke-dashoffset 900ms cubic-bezier(.65, 0, .35, 1) 300ms; }
.portfolio-footer--home.is-armed .footer-smile path { transition: stroke-dashoffset 700ms cubic-bezier(.65, 0, .35, 1) 900ms; }
.portfolio-footer--home.is-armed .footer-socials a {
  opacity: 0;
  translate: 0 16px;
  transition: color 320ms ease, opacity 500ms linear calc(400ms + var(--i, 0) * 90ms), translate 900ms cubic-bezier(.22, 1, .36, 1) calc(400ms + var(--i, 0) * 90ms);
}
.portfolio-footer--home.is-armed .footer-bottom {
  opacity: 0;
  transition: opacity 700ms linear 800ms;
}
.portfolio-footer--home.is-revealed .footer-line { transform: none; }
.portfolio-footer--home.is-revealed .footer-smile svg { rotate: 0deg; scale: 1; }
.portfolio-footer--home.is-revealed .footer-smile :is(circle, path) { stroke-dashoffset: 0; }
.portfolio-footer--home.is-revealed .footer-socials a { opacity: 1; translate: 0 0; }
.portfolio-footer--home.is-revealed .footer-bottom { opacity: 1; }

/* ---------- Reduced motion ---------- */
@media (prefers-reduced-motion: reduce) {
  ::view-transition-old(root), ::view-transition-new(root) { animation: none; }
  .hero-word { transform: none; }
  .footer-socials a:hover, .footer-cta-link:hover { animation: none; }
  .bolt-dashboard-final__screen { transition-duration: 0ms; }
  .work-preview__cursor > .work-preview__overlay,
  .work-preview__cursor.is-visible > .work-preview__overlay,
  .work-preview__label,
  .work-preview__cursor.is-visible .work-preview__label { transition: none; }
  .work-preview__cursor > .work-preview__overlay::before { transform: none; }
}
