/* ==========================================================================
   Home page — hero mesh/particles, hero pillars, featured project, testimonials
   ========================================================================== */

.hero{
  position:relative;
  /* Bottom padding has to reserve room for .scroll-indicator, which is
     absolutely positioned at bottom:28px and stands about 60px tall, so it
     needs ~88px of clear space. The old 76px was short of that, and it only
     looked fine because the stat strip used to sit between the mission/vision
     cards and the hero's bottom edge and absorbed the difference. With that
     strip gone the cards were the last element in flow and the indicator
     printed on top of them. 112px clears the indicator with ~24px to spare at
     every width. */
  padding:148px 0 112px;
  overflow:hidden;
  background:var(--navy);
  color:#fff;
}
.hero-bg{position:absolute; inset:0; z-index:0;}
#particles{position:absolute; inset:0; width:100%; height:100%; opacity:0.5;}
.mesh-blob{
  position:absolute; border-radius:50%; filter:blur(60px); opacity:0.55;
  animation:float 10s ease-in-out infinite;
}
.blob-1{width:420px; height:420px; background:radial-gradient(circle, var(--cyan), transparent 70%); top:-10%; left:0%;}
.blob-2{width:460px; height:460px; background:radial-gradient(circle, var(--violet), transparent 70%); top:20%; right:-8%; animation-delay:2s;}
.blob-3{width:320px; height:320px; background:radial-gradient(circle, var(--mint), transparent 70%); bottom:-15%; left:30%; animation-delay:4s;}
@keyframes float{
  0%,100%{transform:translateY(0) translateX(0);}
  50%{transform:translateY(-26px) translateX(14px);}
}

.hero-grid{position:relative; z-index:2; display:grid; grid-template-columns:minmax(0, 1fr) minmax(0, 1fr); gap:60px; align-items:center;}
.hero-content h1{color:#fff; margin-bottom:20px; min-height:1.1em;}
.hero-content h1 .grad-text{
  background:linear-gradient(100deg, var(--cyan), var(--gold) 55%, var(--violet) 85%);
  background-size:200% auto; -webkit-background-clip:text; background-clip:text; color:transparent;
  animation:gradmove 6s ease infinite;
}
@keyframes gradmove{0%{background-position:0% 50%;}50%{background-position:100% 50%;}100%{background-position:0% 50%;}}
/* The typing caret is drawn, not typed. The span is empty and the bar is a
   background block, so no "|" character exists in the heading's text or in its
   accessible name. It appears only while the animation is actually running:
   js/pages/home.js adds .is-typing to the heading, which it does not do when
   JavaScript is off or the visitor prefers reduced motion. */
/* ---------- Hero heading: reserved space ----------
   The heading is authored with its full text and JavaScript then replaces it with
   one character, so the box used to collapse the moment the animation started
   (measured: 128px -> 43px at 375px, 203px -> 70px at 1440px) and then grew and
   shrank again on every type/delete cycle, moving the sub-heading, the buttons and
   everything below them.

   The two spans are stacked in the same grid cell. The reserver carries the
   complete heading and is the only one that contributes height, so the <h1> is
   always exactly as tall as the finished heading; the live layer types inside that
   box without ever changing it. Both are ordinary text in the same column at the
   same size, so they wrap identically at every width, after the webfont swaps in
   and at any zoom level. No height is fixed and nothing is clipped. The completed
   heading - wording, gradient, type, line-wrap - renders exactly as before. */
.typing-heading{display:grid;}
.typing-heading > .typing-reserve,
.typing-heading > .typing-live{grid-area:1 / 1; min-width:0;}
.typing-reserve{visibility:hidden;}
/* The caret occupies 3px plus a 4px margin on the live layer. Mirroring it as an
   inert spacer keeps the reserver's wrapping identical in the worst case, so the
   caret can never push the live text onto a line the reserver did not account for.
   It is added under the same condition as the caret itself, so it is absent when
   JavaScript is off or reduced motion is preferred. */
.typing-heading.is-typing .typing-reserve::after{
  content:''; display:inline-block; width:3px; height:1em; margin-left:4px; vertical-align:-0.1em;
}

.typing-cursor{display:none;}
.typing-heading.is-typing .typing-cursor{
  display:inline-block; width:3px; height:1em; margin-left:4px;
  background:var(--gold); border-radius:1px; vertical-align:-0.1em;
  animation:cursorBlink 0.9s step-end infinite;
}
@keyframes cursorBlink{0%,100%{opacity:1;}50%{opacity:0;}}
@media (prefers-reduced-motion: reduce){
  .typing-heading.is-typing .typing-cursor{animation:none; display:none;}
}
.hero-sub{font-size:18px; color:rgba(255,255,255,0.78); max-width:540px; margin-bottom:28px;}

.hero-actions{display:flex; gap:16px; flex-wrap:wrap; margin-bottom:8px;}

.hero-visual{position:relative; will-change:transform; transition:transform 0.2s ease-out; display:flex; justify-content:center;}
/* The source image is a tall portrait crop (~2:3). Letting it stretch to
   100% of the column width (its old behavior) made the hero image balloon
   to 1000px+ tall once the layout drops to a single column on tablet/mobile.
   Shrink-wrapping the frame to the image and capping height responsively
   keeps the true aspect ratio (no cropping/distortion) while staying
   visually balanced against the hero copy at every breakpoint. */
.hero-illustration-img{
  border-radius:var(--radius-lg); overflow:hidden; box-shadow:var(--shadow-lg); position:relative; z-index:1;
  width:fit-content; margin:0 auto; display:block;
}
/* The box is reserved before the image arrives. Previously this was
   width:auto; height:auto; max-height:clamp(...), which leaves the element
   with no definite size until the image decodes - and because the parent is
   width:fit-content, the whole hero column collapsed and then jumped when the
   file landed. That produced an intermittent load-time layout shift (observed
   in up to 2 of 5 runs, worst value 0.4547 at 390x844).
   Giving the image a definite height plus an explicit 2:3 aspect ratio means
   the width is known from the first layout pass. The rendered result is
   unchanged: the clamp values are the same, and because they never exceed the
   source's 1200px natural height, height: and max-height: resolve identically.
   Correcting the width/height attributes alone does NOT fix this - with
   width:auto the browser ignores the attribute ratio - which is why the fix
   lives here rather than in the markup. */
.hero-illustration-img img{
  width:auto; max-width:100%;
  height:clamp(320px, 62vh, 640px);
  aspect-ratio:2 / 3;
  margin:0 auto;
}
.hero-visual-glow{
  position:absolute; inset:-40px; z-index:0; border-radius:50%;
  background:radial-gradient(circle, rgba(0,217,200,0.28), rgba(139,92,246,0.14) 55%, transparent 75%);
  filter:blur(30px); animation:heroGlowPulse 6.5s ease-in-out infinite;
}
@keyframes heroGlowPulse{0%,100%{opacity:0.55; transform:scale(1);}50%{opacity:0.8; transform:scale(1.035);}}

/* Desktop-only uplift for the hero visual (right column).
   Rather than stealing width from the copy, the hero band itself widens on
   desktop: both columns push outward from centre — text to the left, image
   to the right — so the left column ends up ROOMIER than its original
   546px, keeping its original wrapping and feel, while the image column
   gains the rest. Only above 1024px:
     1. the hero's own containers widen 1200px -> 1440px (all three widen
        together so the copy, the mission/vision cards and the stat strip
        stay edge-aligned);
     2. the split stays near-even (1 : 1.05) with a 48px gap — a slight
        bias to the image, not a land grab;
     3. the height cap rises while the hero's vertical padding tightens, so
        the taller visual fills the band instead of floating in it.
   The frame still shrink-wraps the image, so the aspect ratio stays exact
   (no crop, no distortion) and the render never exceeds the 1024x1536
   source (no upscaling / quality loss). Left-column typography, spacing,
   alignment, animations and all tablet/mobile behaviour are untouched —
   the base rules above still govern every viewport at or below 1024px. */
@media (min-width:1025px){
  /* Vertical breathing room trimmed to the practical floor. The fixed
     navbar stacks a ~37px topbar over an ~88px nav row (~125px total), so
     128px is as low as the top padding can go before the eyebrow tag starts
     tucking under the bar — anything tighter has to come out of the navbar
     itself, not the hero. Bottom padding and the stat-strip offset tighten
     to match so the band stays visually balanced top-to-bottom. */
  .hero{padding:128px 0 112px;}
  .hero-stats{margin-top:28px; padding-top:20px;}
  .hero > .container{max-width:1440px;}
  /* Small inset on the copy only — nudges the headline block rightward off
     the widened band's edge without changing its internal spacing,
     alignment or type. Its usable text width lands at ~541px, still
     within a few px of the original 546px, so wrapping is preserved. */
  .hero-content{padding-left:28px;}
  /* Split rebalanced to 1 : 1.3 so the extra image size comes from the
     column ratio rather than from widening the band further out of line
     with the sections below. At the 1380px band this leaves the copy
     column at ~544px against its ORIGINAL 546px — the stopping point:
     the copy sits ~90px further left than it originally did while its
     column stays essentially its original width, so the headline
     wrapping and feel are preserved. Widening further would only
     inflate the copy column past its original proportions. */
  .hero-grid{grid-template-columns:minmax(0, 1fr) minmax(0, 1.36fr); gap:48px;}
  /* Frame anchored to the right edge of its column instead of centred, so
     the remaining slack in the column falls on the inner side — the image
     sits flush with the hero's right edge rather than floating mid-column.
     Base rule keeps centring for the stacked tablet/mobile layout. */
  .hero-visual{justify-content:flex-end;}
  /* The copy+image row is the tallest thing in the hero: the image drives
     the row height, and the copy is centred against it, so shortening the
     image shortens the whole first band and pulls the copy's own dead space
     in with it. Cap eased back so the row loses ~180px of height while the
     image stays clearly larger than its original 427x640.

     Eased back again. The copy column is about 630px tall; at 98vh the image
     column rendered 882px on a 1440x900 screen, and because .hero-grid centres
     the two columns against each other, the 250px difference became dead space
     split above and below the copy - the visible "gap" under the buttons. The
     cap now tracks the copy column more closely, which removes most of that
     slack. Nothing else changes: the frame still shrink-wraps the image, so the
     aspect ratio stays exact with no crop, no distortion and no upscaling past
     the 800x1200 source, and the typography, alignment and animations are
     untouched. Still well above the original 427x640 render.

     (The source is 800x1200. The markup used to declare 1024x1536, which this
     comment previously repeated; both are now corrected. The cap stays well
     under 1200px, so there is still no upscaling.)

     height: rather than max-height: for the same reason as the base rule - it
     reserves the box before the image loads. Same clamp, same rendered size. */
  .hero-illustration-img img{
    height:clamp(560px, 76vh, 900px);
  }
  .hero-visual-glow{inset:-64px;}
}

/* ---------- Hero Mission / Vision glass cards ---------- */
.hero-mv-grid{display:grid; grid-template-columns:minmax(0, 1fr) minmax(0, 1fr); gap:20px; margin-top:8px; position:relative; z-index:2;}
.hero-mv-card{
  position:relative; padding:26px 26px 24px; border-radius:20px;
  background:rgba(255,255,255,0.07);
  border:1px solid rgba(255,255,255,0.16);
  backdrop-filter:blur(14px) saturate(140%); -webkit-backdrop-filter:blur(14px) saturate(140%);
  box-shadow:0 8px 30px rgba(0,0,0,0.25);
  transition:transform 0.35s var(--ease), box-shadow 0.35s var(--ease), border-color 0.35s var(--ease), background 0.35s var(--ease);
  overflow:hidden;
}
.hero-mv-card::before{
  content:''; position:absolute; inset:0; opacity:0; transition:opacity 0.35s var(--ease);
  background:radial-gradient(220px 140px at 15% 0%, rgba(0,217,200,0.35), transparent 70%);
  pointer-events:none;
}
.hero-mv-card:nth-child(2)::before{background:radial-gradient(220px 140px at 15% 0%, rgba(139,92,246,0.35), transparent 70%);}
.hero-mv-card:hover{
  transform:translateY(-6px); border-color:rgba(255,255,255,0.32);
  background:rgba(255,255,255,0.10); box-shadow:0 16px 44px rgba(0,0,0,0.32);
}
.hero-mv-card:hover::before{opacity:1;}
.hero-mv-icon{
  width:44px; height:44px; border-radius:12px; display:flex; align-items:center; justify-content:center;
  background:rgba(255,255,255,0.12); color:var(--gold); font-size:18px; margin-bottom:14px; position:relative;
}
.hero-mv-label{display:block; font-size:11px; font-weight:700; text-transform:uppercase; letter-spacing:0.1em; color:rgba(255,255,255,0.55); margin-bottom:6px; position:relative;}
.hero-mv-card .hero-mv-title{color:#fff; font-size:19px; margin-bottom:8px; position:relative; line-height:1.3;}
.hero-mv-card p{color:rgba(255,255,255,0.68); font-size:13.5px; line-height:1.6; position:relative; margin-bottom:0;}

.hero-stats{margin-top:48px; position:relative; z-index:2; padding-top:32px; border-top:1px solid rgba(255,255,255,0.12);}
.hero-stats .stat-item .num{color:#fff;}
.hero-stats .stat-item .num .grad-text{color:var(--gold);}
.hero-stats .stat-item .label{color:rgba(255,255,255,0.6);}

.scroll-indicator{
  position:absolute; left:50%; bottom:28px; transform:translateX(-50%);
  display:flex; flex-direction:column; align-items:center; gap:8px; z-index:2;
  color:rgba(255,255,255,0.55); font-size:11px; letter-spacing:0.08em; text-transform:uppercase;
  animation:bob 2.4s ease-in-out infinite;
}
.scroll-indicator .mouse{width:22px; height:36px; border:2px solid rgba(255,255,255,0.4); border-radius:12px; position:relative;}
.scroll-indicator .mouse::before{
  content:''; position:absolute; top:6px; left:50%; transform:translateX(-50%);
  width:4px; height:8px; border-radius:2px; background:var(--gold); animation:scrolldot 1.8s ease-in-out infinite;
}
@keyframes bob{0%,100%{transform:translateX(-50%) translateY(0);}50%{transform:translateX(-50%) translateY(6px);}}
@keyframes scrolldot{0%{opacity:1; top:6px;}70%{opacity:0; top:18px;}100%{opacity:0; top:6px;}}

/* ---------- Featured project ---------- */
.featured-project{display:grid; grid-template-columns:minmax(0, 0.95fr) minmax(0, 1.05fr); gap:56px; align-items:center;}
.featured-visual{border-radius:var(--radius-lg); overflow:hidden; box-shadow:var(--shadow-md);}
.featured-copy p{margin-bottom:14px;}
.featured-list{margin:18px 0 26px; display:grid; gap:10px;}
.featured-list li{display:flex; align-items:center; gap:10px; font-size:14.5px; color:var(--text);}
.featured-list i{color:var(--mint);}

.avatar-img img{border-radius:50%; object-fit:cover;}

/* ---------- Why DeepAbi premium cards ---------- */
.why-card{padding:var(--sp-4) var(--sp-4) 30px; transition:transform 0.35s var(--ease), box-shadow 0.35s var(--ease), border-color 0.35s var(--ease);}
.why-card .card-icon{
  width:58px; height:58px; font-size:24px; border-radius:16px;
  transition:transform 0.35s var(--ease), background 0.35s var(--ease), color 0.35s var(--ease);
}
.why-card:hover{transform:translateY(-8px) scale(1.015); box-shadow:0 20px 46px rgba(8,31,77,0.14); border-color:rgba(0,217,200,0.3);}
.why-card:hover .card-icon{transform:scale(1.12) rotate(-6deg); background:linear-gradient(135deg, var(--violet), var(--cyan)); color:#fff;}
.why-card h3{font-size:18px;}

/* ---------- What We Build cards ---------- */
.wwb-grid{display:grid; grid-template-columns:repeat(4, minmax(0, 1fr)); gap:var(--sp-3);}
.wwb-card{
  position:relative; background:var(--card-bg); border:1px solid var(--border); border-radius:var(--radius-lg);
  padding:28px 24px; text-align:left; overflow:hidden;
  transition:transform 0.35s var(--ease), box-shadow 0.35s var(--ease), border-color 0.35s var(--ease);
}
.wwb-card::before{
  content:''; position:absolute; inset:0; padding:1.5px; border-radius:var(--radius-lg);
  background:linear-gradient(135deg, var(--cyan), var(--violet));
  -webkit-mask:linear-gradient(#fff 0 0) content-box, linear-gradient(#fff 0 0);
  -webkit-mask-composite:xor; mask-composite:exclude;
  opacity:0; transition:opacity 0.35s var(--ease); pointer-events:none;
}
.wwb-card:hover{transform:translateY(-8px); box-shadow:var(--shadow-md); border-color:transparent;}
.wwb-card:hover::before{opacity:1;}
.wwb-icon{
  width:50px; height:50px; border-radius:14px; display:flex; align-items:center; justify-content:center;
  background:rgba(139,92,246,0.10); color:var(--violet); font-size:21px; margin-bottom:18px;
  transition:transform 0.35s var(--ease), background 0.35s var(--ease), color 0.35s var(--ease);
}
.wwb-card:hover .wwb-icon{transform:scale(1.12) rotate(-6deg); background:linear-gradient(135deg, var(--violet), var(--cyan)); color:#fff;}
.wwb-card h3{font-size:16.5px; margin-bottom:8px;}
.wwb-card p{font-size:13.5px; margin-bottom:0;}
.wwb-more{
  display:inline-flex; align-items:center; gap:6px; margin-top:14px; font-size:12.5px; font-weight:700;
  color:var(--violet); opacity:0; transform:translateY(6px); min-height:24px;
  transition:opacity 0.3s var(--ease), transform 0.3s var(--ease);
}
@media (hover: none){
  /* On touch screens the card hover never happens, so the link would be
     invisible-but-tappable. Show it. */
  .wwb-more{opacity:1; transform:none;}
}
.wwb-more i{font-size:11px; transition:transform 0.3s var(--ease);}
.wwb-card:hover .wwb-more, .wwb-card:focus-within .wwb-more{opacity:1; transform:translateY(0);}
.wwb-card:hover .wwb-more i, .wwb-card:focus-within .wwb-more i{transform:translateX(3px);}

/* ---------- Built for Every Screen: premium auto-cycling showcase ---------- */
.device-stack{
  position:relative; height:420px; max-width:640px; margin:0 auto var(--sp-6);
  display:flex; align-items:center; justify-content:center;
}
.device-stack-card{
  position:absolute; left:50%; top:50%;
  transition:transform 0.9s cubic-bezier(.22,.68,.16,1.02), filter 0.9s var(--ease), opacity 0.9s var(--ease), box-shadow 0.9s var(--ease);
  will-change:transform, filter, opacity;
}
.device-stack-frame{
  border-radius:18px; overflow:hidden; background:#fff;
  border:1px solid rgba(255,255,255,0.5);
  backdrop-filter:blur(6px); -webkit-backdrop-filter:blur(6px);
}
.device-stack-frame img{display:block; width:100%; height:100%; object-fit:cover;}
.device-stack-label{
  position:absolute; left:50%; bottom:-32px; transform:translateX(-50%);
  display:inline-flex; align-items:center; gap:6px; font-size:12.5px; font-weight:700;
  color:var(--text-secondary); background:var(--card-bg); border:1px solid var(--border);
  padding:5px 14px; border-radius:999px; white-space:nowrap;
  opacity:0; transition:opacity 0.5s var(--ease);
}
.device-stack-card[data-role="desktop"] .device-stack-frame{width:460px; height:307px;}
.device-stack-card[data-role="tablet"] .device-stack-frame{width:260px; height:347px;}
.device-stack-card[data-role="mobile"] .device-stack-frame{width:168px; height:363px;}

/* Stack states, cycled by JS: front (active), mid, back */
.device-stack-card.stack-front{
  transform:translate(-50%,-50%) translateY(0) scale(1) rotate(0deg);
  filter:blur(0); opacity:1; z-index:5;
  box-shadow:0 34px 70px rgba(8,31,77,0.30), 0 10px 26px rgba(8,31,77,0.16);
}
.device-stack-card.stack-front .device-stack-label{opacity:1;}
.device-stack-card.stack-mid{
  transform:translate(-50%,-50%) translate(120px,26px) scale(0.82) rotate(6deg);
  filter:blur(1.5px); opacity:0.72; z-index:3;
  box-shadow:0 20px 44px rgba(8,31,77,0.20);
}
.device-stack-card.stack-back{
  transform:translate(-50%,-50%) translate(-120px,44px) scale(0.72) rotate(-7deg);
  filter:blur(2.5px); opacity:0.5; z-index:2;
  box-shadow:0 14px 30px rgba(8,31,77,0.14);
}
.device-stack.paused .device-stack-card{transition-duration:0.4s;}

/* Large-format variant: deepabi-desktop/tablet/mobile share one landscape
   aspect ratio (~3:2), so every card uses the same frame size — depth is
   created purely through the perspective offsets below, at a larger scale
   to suit the bigger source imagery. */
.device-stack-large{height:clamp(320px, 42vw, 460px); max-width:760px;}
.device-stack-large .device-stack-card .device-stack-frame{
  width:clamp(300px, 46vw, 640px); height:clamp(200px, 30.6vw, 427px);
  box-sizing:border-box;
}
.device-stack-large .device-stack-card.stack-front{
  transform:translate(-50%,-50%) translateY(0) scale(1) rotate(0deg) perspective(1200px);
}
.device-stack-large .device-stack-card.stack-mid{
  transform:translate(-50%,-50%) translate(9%,7%) scale(0.86) rotate(4deg);
}
.device-stack-large .device-stack-card.stack-back{
  transform:translate(-50%,-50%) translate(-9%,12%) scale(0.74) rotate(-5deg);
}

.responsive-content{max-width:900px; margin:0 auto; text-align:center;}
.responsive-heading{font-size:clamp(24px,3vw,30px); margin-bottom:12px;}
.responsive-lede{font-size:16px; max-width:640px; margin:0 auto var(--sp-5);}

.rf-grid{display:grid; grid-template-columns:repeat(2, minmax(0, 1fr)); gap:16px; text-align:left;}
.rf-item{
  display:flex; align-items:flex-start; gap:16px; padding:18px 20px; border-radius:16px;
  background:var(--card-bg); border:1px solid var(--border);
  transition:transform 0.3s var(--ease), box-shadow 0.3s var(--ease), border-color 0.3s var(--ease);
}
.rf-item:hover{transform:translateY(-4px); box-shadow:var(--shadow-md); border-color:rgba(0,217,200,0.35);}
.rf-icon{
  width:42px; height:42px; border-radius:12px; flex-shrink:0; display:flex; align-items:center; justify-content:center;
  background:rgba(0,217,200,0.12); color:var(--cyan-ink); font-size:17px;
  transition:transform 0.3s var(--ease);
}
.rf-item:hover .rf-icon{transform:scale(1.1); background:linear-gradient(135deg, var(--cyan), var(--violet)); color:#fff;}
.rf-text h3{font-size:14.5px; margin-bottom:4px; color:var(--navy);}
.rf-text p{font-size:13px; margin-bottom:0; line-height:1.55;}

/* ---------- Testimonial carousel ---------- */
.testimonial-carousel{position:relative; max-width:760px; margin:0 auto; padding:0 64px;}
.testimonial-track{position:relative; overflow:hidden; border-radius:var(--radius-lg);}
.testimonial-slide{
  position:absolute; inset:0; opacity:0; transform:translateX(24px); pointer-events:none;
  transition:opacity 0.5s var(--ease), transform 0.5s var(--ease);
}
.testimonial-slide.active{position:relative; opacity:1; transform:translateX(0); pointer-events:auto;}
.testimonial-card{
  position:relative; background:rgba(255,255,255,0.72); border:1px solid rgba(255,255,255,0.5); border-radius:var(--radius-lg);
  padding:44px 40px 32px; text-align:center; box-shadow:var(--shadow-sm);
  backdrop-filter:blur(18px) saturate(160%); -webkit-backdrop-filter:blur(18px) saturate(160%);
  transition:transform 0.35s var(--ease), box-shadow 0.35s var(--ease);
}
.testimonial-card:hover{transform:translateY(-6px); box-shadow:0 24px 54px rgba(8,31,77,0.16);}
.testimonial-quote-mark{
  position:absolute; top:20px; left:50%; transform:translateX(-50%);
  font-size:34px; color:rgba(139,92,246,0.18);
}
.testimonial-stars{color:var(--gold); font-size:14px; margin-bottom:16px; display:flex; gap:4px; justify-content:center;}
.testimonial-card .quote{color:var(--text); font-size:16.5px; line-height:1.6; margin-bottom:24px;}
.testimonial-person{display:flex; align-items:center; gap:12px; justify-content:center;}
.testimonial-person img{width:52px; height:52px; border-radius:50%; object-fit:cover; border:2px solid var(--surface); box-shadow:0 0 0 2px var(--violet);}
.testimonial-person h3{font-size:15px; margin-bottom:2px;}
.testimonial-person span{font-size:12.5px; color:var(--text-secondary);}
/* The dots stay 9px visually, but each button is a 24x24 target with the dot
   drawn as a pseudo-element inside it. Below 24px they were the smallest
   controls on the site and awkward to hit on a phone; the look is unchanged. */
.testimonial-dots{display:flex; justify-content:center; align-items:center; gap:4px; margin-top:22px;}
.testimonial-dots button{
  width:24px; height:24px; min-width:24px; border-radius:50%; background:none; padding:0; border:none;
  display:inline-flex; align-items:center; justify-content:center; cursor:pointer;
}
.testimonial-dots button::before{
  content:""; display:block; width:9px; height:9px; border-radius:50%;
  background:var(--border); transition:all 0.3s var(--ease);
}
.testimonial-dots button.active{width:36px; min-width:36px; border-radius:12px; background:none;}
.testimonial-dots button.active::before{width:26px; height:9px; border-radius:6px;
  background:linear-gradient(90deg, var(--violet), var(--cyan));}
.testimonial-dots button:focus-visible{outline:3px solid var(--violet); outline-offset:1px;}
.testimonial-arrow{
  position:absolute; top:50%; transform:translateY(-50%); z-index:3;
  width:46px; height:46px; border-radius:50%; background:#fff; border:1px solid var(--border);
  display:flex; align-items:center; justify-content:center; color:var(--navy); font-size:15px;
  box-shadow:var(--shadow-sm); cursor:pointer;
  transition:transform 0.25s var(--ease), box-shadow 0.25s var(--ease), background 0.25s var(--ease), color 0.25s var(--ease);
}
.testimonial-arrow:hover{background:linear-gradient(135deg, var(--violet), var(--cyan)); color:#fff; transform:translateY(-50%) scale(1.08); box-shadow:var(--shadow-md);}
.testimonial-arrow.prev{left:0;}
.testimonial-arrow.next{right:0;}

/* ==========================================================================
   Responsive — consolidated, largest breakpoint to smallest
   ========================================================================== */
@media (max-width:1024px){
  /* 60px reads as a column gap beside the copy on desktop; stacked, it becomes
     a row gap that sits on top of the ~13% of empty canvas at the top of the
     illustration file, so the band between the buttons and the globe measured
     about 160px. 32px closes most of that without crowding the two. */
  .hero-grid{grid-template-columns:minmax(0, 1fr); gap:32px;}
  /* The illustration file is a 800x1200 canvas with the globe occupying rows
     278-858; the 278px above it and 341px below it are FULLY TRANSPARENT
     (measured alpha 0), so 52% of the element's height renders nothing and
     still pushes the rest of the hero down. These negative margins pull that
     empty canvas back in. The globe itself is untouched - no crop, no
     object-fit, no change of aspect ratio, no scaling - only the blank area
     around it stops taking up layout space. Percentages resolve against the
     frame's width, and the frame shrink-wraps the image at a fixed 2:3 ratio,
     so one pair of values holds at every width in this range. Deliberately
     short of the full transparent band, which leaves clear space around the
     globe. Desktop (>=1025px) is untouched. */
  .hero-illustration-img img{margin-top:-18%; margin-bottom:-22%;}
  /* The illustration used to be pulled above the copy here (order:-1). On a
     single column that pushed the headline and the "Start Your Project" button
     below the fold at every width in this range - 805px down on a 375x812
     screen - so the first thing a visitor saw was artwork with no idea what the
     page was selling. The image now follows the copy in source order, which is
     also its natural DOM order, so the headline and the primary call to action
     come first and the globe sits directly beneath them. Desktop (>=1025px) is
     a two-column grid and is unaffected by this rule. */
  .featured-project{grid-template-columns:minmax(0, 1fr);}
  .wwb-grid{grid-template-columns:repeat(2, minmax(0, 1fr));}
}
@media (max-width:768px){
  /* Bottom padding matched to the base rule for the same reason: the scroll
     indicator needs ~90px of clear space and 70px left it printing over the
     mission/vision cards once the stat strip was removed. */
  /* Top padding was 160px against a fixed header that is only 57px tall at
     375px and 45px at 768px, leaving ~100px of empty band above the eyebrow.
     96px clears the tallest header in this range with ~39px of breathing room
     and returns that space to the content. */
  .hero{padding:96px 0 112px;}
  .hero-mv-grid{grid-template-columns:minmax(0, 1fr);}
  .featured-project{gap:32px;}
}
@media (max-width:600px){
  .device-stack{height:340px;}
  .device-stack-card[data-role="desktop"] .device-stack-frame{width:300px; height:200px;}
  .device-stack-card[data-role="tablet"] .device-stack-frame{width:180px; height:240px;}
  .device-stack-card[data-role="mobile"] .device-stack-frame{width:120px; height:260px;}
  .device-stack-card.stack-mid{transform:translate(-50%,-50%) translate(70px,20px) scale(0.8) rotate(6deg);}
  .device-stack-card.stack-back{transform:translate(-50%,-50%) translate(-70px,32px) scale(0.7) rotate(-7deg);}
  .device-stack-large{height:280px;}
  .device-stack-large .device-stack-card.stack-mid{transform:translate(-50%,-50%) translate(7%,6%) scale(0.84) rotate(4deg);}
  .device-stack-large .device-stack-card.stack-back{transform:translate(-50%,-50%) translate(-7%,10%) scale(0.7) rotate(-5deg);}
  .wwb-grid{grid-template-columns:minmax(0, 1fr);}
  .testimonial-card{padding:32px 22px 26px;}
  .rf-grid{grid-template-columns:minmax(0, 1fr);}
  .testimonial-carousel{padding:0 4px;}
  .testimonial-arrow{width:36px; height:36px; font-size:12px;}
  .testimonial-arrow.prev{left:-2px;}
  .testimonial-arrow.next{right:-2px;}
}
@media (max-width:375px){
  .hero-actions{gap:12px;}
  .hero-mv-card{padding:20px 18px 18px;}
}
