/* HERO — homepage hero: layout, background decor, circular portfolio carousel */

/* HERO — split premium layout */
.hero {
  position:relative; min-height:100vh; display:flex; align-items:center;
  padding:132px 5vw 64px; overflow:hidden; isolation:isolate;
}

.hero-bg { position:absolute; inset:0; z-index:-1; overflow:hidden; pointer-events:none; }

/* extremely faint grain so the big soft gradients don't read as flat
   digital banding — a classic premium-site texture (Linear, Stripe) */
.hero-grain {
  position:absolute; inset:-10%;
  background-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='140' height='140'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.85' numOctaves='2' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23n)'/%3E%3C/svg%3E");
  opacity:.035; mix-blend-mode:overlay;
}

.hero-blob {
  position:absolute; border-radius:50%; filter:blur(70px); opacity:.6;
  animation:blobDrift 32s ease-in-out infinite;
}
.hero-blob-a {
  width:520px; height:520px; top:-140px; left:-160px;
  background:radial-gradient(circle at 30% 30%, rgba(103,58,183,.30), rgba(103,58,183,0) 70%);
}
.hero-blob-b {
  width:460px; height:460px; bottom:-180px; right:-120px;
  background:radial-gradient(circle at 60% 60%, rgba(255,193,7,.22), rgba(255,193,7,0) 70%);
  animation-duration:38s; animation-delay:-6s;
}
@keyframes blobDrift {
  0%,100% { transform:translate(0,0) scale(1); }
  50% { transform:translate(3%,4%) scale(1.05); }
}

.hero-particle {
  position:absolute; width:5px; height:5px; border-radius:50%;
  background:var(--purple); opacity:.18;
  animation:heroParticleFloat 14s ease-in-out infinite;
}
.hero-particle-1 { top:22%; left:12%; animation-delay:0s; animation-duration:15s; }
.hero-particle-2 { top:68%; left:22%; width:4px; height:4px; background:var(--yellow); opacity:.22; animation-delay:3s; animation-duration:12.5s; }
.hero-particle-3 { top:38%; left:6%; width:3px; height:3px; animation-delay:6.5s; animation-duration:17s; }
.hero-particle-4 { top:80%; left:16%; width:4px; height:4px; background:var(--yellow); opacity:.2; animation-delay:2s; animation-duration:13.5s; }
.hero-particle-5 { top:12%; left:24%; width:3px; height:3px; animation-delay:9s; animation-duration:16s; }
@keyframes heroParticleFloat {
  0%,100% { transform:translateY(0) translateX(0); opacity:.12; }
  50% { transform:translateY(-22px) translateX(6px); opacity:.35; }
}
/* decor cluster carries the same left-shift as .hero-visual so the glow
   and orbit rings travel with the eye as one composition */
.hero-decor-cluster { position:absolute; inset:0; }

.hero-glow {
  position:absolute; top:-10%; right:-5%; width:1000px; height:1000px;
  border-radius:50%;
  background:radial-gradient(circle at center, rgba(103,58,183,.15) 0%, rgba(103,58,183,.08) 30%, rgba(225,255,0,.03) 50%, rgba(255,193,7,0) 70%);
  filter: blur(60px);
  pointer-events: none;
}
.hero-orbit { position:absolute; border-radius:50%; }
.hero-orbit-a {
  width:700px; height:700px; top:50%; right:4%; margin-top:-350px;
  border:1px dashed rgba(103,58,183,.17);
  animation:orbitSpin 34s linear infinite;
}
.hero-orbit-b {
  width:490px; height:490px; top:51%; right:13%; margin-top:-245px;
  border:1px dashed rgba(0,200,83,.15);
  animation:orbitSpin 26s linear infinite reverse;
}
.hero-orbit-c {
  width:840px; height:840px; top:48%; right:-3%; margin-top:-420px;
  border:1px dotted rgba(103,58,183,.09);
  animation:orbitSpin 46s linear infinite;
}
.hero-orbit-d {
  width:350px; height:350px; top:54%; right:17%; margin-top:-175px;
  border:1px dotted rgba(255,193,7,.18);
  animation:orbitSpin 20s linear infinite reverse;
}
.hero-orbit-e {
  width:570px; height:570px; top:45%; right:7%; margin-top:-285px;
  border:1px solid rgba(12,16,32,.05);
  animation:orbitFloat 9s ease-in-out infinite;
}
@keyframes orbitSpin { from { transform:rotate(0deg); } to { transform:rotate(360deg); } }
@keyframes orbitFloat { 0%,100% { transform:translateY(0); } 50% { transform:translateY(-14px); } }

/* paused once the hero scrolls out of view (see initHeroDecorPause in
   js/animations.js) — these run on an infinite loop with no visibility
   awareness of their own, so left alone they'd keep animating for the
   entire rest of the page's scroll for zero visible benefit. */
.hero.hero-decor-paused .hero-blob,
.hero.hero-decor-paused .hero-orbit-a,
.hero.hero-decor-paused .hero-orbit-b,
.hero.hero-decor-paused .hero-orbit-c,
.hero.hero-decor-paused .hero-orbit-d,
.hero.hero-decor-paused .hero-orbit-e,
.hero.hero-decor-paused .hero-particle {
  animation-play-state:paused;
}

.hero-dots {
  position:absolute; inset:0;
  background-image:radial-gradient(rgba(12,16,32,.12) 1.5px, transparent 1.5px);
  background-size:26px 26px;
  -webkit-mask-image:radial-gradient(ellipse 55% 55% at 68% 50%, #000 0%, transparent 72%);
  mask-image:radial-gradient(ellipse 55% 55% at 68% 50%, #000 0%, transparent 72%);
}

.hero-inner {
  position:relative; z-index:1; width:100%; max-width:1360px; margin:0 auto;
  display:grid; grid-template-columns:11fr 9fr; gap:32px; align-items:center;
}

.hero-content { max-width:560px; }

.hero-badge {
  display:inline-flex; align-items:center; gap:8px;
  font-size:.8rem; font-weight:700; color:var(--purple);
  background:var(--purple-lt); border:1px solid rgba(103,58,183,.14);
  padding:8px 16px; border-radius:999px; margin-bottom:40px;
}

.hero-title {
  font-size:clamp(2.6rem,4.6vw,4.2rem);
  font-weight:800; line-height:1.05; letter-spacing:-.035em; margin-bottom:36px;
}
.hero-title em { font-style:normal; color:var(--purple); }

.hero-sub {
  font-size:1.08rem; color:var(--muted); line-height:1.7;
  max-width:480px; margin-bottom:44px;
}

.hero-cta { display:flex; gap:14px; flex-wrap:wrap; margin-bottom:40px; }
.hero-cta .btn { padding:15px 28px; font-size:.92rem; }

.hero-trust { list-style:none; display:flex; flex-wrap:wrap; gap:14px 28px; }
.hero-trust li {
  display:flex; align-items:center; gap:8px;
  font-size:.86rem; font-weight:600; color:var(--ink);
}
.hero-trust-check {
  display:inline-flex; align-items:center; justify-content:center;
  width:18px; height:18px; border-radius:50%; flex-shrink:0;
  background:var(--green-lt); color:var(--green-dk);
  font-size:.62rem; font-weight:800;
}

/* HERO VISUAL — circular portfolio carousel (the sole visual centerpiece) */
.hero-visual {
  position:relative; height:min(660px,70vh);
  display:flex; align-items:center; justify-content:flex-end;
}

.hero-carousel-wrap { position:relative; width:min(560px,48vw); height:100%; }
.hero-carousel-halo {
  position:absolute; inset:-8% -10% -8% 12%; z-index:-1; border-radius:50%;
  background:radial-gradient(circle, rgba(255,193,7,.20), rgba(103,58,183,.14) 55%, transparent 75%);
  filter:blur(30px);
}

/* the circle's center is pushed out past this element's own right edge
   (and below its middle) so only a slice of the "wheel" is ever on
   screen — js/carousel.js measures .hero-carousel's own box and drives
   each card's translate3d every frame. Nothing in that chain ever carries
   a CSS transform of its own, or the measurement would drift mid-animation
   (getBoundingClientRect reflects applied transforms). */
.hero-carousel { position:relative; width:100%; height:100%; overflow:visible; touch-action:none; }

/* portrait, poster-like cards — blades mounted on the wheel's rim. Height
   leads (per spec), width follows a 9:16 ratio via aspect-ratio so it
   never has to be computed separately. transform-origin sits near the
   INNER edge (80% down, matching PIVOT_Y in js/carousel.js) rather than
   the center — js/carousel.js anchors that same point to the circular
   path, so the circle passes through each card's inner ~20% and the rest
   of the card projects outward from it, instead of the card straddling
   the path from its own center. */
.hero-carousel-card {
  position:absolute; top:0; left:0; z-index:3;
  height:clamp(480px,70vh,780px); aspect-ratio:9/16;
  transform-origin:50% 80%;
  will-change:transform;
  opacity: 0;
  transition: opacity 0.5s ease;
}
.hero-carousel.positioned .hero-carousel-card {
  opacity: 1;
}
.hero-carousel-card-surface {
  position:relative; width:100%; height:100%; overflow:hidden;
  border-radius:32px; background:var(--surface);
  user-select:none; -webkit-user-select:none; -webkit-user-drag:none;
  box-shadow:
    0 1px 0 rgba(255,255,255,.6) inset,
    0 30px 70px rgba(12,16,32,.24),
    0 0 50px rgba(103,58,183,.10);
  cursor:grab;
  transition:transform .5s cubic-bezier(.22,1,.36,1), box-shadow .45s var(--ease);
}
.hero-carousel-card-surface:hover {
  transform:translateY(-8px) scale(1.025);
  box-shadow:
    0 1px 0 rgba(255,255,255,.6) inset,
    0 40px 84px rgba(12,16,32,.30),
    0 0 60px rgba(103,58,183,.16);
}
.hero-carousel.dragging .hero-carousel-card-surface { cursor:grabbing; }

/* premium animated edge — a slow conic "shine" sweeps around a hairline
   ring (padding + mask-composite:exclude keeps only the ring itself
   opaque), like light catching brushed glass. Mostly translucent white,
   one soft glint, so it reads as a material rather than a neon outline. */
.hero-carousel-card-border {
  position:absolute; inset:0; z-index:2; border-radius:inherit;
  padding:1.5px; pointer-events:none; overflow:hidden;
  -webkit-mask:linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0);
  -webkit-mask-composite:xor;
  mask-composite:exclude;
}
/* The sweep lives on an oversized ::before that only ever animates
   `transform` (GPU compositing, no repaint) instead of animating the
   conic-gradient's own angle custom property directly on the ring
   (which forced a full gradient repaint on all 15 cards every frame —
   measurably the single largest source of dropped frames on the hero,
   ~10ms/frame). The parent's mask still clips this child down to the
   same hairline ring; 250% + centering guarantees the oversized square
   covers the card's corners at every rotation angle regardless of the
   card's own (tall, non-square) aspect ratio. */
.hero-carousel-card-border::before {
  content:''; position:absolute;
  width:250%; height:250%; left:-75%; top:-75%;
  background:conic-gradient(from 0deg,
    rgba(255,255,255,.10) 0%,
    rgba(255,255,255,.10) 70%,
    rgba(255,255,255,.75) 80%,
    rgba(197,176,255,.45) 86%,
    rgba(255,255,255,.75) 92%,
    rgba(255,255,255,.10) 100%);
  animation:carouselBorderSpin 22s linear infinite;
}
@keyframes carouselBorderSpin { to { transform:rotate(360deg); } }

/* paused while the carousel is scrolled out of view (see carousel.js'
   IntersectionObserver) — still worth pausing even though the sweep is
   now a cheap compositor-only transform, since 15 idle GPU layers add up */
.hero-carousel.offscreen .hero-carousel-card-border::before { animation-play-state:paused; }

/* Mobile only, <1024px (matches the compact/desktop split in
   js/carousel.js's measure()): this sweep is a compositor-only transform
   (see above), so per-frame *scripting* cost is near zero — but it is
   still a genuinely independent, continuously-animating layer on all 15
   cards at once, entirely separate from and unaffected by the carousel's
   own JS render-rate throttle (that throttle only touches how often
   carousel.js writes each card's own position/rotation transform; this
   sweep keeps compositing on its own animation timeline regardless).
   15 concurrently-animating masked gradient layers is real, continuous
   GPU compositing load a weaker mobile GPU has to sustain the entire
   time the hero is on screen. Freezing it removes that load; the static
   conic-gradient ring — the actual "glass edge" detail — stays fully
   visible, just no longer sweeping. Desktop is untouched: this rule
   cannot match above 1023px. */
@media (max-width:1023px) {
  /* touch-action:none (the desktop/base rule above) tells the browser to
     hand EVERY touch gesture starting on the wheel to JS, with no native
     scrolling at all — that's what let the carousel intercept a plain
     vertical swipe instead of the page scrolling. pan-y is the browser-
     native way to arbitrate this: it permits native vertical panning over
     this element, so a vertical swipe scrolls the page on its own, while
     horizontal/diagonal movement still reaches carousel.js's pointer
     handlers to drag the wheel (see the touch-intent gate in
     onPointerDown/onPointerMove there — the two changes only work
     together). Desktop keeps touch-action:none unconditionally: this
     value only affects touchscreen input, and a mouse/trackpad drag has
     no equivalent "was this meant as a scroll" ambiguity to resolve. */
  .hero-carousel { touch-action: pan-y; }
  .hero-carousel-card-border::before { animation:none; }
  /* Concentrated text-legibility blur behind each card's title/description —
     filter:blur() cost scales with blur radius, and this is 15 concurrent
     instances sitting on top of the same continuously-transforming cards
     as the sweep above. Halving the radius keeps the same purpose (darken
     and soften the artwork directly behind the white text) with roughly a
     quarter of the fill-rate cost; at this element's ~130px height the
     difference in the softness of the fade is not a visible design change. */
  .hero-carousel-card-blur { filter:blur(8px); }
}

/* plain image slot — no gradient/icon treatment. background-color is
   only a fallback while the <img> loads; swap each img's src in
   index.html for a real case-study screenshot when ready. */
.hero-carousel-card-media {
  position:absolute; inset:0; overflow:hidden;
  background-color: transparent;
}
.hero-carousel-card-media img {
  display:block; width:100%; height:100%;
  object-fit:cover;
}

/* All five archetype SVGs (branding/social-content/website/performance/
   content-shoots) share one authored viewBox — 336Γ—644, aspect β‰ˆ0.522 —
   deliberately close to the card's own 9:16 ratio with generous
   top/bottom margin baked into each composition, so plain
   object-fit:cover (no extra per-asset transform) already crops every
   one of them cleanly. */

/* Smooth simulated blur layer injected via JS to avoid backdrop-filter bugs.
   A plain child of the card surface — no transform of its own — so it
   rotates, scales and clips exactly with the card; overflow:hidden on
   .hero-carousel-card-surface (plus its matching border-radius) is what
   keeps both this and the overlay below perfectly confined to the card. */
.hero-carousel-card-blur {
  position: absolute;
  z-index: 5;
  left: 0;
  right: 0;
  bottom: 0;
  height: 130px; /* Concentrated blur directly behind text */
  pointer-events: none;
  background-repeat: no-repeat;
  background-size: cover;
  background-position: center;
  filter: blur(16px);
  -webkit-mask-image: linear-gradient(to top, rgba(0, 0, 0, 1) 0%, rgba(0, 0, 0, 0) 100%);
  mask-image: linear-gradient(to top, rgba(0, 0, 0, 1) 0%, rgba(0, 0, 0, 0) 100%);
  transform: scale(1.1); /* Slight scale to prevent blurred edges from bleeding inward */
  transform-origin: bottom center;
}

/* Title/description plate: bottom-anchored gradient + text, structurally
   just another child of the card surface (not a viewport- or
   carousel-positioned layer), so it's carried along by the card's own
   translate3d/rotate as one piece — never a floating rectangle. */
.hero-carousel-card-overlay {
  position: absolute;
  z-index: 10;
  left: 0;
  right: 0;
  bottom: 0;
  padding: 32px 32px 28px;
  color: #fff;
  display: flex;
  flex-direction: column;
  gap: 8px;
  pointer-events: none;
  background: linear-gradient(to top, rgba(8, 10, 20, 0.85) 0%, rgba(8, 10, 20, 0.35) 55%, rgba(8, 10, 20, 0) 100%);
}
.hero-carousel-card-overlay strong {
  font-size: 1.15rem;
  font-weight: 800;
  letter-spacing: -0.01em;
  line-height: 1.2;
  text-shadow: 0 2px 4px rgba(0,0,0,0.5);
}
.hero-carousel-card-overlay span {
  font-size: 0.9rem;
  font-weight: 500;
  opacity: 0.9;
  line-height: 1.4;
  text-shadow: 0 1px 3px rgba(0,0,0,0.4);
}


@media (prefers-reduced-motion:reduce) {
  .hero-carousel-card-surface { transition:box-shadow .3s var(--ease); }
  .hero-carousel-card-border::before { animation:none; }
}
