/* Dark hero: atmosphere built from gradients (no photo). Layers move a few px with the cursor via --mx/--my. */
.hdark{position:relative;isolation:isolate;overflow:hidden;min-height:100svh;padding-top:var(--header-h);background:#030a16;color:#fff;--blue-light:#3b9de8;--mx:0;--my:0}
.hdark__bg{position:absolute;inset:0;z-index:0;pointer-events:none;overflow:hidden}
.hdark__bg>*{position:absolute;will-change:transform}
.hdark__base{inset:0;background:radial-gradient(120% 80% at 50% -10%,#0d2242 0%,#061226 45%,#030a16 100%)}
/* Plain colour: one solid ground. The dotted standard word still reads over it; the gradient,
   shields, streak and grain are dropped so the colour the client picks is exactly the colour. */
.hdark--plain{background:var(--hd-ground,#030a16)}
.hdark--plain .hdark__base,.hdark--plain .hdark__shield,.hdark--plain .hdark__streak,.hdark--plain .hdark__grain{display:none}
/* The light is shaped like the logo's shield: a large soft glow masked to the shield path, plus a faint edge. */
.hdark__shield{--shield:url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 41.5 48.58'><path d='M20.75 0.79L40.75 7.79V23.79C40.75 35.79 32.25 44.29 20.75 47.79C9.25 44.29 0.75 35.79 0.75 23.79V7.79L20.75 0.79Z' fill='black'/></svg>");aspect-ratio:41.5/48.58}
.hdark__shield--main{right:-4vw;bottom:-26vh;width:min(64vw,62rem);-webkit-mask:var(--shield) center/contain no-repeat;mask:var(--shield) center/contain no-repeat;background:linear-gradient(205deg,rgba(10,40,100,.35) 0%,rgba(18,85,190,.55) 40%,rgba(30,130,235,.8) 72%,rgba(70,175,255,.92) 100%);filter:blur(22px);transform:rotate(-14deg) translate3d(calc(var(--mx)*30px),calc(var(--my)*20px),0)}
.hdark__shield--edge{right:-4vw;bottom:-26vh;width:min(64vw,62rem);overflow:visible;filter:blur(1.2px);opacity:.7;transform:rotate(-14deg) translate3d(calc(var(--mx)*30px),calc(var(--my)*20px),0)}
.hdark__shield--ghost{left:-30vw;top:-8vh;width:min(78vw,78rem);-webkit-mask:var(--shield) center/contain no-repeat;mask:var(--shield) center/contain no-repeat;background:linear-gradient(160deg,rgba(24,90,190,.42) 0%,rgba(12,50,120,.26) 55%,rgba(6,25,70,.08) 100%);filter:blur(36px);transform:rotate(18deg) translate3d(calc(var(--mx)*-22px),calc(var(--my)*-14px),0)}
/* one soft diagonal streak, as if light grazes the shield's edge */
.hdark__streak{left:-20%;bottom:-10%;width:110vw;height:16vh;background:linear-gradient(90deg,rgba(0,0,0,0) 0%,rgba(60,150,240,.10) 35%,rgba(150,210,255,.28) 52%,rgba(60,150,240,.10) 70%,rgba(0,0,0,0) 100%);filter:blur(22px);transform:rotate(-24deg) translate3d(calc(var(--mx)*-26px),calc(var(--my)*-16px),0)}
/* Uploaded background photo: covers the section, drifts slightly with the cursor; generated shields are hidden. */
.hdark__photo{inset:-3%;width:106%;height:106%;max-width:none;object-fit:cover;object-position:center;transform:translate3d(calc(var(--mx)*18px),calc(var(--my)*12px),0)}
/* The picture is black down its left and blue across its right. Centred, a wide screen sits
   on the dark middle and the blue barely shows, so the crop is pulled towards the blue: the
   card still gets dark behind it, the copy gets the colour. Plain image treatment only; the
   flow one paints its own blue and must stay centred. */
.hdark--image:not(.hdark--flow) .hdark__photo{object-position:78% 50%;transform:scaleX(-1) translate3d(calc(var(--mx)*18px),calc(var(--my)*12px),0)}
/* Desktop crop from Figma Frame 2 (564:1321): the photo is scaled to 4060px on a 1920 frame,
   turned 180deg and pulled up-left, so its blue corner sweeps in from the top-left and the
   black side sits under the copy. Expressed in vw so the crop scales with the screen; the
   mobile sizes keep the plain cover crop above, where this box would not reach the bottom. */
@media (min-width:64em){
  .hdark--image:not(.hdark--flow) .hdark__photo{
    inset:auto;top:-27.08vw;left:-62.34vw;width:211.46vw;height:auto;
    transform:scaleX(-1) rotate(180deg) translate3d(calc(var(--mx)*18px),calc(var(--my)*12px),0)}
}
.hdark--image .hdark__shield,.hdark--image .hdark__streak{display:none}
.hdark--image .hdark__grain{opacity:.05}
/* Dotted word: a particle field that assembles into the card's compliance name, behind the copy and the card. */
.hdark__word{inset:0;width:100%;height:100%;transform:none}
@media (max-width:64em){.hdark__word{display:none}}
/* film grain keeps the gradients from looking flat */
.hdark__grain{inset:0;opacity:.07;mix-blend-mode:overlay;background-image:url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='180' height='180'><filter id='n'><feTurbulence type='fractalNoise' baseFrequency='.9' numOctaves='2' stitchTiles='stitch'/><feColorMatrix values='0 0 0 0 1 0 0 0 0 1 0 0 0 0 1 0 0 0 .8 0'/></filter><rect width='100%' height='100%' filter='url(%23n)'/></svg>");background-size:180px 180px}

/* Everything must fit one viewport: sizes and gaps scale with height. Figma @1080: h1 48px x2 lines, intro 16px x2, button, 64px, card, logos. */
.hdark{--hd-gap:clamp(.75rem,2.4vh,1.75rem);--hd-h1:clamp(2.125rem,calc(1rem + 1.4vw + 1.6vh),3.25rem)}
/* A rounded section riding up over this hero needs room to do it, or it lands on the
   client logos. The hero grows by exactly the lift, so its own content still fits one
   viewport and the panel's top edge comes to rest on the fold. */
.hdark:has(+ .svc),
.hdark:has(+ .lc),
.hdark:has(+ .fl){padding-bottom:var(--section-lift)}

.hdark__inner{position:relative;z-index:1;display:flex;flex-direction:column;align-items:center;padding-block:clamp(1.25rem,5vh,3.75rem) clamp(2.5rem,6vh,4rem);min-height:calc(100svh - var(--header-h))}
.hdark__copy{text-align:center;max-width:46rem}
.hdark__title{margin:0 auto;max-width:13.5em;font-family:"Montserrat",var(--font-display);font-weight:var(--w-display);font-size:var(--hd-h1);line-height:1.1;letter-spacing:var(--h1-track);color:#fff}
.hdark__title em{font-style:italic;font-weight:var(--w-display);color:var(--blue-light)}
.hdark__title strong,.hdark__title b{font-weight:var(--w-display);color:var(--blue-light)}
.hdark__sub{margin:var(--hd-gap) auto 0;max-width:30rem;font-size:1rem;line-height:1.6;letter-spacing:0;color:rgba(255,255,255,.82)}
.hdark__cta{display:flex;flex-wrap:wrap;gap:.875rem;justify-content:center;margin-top:calc(var(--hd-gap)*1.3)}
.hdark__stage{position:relative;width:100%;margin-top:clamp(1.5rem,5.9vh,4rem)}
.epack{z-index:2}

@media (max-width:64em){
  .hdark__shield--main,.hdark__shield--edge{right:-30vw;bottom:-12vh;width:120vw}
  .hdark__shield--ghost{left:-60vw;width:140vw}
  .hdark__streak{left:-60%;width:200vw}
}
@media (prefers-reduced-motion:reduce){.hdark__bg>*{transform:none!important}}

/* Client logos under the card: a ticker. The row is a track that view.js clones until it is
   at least two containers wide, then slides by exactly one set (--set-w) so the loop is
   seamless at any logo count. Speed is a constant px/s, so the duration (--set-dur) scales
   with the set rather than racing when there are few logos. Without JS, or with reduced
   motion, it is a plain centred row. The edges fade so logos arrive and leave softly. */
.hdark__logos{position:relative;width:100%;margin-top:clamp(2.25rem,6vh,4.5rem);overflow:hidden;
  -webkit-mask-image:linear-gradient(90deg,transparent,#000 12%,#000 88%,transparent);mask-image:linear-gradient(90deg,transparent,#000 12%,#000 88%,transparent)}
.hdark__logos-track{display:flex;justify-content:center;align-items:center;gap:clamp(2.5rem,4vw,4.5rem);width:max-content;min-width:100%;margin:0 auto}
.hdark__logo{height:1.1875rem;width:auto;max-width:8rem;object-fit:contain;opacity:.85;flex:none}
/* Client logos on the dark hero (the scrolling wordmark ticker): the uploaded marks are dark
   on transparent, meant for a light page, so on the dark ground they are flattened to light
   silhouettes at a muted opacity, the testcomet.io way. Tiles keep their own colour. */
.hdark:not(.hdark--tiles) .hdark__logo{height:2.125rem;max-width:12rem;filter:brightness(0) invert(1);opacity:.68}
/* A white evidence card can sit on the dark hero; its carousel dots are styled for a light
   page, so keep them light here or they vanish against the hero. */
.hdark .epack--light .epack__dot{background:rgba(255,255,255,.3)}
.hdark .epack--light .epack__dot.is-on{background:#fff}
.js .hdark__logos{opacity:0;animation:hdark-logos-in .9s var(--ease) .9s both}
@keyframes hdark-logos-in{from{opacity:0;transform:translateY(6px)}to{opacity:1;transform:none}}
.js .hdark__logos.is-ticking{-webkit-mask-image:linear-gradient(90deg,transparent,#000 12%,#000 88%,transparent)}
.js .hdark__logos.is-ticking .hdark__logos-track{justify-content:flex-start;margin:0;animation:hdark-ticker var(--set-dur,20s) linear infinite}
.js .hdark__logos.is-ticking:hover .hdark__logos-track{animation-play-state:paused}
@keyframes hdark-ticker{from{transform:translateX(0)}to{transform:translateX(calc(var(--set-w,0px) * -1))}}
@media (prefers-reduced-motion:reduce){
  .js .hdark__logos{animation:none;opacity:1}
  .js .hdark__logos.is-ticking .hdark__logos-track{animation:none;justify-content:center;margin:0 auto}
}
@media (max-width:36em){.hdark__logos{margin-top:2rem}.hdark__logos-track{gap:1.75rem}}
/* App-icon tiles (Figma Frame 2): three 67px squares, 20px apart, left-aligned under the
   numbers. No ticker, no edge mask, full opacity; the icons carry their own corners. */
.hdark--tiles .hdark__logos{margin-top:clamp(1.25rem,2.4vh,1.5rem);overflow:visible;-webkit-mask-image:none;mask-image:none}
.hdark--tiles .hdark__logos-track{justify-content:flex-start;gap:1.25rem;min-width:0;width:auto;margin:0}
.hdark--tiles .hdark__logo{width:4.1875rem;height:4.1875rem;max-width:none;border-radius:.875rem;object-fit:cover;opacity:1}
@media (max-width:36em){.hdark--tiles .hdark__logo{width:3.5rem;height:3.5rem;border-radius:.75rem}.hdark--tiles .hdark__logos-track{gap:1rem}}

/* Short laptop screens: two rows visible instead of three, tighter bottom. */
@media (min-width:36.01em) and (max-height:840px){
  .epack__rowbox{height:calc(2*2.75rem + 1.25rem)}
  .hdark__inner{padding-bottom:1rem}
}

/* Blue wash background, lifted from the flow hero so the two can share a look.
   Flat near-black ground instead of the radial, more contrast in the photograph, and a saturated
   blue multiplied in from past the middle, which deepens the pale corner without greying it. */
.hdark--flow .hdark__base{background:#07081d}
.hdark--flow .hdark__photo{filter:brightness(.85) contrast(1.35) saturate(1.05)}
.hdark--flow .hdark__bg::after{
  content:"";position:absolute;inset:0;pointer-events:none;mix-blend-mode:multiply;
  background:linear-gradient(100deg,rgba(255,255,255,0) 42%,rgba(16,48,170,.55) 70%,rgb(8,30,130) 100%)}

/* ---------- Light tone -------------------------------------------------------
   The same banner reversed onto the page's own ground, for inner pages that should
   not read as a second home page. No photograph, no wash, no shields: the only
   things that carry over are the composition and the card. The header is left in
   its normal state by aitg_hero_is_dark(), so nothing here has to fight it. */
.hdark--light{
  background:#fff;color:var(--navy);
  min-height:0;padding-bottom:clamp(1.5rem,3vh,2.5rem);
}
.hdark--light .hdark__bg{display:none}
.hdark--light .hdark__inner{min-height:0}
.hdark--light .hdark__title{color:var(--navy)}
.hdark--light .hdark__title strong{color:var(--blue)}
.hdark--light .hdark__sub{color:var(--muted)}
.hdark--light .hdark__note{color:var(--muted-3,var(--muted))}
.hdark--light .hdark__caption{color:var(--muted-3,var(--muted))}

/* Chip and its travelling note, reversed. */
.hdark--light .hdark__chip{background:var(--chip-bg);border-color:rgba(10,26,51,.12);color:var(--navy)}
.hdark--light .hdark__tickeritem{color:var(--muted)}
.hdark--light .hdark__ticker{
  -webkit-mask-image:linear-gradient(90deg,#000 0,#000 calc(100% - 3rem),transparent 100%);
          mask-image:linear-gradient(90deg,#000 0,#000 calc(100% - 3rem),transparent 100%)}

/* Proof numbers take the page's own ink rather than white. */
.hdark--light .hdark__proofnum{color:var(--navy)}
.hdark--light .hdark__prooflabel{color:var(--muted)}

/* A light banner opening a page needs the same run-up as any other section. */
.hdark--light .hdark__inner{padding-block:clamp(2.5rem,7vh,4.5rem) 0}

/* ---------- Split layout: copy left, evidence card right, standards in dots along the bottom ----------
   Composition only. Buttons, type scale and card are untouched; below the canvas breakpoint it falls
   back to the centred stack, since the dotted band is not drawn there anyway. */
@media (min-width:64.01em){
  .hdark--split .hdark__inner{
    display:grid;
    grid-template-columns:minmax(0,1.04fr) minmax(0,1fr);
    grid-template-rows:1fr auto 1fr auto;
    align-items:center;
    column-gap:clamp(2rem,5vw,5.5rem);
    padding-block:0 clamp(1.5rem,4vh,3rem);
  }
  /* Copy and card share the middle auto row; the two 1fr rows flanking it are equal, so the
     space from the nav to the card matches the space from the card to the logo row below it. */
  .hdark--split .hdark__copy{grid-column:1;grid-row:2;align-self:center;justify-self:start;max-width:36rem;text-align:left}
  .hdark--split .hdark__title{margin:0;max-width:11.5em}
  .hdark--split .hdark__sub{margin-left:0;margin-right:0;max-width:32rem}
  .hdark--split .hdark__cta{justify-content:flex-start}
  .hdark--split .hdark__chipline{justify-content:flex-start}
  .hdark--split .hdark__proof{justify-content:flex-start}
  .hdark--split .hdark__stage{grid-column:2;grid-row:2;align-self:center;margin-top:0}
  .hdark--split .epack{margin-left:auto;margin-right:0;width:min(100%,34rem)}
  /* The logo ticker spans the full hero width under both columns, not just the copy side, so
     it uses more of the screen and more marks are in view. */
  .hdark--split .hdark__logos{grid-column:1/-1;grid-row:4;justify-content:flex-start;align-self:center;margin-top:0}
}
/* Split reversed: the same composition mirrored, card left and copy right. Column widths swap too,
   so the copy keeps the slightly wider side it has in the standard split. */
@media (min-width:64.01em){
  .hdark--split.hdark--mirror .hdark__inner{grid-template-columns:minmax(0,1fr) minmax(0,1.04fr)}
  .hdark--split.hdark--mirror .hdark__copy{grid-column:2}
  .hdark--split.hdark--mirror .hdark__stage{grid-column:1}
  .hdark--split.hdark--mirror .epack{margin-left:0;margin-right:auto}
  .hdark--split.hdark--mirror .hdark__logos{grid-column:1/-1}
  .hdark--split.hdark--mirror .hdark__caption{margin-left:0;margin-right:auto;text-align:left}
  /* The copy side carries two buttons here, which need more room than the 36rem the standard split gives. */
  .hdark--split.hdark--mirror .hdark__copy{max-width:42rem}
  .hdark--split.hdark--mirror .hdark__sub{max-width:34rem}
}

/* The bottom band needs its room back on a short screen, where the centred layout gives it up. */
@media (min-width:64.01em) and (max-height:840px){
  .hdark--split .hdark__inner{grid-template-rows:1fr auto 1fr auto;padding-bottom:clamp(1.5rem,3vh,2.5rem)}
}

/* ---------- Chip and the travelling note beside it ----------
   The chip is fixed; the note runs right to left in its own track and is cut off hard at the chip
   edge, so the sentence disappears into it rather than fading out. Two identical copies and a
   -50% travel make the loop seamless; the padding, not a gap, keeps that arithmetic exact. */
.hdark__chipline{display:flex;align-items:center;gap:1rem;margin:0 0 clamp(.75rem,1.8vh,1.25rem);justify-content:center;min-width:0}
.hdark__chip{flex:none;display:inline-flex;align-items:center;gap:.6875rem;
  padding:.8125rem 1.5rem .8125rem 1.125rem;border-radius:100px;
  background:rgba(255,255,255,.08);border:1px solid rgba(255,255,255,.22);
  font-size:.9375rem;line-height:1.4;font-weight:600;color:#fff;white-space:nowrap}
.hdark__chipdot{flex:none;width:9px;height:9px;border-radius:50%;background:#0cb500}
.hdark__ticker{position:relative;flex:1 1 18rem;min-width:0;max-width:34rem;height:1.375rem;overflow:hidden;
  -webkit-mask-image:linear-gradient(90deg,#000 0,#000 calc(100% - 3rem),transparent 100%);
          mask-image:linear-gradient(90deg,#000 0,#000 calc(100% - 3rem),transparent 100%)}
.hdark__tickertrack{display:flex;width:max-content;animation:hdarkTicker 26s linear infinite;will-change:transform}
.hdark__tickeritem{padding-right:3.5rem;white-space:nowrap;font-size:.9375rem;line-height:1.375rem;color:rgba(255,255,255,.62)}
@keyframes hdarkTicker{from{transform:translate3d(0,0,0)}to{transform:translate3d(-50%,0,0)}}
@media (prefers-reduced-motion:reduce){.hdark__tickertrack{animation:none}}
@media (max-width:64em){
  .hdark__chipline{flex-wrap:wrap;gap:.75rem}
  .hdark__ticker{flex:1 1 100%;max-width:none}
}
/* On a phone the sliding line is wider than the screen in both directions, so it shows a
   half word at each edge and reads as broken text rather than as motion. It stops being a
   ticker and becomes the sentence, centred under the chip. */
@media (max-width:36em){
  .hdark__ticker{height:auto;overflow:visible;-webkit-mask-image:none;mask-image:none}
  .hdark__tickertrack{display:block;width:auto;animation:none}
  .hdark__tickeritem{display:block;white-space:normal;padding-right:0;text-align:center}
  .hdark__tickeritem + .hdark__tickeritem{display:none}
}

/* Proof numbers under the buttons. Same rhythm as the stats elsewhere on the site, but white,
   since this sits on the dark banner rather than a light section. */
.hdark__proof{list-style:none;margin:clamp(1.75rem,4vh,2.75rem) 0 0;padding:0;display:flex;flex-wrap:wrap;gap:clamp(1.75rem,4vw,3.5rem);justify-content:center}
.hdark__proofitem{min-width:0}
.hdark__proofnum{margin:0;font-family:var(--font-display);font-weight:var(--w-ui);font-size:clamp(2.25rem,1.4rem + 1.8vw,3.25rem);line-height:1.05;letter-spacing:var(--h1-track);color:#fff;white-space:nowrap}
.hdark__prooflabel{margin:.5rem 0 0;max-width:10rem;font-size:var(--ui-size);line-height:var(--ui-lh);letter-spacing:var(--ui-track);color:rgba(255,255,255,.6)}

/* Label, the muted line under the button, and the card caption. */
.hdark .hdark__eyebrow{margin:0 0 clamp(.75rem,1.8vh,1.25rem);color:rgba(255,255,255,.7)}
.hdark__note{margin:calc(var(--hd-gap)*.9) 0 0;font-size:.8125rem;line-height:1.5;color:rgba(255,255,255,.55)}
.hdark__caption{max-width:min(100%,32.75rem);margin:clamp(.75rem,1.6vh,1.25rem) auto 0;font-size:.75rem;line-height:1.5;color:rgba(255,255,255,.45);text-align:right}
@media (max-width:64em){.hdark__caption{text-align:center}}

@media (min-width:64.01em){.hdark--split .hdark__caption{max-width:min(100%,34rem);margin-right:0}}

/* ---- phones: the banner is one screen -------------------------------------
   The banner fills the window and the card is cropped by its bottom edge rather than shrunk
   to fit. The card is an illustration of evidence being written, not a list to read: showing
   its top and letting the rest run off the screen says "this continues" far better than a
   miniature of the whole thing, and it leaves the copy the room it needs. */
@media (max-width:36em){
  /* The section's own bottom padding goes with the lift it was reserved for (see services). */
  .hdark{padding-bottom:0}
  /* The banner used to be pinned to exactly one screen with the card cropped by the bottom
     edge. On a phone that left the card a strip too short to show a single row, so the
     animation it exists for never played. It takes its natural height instead. */
  .hdark__inner{height:auto;padding-block:1.75rem 2rem;overflow:visible}
  /* flex:none also stops it shrinking across the column, so the copy kept its 46rem
     desktop width and the page's overflow-x:hidden simply cropped both sides. */
  .hdark__copy{flex:none;width:100%;max-width:100%}
  .hdark__title,.hdark__sub,.hdark__chipline,.hdark__cta,.hdark__note{max-width:100%}
  .hdark__cta{flex-wrap:wrap}
  .hdark__cta .btn2{max-width:100%}

  /* The three figures are supporting detail, not the message: on a phone they run as one
     compact row rather than wrapping to three, which also buys the card its strip back. */
  .hdark__proof{flex-wrap:nowrap;gap:.625rem;margin-top:1.125rem;align-items:flex-start}
  .hdark__proofitem{flex:1 1 0;min-width:0;text-align:center}
  .hdark__proofnum{font-size:1.5rem;line-height:1.1}
  .hdark__prooflabel{margin-top:.1875rem;max-width:none;font-size:.6875rem;line-height:1.25}

  /* Tightened, so the copy ends high enough for the card to get a worthwhile strip. */
  .hdark .hdark__eyebrow{font-size:.75rem;margin-bottom:.625rem}
  .hdark__copy .sec-sub{margin-top:.875rem}
  .hdark__cta{margin-top:1.25rem}
  .hdark__note{margin-top:.75rem}

  /* The logos move above the card: they are a short row and they would otherwise be the thing
     cropped off the bottom, which is the one element here that has to be seen in full. */
  .hdark__logos{order:1;flex:none;margin-top:1.25rem}
  .hdark__stage{order:2;flex:none;min-height:0;overflow:visible;margin-top:1.25rem}

  /* Small print about a cropped illustration, itself cropped, is noise. It stays on every
     wider screen where the card is shown whole. */
  .hdark__caption{display:none}

  /* With the banner at its natural height the card is whole, so the rows stack and write
     themselves the way they do on a wide screen rather than sharing one cropped slot. */
}

/* ---------- Light tone, spacing corrections ----------------------------------
   These sit at the end on purpose. The split layout reserves a deep bottom pad for the
   dotted band that runs under a dark banner; a light one has no band, and at equal
   specificity the split rule was winning on source order. A light banner also opens a
   page rather than filling the screen, so whatever follows gives up most of its run-up
   instead of stacking a second full section pad under the first. */
.hdark--light.hdark--split .hdark__inner{padding-bottom:0}
@media (min-width:64.01em) and (max-height:840px){
  .hdark--light.hdark--split .hdark__inner{padding-bottom:0}
}
.hdark--light + .steps,
.hdark--light + .lc,
.hdark--light + .nt,
.hdark--light + .sc,
.hdark--light + .diff,
.hdark--light + .prod,
.hdark--light + .faq,
.hdark--light + .svc{padding-top:calc(var(--section-pad) * .7)}
