/* =========================================================================
   Aspient — elegant, colorful design layer + vivid 3D hero scene.
   Plain CSS, loaded after tailwind.css. Motion respects prefers-reduced-motion.
   ========================================================================= */

:root {
  --grad-brand: linear-gradient(100deg,
    rgb(var(--c-violet)) 0%,
    rgb(var(--c-indigo)) 22%,
    rgb(var(--brand-500)) 48%,
    rgb(var(--c-sky)) 74%,
    rgb(var(--accent-400)) 100%);
  --grad-brand-soft: linear-gradient(120deg,
    rgb(var(--c-violet) / .14), rgb(var(--brand-500) / .12), rgb(var(--accent-500) / .14));
}

/* ---------- Headlines & gradient text ---------- */
.hero-headline { letter-spacing: -0.025em; font-weight: 800; text-wrap: balance; }
.text-gradient {
  background-image: var(--grad-brand);
  background-size: 200% auto;
  -webkit-background-clip: text; background-clip: text; color: transparent;
  animation: hueShift 9s ease-in-out infinite alternate;
}
@keyframes hueShift { from { background-position: 0% 50%; } to { background-position: 100% 50%; } }

/* ---------- Buttons ---------- */
.btn-primary {
  background-image: linear-gradient(135deg, rgb(var(--c-indigo)) 0%, rgb(var(--brand-500)) 45%, rgb(var(--accent-500)) 110%);
  background-size: 160% auto;
  box-shadow: 0 1px 0 0 rgba(255,255,255,.3) inset,
              0 10px 26px -8px rgb(var(--c-indigo) / .55),
              0 6px 18px -10px rgb(var(--accent-500) / .6);
  transition: background-position .5s ease, box-shadow .3s, transform .3s;
}
.btn-primary:hover {
  background-position: right center;
  box-shadow: 0 14px 34px -8px rgb(var(--c-violet) / .6), 0 8px 22px -8px rgb(var(--accent-500) / .65);
}
.btn-secondary { backdrop-filter: blur(6px); }
.btn-secondary:hover { border-color: rgb(var(--c-indigo) / .45); color: rgb(var(--c-indigo)); }

/* ---------- Cards ---------- */
.card-hover { transition: transform .35s cubic-bezier(.2,.7,.2,1), box-shadow .35s, border-color .35s; }
.card-hover:hover {
  transform: translateY(-6px);
  box-shadow: 0 30px 60px -22px rgb(var(--c-violet) / .45),
              0 0 0 1px rgb(var(--brand-500) / .28),
              0 0 52px -16px rgb(var(--accent-500) / .65);
  border-color: rgb(var(--brand-500) / .5);
}

/* ---------- Icon tiles (colorful, with cycling hue accents) ---------- */
.icon-tile {
  background-image: linear-gradient(150deg, rgb(var(--c-violet)) 0%, rgb(var(--brand-500)) 50%, rgb(var(--accent-500)) 120%);
  box-shadow: 0 10px 26px -8px rgb(var(--c-indigo) / .6),
              inset 0 1px 0 rgba(255,255,255,.5),
              inset 0 -8px 14px -6px rgba(0,0,0,.35);
}
/* Subtle hue variety so grids of tiles feel colorful, not uniform */
.grid > *:nth-child(3n+2) .icon-tile { background-image: linear-gradient(150deg, rgb(var(--c-indigo)), rgb(var(--c-sky)) 55%, rgb(var(--accent-400)) 120%); }
.grid > *:nth-child(3n+3) .icon-tile { background-image: linear-gradient(150deg, rgb(var(--c-teal)), rgb(var(--accent-500)) 55%, rgb(var(--c-sky)) 120%); }
.grid > *:nth-child(4n+4) .icon-tile { background-image: linear-gradient(150deg, rgb(var(--c-pink)), rgb(var(--c-violet)) 60%, rgb(var(--brand-500)) 120%); }

/* ---------- Chips ---------- */
.chip {
  border-color: rgb(var(--brand-500) / .25);
  background: linear-gradient(180deg, rgb(var(--c-bg) / .9), rgb(var(--brand-50) / .6));
}
.chip:hover { border-color: rgb(var(--c-violet) / .4); }

/* ---------- Services dropdown panel (bg-white/98 didn't compile → was see-through) ---------- */
#services-submenu {
  background-color: rgba(255, 255, 255, 0.98);
  backdrop-filter: blur(16px);
  -webkit-backdrop-filter: blur(16px);
  box-shadow: 0 24px 50px -12px rgb(var(--c-indigo) / .25), 0 0 0 1px rgb(var(--brand-500) / .08);
}
/* Caret pointing up to the Services nav item */
#services-submenu::before {
  content: ""; position: absolute; top: -7px; left: 30px;
  width: 14px; height: 14px; background-color: inherit;
  border-left: 1px solid rgb(var(--brand-500) / .12);
  border-top: 1px solid rgb(var(--brand-500) / .12);
  border-radius: 4px 0 0 0; transform: rotate(45deg);
}
/* Polished items + clear gradient hover highlight */
#services-submenu .dropdown-link {
  position: relative; font-weight: 500; border-radius: 0.75rem;
  transition: background-color .16s ease, color .16s ease, transform .16s ease;
}
#services-submenu .dropdown-link::before {
  content: ""; position: absolute; left: 6px; top: 50%; height: 0; width: 3px;
  border-radius: 3px; background: var(--grad-brand);
  transform: translateY(-50%); transition: height .18s ease;
}
#services-submenu .dropdown-link:hover,
#services-submenu .dropdown-link.nav-link-active,
#services-submenu .dropdown-link.glassy-active {
  background-image: linear-gradient(90deg, rgb(var(--brand-500) / .12), rgb(var(--c-violet) / .10));
  color: rgb(var(--c-indigo)); transform: translateX(5px);
}
#services-submenu .dropdown-link:hover::before,
#services-submenu .dropdown-link.nav-link-active::before { height: 60%; }
#services-submenu .dropdown-link:hover i { color: rgb(var(--c-violet)); }

/* ---------- Colorful section ambiance ---------- */
.aurora { position: relative; isolation: isolate; }
.aurora::before {
  content: ""; position: absolute; inset: 0; z-index: -1; pointer-events: none;
  background-image: url('../images/backgrounds/hero-aurora.svg');
  background-size: cover;
  background-position: center;
}

/* =========================================================================
   3D HERO SCENE — vivid, colorful, elegant
   ========================================================================= */
.scene-3d {
  position: relative; width: 100%; min-height: 480px;
  perspective: 1300px; transform-style: preserve-3d; --mx: 0; --my: 0;
}
@media (max-width: 1023px) { .scene-3d { min-height: 400px; } }
@media (max-width: 767px)  { .scene-3d { min-height: 340px; } }

.scene-3d .scene-glow {
  position: absolute; inset: 4%;
  background:
    radial-gradient(42% 46% at 30% 26%, rgb(var(--c-violet) / .92), transparent 70%),
    radial-gradient(44% 50% at 76% 30%, rgb(var(--brand-500) / .9), transparent 72%),
    radial-gradient(46% 52% at 60% 82%, rgb(var(--accent-500) / .92), transparent 72%),
    radial-gradient(36% 44% at 88% 78%, rgb(var(--c-pink) / .72), transparent 70%);
  filter: blur(40px) saturate(1.25); border-radius: 50%; z-index: 0;
  animation: glowPulse 7s ease-in-out infinite alternate;
}
@keyframes glowPulse { from { opacity: .8; transform: scale(.98); } to { opacity: 1; transform: scale(1.04); } }

.scene-3d .stage {
  position: absolute; inset: 0; transform-style: preserve-3d;
  transform: rotateX(calc(var(--my) * -6deg + var(--sy, 0) * 8deg)) rotateY(calc(var(--mx) * 8deg)) translateY(calc(var(--sy, 0) * -42px));
  transition: transform .25s ease-out;
}

/* Orbiting AI core */
.scene-3d .core-wrap {
  position: absolute; top: 50%; left: 50%; width: 140px; height: 140px;
  margin: -70px 0 0 -70px; transform-style: preserve-3d; z-index: 5;
}
.scene-3d .core {
  position: absolute; inset: 0; border-radius: 30px; display: grid; place-items: center;
  color: #fff; font-size: 2.8rem;
  background: linear-gradient(150deg, #a855f7 0%, #4f46e5 38%, #2563ff 68%, #06c8eb 130%);
  box-shadow: 0 26px 64px -16px rgb(var(--c-violet) / .8),
              0 18px 50px -18px rgb(var(--accent-500) / .8),
              inset 0 2px 0 rgba(255,255,255,.55), inset 0 -10px 22px -8px rgba(0,0,0,.4);
  animation: coreFloat 6s ease-in-out infinite;
}
.scene-3d .core::after {
  content: ""; position: absolute; inset: -2px; border-radius: 32px; padding: 2px;
  background: linear-gradient(140deg, rgba(255,255,255,.85), transparent 42%);
  -webkit-mask: linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0);
  -webkit-mask-composite: xor; mask-composite: exclude;
}
.scene-3d .orbit { position: absolute; top: 50%; left: 50%; border-radius: 50%; transform-style: preserve-3d; }
.scene-3d .orbit-1 { width: 250px; height: 250px; margin: -125px 0 0 -125px; transform: rotateX(74deg); border: 1.5px dashed rgb(var(--c-violet) / .45); animation: spin 14s linear infinite; }
.scene-3d .orbit-2 { width: 340px; height: 340px; margin: -170px 0 0 -170px; transform: rotateX(74deg) rotateZ(60deg); border: 1.5px dashed rgb(var(--accent-500) / .45); animation: spin2 22s linear infinite; }
.scene-3d .orbit .node {
  position: absolute; top: -8px; left: 50%; margin-left: -8px; width: 16px; height: 16px; border-radius: 50%;
  background: radial-gradient(circle at 35% 30%, #fff, #06c8eb 55%, #4f46e5);
  box-shadow: 0 0 18px 3px rgb(var(--accent-500) / .9);
}
.scene-3d .orbit-2 .node { background: radial-gradient(circle at 35% 30%, #fff, #ec4899 55%, #7c3aed); box-shadow: 0 0 18px 3px rgb(var(--c-pink) / .85); }

/* Floating glass cards — tinted, with colored icon chips */
.scene-3d .fcard {
  position: absolute; border-radius: 16px; padding: 13px 15px;
  background: rgba(255,255,255,.78); border: 1px solid rgba(255,255,255,.9);
  backdrop-filter: blur(12px); -webkit-backdrop-filter: blur(12px);
  box-shadow: 0 24px 50px -22px rgb(var(--c-indigo) / .5), inset 0 1px 0 rgba(255,255,255,.95);
  z-index: 6; animation: cardFloat 7s ease-in-out infinite;
}
.scene-3d .fcard .fc-row { display: flex; align-items: center; gap: 10px; }
.scene-3d .fcard .fc-ic { width: 36px; height: 36px; border-radius: 11px; display: grid; place-items: center; color: #fff; font-size: 1.02rem; box-shadow: 0 6px 16px -6px rgba(0,0,0,.4); }
.scene-3d .fc-1 .fc-ic { background: linear-gradient(145deg, #4f46e5, #2563ff); }
.scene-3d .fc-2 .fc-ic { background: linear-gradient(145deg, #0ea5e9, #06c8eb); }
.scene-3d .fc-3 .fc-ic { background: linear-gradient(145deg, #a855f7, #7c3aed); }
.scene-3d .fc-4 .fc-ic { background: linear-gradient(145deg, #14b8a6, #06c8eb); }
.scene-3d .fcard .fc-t { font: 700 .82rem/1.1 'Inter', sans-serif; color: #0f172a; }
.scene-3d .fcard .fc-s { font: 500 .7rem/1.2 'Inter', sans-serif; color: #64748b; margin-top: 2px; }

.scene-3d .fc-1 { top: 6%;  left: 0%;  transform: translateZ(75px) translate(calc(var(--mx)*18px), calc(var(--my)*14px)); animation-delay: -1s; }
.scene-3d .fc-2 { top: 2%;  right: 0%; transform: translateZ(45px) translate(calc(var(--mx)*-14px), calc(var(--my)*10px)); animation-delay: -3.5s; }
.scene-3d .fc-3 { bottom: 8%; left: 6%; transform: translateZ(60px) translate(calc(var(--mx)*12px), calc(var(--my)*-16px)); animation-delay: -2s; }
.scene-3d .fc-4 { bottom: 12%; right: 2%; transform: translateZ(90px) translate(calc(var(--mx)*-20px), calc(var(--my)*-12px)); animation-delay: -4.5s; }

/* Geometric accents (colorful) */
.scene-3d .shape { position: absolute; z-index: 4; animation: cardFloat 9s ease-in-out infinite; }
.scene-3d .shape.s-ring { width: 58px; height: 58px; border-radius: 50%; border: 7px solid rgb(var(--c-pink) / .55); top: 22%; right: 12%; }
.scene-3d .shape.s-tri { top: 68%; right: 20%; width: 0; height: 0; border-left: 21px solid transparent; border-right: 21px solid transparent; border-bottom: 36px solid rgb(var(--c-violet) / .6); animation-delay: -3s; }
.scene-3d .shape.s-dot { width: 20px; height: 20px; border-radius: 50%; background: rgb(var(--accent-500) / .85); top: 84%; left: 38%; box-shadow: 0 0 20px 4px rgb(var(--accent-500) / .7); }

/* Dark theme glass */

@keyframes coreFloat { 0%,100% { transform: translateY(0) rotateY(0); } 50% { transform: translateY(-13px) rotateY(14deg); } }
@keyframes cardFloat { 0%,100% { translate: 0 0; } 50% { translate: 0 -15px; } }
@keyframes spin  { from { transform: rotateX(74deg) rotateZ(0); }  to { transform: rotateX(74deg) rotateZ(360deg); } }
@keyframes spin2 { from { transform: rotateX(74deg) rotateZ(60deg); } to { transform: rotateX(74deg) rotateZ(-300deg); } }

/* =========================================================================
   WEBGL HERO — progressive-enhancement layer (layout/hero-webgl.php +
   scripts/hero-webgl.js). The CSS .scene-3d fallback above renders first and
   stays in the DOM; the canvas only fades in — and the fallback only fades
   out — after scripts/hero-webgl.js confirms Three.js loaded AND a WebGL
   context rendered successfully. Until then (or if it never happens: blocked
   script, old browser, reduced motion, narrow viewport) this block is inert
   and the CSS scene is all that's visible, at full opacity, doing its normal
   thing. Note the fallback is only ever hidden with opacity — never
   display:none — because it's the in-flow child that gives the wrapper its
   height; the canvas is absolutely positioned and contributes none.
   ========================================================================= */
.hero-webgl-stage { position: relative; }
.hero-webgl-canvas {
  position: absolute; inset: 0; width: 100%; height: 100%; display: block;
  opacity: 0; pointer-events: none; z-index: 10;
  transition: opacity .7s var(--ease-out-soft, ease);
}
.hero-webgl-stage.is-webgl-active .hero-webgl-canvas { opacity: 1; pointer-events: auto; }
/* Only hide the CSS scene's own 3D shapes (core/orbit rings/geometric shapes)
   when WebGL takes over — the floating label cards ("Deploy passed",
   "AI assistant / RAG on your data", etc.) and ambient glow stay visible,
   layered above the canvas, so the WebGL network still reads as meaningful
   rather than unlabeled abstract shapes. */
.hero-webgl-stage.is-webgl-active .scene-3d .core-wrap,
.hero-webgl-stage.is-webgl-active .scene-3d .shape {
  opacity: 0; pointer-events: none;
  transition: opacity .5s var(--ease-out-soft, ease);
}
.hero-webgl-stage.is-webgl-active .scene-3d .fcard { z-index: 12; }
@media (prefers-reduced-motion: reduce) {
  .hero-webgl-canvas { transition: none; }
  .hero-webgl-stage.is-webgl-active .scene-3d .core-wrap,
  .hero-webgl-stage.is-webgl-active .scene-3d .shape { transition: none; }
}

/* =========================================================================
   Scroll experience — Lenis smooth scroll + scroll-reveal + colorful CTA band
   ========================================================================= */
html.lenis, html.lenis body { height: auto; }
.lenis.lenis-smooth { scroll-behavior: auto !important; }
.lenis.lenis-smooth [data-lenis-prevent] { overscroll-behavior: contain; }
.lenis.lenis-stopped { overflow: hidden; }

/* Cookie consent banner hidden state */
.cc-hide { display: none !important; }

/* Horizontal "works" scroll — swipeable strip by default; JS upgrades to pinned on desktop */
.works-strip .works-viewport { overflow-x: auto; overflow-y: hidden; -webkit-overflow-scrolling: touch; scrollbar-width: none; }
.works-strip .works-viewport::-webkit-scrollbar { display: none; }
.works-strip.is-pinned { overflow: hidden; }
.works-strip.is-pinned .works-viewport { overflow: visible; }
.works-strip .works-track { will-change: transform; }
.work-panel { scroll-snap-align: start; }
.works-strip .works-viewport { scroll-snap-type: x mandatory; }
.works-strip.is-pinned .works-viewport { scroll-snap-type: none; }

/* Reveal-on-scroll. Hidden state ONLY when JS adds .has-reveal, so no-JS is safe. */
.reveal { transition: opacity .8s cubic-bezier(.2,.7,.2,1), transform .8s cubic-bezier(.2,.7,.2,1); will-change: opacity, transform; }
.has-reveal .reveal { opacity: 0; transform: translateY(46px) scale(.98); }
.has-reveal .reveal.in { opacity: 1; transform: none; }

/* Colorful animated CTA band — vivid aurora MESH (replaces the flat blue gradient) */
.cta-band {
  background-color: #2348e6;
  background-image:
    radial-gradient(at 16% 20%, #8b5cf6 0px, transparent 48%),
    radial-gradient(at 84% 14%, #06c8eb 0px, transparent 46%),
    radial-gradient(at 78% 88%, #4f46e5 0px, transparent 52%),
    radial-gradient(at 22% 82%, #0ea5e9 0px, transparent 48%),
    radial-gradient(at 55% 50%, #2563ff 0px, transparent 60%);
  background-size: 170% 170%;
  animation: ctaShift 16s ease-in-out infinite;
}
@keyframes ctaShift { 0%, 100% { background-position: 0% 0%; } 50% { background-position: 100% 100%; } }

@media (prefers-reduced-motion: reduce) {
  .scene-3d .core, .scene-3d .orbit, .scene-3d .fcard, .scene-3d .shape, .scene-3d .scene-glow, .text-gradient, .cta-band { animation: none !important; }
  .scene-3d .stage { transition: none; }
  .has-reveal .reveal { opacity: 1 !important; transform: none !important; }
}

/* =========================================================================
   Apple-grade refinement — remove background noise, crisp edges, pill buttons,
   tighter type, calmer precise shadows. Loaded last, so it wins.
   ========================================================================= */

/* Kill the busy global particle background + pattern overlays for a clean canvas */
#particles-bg { display: none !important; }
.bg-grid-pattern, .bg-noise-overlay, .bg-dots-pattern { background-image: none !important; }

/* Tighter, cleaner display type (Apple-tight tracking) */
h1, h2, h3, .hero-headline, .font-display { letter-spacing: -0.028em; }

/* Pill buttons with a refined, precise shadow */
.btn-primary, .btn-secondary, .btn-ghost { border-radius: 9999px; }
.btn-primary {
  box-shadow: 0 1px 0 rgba(255,255,255,.22) inset,
              0 8px 20px -10px rgb(var(--c-primary-glow) / 0.55);
}
.btn-primary:hover { box-shadow: 0 10px 26px -10px rgb(var(--c-primary-glow) / 0.6); }

/* Calmer, crisper card elevation (hairline + soft precise shadow) */
.card-hover, .card-surface {
  box-shadow: 0 1px 2px rgb(var(--shadow-color) / 0.04),
              0 12px 32px -22px rgb(var(--shadow-color) / 0.18);
}
.card-hover:hover {
  transform: translateY(-4px);
  box-shadow: 0 2px 6px rgb(var(--shadow-color) / 0.06),
              0 26px 50px -26px rgb(var(--c-indigo) / 0.22);
  border-color: rgb(var(--brand-500) / 0.35);
}

/* Cleaner icon tile — keep the brand gradient, lighten the glow */
.icon-tile {
  box-shadow: 0 6px 16px -8px rgb(var(--c-primary-glow) / 0.42),
              inset 0 1px 0 rgba(255,255,255,.45),
              inset 0 -6px 12px -8px rgba(0,0,0,.3);
}

/* Richer, more saturated aurora — reads as a deliberate background at wide
   viewports instead of a flat, empty wash (previously .72 opacity, no saturation boost) */
.aurora::before { opacity: 0.92; filter: saturate(1.28) contrast(1.05); }

/* Fine dot-grid texture layered under the aurora so hero edges never look blank
   on very wide screens. Pure CSS, no extra image request. */
.aurora::after {
  content: ""; position: absolute; inset: 0; z-index: -1; pointer-events: none;
  background-image: radial-gradient(rgb(var(--brand-500) / 0.16) 1.5px, transparent 1.5px);
  background-size: 26px 26px;
  -webkit-mask-image: radial-gradient(ellipse 85% 70% at 50% 30%, #000 55%, transparent 100%);
  mask-image: radial-gradient(ellipse 85% 70% at 50% 30%, #000 55%, transparent 100%);
}

/* Reusable full-bleed section wash for non-hero sections — softer than .aurora,
   fills wide-viewport edges with color/texture instead of a flat single tone. */
.section-mesh {
  position: relative; isolation: isolate;
  background-color: rgb(var(--c-surface));
}
.section-mesh::before {
  content: ""; position: absolute; inset: 0; z-index: -1; pointer-events: none;
  background-image:
    radial-gradient(60% 70% at 8% 12%, rgb(var(--brand-500) / 0.08), transparent 60%),
    radial-gradient(55% 65% at 95% 88%, rgb(var(--accent-500) / 0.10), transparent 60%),
    radial-gradient(rgb(var(--c-border)) 1px, transparent 1px);
  background-size: auto, auto, 28px 28px;
}

/* ---- Mobile polish ---- */
@media (max-width: 639px) {
  /* Hero CTA rows (flex-col that becomes flex-row at sm) -> full-width, tappable buttons */
  .flex-col.sm\:flex-row > .btn-primary,
  .flex-col.sm\:flex-row > .btn-secondary,
  .flex-col.sm\:flex-row > .btn-ghost {
    width: 100%;
    justify-content: center;
  }
  /* Calmer hero top spacing already handled per-page; tighten oversized arbitrary headlines */
  .hero-headline { line-height: 1.12; }
}

/* Clear the fixed mobile action bar (61px) so footer content isn't hidden behind it */
@media (max-width: 1023px) {
  body { padding-bottom: 4.5rem; }
}

/* CTA band — pattern overlay + floating glow orbs (crisp & light; smoother than a GIF) */
.cta-grid {
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='40' height='40'%3E%3Cpath d='M0 .5H40M.5 0V40' stroke='white' fill='none'/%3E%3C/svg%3E");
  background-size: 42px 42px;
  opacity: 0.13;
  -webkit-mask-image: radial-gradient(circle at 50% 35%, #000 8%, transparent 78%);
  mask-image: radial-gradient(circle at 50% 35%, #000 8%, transparent 78%);
}
.cta-orb { position: absolute; border-radius: 50%; pointer-events: none; filter: blur(8px); z-index: 0; }
.cta-orb-1 { width: 240px; height: 240px; top: -70px; left: -50px;
  background: radial-gradient(circle at 35% 30%, rgba(255,255,255,.32), transparent 70%);
  animation: ctaOrb 18s ease-in-out infinite; }
.cta-orb-2 { width: 180px; height: 180px; bottom: -60px; right: 8%;
  background: radial-gradient(circle at 35% 30%, rgba(45,224,255,.42), transparent 70%);
  animation: ctaOrb 22s ease-in-out infinite reverse; }
.cta-orb-3 { width: 130px; height: 130px; top: 28%; right: 24%;
  background: radial-gradient(circle at 35% 30%, rgba(168,85,247,.45), transparent 70%);
  animation: ctaOrb 26s ease-in-out infinite; }
@keyframes ctaOrb { 0%,100% { transform: translate(0,0) scale(1); } 50% { transform: translate(26px,-30px) scale(1.08); } }
@media (prefers-reduced-motion: reduce) { .cta-orb { animation: none !important; } }

/* Faint grid texture for the featured AI bento card */
.ai-grid {
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='38' height='38'%3E%3Cpath d='M0 .5H38M.5 0V38' stroke='white' fill='none'/%3E%3C/svg%3E");
  background-size: 38px 38px;
  opacity: 0.12;
}

/* Below desktop, the works strip is a natural vertical stack — let it flow (no swipe gutter) */
@media (max-width: 1023px) {
  .works-strip .works-viewport { overflow: visible; }
}

/* =========================================================================
   Choreography primitives — Phase 1 design-system additions.
   All driven by [data-reveal]/[data-stagger] (initial state set by JS below
   when GSAP is present, or by .has-reveal/.reveal for the no-GSAP fallback)
   plus .magnetic / .tilt (mousemove-driven, see scripts/scroll-fx.js).
   Reduced-motion is respected: the JS never attaches these listeners when
   prefers-reduced-motion is set, and the transforms below simply have no
   inline style pushed to them in that case.
   ========================================================================= */

/* Magnetic buttons/CTAs — JS sets --mx/--my (px offsets); transform-only, GPU-friendly */
.magnetic {
  --mx: 0px; --my: 0px;
  transform: translate(var(--mx), var(--my));
  transition: transform var(--duration-base) var(--ease-out-soft);
  will-change: transform;
}

/* Tilt cards — JS sets --tx/--ty (deg); perspective must exist on a parent or here */
.tilt {
  transform-style: preserve-3d;
  transform: perspective(900px) rotateX(var(--ty, 0deg)) rotateY(var(--tx, 0deg));
  transition: transform var(--duration-base) var(--ease-out-soft);
  will-change: transform;
}
.tilt-content { transform: translateZ(24px); }

@media (prefers-reduced-motion: reduce) {
  .magnetic, .tilt { transition: none; transform: none !important; }
}

/* =========================================================================
   Hero scroll-down indicator
   ========================================================================= */
.scroll-indicator-mouse {
  display: block; width: 22px; height: 34px; border-radius: 12px;
  border: 2px solid currentColor; position: relative;
}
.scroll-indicator-dot {
  position: absolute; top: 6px; left: 50%; width: 4px; height: 8px;
  margin-left: -2px; border-radius: 2px; background: currentColor;
  animation: scrollDot 1.8s ease-in-out infinite;
}
@keyframes scrollDot {
  0% { opacity: 1; transform: translateY(0); }
  70% { opacity: 0; transform: translateY(10px); }
  100% { opacity: 0; transform: translateY(0); }
}
@media (prefers-reduced-motion: reduce) {
  .scroll-indicator-dot { animation: none; opacity: 1; }
}

/* =========================================================================
   Technology orbit — pure-CSS rotating ring with upright icon nodes
   ========================================================================= */
.tech-orbit { position: relative; width: 100%; max-width: 420px; aspect-ratio: 1 / 1; margin: 0 auto; }
.tech-orbit .orbit-core {
  position: absolute; top: 50%; left: 50%; width: 84px; height: 84px; margin: -42px 0 0 -42px;
  border-radius: 50%; display: grid; place-items: center; z-index: 5;
  background: linear-gradient(150deg, rgb(var(--c-violet)) 0%, rgb(var(--brand-500)) 55%, rgb(var(--accent-500)) 120%);
  color: #fff; font-size: 1.7rem;
  box-shadow: 0 18px 40px -12px rgb(var(--c-indigo) / .55), inset 0 1px 0 rgba(255,255,255,.5);
}
.tech-orbit .orbit-ring {
  position: absolute; inset: 0; border-radius: 50%;
  border: 1.5px dashed rgb(var(--brand-500) / .35);
  animation: techOrbitSpin 34s linear infinite;
}
.tech-orbit .orbit-ring.ring-inner { inset: 14%; animation-duration: 24s; animation-direction: reverse; border-color: rgb(var(--c-sky) / .4); }
.tech-orbit .orbit-ring .orbit-node {
  position: absolute; display: inline-flex; align-items: center; justify-content: center;
  white-space: nowrap; padding: .4rem .65rem; min-width: 2.6rem;
  border-radius: 999px;
  background: rgb(var(--c-bg)); border: 1px solid rgb(var(--brand-500) / .2);
  box-shadow: 0 10px 22px -10px rgb(var(--c-indigo) / .35);
  animation: techOrbitSpinReverse 34s linear infinite;
  font-size: .78rem; font-weight: 700; color: rgb(var(--brand-500));
}
.tech-orbit .orbit-ring.ring-inner .orbit-node { animation-duration: 24s; animation-direction: reverse; }
@keyframes techOrbitSpin { from { transform: rotate(0deg); } to { transform: rotate(360deg); } }
@keyframes techOrbitSpinReverse {
  from { transform: translate(-50%, -50%) rotate(0deg); }
  to   { transform: translate(-50%, -50%) rotate(-360deg); }
}
@media (prefers-reduced-motion: reduce) {
  .tech-orbit .orbit-ring, .tech-orbit .orbit-ring .orbit-node { animation: none !important; }
  .tech-orbit .orbit-ring .orbit-node { transform: translate(-50%, -50%) !important; }
}

/* =========================================================================
   Custom cursor accent — desktop/fine-pointer only (JS in scroll-fx.js gates
   this to `(hover: hover) and (pointer: fine)` and skips it entirely under
   reduced-motion). Hidden by default; JS reveals it once real mouse movement
   is observed, so it never flashes at 0,0 before the first move event.
   ========================================================================= */
#cursor-glow {
  position: fixed; top: 0; left: 0; z-index: 9999;
  width: 22px; height: 22px; border-radius: 50%;
  background: radial-gradient(circle at 35% 30%, rgb(var(--accent-400) / .9), rgb(var(--brand-500) / .55) 60%, transparent 75%);
  transform: translate3d(-50%, -50%, 0);
  opacity: 0; pointer-events: none;
  transition: width .28s var(--ease-out-soft), height .28s var(--ease-out-soft),
              opacity .3s ease, background .28s ease;
  mix-blend-mode: multiply;
}
#cursor-glow.is-visible { opacity: 1; }
#cursor-glow.is-active {
  width: 46px; height: 46px;
  background: radial-gradient(circle at 35% 30%, rgb(var(--c-violet) / .85), rgb(var(--accent-500) / .5) 65%, transparent 78%);
}
@media (prefers-reduced-motion: reduce) { #cursor-glow { display: none !important; } }

/* =========================================================================
   Clip-path headline wipe reveal ([.reveal-clip], applied by
   aspient_section_header()) — a left-to-right mask wipe, distinct from the
   generic fade+scale [data-reveal] treatment. Negative top/bottom keep
   ascenders/descenders (g, y, accented caps) from clipping mid-animation.

   FAIL-SAFE BY DEFAULT: unlike a naive "hidden until JS reveals it" pattern,
   .reveal-clip is fully visible (no clip-path) unless the JS in scroll-fx.js
   has confirmed it's actually running, by adding html.has-reveal-clip. This
   mirrors the existing [data-reveal] fallback's own safety contract ("Hidden
   state ONLY when JS adds .has-reveal, so no-JS is safe") — a section
   headline must never be able to go permanently invisible just because a
   script errored, was blocked, or an observer never fired.
   ========================================================================= */
.reveal-clip { transition: clip-path 1s var(--ease-out-expo); will-change: clip-path; }
html.has-reveal-clip .reveal-clip { clip-path: inset(-15% 100% -15% 0); }
html.has-reveal-clip .reveal-clip.in-view { clip-path: inset(-15% 0% -15% 0); }
@media (prefers-reduced-motion: reduce) {
  .reveal-clip { transition: none !important; clip-path: none !important; }
}

/* =========================================================================
   Splash / preloader (#page-loader, see layout/header.php) — shown once per
   browser session (scripts/main.js gates repeat page loads via
   sessionStorage). Always defaults to visible in markup order so it covers
   first paint with no flash-of-unstyled-content; JS is solely responsible
   for hiding it, with a hard timeout safety net so a slow/failed load can
   never leave a visitor stuck behind it.
   ========================================================================= */
#page-loader {
  position: fixed; inset: 0; z-index: 9998;
  display: flex; align-items: center; justify-content: center;
  background: rgb(var(--c-bg));
  transition: opacity .45s ease;
}
#page-loader.is-hidden { opacity: 0; pointer-events: none; }
.page-loader-inner { display: flex; flex-direction: column; align-items: center; gap: 1.25rem; }
.page-loader-logo { width: 56px; height: 56px; object-fit: contain; animation: pageLoaderPulse 1.4s ease-in-out infinite; }
.page-loader-bar { width: 130px; height: 3px; border-radius: 999px; background: rgb(var(--c-border)); overflow: hidden; }
.page-loader-bar span {
  display: block; height: 100%; width: 40%; border-radius: 999px;
  background-image: var(--grad-brand);
  animation: pageLoaderBar 1.15s ease-in-out infinite;
}
@keyframes pageLoaderPulse { 0%, 100% { transform: scale(1); opacity: .88; } 50% { transform: scale(1.08); opacity: 1; } }
@keyframes pageLoaderBar { 0% { transform: translateX(-130%); } 100% { transform: translateX(290%); } }
@media (prefers-reduced-motion: reduce) {
  #page-loader { transition: none; }
  .page-loader-logo, .page-loader-bar span { animation: none; }
}

/* =========================================================================
   Corner-accent image fade — applied to the small decorative photo/graphic
   used in card corners across the site (bento grid, "AI woven in" card,
   SmartReports card, etc). A plain rounded-rectangle photo at low opacity
   reads as a faint "ghost box" artifact rather than a soft accent; masking
   the edges to transparent (strongest near the card's edge, fully solid at
   the anchored corner) removes that hard boundary so it blends into the
   card background instead.
   ========================================================================= */
.corner-fade {
  -webkit-mask-image: radial-gradient(circle at center, #000 0%, #000 30%, transparent 78%);
          mask-image: radial-gradient(circle at center, #000 0%, #000 30%, transparent 78%);
}

/* =========================================================================
   Contact-form submission modal (#contact-modal, see contact.php +
   scripts/contact-form.js). The spinner is the only genuinely animated
   part; reduced-motion swaps it for a static ring so the "sending" state
   still reads clearly without motion.
   ========================================================================= */
[data-modal-spinner] { animation: modalSpin 0.85s linear infinite; }
@keyframes modalSpin { to { transform: rotate(360deg); } }
@media (prefers-reduced-motion: reduce) {
  [data-modal-spinner] { animation: none; border-top-color: rgb(var(--brand-500)); }
}

/* =========================================================================
   Rich-text message editor (#message-rich, see contact.php +
   scripts/contact-form.js) — a contenteditable div progressively swapped in
   for the plain <textarea> once JS confirms it can drive it.
   ========================================================================= */
#message-rich:empty:before {
  content: attr(data-placeholder);
  color: rgb(var(--c-text-faint));
  pointer-events: none;
}
#message-rich:focus { outline: none; box-shadow: 0 0 0 2px rgb(var(--brand-500) / .5); }
#message-rich ol { list-style: decimal; margin-left: 1.25rem; }
#message-rich ul { list-style: disc; margin-left: 1.25rem; }
#message-rich div { min-height: 1em; }
