/**
 * Site-wide motion + hand-added portfolio gallery styling.
 *
 * This used to live appended to the end of the compiled Tailwind chunk
 * (_next/static/chunks/*.css) because that was the fastest way to get
 * new CSS live on a static export with no build step. That's a real
 * durability problem though: a real rebuild from source (see README's
 * "Route B") regenerates that chunk from scratch and would silently
 * drop everything hand-added to it -- unlike this file and images/,
 * which the documented re-export recipe explicitly carries forward
 * ("re-inject site-motion.js", "merge back images/"). Pulling this out
 * into its own hand-maintained file, loaded via a <link> next to
 * site-motion.js's <script> tag on every page, makes it survive a
 * rebuild the same way the JS already does -- nothing to remember.
 *
 * Every selector here targets classes that already exist in the
 * compiled output and in every page's markup (.card-light, header
 * nav links, bg-lime buttons, the rounded-xl+bg-gradient-to-br icon
 * badges) -- nothing needed to change in any HTML file for the base
 * motion pass below to apply everywhere. The only new hooks are two
 * things a companion script (assets/js/site-motion.js) does at
 * runtime: it adds a `js` class to <html> and an `in-view` class to
 * elements as they scroll into place. .reveal-up is written so it
 * stays fully visible unless that `js` class is present (see below)
 * -- if the script fails to load for any reason, nothing on the page
 * goes invisible.
 *
 * Nothing here needs its own prefers-reduced-motion handling: the
 * compiled Tailwind output already clamps every animation/transition
 * duration to ~0 for reduced-motion users globally, so nothing added
 * below has to repeat that.
 */

/* ---- scroll reveal ---- */
.reveal-up{opacity:1;transform:none}
html.js .reveal-up{opacity:0;transform:translateY(28px);transition:opacity .7s cubic-bezier(.16,.84,.44,1),transform .7s cubic-bezier(.16,.84,.44,1)}
html.js .reveal-up.in-view{opacity:1;transform:none}
html.js .reveal-up.reveal-pop{transform:translateY(20px) scale(.94)}
html.js .reveal-up.reveal-pop.in-view{transform:none}

/* ---- card hover: sheen + icon-badge tilt ----
   .card-light already ships a lift (hover:-translate-y-1, inline per
   card) and a layered glow box-shadow (see .card-light:hover, compiled
   from the source design) -- this only adds a diagonal light sheen
   across the card and a spring tilt on its icon badge, nothing here
   touches the existing shadow/lift so they combine. */
.card-light{position:relative;overflow:hidden}
.card-light::before{content:"";position:absolute;inset:0;background:linear-gradient(135deg,rgba(24,68,216,.07),rgba(227,254,105,.13) 55%,transparent 75%);opacity:0;transition:opacity .5s ease;pointer-events:none}
.card-light:hover::before{opacity:1}
.card-light [class~="rounded-xl"][class*="bg-gradient-to-br"]{transition:transform .5s cubic-bezier(.34,1.56,.64,1)}
.card-light:hover [class~="rounded-xl"][class*="bg-gradient-to-br"]{transform:rotate(-6deg) scale(1.08)}

/* ---- CTA button shine sweep ----
   `class~=` matches the exact "bg-lime" token only -- deliberately
   not a substring selector, which would also catch the *different*
   literal class "bg-lime/55" used on the decorative blur blobs. */
a[class~="bg-lime"],button[class~="bg-lime"]{position:relative;overflow:hidden}
a[class~="bg-lime"]::after,button[class~="bg-lime"]::after{content:"";position:absolute;top:0;left:-60%;width:35%;height:100%;background:linear-gradient(115deg,transparent,rgba(137,186,215,.75),transparent);transform:skewX(-18deg);transition:left .7s ease;pointer-events:none}
a[class~="bg-lime"]:hover::after,button[class~="bg-lime"]:hover::after{left:130%}

/* ---- header nav underline sweep ---- */
header nav>a{position:relative}
header nav>a::after{content:"";position:absolute;left:0;right:0;bottom:-4px;height:2px;background:linear-gradient(90deg,var(--color-brand-blue),var(--color-lime));transform:scaleX(0);transform-origin:left;transition:transform .35s cubic-bezier(.16,.84,.44,1)}
header nav>a:hover::after,header nav>a[class~="text-brand-blue"]::after{transform:scaleX(1)}

/* ---- header logo micro-interaction ---- */
header a[href="/"] img{transition:transform .45s cubic-bezier(.34,1.56,.64,1)}
header a[href="/"]:hover img{transform:rotate(-8deg) scale(1.06)}

/* Hero cursor spotlight removed on request (the radial glow that tracked
   the pointer inside hero sections). .bg-hero-gradient stays position:relative
   -- the pan animation (::before below) and the hero particles/media
   inserted at runtime are absolutely positioned against it. */
.bg-hero-gradient{position:relative}

/* ---- scroll progress bar ----
   Element itself is created at runtime by site-motion.js (it is not
   part of any page's markup) -- styled here so both live together. */
#site-scroll-progress{position:fixed;top:0;left:0;height:3px;width:0%;background:linear-gradient(90deg,var(--color-lime),var(--color-brand-blue));z-index:999;pointer-events:none}

/* ---- stat number pop ----
   site-motion.js count-up-animates any big font-extrabold number
   (800+, 10+, 3, 100%...) from 0 as it scrolls into view; this just
   gives the moment the count lands a little extra life. */
.stat-counted{display:inline-block;animation:statPop .5s cubic-bezier(.34,1.56,.64,1)}
@keyframes statPop{0%{transform:scale(1)}40%{transform:scale(1.12)}100%{transform:scale(1)}}

/* ---- 3D tilt on cards ----
   site-motion.js sets the transform inline per mousemove (needs the
   live cursor position, can't be done in CSS alone); this just adds
   the perspective + a smooth spring back to flat on mouseleave. */
.card-light{transform-style:preserve-3d;transition:transform .5s cubic-bezier(.16,.84,.44,1),box-shadow .4s ease,border-color .35s}

/* Cursor glow follower removed on request (the blurred circle that used to
   trail the mouse across the whole page). site-motion.js no longer creates
   or moves #site-cursor-glow -- see that file. */

/* ---- hero: slow animated gradient pan + ambient particles ---- */
.bg-hero-gradient::before{content:"";position:absolute;inset:0;pointer-events:none;background:linear-gradient(115deg,rgba(227,254,105,.16),transparent 30%,transparent 70%,rgba(24,68,216,.14));background-size:220% 220%;animation:heroPan 16s ease-in-out infinite}
@keyframes heroPan{0%,100%{background-position:0% 30%}50%{background-position:100% 70%}}
.hero-particle{position:absolute;border-radius:50%;pointer-events:none;background:radial-gradient(circle,rgba(227,254,105,.95),rgba(137,186,215,.4) 55%,transparent 75%);opacity:.55;animation:heroParticleFloat 9s ease-in-out infinite}
@keyframes heroParticleFloat{0%,100%{transform:translate(0,0)}50%{transform:translate(14px,-22px)}}

/* ---- eyebrow accent line draw-in ---- */
span[class~="h-px"]{transform:scaleX(0);transform-origin:left;transition:transform .6s cubic-bezier(.16,.84,.44,1)}
html.js .reveal-up.in-view span[class~="h-px"],span[class~="h-px"].in-view{transform:scaleX(1)}

/* ---- gradient accent text shimmer ----
   Targets the bg-clip-text gradient words (e.g. "studio.") already
   compiled into the page -- widens the gradient and slowly pans it. */
[class~="bg-clip-text"]{background-size:220% auto;animation:accentShimmer 6s ease-in-out infinite}
@keyframes accentShimmer{0%,100%{background-position:0% 50%}50%{background-position:100% 50%}}
/* text-shadow works independently of the background-clip/transparent-fill
   trick above (it follows the glyph outline, not the fill color), so this
   layers a soft navy-tinted shadow under the gradient text without
   touching the lime/sky colors themselves -- those are brand colors used
   elsewhere (buttons, etc.), so recoloring them here would ripple wider
   than intended. .bg-hero-gradient runs cream -> pale blue -> navy
   (see that rule above), and lime/sky are both light colors, so wherever
   the word lands on the paler end of that background the two light
   colors were sitting too close in value to read cleanly. This darkens
   the edge just enough to separate them everywhere the background can be,
   without adding a hard outline. */
[class~="bg-clip-text"]{text-shadow:0 1px 3px rgba(1,21,49,.35),0 0 14px rgba(1,21,49,.15)}

/* ---- button click ripple ----
   Ripple <span> is created per-click by site-motion.js and removed
   after the animation finishes. */
.motion-ripple{position:absolute;border-radius:50%;transform:scale(0);background:rgba(137,186,215,.6);pointer-events:none;animation:rippleOut .6s ease-out forwards}
@keyframes rippleOut{to{transform:scale(2.6);opacity:0}}

/* ---- footer social icons ---- */
footer a[aria-label]{transition:transform .4s cubic-bezier(.34,1.56,.64,1),background-color .3s ease}
footer a[aria-label]:hover{transform:translateY(-4px) rotate(-8deg)}

/* ---- hero background media (photo on About/Services/Contact, the
   existing profile cover video reused on Home) ----
   Created at runtime by site-motion.js (search "hero background
   media" there), never baked into any page's HTML. */
.hero-bg-media{position:absolute;top:0;right:0;width:60%;height:100%;object-fit:cover;z-index:0;pointer-events:none;opacity:.85;filter:saturate(.55) brightness(1.05);-webkit-mask-image:linear-gradient(90deg,transparent,#000 38%);mask-image:linear-gradient(90deg,transparent,#000 38%)}
/* Blend-mode overlay that locks the media above to brand hues only -- mix-blend-mode:color takes THIS layer's hue/saturation and the video/photo's luminance, so no matter what colors are actually in the source footage (a stock video looping through unrelated hues, a photo with its own palette), only navy/brand-blue/sky/lime are ever visible. Same mask + position as .hero-bg-media so it sits exactly on top of it. */
.hero-media-tint{position:absolute;top:0;right:0;width:60%;height:100%;z-index:0;pointer-events:none;mix-blend-mode:color;background:linear-gradient(135deg,var(--color-brand-blue),var(--color-sky) 45%,var(--color-lime) 100%);-webkit-mask-image:linear-gradient(90deg,transparent,#000 38%);mask-image:linear-gradient(90deg,transparent,#000 38%)}
@media (max-width:900px){.hero-media-tint{display:none}}
.hero-bg-media.is-image{animation:heroKenBurns 18s ease-in-out infinite alternate}
@keyframes heroKenBurns{0%{transform:scale(1)}100%{transform:scale(1.09)}}
@media (max-width:900px){.hero-bg-media{display:none}}

[aria-label="Skip loading screen"]{display:none!important}

/* ====================================================================
   Portfolio galleries (Media Solutions + IT Solutions)

   Each gallery card's samples are real <button> elements injected by
   assets/js/site-motion.js (ensureMediaGallery), not CSS ::after
   content, specifically so they're clickable/focusable and open the
   shared lightbox below. Positioning/framing styling lives here so the
   JS only has to create plain elements with these classes.

   No "Recent Work" label on any of these by design -- deliberately
   removed everywhere, don't re-add it to a card individually. */
#media .grid.gap-6.sm\:grid-cols-2.lg\:grid-cols-3>div:first-child .card-light,#media .grid.gap-6.sm\:grid-cols-2.lg\:grid-cols-3>div:nth-child(2) .card-light,#media .grid.gap-6.sm\:grid-cols-2.lg\:grid-cols-3>div:nth-child(3) .card-light,#media .grid.gap-6.sm\:grid-cols-2.lg\:grid-cols-3>div:nth-child(4) .card-light{position:relative;overflow:hidden;padding-bottom:10.75rem}

/* IT Solutions: Website Design & Development -- this is the section's
   full-width *lead* card (same role as "Branding & Visual Identity" in
   Media Solutions, which has no gallery), not one of the small grid
   cards, so it needs its own selector rather than an nth-child() added
   to the block above. It's also much wider than the grid cards, so the
   existing 5-slot .media-photo--1..5 fan (sized/spaced for a ~330px
   card) has plenty of room here with no new position variants needed. */
#it .mt-12.grid.gap-6>div:first-child .card-light{position:relative;overflow:hidden;padding-bottom:10.75rem}

/* IT Solutions: AI Agents -- 3rd card in the same small grid as
   UI/UX Design and E-commerce Solutions (#it's version of the #media
   grid above). Only 4 of the 5 shared fan slots are used for this one
   (see the gallery data in site-motion.js for why), which needs no CSS
   change -- an unused .media-photo--5 slot simply never gets created. */
#it .grid.gap-6.sm\:grid-cols-2.lg\:grid-cols-3>div:nth-child(3) .card-light{position:relative;overflow:hidden;padding-bottom:10.75rem}

.media-photo{position:absolute;bottom:1.9rem;width:4.6rem;height:5.75rem;padding:0;margin:0;border-radius:.55rem;border:3px solid #fff;background-color:var(--color-navy);background-size:cover;background-position:center 20%;box-shadow:0 10px 20px -8px #0115313d,0 2px 6px #01153126;box-sizing:border-box;cursor:pointer;-webkit-appearance:none;appearance:none;font:inherit;transition:transform .25s,box-shadow .25s;-webkit-tap-highlight-color:transparent}
.media-photo:hover,.media-photo:focus-visible{box-shadow:0 16px 28px -8px #01153152,0 3px 8px #0115312e;z-index:7}
.media-photo:focus-visible{outline:2px solid var(--color-brand-blue);outline-offset:3px}
.media-photo--1{left:1.75rem;transform:rotate(-14deg);transform-origin:bottom left;z-index:1}
.media-photo--2{left:3.8rem;bottom:2.3rem;transform:rotate(-7deg);transform-origin:bottom left;z-index:2}
.media-photo--3{left:5.85rem;bottom:2.5rem;transform:rotate(0deg);transform-origin:bottom center;z-index:3}
.media-photo--4{left:7.9rem;bottom:2.3rem;transform:rotate(7deg);transform-origin:bottom right;z-index:4}
.media-photo--5{left:9.95rem;transform:rotate(14deg);transform-origin:bottom right;z-index:5}
/* --6 only exists for AI Static & Motion Posts (the one card with 6
   samples instead of 5). Continues --1..--5's same 2.05rem left-step and
   7deg rotation-step outward rather than re-centering the whole arc, so
   --1..--5 stay pixel-identical for the other galleries that use them. */
.media-photo--6{left:12rem;transform:rotate(21deg);transform-origin:bottom right;z-index:6}
.media-photo--1:hover,.media-photo--1:focus-visible{transform:translateY(-8px) scale(1.06) rotate(-14deg)}
.media-photo--2:hover,.media-photo--2:focus-visible{transform:translateY(-8px) scale(1.06) rotate(-7deg)}
.media-photo--3:hover,.media-photo--3:focus-visible{transform:translateY(-8px) scale(1.06) rotate(0deg)}
.media-photo--4:hover,.media-photo--4:focus-visible{transform:translateY(-8px) scale(1.06) rotate(7deg)}
.media-photo--5:hover,.media-photo--5:focus-visible{transform:translateY(-8px) scale(1.06) rotate(14deg)}
.media-photo--6:hover,.media-photo--6:focus-visible{transform:translateY(-8px) scale(1.06) rotate(21deg)}

/* Lightbox: click any photo/video above to view it full-size, one at a time */
.media-lightbox{position:fixed;inset:0;z-index:1000;display:none;align-items:center;justify-content:center;padding:2rem;background:#011531e6;-webkit-backdrop-filter:blur(4px);backdrop-filter:blur(4px)}
.media-lightbox.is-open{display:flex}
.media-lightbox__frame{position:relative;max-width:min(90vw,32rem);max-height:85vh;display:flex;flex-direction:column;align-items:center}
.media-lightbox__img{display:block;max-width:100%;max-height:85vh;border-radius:1rem;box-shadow:0 30px 60px -20px #000a;background:var(--color-navy);opacity:0;transform:scale(.97);transition:opacity .25s,transform .25s}
.media-lightbox__img.is-visible{opacity:1;transform:scale(1)}
.media-lightbox__close,.media-lightbox__nav{border:none;cursor:pointer;box-shadow:0 6px 16px #0000004d;display:flex;align-items:center;justify-content:center;color:var(--color-navy);background:#fff;-webkit-appearance:none;appearance:none;padding:0;font-family:Arial,Helvetica,sans-serif;font-weight:700}
.media-lightbox__close{position:absolute;top:-.5rem;right:-.5rem;width:2.5rem;height:2.5rem;border-radius:9999px;font-size:1.5rem;line-height:1}
.media-lightbox__nav{position:absolute;top:50%;transform:translateY(-50%);width:2.75rem;height:2.75rem;border-radius:9999px;font-size:1.4rem}
.media-lightbox__prev{left:-1.25rem}
.media-lightbox__next{right:-1.25rem}
.media-lightbox__count{margin-top:.85rem;color:#fff;font-size:.8125rem;letter-spacing:.08em;opacity:.85}
/* Visible category tag (e.g. AI Agents' "AI Chat" / "Booking" / "Tasks")
   -- site-motion.js only shows this when the gallery item sets `label`,
   so it's simply never created/visible for the other galleries. */
.media-lightbox__label{margin-top:1rem;padding:.3rem .85rem;border-radius:9999px;background:var(--color-lime);color:var(--color-navy);font-size:.6875rem;font-weight:700;letter-spacing:.08em;text-transform:uppercase}
@media (max-width:640px){.media-lightbox__prev{left:.25rem}.media-lightbox__next{right:.25rem}.media-lightbox__close{top:.5rem;right:.5rem}}

/* Home page: client logo strip, inserted by ensureClientLogos() right
   after the testimonial section (see that function's own comment for
   why it's runtime-inserted rather than baked into index.html). A
   continuously-scrolling marquee of 8 separate logo files -- the track
   holds the 8-logo set twice back to back purely so a translateX(-50%)
   loop has a seamless second copy to hand off to (see that function's
   comment); it's still exactly 8 distinct clients.
   overflow:hidden on the outer .client-logos is what clips the track to
   one visible row instead of showing the full doubled-up strip. */
.client-logos{background:#e5e5e5;padding:2.5rem 0 3.5rem;overflow:hidden}
.client-logos__track{display:flex;align-items:center;gap:3.5rem;width:max-content;animation:clientLogosMarquee 64s linear infinite}
.client-logos__logo{display:block;height:4rem;width:auto;flex-shrink:0;opacity:.8}
@keyframes clientLogosMarquee{from{transform:translateX(0)}to{transform:translateX(-50%)}}
@media (max-width:640px){.client-logos__logo{height:3.25rem}.client-logos__track{gap:2.25rem}}
/* The testimonial section's own border-bottom (border-y border-navy/10,
   compiled elsewhere) reads as a bit heavy sitting directly above the
   logo strip's gray background -- site-motion.js tags that one section
   .has-client-logos so this can drop its normal 1px border-bottom and
   draw a genuinely thinner hairline instead (scaleY(.5) on a dedicated
   line -- a real 1px border can't go visually thinner than 1px, this
   can). Only this one section is affected; the same border elsewhere
   on the page/site is untouched. */
.has-client-logos{border-bottom-width:0}
.has-client-logos::after{content:"";position:absolute;left:0;right:0;bottom:0;height:1px;background:var(--color-lime);transform:scaleY(.5);transform-origin:bottom}
