/* Motion layer — additive only, loaded after Webflow's generated CSS.
   Every hidden state here is gated behind .w-mod-js (Webflow's own
   inline script adds this to <html> before body paints), so a visitor
   with JavaScript disabled always sees the full page with no animation. */

@view-transition {
  navigation: auto;
}

/* Shared-element hooks: give the homepage card and its case-study hero
   the same class to pair them for the cross-document morph. */
.vt-locofy-plugin {
  view-transition-name: locofy-plugin-hero;
}

.vt-homage {
  view-transition-name: homage-hero;
}

.vt-locofy-website {
  view-transition-name: locofy-website-hero;
}

.vt-locofy-builder {
  view-transition-name: locofy-builder-hero;
}

.vt-homage-enterprise {
  view-transition-name: homage-enterprise-hero;
}

/* ---- Cinematic entrance overlay (case-study pages) ---- */
.page-transition-overlay {
  position: fixed;
  inset: 0;
  background: var(--_main-style---bacground);
  z-index: 9999;
  opacity: 0;
  visibility: hidden;
  pointer-events: none;
}

html.motion-loading {
  overflow: hidden;
}

html.motion-loading .page-transition-overlay {
  opacity: 1;
  visibility: visible;
}

/* ---- Homepage entrance: dot -> avatar -> wave -> expand -> reveal ----
   Every stage is driven by GSAP in motion.js (autoAlpha, scale, rotation).
   Everything here defaults to hidden so a visitor with JS disabled, or if
   motion.js fails outright, just sees the homepage immediately with
   nothing covering it and no reveal animation to wait on. */
.home-intro-overlay {
  position: fixed;
  inset: 0;
  z-index: 9998;
  background: var(--_main-style---bacground);
  opacity: 0;
  visibility: hidden;
  pointer-events: none;
}

html.motion-loading .home-intro-overlay {
  opacity: 1;
  visibility: visible;
}

.home-intro-dot {
  position: fixed;
  top: 50%;
  left: 50%;
  width: 40px;
  height: 40px;
  border-radius: 50%;
  background: var(--_main-style---dark-accent);
  transform: translate(-50%, -50%);
  box-shadow: 0 0 24px 8px rgba(12, 208, 138, 0.55);
  z-index: 9999;
  opacity: 0;
  pointer-events: none;
}

.home-intro-avatar-wrap {
  position: fixed;
  top: 50%;
  left: 50%;
  width: 84px;
  height: 84px;
  transform: translate(-50%, -50%);
  z-index: 9999;
  opacity: 0;
  pointer-events: none;
}

.home-intro-avatar {
  display: block;
  width: 100%;
  height: 100%;
  border-radius: 50%;
  object-fit: cover;
  box-shadow: 0 0 24px 8px rgba(12, 208, 138, 0.45);
}

.home-intro-hand {
  position: absolute;
  top: -14px;
  right: -18px;
  width: 26px;
}

html.motion-fallback .home-intro-avatar-wrap {
  display: none !important;
}

/* Two dark panels that wipe in from the left to cover the fully-expanded
   green dot, before the homepage fades in underneath. Positioned off-
   screen by default via a plain transform (no opacity/JS dependency), so
   with JS disabled they simply never appear. */
.home-intro-wipe {
  position: fixed;
  inset: 0;
  z-index: 10000;
  background: var(--_main-style---bacground);
  transform: translateX(-100%);
  pointer-events: none;
}

html.motion-fallback .home-intro-wipe {
  display: none !important;
}

/* ---- Homepage entrance: header, hero, and the works section fade in
   together as the intro overlay clears. ---- */
.w-mod-js [data-home-reveal] {
  opacity: 0;
}

html.motion-fallback [data-home-reveal] {
  opacity: 1 !important;
  visibility: visible !important;
  transform: none !important;
}

html.motion-fallback .home-intro-overlay,
html.motion-fallback .home-intro-dot {
  display: none !important;
}

/* ---- Entrance-hidden groups (title / metadata) ----
   The hero image is intentionally NOT hidden here: it stays visible the
   whole time and is simply covered by the opaque overlay above, so the
   "image reveal" step is just the overlay fading away. Hiding it too was
   the earlier bug that left case-study hero images stuck invisible when
   the entrance timeline never touched their opacity. */
.w-mod-js [data-motion-reveal="title"],
.w-mod-js [data-motion-reveal="meta"] {
  opacity: 0;
}

/* ---- Scroll reveal ---- */
.w-mod-js [data-animate] {
  opacity: 0;
}

/* ---- Homepage gallery: label-on-hover + custom cursor ----
   Pure CSS (no JS dependency) so it still works even if the cursor
   script never loads. The thumbnail (flex: 1 with a 0% basis) already
   grows to fill whatever space is left in the fixed-height card, so it
   shrinks automatically as the label below it grows — no height math
   needed here, and the card's own total height never changes. The
   label itself is a solid block in the same color as the card, so it
   reads as the card's own bottom padding expanding to reveal it rather
   than a caption floating on top of the image. */
.port-item {
  overflow: hidden;
}

/* The thumbnail's own box changes height on hover (see below), and with
   background-size: cover that recomputed the image's crop and made it
   read as zooming out. A ::before layer decouples the zoom from that:
   it's inherit-copied from the box's own background so the image itself
   never needs duplicating per thumbnail, sized to fill the box exactly,
   and only it (not the box) scales up — clipped by the box's own
   overflow:hidden + border-radius, so it can't bleed into the label. */
.image-con {
  position: relative;
  overflow: hidden;
  transition: border-radius 0.4s ease;
}

.image-con::before {
  content: '';
  position: absolute;
  inset: 0;
  background-image: inherit;
  background-position: inherit;
  background-size: cover;
  transition: transform 0.4s ease-out;
}

.port-item:hover .image-con {
  border-radius: 7px 7px 0 0;
}

/* Slow, continuous creep while the mouse stays over the card — the fast
   transition above only takes over once hover ends, so it snaps back to
   normal quickly instead of un-zooming just as slowly. */
.port-item:hover .image-con::before {
  transform: scale(1.08);
  transition: transform 6s ease-out;
}

.port-item .hover-info {
  height: 0;
  margin-top: -20px;
  overflow: hidden;
  background-color: var(--_main-style---lightbg);
  border-radius: 0 0 7px 7px;
  padding: 0;
  opacity: 0;
  transition: height 0.4s ease, margin-top 0.4s ease, padding 0.4s ease, opacity 0.3s ease;
  pointer-events: none;
}

.port-item:hover .hover-info {
  height: 75px;
  margin-top: 0;
  padding: 5px 0;
  opacity: 1;
}

/* ---- About page: Instagram thumbnail hover overlay ----
   A tinted sheet slides in from the left on hover and back out the same
   way on leave. The ease-out-back curve overshoots slightly past full
   coverage before settling, so the slide reads as a deliberate motion
   rather than a flat linear wipe. */
.ig-lightbox {
  position: relative;
  overflow: hidden;
  display: block;
}

.ig-lightbox::after {
  content: '';
  position: absolute;
  inset: 0;
  background-color: #44ffbc;
  opacity: 0.4;
  transform: translateX(-100%);
  transition: transform 0.6s cubic-bezier(0.34, 1.56, 0.64, 1);
  pointer-events: none;
}

.ig-lightbox:hover::after {
  transform: translateX(0);
}

/* "View all photos" link: brightens (text + icon together, via filter
   rather than recoloring each separately) and its icon eases 3px
   further from the text on hover. */
.view-photos {
  transition: gap 0.3s ease, filter 0.3s ease;
}

.view-photos:hover {
  filter: brightness(1.2);
  gap: 11px;
}

/* ---- Footer contact links: email + the 3 social icons ----
   Shared across every page. Icons are plain <img>s referencing static
   SVGs (not currentColor-aware), so their own feedback is a subtle
   scale/nudge via transform rather than a color change; the text next
   to each one shifts to the accent green, and the whole link lifts
   slightly, tying the two together. */
.email-block,
.socialblk {
  transition: transform 0.3s ease;
}

.email-block:hover,
.socialblk:hover {
  transform: translateY(-2px);
}

.email-block img,
.socialblk img {
  transition: transform 0.35s cubic-bezier(0.22, 1, 0.36, 1);
}

.email-block:hover img {
  transform: translate(3px, -3px);
}

.socialblk:hover img {
  transform: scale(1.08);
}

.email-block .email,
.socialblk .myh3 {
  transition: color 0.3s ease;
}

.email-block:hover .email,
.socialblk:hover .myh3 {
  color: var(--_main-style---dark-accent);
}

.custom-cursor {
  position: fixed;
  top: 0;
  left: 0;
  width: 72px;
  height: 72px;
  border-radius: 50%;
  background-color: #0cd08ac2;
  -webkit-backdrop-filter: blur(3px);
  backdrop-filter: blur(3px);
  display: flex;
  align-items: center;
  justify-content: center;
  pointer-events: none;
  z-index: 10000;
  opacity: 0;
}

.custom-cursor img {
  width: 22px;
  height: 22px;
}

@media (hover: hover) and (pointer: fine) {
  a[data-cursor-hover] {
    cursor: none;
  }
}

/* Hides the native cursor while the avatar-cursor is visible (see
   initAvatarCursor below, which toggles this class). Driven by JS
   rather than a plain :hover rule because one of its two triggers is
   the hero graphic, which sits at z-index: -1 and never registers as
   "hovered" for native hit-testing (same reason it needs a geometric
   mousemove check instead of mouseenter/leave). */
html.avatar-cursor-active {
  cursor: none;
}

/* ---- Hero: avatar + waving hand as a custom cursor ----
   Originally a static graphic sitting next to "Raymond"; now hidden by
   default and repositioned to follow the mouse instead (js/motion.js
   initAvatarCursor), appearing only while hovering the name or the hero
   graphic. Fixed + top/left 0 so GSAP can drive its position with
   x/y — the same approach as .custom-cursor above, kept as a separate
   element since this one has its own artwork (a photo + a hand icon)
   rather than a plain circle. */
.avatar-pointer {
  position: fixed;
  top: 0;
  left: 0;
  right: auto;
  bottom: auto;
  z-index: 10000;
  opacity: 0;
  pointer-events: none;
}

/* ---- Hero graphic: organic glow/blur base ----
   The filter (SVG feTurbulence + feDisplacementMap + glow) lives inline in
   index.html and drives the soft, shifting glow/blur base; its attributes
   are animated by GSAP in motion.js. Default attribute values render a
   normal, undistorted image, so this is a harmless no-op with JS disabled. */
.hero-glass-wrap {
  position: relative;
  flex: 1;
  max-width: 100%;
}

.hero-glass-wrap .rgraphic {
  display: block;
  width: 100%;
  height: auto;
  filter: url("#hero-glass");
}

/* ---- Safety net: if motion.js never finishes (blocked CDN, JS error),
   force everything visible instead of leaving the page stuck. ---- */
html.motion-fallback [data-motion-reveal],
html.motion-fallback [data-animate] {
  opacity: 1 !important;
  transform: none !important;
}

html.motion-fallback .page-transition-overlay {
  display: none !important;
}

html.motion-fallback {
  overflow-y: visible !important;
}
