/* ==========================================================================
   ADy AI Studios — device polish
   Loaded LAST, after tailwind.css, so these rules win against utilities of
   equal specificity. Three jobs:
     1. make the phone layout its own design rather than a squeezed desktop
     2. keep fixed chrome clear of notches and home indicators
     3. drop effects that cost GPU time and buy nothing without a mouse
   Desktop (>=1024px) is deliberately left alone except where marked.
   ========================================================================== */

/* ==========================================================================
   1. BASE — input feel
   ========================================================================== */
html{
  -webkit-text-size-adjust:100%;          /* iOS inflates text in landscape */
     -moz-text-size-adjust:100%;
          text-size-adjust:100%;
  -webkit-tap-highlight-color:transparent;
}
body{ overscroll-behavior-y:none; }        /* no rubber-band flash past the ends */

button, a, [data-connect], summary{ touch-action:manipulation; }

/* Tap feedback. Touch devices get no :hover, so without this a tap looks
   like nothing happened until the next screen paints. */
.tap{ -webkit-user-select:none; user-select:none; transition:transform .12s ease, opacity .12s ease; }
.tap:active{ transform:scale(.97); opacity:.9; }

/* Anything scrollable that isn't the page keeps its momentum to itself. */
.svc-rail, #connectScroll{ -webkit-overflow-scrolling:touch; overscroll-behavior:contain; }

/* ==========================================================================
   2. FIXED CHROME — safe areas
   ========================================================================== */
#topLogo{
  top:calc(env(safe-area-inset-top, 0px) + 14px);
  left:calc(env(safe-area-inset-left, 0px) + 14px);
}
#topConnect{
  top:calc(env(safe-area-inset-top, 0px) + 14px);
  right:calc(env(safe-area-inset-right, 0px) + 14px);
  min-height:44px;                      /* the padding alone left it at 40px */
}
@media (min-width:640px){
  #topLogo{   top:calc(env(safe-area-inset-top, 0px) + 20px); left:calc(env(safe-area-inset-left, 0px) + 24px); }
  #topConnect{top:calc(env(safe-area-inset-top, 0px) + 20px); right:calc(env(safe-area-inset-right, 0px) + 24px); }
}
#scrollCue{ bottom:calc(env(safe-area-inset-bottom, 0px) + 20px); }
#introSplash .skip-hint{ bottom:calc(env(safe-area-inset-bottom, 0px) + 28px); }

/* ==========================================================================
   3. HERO — phone tier (<768px)
   The video is a 9:16 centre crop at this size, so the character already fills
   the frame: the side washes would only fog her, and the type needs a proper
   band of its own at the top instead.
   ========================================================================== */
/* ==========================================================================
   3a. HERO HEADING — "AI" over "ADVERTISEMENTS"
   These two lines used to be pulled together with a hand-picked negative
   margin (clamp(-80px,-5vw,-30px)), which overlapped the ink by ~46px at
   1440 and ~15px on a phone — "ADVERTISEMENTS" was being drawn across the
   bottom of the A and the I.

   The gap is now derived instead of guessed. With line-height .82 on the AI
   line and 1 on the ADVERTISEMENTS line, the distance between the AI baseline
   and the top of the ADVERTISEMENTS caps works out as

       gap = 0.0334*--ai-fs + margin + 0.213*--adv-fs

   (the two constants are this font pair's ascent metrics). Solving that for a
   target gap of 0.5 * --adv-fs gives the margin below, and because it is
   expressed in the same custom properties as the font sizes it stays correct
   at every width — including where either clamp saturates.
   ========================================================================== */
#hero-head{
  --ai-fs:clamp(120px, 24vw, 380px);
  --adv-fs:clamp(20px, 4vw, 65px);
  right:0;
  padding-right:1.5vw;
  text-align:right;

  /* The heading and the "Connect Now" chip share the top-right corner, and the
     design's plain `top: 5%` is a percentage of a viewport height that has
     nothing to do with either. At 1024x768 the cap-line landed 0.7px under the
     chip; on a phone it collided outright.
     The AI ink starts ~0.104 * --ai-fs below this box's top edge and the chip's
     bottom edge is ~65px down, so this floor keeps ~16px of air between them —
     and being a max(), it only ever engages where 5% is not already enough.
     1920 and 1440 are untouched; 1024 and below get pushed clear. */
  top:max(5%, calc(env(safe-area-inset-top, 0px) + 81px - 0.104 * var(--ai-fs)));
}

/* From xl up, centre the whole AI + ADVERTISEMENTS block vertically instead of
   hanging it off the top. Nothing about the type changes — same sizes, same
   gap, same right edge — it just sits in the middle of the hero, which also
   puts it a long way clear of the "Connect Now" chip.
   Auto block margins, not a transform: #hero-head is the data-parallax element,
   so GSAP owns its `transform` and would overwrite a translate-based centring
   (the same trap #hero-panel hit). Needs a definite height, hence fit-content.
   Held to xl+ deliberately: from 1024-1279 the hero panel is still a
   full-width bar across the bottom and a centred heading would land on it. */
@media (min-width:1280px){
  #hero-head{
    top:0; bottom:0;
    margin-top:auto; margin-bottom:auto;
    height:-webkit-fit-content; height:fit-content;
  }
}
.hero-ai{
  font-family:'Archivo', sans-serif;
  font-size:var(--ai-fs);
  -webkit-text-stroke:1.5px #fff;
  paint-order:stroke fill;
}
.hero-adv{
  font-family:'Space Grotesk', sans-serif;
  font-size:var(--adv-fs);
  -webkit-text-stroke:1px #fff;
  paint-order:stroke fill;
}
.hero-adv-line{ margin-top:calc(0.287 * var(--adv-fs) - 0.0334 * var(--ai-fs)); }

@media (max-width:767px){
  .wash-left, .wash-right{ display:none; }
  .wash-top{
    height:34%;
    background:linear-gradient(180deg, #fff 0%, rgba(255,255,255,.92) 34%, rgba(255,255,255,.55) 62%, rgba(255,255,255,0) 100%);
  }
  .wash-bottom{
    height:52%;
    background:linear-gradient(0deg, #fff 0%, rgba(255,255,255,.94) 26%, rgba(255,255,255,.6) 52%, rgba(255,255,255,0) 100%);
  }

  /* Only the two sizes change — the margin formula above re-solves itself. */
  #hero-head{
    padding-right:16px;
    --ai-fs:clamp(104px, 31vw, 168px);
    --adv-fs:clamp(17px, 5vw, 26px);
  }
  .hero-adv{ letter-spacing:.12em; }

  /* Bottom sheet: edge-to-edge, above the home indicator, not floating in the
     middle of the character. */
  #hero-panel{
    left:12px; right:12px;
    bottom:calc(env(safe-area-inset-bottom, 0px) + 16px);
    padding:18px 18px 18px;
    border-radius:24px;
  }
  #hero-panel .hero-stats{ margin-top:14px; padding-top:12px; padding-bottom:12px; }
}

/* Short phones (SE, 360x640). The panel was eating 51% of the viewport and
   leaving the character a 140px slot. The stat strip is the part that can go —
   the same three numbers are the first thing the page says anyway. */
@media (max-width:767px) and (max-height:700px){
  #hero-panel{ padding:14px; }
  #hero-panel .hero-stats{ display:none; }
  #hero-panel p{ font-size:13px; }
}

/* 2xl centres the panel in the left column. The markup used to do it with
   `top-1/2 -translate-y-1/2`, but GSAP animates #hero-panel and owns that
   element's `transform` — depending on when it first parsed the element it
   either wiped the -50% (panel 1px past the fold on 1920x1080) or kept it and
   applied the shift twice. Both were wrong. Those two utilities are gone from
   the markup; auto block margins centre it here instead, and no transform can
   interfere. Needs a definite height, hence fit-content. */
@media (min-width:1360px){
  #hero-panel{
    top:0; bottom:0;
    margin-top:auto; margin-bottom:auto;
    height:-webkit-fit-content; height:fit-content;
    max-height:calc(100svh - 6vh);
  }
}

/* Landscape phones: almost no vertical room. Shrink the type hard and let the
   panel scroll with the page rather than pinning it over the video. */
@media (max-width:920px) and (orientation:landscape) and (max-height:520px){
  #hero-head{
    --ai-fs:clamp(64px, 13vh, 110px);
    --adv-fs:clamp(13px, 2.6vh, 18px);
  }
  /* Full-width panel + right-aligned heading do not both fit in 390px of
     height. Give the panel the left half and leave the right column to the
     type — the same split the desktop layout uses, just tighter. */
  #hero-panel{
    right:auto;
    width:min(58%, 420px);
  }
  #hero-panel .hero-stats{ display:none; }
  #hero-panel p{ font-size:12.5px; line-height:1.35; }
}

/* ==========================================================================
   4. SERVICES — phone/tablet swipe rail
   Replaces five stacked full-height cards (4600px of scroll, five live
   videos) with one screen and one playing video.
   ========================================================================== */
.svc-rail{
  display:flex;
  gap:14px;
  overflow-x:auto;
  scroll-snap-type:x mandatory;
  scroll-padding-inline:20px;
  padding:4px 20px 8px;
  scrollbar-width:none;
}
.svc-rail::-webkit-scrollbar{ display:none; }
.svc-slide{
  flex:0 0 auto;
  width:min(86vw, 400px);          /* the next card peeks — that's the swipe cue */
  scroll-snap-align:center;
  scroll-snap-stop:always;
}
.svc-slide-media{
  height:clamp(230px, 33vh, 320px);
  overflow:hidden;
}
@media (min-width:768px){
  .svc-rail{ gap:20px; padding-inline:32px; scroll-padding-inline:32px; }
  .svc-slide{ width:min(58vw, 420px); }
  .svc-slide-media{ height:clamp(260px, 34vh, 360px); }
}

/* Arrow buttons + a nudging hint. A snap rail with nothing but dots reads as a
   static card to plenty of people — these say "there is more to the right"
   without needing anyone to guess. */
.rail-nav{
  display:grid; place-items:center;
  height:40px; width:40px;
  border-radius:100px;
  border:1px solid rgba(10,10,11,.14);
  background:#fff;
  color:var(--ink);
  box-shadow:0 6px 16px -10px rgba(10,10,11,.5);
  transition:opacity .25s, border-color .25s, background .25s;
}
.rail-nav svg{ height:18px; width:18px; }
.rail-nav:disabled{ opacity:.28; box-shadow:none; }
.rail-nav:not(:disabled):hover{ border-color:var(--accent); color:var(--accent); }

@keyframes swipeNudge{
  0%,100%{ transform:translateX(0);   opacity:.55 }
  50%    { transform:translateX(5px); opacity:1 }
}
.swipe-arrow{ display:inline-block; animation:swipeNudge 1.6s ease-in-out infinite; }
/* Once they have swiped, stop nagging. */
#svcRailWrap.has-moved .swipe-arrow{ animation:none; opacity:.4; }

/* The dot you see is the ::before; the button itself is a 44px-tall invisible
   hit area around it, so the target is thumb-sized without the design growing. */
.rail-dot{
  position:relative;
  height:44px; width:24px;
  background:none; border:0; padding:0;
}
.rail-dot.is-active{ width:40px; }
.rail-dot::before{
  content:'';
  position:absolute; top:50%; left:50%; transform:translate(-50%,-50%);
  height:8px; width:8px; border-radius:100px;
  background:rgba(10,10,11,.18);
  transition:width .4s cubic-bezier(.23,1,.32,1), background .3s;
}
.rail-dot.is-active::before{ width:28px; background:var(--accent); }

/* ==========================================================================
   4b. AD SHOWCASE — full screen
   The wrapper goes fullscreen (not the <video>) so the custom controls come
   with it. The stage is aspect-video by default; in fullscreen it has to fill
   the screen instead, and the video letterboxes inside it.
   iOS Safari cannot fullscreen a <div> at all — main.js falls back to
   video.webkitEnterFullscreen() there, which uses the native player.
   ========================================================================== */
#adWrap:fullscreen, #adWrap:-webkit-full-screen{
  width:100vw; height:100vh;
  border-radius:0; border:0;
  background:#000;
}
#adWrap:fullscreen #adStage, #adWrap:-webkit-full-screen #adStage{
  aspect-ratio:auto;
  height:100%; width:100%;
}
#adWrap:fullscreen #adVideo, #adWrap:-webkit-full-screen #adVideo{
  object-fit:contain;
}
#adWrap:fullscreen #adControls, #adWrap:-webkit-full-screen #adControls{
  padding-bottom:calc(env(safe-area-inset-bottom, 0px) + 16px);
}

/* ==========================================================================
   5. STICKY MOBILE CTA
   Hidden until the hero (which carries the same two buttons) is gone.
   ========================================================================== */
#mobileCta{
  transform:translateY(120%);
  transition:transform .45s cubic-bezier(.23,1,.32,1);
}
#mobileCta.is-visible{ transform:translateY(0); }
.mobile-cta-inner{
  pointer-events:auto;
  margin:0 12px calc(env(safe-area-inset-bottom, 0px) + 12px);
  padding:10px 10px 10px 16px;
  border-radius:100px;
  background:rgba(10,10,11,.92);
  border:1px solid rgba(255,255,255,.12);
  box-shadow:0 18px 40px -14px rgba(0,0,0,.7);
}
/* While the modal is open the bar would sit on top of the backdrop. */
body.modal-open #mobileCta{ transform:translateY(120%); }

/* ==========================================================================
   6. TOUCH / LOW-POWER — stop paying for things a finger can't use
   ========================================================================== */
@media (hover:none){
  /* Hover states latch on touch ("sticky hover") and look broken. */
  .why-card:hover{ transform:none; border-color:rgba(255,255,255,.08); box-shadow:none; }
  .why-card:hover::before{ opacity:0; }
  .why-card:hover .why-num{ -webkit-text-stroke:1.5px rgba(255,255,255,.22); }
  .group:hover .group-hover\:translate-x-1{ transform:none; }
  #topLogo:hover{ transform:none; }

  /* The cursor spotlight and particle fields are mouse-driven by definition.
     particles.js also bails early — this is the paint-side half. */
  .fx-grid::after{ display:none; }
  .px-canvas{ display:none; }
}

/* backdrop-filter is the single most expensive thing on this page for a
   mobile GPU, and there are ~40 pills. Keep the glass look on the two big
   surfaces, make the small ones plain. */
@media (max-width:1023px){
  .pill{ backdrop-filter:none; -webkit-backdrop-filter:none; }
  .glass-dark{ backdrop-filter:none; -webkit-backdrop-filter:none; background:rgba(16,16,18,.94); }
}
@media (max-width:767px){
  .glass{
    backdrop-filter:blur(14px) saturate(1.3);
    -webkit-backdrop-filter:blur(14px) saturate(1.3);
    background:rgba(255,255,255,.72);      /* less blur to see through == cheaper */
  }
}
/* Weak GPU or Save-Data: no blur anywhere. */
.is-save-data .glass{ backdrop-filter:none; -webkit-backdrop-filter:none; background:rgba(255,255,255,.9); }
.is-save-data .px-canvas{ display:none; }

/* Save-Data never fetches the hero loop, so hold the poster still. */
.hero-poster-only #hero-media{ transform:none !important; }

/* ==========================================================================
   7. PAINT BUDGET
   No content-visibility here on purpose. It was tried on #why / #showcase /
   #contact and it works — but their real heights (2163 / 674 / 1127 on a
   phone) are nothing like any single contain-intrinsic-size guess, so the
   document height changed as each one rendered, which moved the scroll
   position and desynced the ScrollTriggers underneath it. A cheaper first
   paint is not worth a scroll that jumps. The paint cost it was buying back
   is mostly gone anyway now that the particle canvases do not run on touch.
   ========================================================================== */

/* Marquee only animates while it is on screen (set by main.js). */
.marquee-track.is-paused{ animation-play-state:paused; }

/* ==========================================================================
   8. TYPE — phone scale
   ========================================================================== */
@media (max-width:640px){
  .display{ letter-spacing:-.025em; }
  #contact h3.display{ font-size:2.75rem; line-height:.95; }
  #why h3.display, #showcase h3.display, #services h3.display{ font-size:2.25rem; }
  /* Long tracked-out eyebrows wrap badly at 375px. */
  .font-grotesk.tracking-\[\.34em\]{ letter-spacing:.24em; }
}

/* Bottom of the page needs room for the sticky bar so the last line isn't
   sitting underneath it. */
@media (max-width:1023px){
  #contact{ padding-bottom:calc(env(safe-area-inset-bottom, 0px) + 88px); }
}

/* ==========================================================================
   9. REDUCED MOTION — belt and braces
   ========================================================================== */
@media (prefers-reduced-motion: reduce){
  *, *::before, *::after{
    animation-duration:.001ms !important;
    animation-iteration-count:1 !important;
    transition-duration:.001ms !important;
    scroll-behavior:auto !important;
  }
  .svc-rail{ scroll-snap-type:none; }
  #mobileCta{ transition:none; }
}
