/**
 * Animated Hero for Bricks.
 *
 * Everything here is a default. Values set in the builder arrive as Bricks
 * generated CSS with higher specificity, including the per-breakpoint rules,
 * so nothing in this file needs !important.
 */

/* Declared, not assumed. Without it the content clamp is a content-box width
   and the side padding is added on top, so a 1400px clamp measures 1576px. */
.brxe-animated-hero,
.brxe-animated-hero *,
.brxe-animated-hero *::before,
.brxe-animated-hero *::after{box-sizing:border-box}

.brxe-animated-hero{
  --ahb-gap:0;          /* extra separation between the two pieces, mark units */
  --ahb-idle:3.2;         /* idle swing, mark units */
  --ahb-idle-tilt:1.6;    /* rotation at the peak, degrees */
  --ahb-idle-shift:2.5;   /* sideways travel at the peak, px - swing style only */

  /* buttons */
  --ahb-btn-pad-y:14px;
  --ahb-btn-pad-x:24px;
  --ahb-btn-font:13.5px;
  --ahb-btn-radius:999px;
  --ahb-btn-icon:17px;
  --ahb-trust-gap:clamp(14px, 2.2vw, 34px);
  --ahb-stack-gap:clamp(20px, 4vw, 56px);   /* mark to copy */
  --ahb-lead-gap:clamp(12px, 2.2vh, 20px);  /* headline to lead */
  --ahb-cta-gap:clamp(16px, 2.8vh, 28px);   /* lead to buttons */
  --ahb-btn-gap:16px;                       /* between buttons */
  --ahb-trust-top:clamp(18px, 3.4vh, 38px); /* buttons to proof row */
  --ahb-pad-top:clamp(20px, 4vh, 64px);     /* above the whole stack */
  --ahb-pad-bottom:clamp(20px, 4vh, 48px);  /* below the whole stack */

  /* idle rings */
  --ahb-ring-speed:7s;
  --ahb-ring-thickness:1px;
  --ahb-ring-opacity:.28;
  --ahb-ring-fade:.35;      /* opacity left at mid-travel; lower fades sooner */
  --ahb-ring-reach:1700px;
  --ahb-idle-dur:10s;
  --ahb-idle-start:2.75s;
  --ahb-x:0px;
  --ahb-y:0px;
  --ahb-face-a:#ffffff;
  --ahb-face-b:#cfe4ff;
  --ahb-side:#0a39a8;
  --ahb-gloss:.9;
  --ahb-impact-at:2.24s;
  --ahb-pad-x:clamp(20px, 5vw, 88px);
  --ahb-content:1400px;   /* content clamp; the section itself stays full width */

  position:relative;
  display:flex;
  flex-direction:column;
  overflow:hidden;
  isolation:isolate;
  /* svh, not vh: iOS changes vh when its toolbars slide and a vh hero jumps
     mid-scroll. Safe-area insets keep content clear of the notch. */
  justify-content:center;
  min-height:100svh;
  padding-top:calc(var(--ahb-pad-top) + env(safe-area-inset-top, 0px));
  padding-bottom:calc(var(--ahb-pad-bottom) + env(safe-area-inset-bottom, 0px));
}

/* ---------------------------------------------------------------- layout -- */
.ahb__inner{
  /* Not flex:1. Growing this to fill the hero is what pinned the proof-point
     row to the bottom edge; the hero centres its children instead. */
  flex:0 0 auto;
  display:grid;
  grid-template-columns:1.08fr .92fr;
  align-items:center;
  gap:var(--ahb-stack-gap);
  /* Horizontal only. Any vertical padding here lands inside a gap that a
     spacing control is meant to own, so setting that control to 0 would
     never close it. Outer breathing room lives on the hero. */
  padding:0 var(--ahb-pad-x);
  width:100%;
  max-width:var(--ahb-content);
  margin-inline:auto;
}
/* The mark gets its own column so it can never overlap the headline. */
.ahb__copy{position:relative;z-index:2;min-width:0}
.ahb__stage{position:relative;z-index:1;display:grid;place-items:center;min-width:0}

.brxe-animated-hero[data-mark-side="start"] .ahb__copy{order:2}
.brxe-animated-hero[data-mark-side="start"] .ahb__stage{order:1}

.ahb__title{margin:0;line-height:1.03;font-weight:600;letter-spacing:-.025em;color:#fff}
[dir="rtl"] .ahb__title{letter-spacing:0;line-height:1.24;font-weight:700}
.ahb__lead{margin:var(--ahb-lead-gap) 0 0;line-height:1.65;color:rgba(255,255,255,.9);max-width:32em}

/* Parent in the selector deliberately. .ahb__reveal > * sets display:block and
   outranks a lone class, so this collapsed to a block container and gap was
   silently ignored - the same trap that flattened the proof point row. */
.ahb__reveal .ahb__cta,
.ahb__cta{display:flex;flex-wrap:wrap;gap:var(--ahb-btn-gap);margin-top:var(--ahb-cta-gap)}
.ahb__btn{
  display:inline-flex;align-items:center;gap:10px;
  background:#1f1f1f;color:#fff;text-decoration:none;
  font-size:var(--ahb-btn-font);font-weight:600;
  padding:var(--ahb-btn-pad-y) var(--ahb-btn-pad-x);
  border-radius:var(--ahb-btn-radius);
  box-shadow:0 6px 20px rgba(4,24,70,.3);
  transition:transform .18s ease, box-shadow .18s ease;
}
.ahb__btn:hover{transform:translateY(-2px);box-shadow:0 12px 28px rgba(4,24,70,.4)}
.ahb__btn img,.ahb__btn svg{width:var(--ahb-btn-icon);height:var(--ahb-btn-icon);flex:none;display:block;fill:currentColor}

/* Button styles. Parent on the selector so these outrank the base .ahb__btn. */
.brxe-animated-hero[data-btn-style="outline"] .ahb__btn{
  background:transparent;
  border:1px solid currentColor;
  box-shadow:none;
}
.brxe-animated-hero[data-btn-style="ghost"] .ahb__btn{
  background:rgba(255,255,255,.16);
  box-shadow:none;
  backdrop-filter:blur(8px);
  -webkit-backdrop-filter:blur(8px);
}
.brxe-animated-hero[data-btn-style="ghost"] .ahb__btn:hover{background:rgba(255,255,255,.26)}

/* Full-width strip: four labels never fit across a half-width column, and
   stacked they read as a weak list rather than a row of proof points. */
.ahb__trust{
  padding:0 var(--ahb-pad-x);
  width:100%;max-width:var(--ahb-content);
  margin:var(--ahb-trust-top) auto 0;
}
/* .ahb__reveal > * sets display:block and outranks a single class, which
   flattens this row into a list. */
.ahb__trust .ahb__trust-list{display:flex;flex-wrap:wrap;gap:10px var(--ahb-trust-gap)}
.ahb__trust-item{display:flex;align-items:center;line-height:1;gap:8px;font-size:11.5px;font-weight:500;color:rgba(255,255,255,.9)}
.ahb__trust-icon{
  width:26px;height:26px;border-radius:50%;flex:none;
  border:1px solid rgba(255,255,255,.5);
  display:grid;place-items:center;
}
.ahb__trust-icon img,.ahb__trust-icon svg{width:13px;height:13px;display:block;stroke:currentColor;fill:none;stroke-width:1.7}

/* Centred layout: one column, mark above the words. Same shape the phone
   layout already uses, so the two agree by construction rather than by two sets
   of rules that have to be kept in step. */
.brxe-animated-hero[data-layout="centred"] .ahb__inner{
  grid-template-columns:1fr;
  text-align:center;
  justify-items:center;
  /* Must stay on the variable. Hardcoding a gap here silently overrode the
     "Mark to text" control, so setting it to 0 still left 2vh in place. */
  gap:var(--ahb-stack-gap);
}
.brxe-animated-hero[data-layout="centred"] .ahb__stage{order:-1}
.brxe-animated-hero[data-layout="centred"] .ahb__copy{display:flex;flex-direction:column;align-items:center}
.brxe-animated-hero[data-layout="centred"] .ahb__lead{margin-inline:auto}
.brxe-animated-hero[data-layout="centred"] .ahb__cta{justify-content:center}
.brxe-animated-hero[data-layout="centred"] .ahb__trust .ahb__trust-list{justify-content:center}

@media (max-width:900px){
  /* Phones get a tighter rhythm out of the box. Any value set on a wider
     breakpoint in the builder still cascades down and wins, as it should. */
  .brxe-animated-hero{
    --ahb-stack-gap:clamp(4px, 1.4vh, 14px);
    --ahb-lead-gap:clamp(8px, 1.6vh, 14px);
    --ahb-cta-gap:clamp(12px, 2vh, 20px);
    --ahb-btn-gap:12px;
    --ahb-trust-top:clamp(12px, 2.4vh, 22px);
    --ahb-pad-top:clamp(10px, 2vh, 24px);
    --ahb-pad-bottom:clamp(10px, 2vh, 24px);
  }
  .ahb__inner{grid-template-columns:1fr;text-align:center}
  /* Artwork above the words on phones, whichever side it sits on at desktop. */
  .ahb__stage,.brxe-animated-hero[data-mark-side="start"] .ahb__stage{order:-1}
  .ahb__copy,.brxe-animated-hero[data-mark-side="start"] .ahb__copy{order:0;display:flex;flex-direction:column;align-items:center}
  .ahb__lead{margin-inline:auto}
  .ahb__cta{justify-content:center}
  .ahb__trust .ahb__trust-list{justify-content:center}

  .ahb__btn{flex:1 1 auto;justify-content:center;min-width:148px}
}
@media (max-width:900px){
  /* The gloss sweeps a clipped rect across the mark every frame, and because
     the mark is SVG that repaints the whole subtree including every extrusion
     layer. It is a subtle highlight on a 400px mark and invisible on a phone,
     so it is not worth a repaint per frame there. */
  .ahb__sheen{display:none}
}
@media (max-width:430px){
  .ahb__trust-item{font-size:10.5px}
  .ahb__trust-icon{width:22px;height:22px}
}
/* A short landscape phone gets its own layout rather than a hero taller than
   the screen. */
@media (max-height:520px) and (orientation:landscape){
  .brxe-animated-hero:not([data-layout="centred"]) .ahb__inner{grid-template-columns:1.1fr .9fr;text-align:start}
  .brxe-animated-hero:not([data-layout="centred"]) .ahb__stage{order:0}
  .brxe-animated-hero:not([data-layout="centred"]) .ahb__copy{align-items:flex-start}
  .ahb__trust{display:none}
}

/* ------------------------------------------------------------------ mark -- */
.ahb__mark-wrap{
  position:relative;
  width:min(100%, clamp(180px,30vw,400px));
  aspect-ratio:1/1;
  transform:translate(var(--ahb-x,0px), var(--ahb-y,0px));
}
@media (max-width:900px){.ahb__mark-wrap{width:clamp(140px,38vw,240px)}}
@media (max-height:520px) and (orientation:landscape){.ahb__mark-wrap{width:clamp(110px,20vw,170px)}}

.ahb__mark{width:100%;height:100%;display:block;overflow:visible}
.ahb__halo{
  position:absolute;inset:-20%;border-radius:50%;z-index:-1;pointer-events:none;
  background:radial-gradient(circle at 50% 50%, rgba(255,255,255,.26), rgba(255,255,255,0) 62%);
}

/* The gap sits on the wrapper group and the entrance animations sit on the
   path inside it, so the two never fight over the transform property. */
/* view-box, NOT fill-box. fill-box resolves the origin against this group's own
   bounding box, and that box contains the gloss rect sweeping across the piece.
   So the scale pivot drifted with the gloss and jumped 6px when the sweep loop
   wrapped - visible as the mark twitching sideways at the peak, where the scale
   is largest. view-box pins the origin to the viewBox, independent of children. */
.ahb__lift{transform-box:view-box;transform-origin:50% 50%}
.ahb__lift--top   {transform:translateY(calc(var(--ahb-gap) * -1px))}
.ahb__lift--bottom{transform:translateY(calc(var(--ahb-gap) *  1px))}

.ahb__piece{fill:#fff}
.brxe-animated-hero[data-style="dimensional"] .ahb__piece{fill:url(#ahbFace)}
.ahb__depth-layer{fill:var(--ahb-side)}

/* Per-mode extras default to hidden. An unstyled SVG path defaults to
   fill:black and paints a solid shape over the artwork. */
.ahb__outline,.ahb__ring,.ahb__wave,.ahb__part{display:none;fill:none;stroke:none}
.ahb__sheen{opacity:0}

/* Image mode */
.ahb__img{position:absolute;inset:0;width:100%;height:100%;object-fit:contain}

/* --------------------------------------------------------------- trace ---- */
.brxe-animated-hero[data-anim="trace"] .ahb__outline{
  display:block;fill:none;stroke:#fff;stroke-width:1.4;
  stroke-dasharray:var(--len,400);stroke-dashoffset:var(--len,400);
  animation:ahb-draw 1.5s cubic-bezier(.6,0,.35,1) forwards,
            ahb-outline-out .55s ease 1.75s forwards;
}
.brxe-animated-hero[data-anim="trace"] .ahb__outline--b{animation-delay:.22s, 1.97s}
@keyframes ahb-draw{to{stroke-dashoffset:0}}
/* The outline is scaffolding. Left on screen it sits exactly on the silhouette
   of a flat fill and is invisible, but over a gradient it reads as a sticker
   edge, so it hands over to the fill and gets out of the way. */
@keyframes ahb-outline-out{to{opacity:0}}

.brxe-animated-hero[data-anim="trace"] .ahb__piece,
.brxe-animated-hero[data-anim="trace"] .ahb__depth-layer{opacity:0;animation:ahb-fill .7s ease .95s forwards}
.brxe-animated-hero[data-anim="trace"] .ahb__piece--bottom,
.brxe-animated-hero[data-anim="trace"] .ahb__depth--bottom .ahb__depth-layer{animation-delay:1.15s}
@keyframes ahb-fill{to{opacity:1}}

.brxe-animated-hero[data-anim="trace"] .ahb__sheen{animation:ahb-sweep 5s ease-in-out 2.1s infinite}
@keyframes ahb-sweep{
  0%{opacity:0;transform:translateX(-70px)}
  8%{opacity:1}
  38%{opacity:1}
  46%,100%{opacity:0;transform:translateX(70px)}
}

/* Pull back, punch out. The hit lands at --ahb-impact-at. */
.brxe-animated-hero[data-anim="trace"] .ahb__mark-wrap{
  animation:ahb-slam 1s cubic-bezier(.3,.9,.3,1) 1.75s backwards;
}
@keyframes ahb-slam{
  0%{transform:translate(var(--ahb-x,0px),var(--ahb-y,0px)) scale(1)}
  34%{transform:translate(var(--ahb-x,0px),var(--ahb-y,0px)) scale(.84)}
  49%{transform:translate(var(--ahb-x,0px),var(--ahb-y,0px)) scale(1.07)}
  68%{transform:translate(var(--ahb-x,0px),var(--ahb-y,0px)) scale(.985)}
  100%{transform:translate(var(--ahb-x,0px),var(--ahb-y,0px)) scale(1)}
}
.brxe-animated-hero[data-impact="none"] .ahb__mark-wrap{animation:none}

/* ---------------------------------------------------------------- idle ---- */
/* Phase matters more than shape. With the open state at 0% the first frame of
   the loop is --ahb-idle units from where the pieces rest, so the mark snaps
   apart the instant the idle begins. Starting and ending CLOSED makes frame one
   identical to the resting transform, and ease-in-out leaves at zero velocity,
   so the idle grows out of the stillness instead of interrupting it. */
.brxe-animated-hero[data-idle="on"] .ahb__lift--top{
  animation:ahb-v-top var(--ahb-idle-dur) ease-in-out var(--ahb-idle-start) infinite;
}
.brxe-animated-hero[data-idle="on"] .ahb__lift--bottom{
  animation:ahb-v-bot var(--ahb-idle-dur) ease-in-out var(--ahb-idle-start) infinite;
}
/* The halves part on a slight diagonal and counter-rotate a degree or so, which
   is closer to the original render than a straight vertical split: that clip is
   a slow pitch, so the two pieces swing rather than slide. One control drives
   both the rotation and the small lateral drift, so they can never disagree. */
/* Hinged, not symmetrical. Only the TOP piece swings, and it swings right; the
   bottom simply drops straight down. Both pieces drifting in opposite
   directions read as the mark tearing in half, which is not what the original
   does. Idle tilt drives the rotation and the matching sideways travel. */
/* Four idle styles. Every one starts and ends CLOSED, so frame one matches the
   resting transform and the loop grows out of the stillness instead of snapping
   into it. */

/* vertical - straight apart and back */
@keyframes ahb-v-top{
  0%,100%{transform:translateY(calc(var(--ahb-gap) * -1px))}
  50%    {transform:translateY(calc((var(--ahb-gap) + var(--ahb-idle)) * -1px))}
}
@keyframes ahb-v-bot{
  0%,100%{transform:translateY(calc(var(--ahb-gap) * 1px))}
  50%    {transform:translateY(calc((var(--ahb-gap) + var(--ahb-idle)) * 1px))}
}

/* swing - top lifts, drifts right and rotates; bottom drops straight */
@keyframes ahb-s-top{
  0%,100%{transform:translate(0, calc(var(--ahb-gap) * -1px)) rotate(0deg)}
  50%    {transform:translate(calc(var(--ahb-idle-shift) * 1px),
                              calc((var(--ahb-gap) + var(--ahb-idle)) * -1px))
                    rotate(calc(var(--ahb-idle-tilt) * 1deg))}
}

/* hinge - top pivots about its lower right corner, opening like a lid */
@keyframes ahb-h-top{
  0%,100%{transform:translateY(calc(var(--ahb-gap) * -1px)) rotate(0deg)}
  50%    {transform:translateY(calc(var(--ahb-gap) * -1px))
                    rotate(calc(var(--ahb-idle-tilt) * 2deg))}
}
@keyframes ahb-h-bot{
  0%,100%{transform:translateY(calc(var(--ahb-gap) * 1px))}
  50%    {transform:translateY(calc((var(--ahb-gap) + var(--ahb-idle) * .35) * 1px))}
}

/* breathe - both scale a few percent as they part */
@keyframes ahb-b-top{
  0%,100%{transform:translateY(calc(var(--ahb-gap) * -1px)) scale(1)}
  50%    {transform:translateY(calc((var(--ahb-gap) + var(--ahb-idle)) * -1px)) scale(1.03)}
}
@keyframes ahb-b-bot{
  0%,100%{transform:translateY(calc(var(--ahb-gap) * 1px)) scale(1)}
  50%    {transform:translateY(calc((var(--ahb-gap) + var(--ahb-idle)) * 1px)) scale(1.03)}
}

/* Both attributes on the selector deliberately. The rule that sets up the idle
   is .brxe-animated-hero[data-idle="on"] .ahb__lift--*, so a plain
   [data-idle-style] selector is a specificity step LOWER and silently loses:
   every style would run the vertical keyframes. */
.brxe-animated-hero[data-idle="on"][data-idle-style="vertical"] .ahb__lift--top   {animation-name:ahb-v-top}
.brxe-animated-hero[data-idle="on"][data-idle-style="vertical"] .ahb__lift--bottom{animation-name:ahb-v-bot}
.brxe-animated-hero[data-idle="on"][data-idle-style="swing"]    .ahb__lift--top   {animation-name:ahb-s-top}
.brxe-animated-hero[data-idle="on"][data-idle-style="swing"]    .ahb__lift--bottom{animation-name:ahb-v-bot}
.brxe-animated-hero[data-idle="on"][data-idle-style="hinge"]    .ahb__lift--top   {animation-name:ahb-h-top;transform-origin:82% 92%}
.brxe-animated-hero[data-idle="on"][data-idle-style="hinge"]    .ahb__lift--bottom{animation-name:ahb-h-bot}
.brxe-animated-hero[data-idle="on"][data-idle-style="breathe"]  .ahb__lift--top   {animation-name:ahb-b-top}
.brxe-animated-hero[data-idle="on"][data-idle-style="breathe"]  .ahb__lift--bottom{animation-name:ahb-b-bot}
.brxe-animated-hero[data-idle="on"] .ahb__mark{
  animation:ahb-bob var(--ahb-idle-dur) ease-in-out var(--ahb-idle-start) infinite;
}
@keyframes ahb-bob{
  0%,50%,100%{transform:translateY(0)}
  25%{transform:translateY(-1.2px)}
  75%{transform:translateY(1.2px)}
}

/* Hover lives on the wrapper so it never fights the breathing transform on the
   pieces. Gentle on purpose: a punchy hover beside a calm idle reads as a
   glitch rather than a response. */
.brxe-animated-hero[data-hover="on"] .ahb__mark-wrap{cursor:pointer}
.brxe-animated-hero[data-hover="on"] .ahb__mark-wrap.is-hot{
  transform:translate(var(--ahb-x,0px),var(--ahb-y,0px)) scale(1.03);
  transition:transform .75s cubic-bezier(.25,.8,.25,1);
}
.ahb__mark-wrap.is-replay .ahb__sheen{animation:ahb-sweep 1.6s ease-in-out}

/* ------------------------------------------------------------ assemble ---- */
.brxe-animated-hero[data-anim="assemble"] .ahb__piece{transform-box:fill-box;transform-origin:50% 50%}
.brxe-animated-hero[data-anim="assemble"] .ahb__piece--top{animation:ahb-in-top .95s cubic-bezier(.16,1,.3,1) .12s backwards}
.brxe-animated-hero[data-anim="assemble"] .ahb__piece--bottom{animation:ahb-in-bottom .95s cubic-bezier(.16,1,.3,1) .3s backwards}
@keyframes ahb-in-top{from{opacity:0;transform:translateY(-26px) scale(.94)}to{opacity:1;transform:none}}
@keyframes ahb-in-bottom{from{opacity:0;transform:translateY(30px) scale(.94)}to{opacity:1;transform:none}}

/* --------------------------------------------------------------- depth ---- */
.brxe-animated-hero[data-anim="depth"] .ahb__piece--top{
  transform:translate(calc(var(--ahb-px,0)*9px), calc(var(--ahb-py,0)*9px));
  transition:transform .45s cubic-bezier(.2,.8,.2,1);
}
.brxe-animated-hero[data-anim="depth"] .ahb__piece--bottom{
  transform:translate(calc(var(--ahb-px,0)*19px), calc(var(--ahb-py,0)*19px));
  transition:transform .45s cubic-bezier(.2,.8,.2,1);
}
@media (hover:none){
  .brxe-animated-hero[data-anim="depth"] .ahb__piece--top{animation:ahb-sway-back 7s ease-in-out infinite}
  .brxe-animated-hero[data-anim="depth"] .ahb__piece--bottom{animation:ahb-sway-front 7s ease-in-out infinite}
}
@keyframes ahb-sway-back{0%,100%{transform:translate(-5px,-3px)}50%{transform:translate(5px,3px)}}
@keyframes ahb-sway-front{0%,100%{transform:translate(9px,5px)}50%{transform:translate(-9px,-5px)}}

/* ------------------------------------------------------------- network ---- */
.brxe-animated-hero[data-anim="network"] .ahb__ring{
  display:block;fill:none;stroke:rgba(255,255,255,.55);stroke-width:1.3;
  transform-origin:50% 50%;opacity:0;animation:ahb-net-ring 4.2s ease-out infinite;
}
.brxe-animated-hero[data-anim="network"] .ahb__ring:nth-of-type(2){animation-delay:1.4s}
.brxe-animated-hero[data-anim="network"] .ahb__ring:nth-of-type(3){animation-delay:2.8s}
@keyframes ahb-net-ring{0%{transform:scale(.3);opacity:0}18%{opacity:.8}100%{transform:scale(1.55);opacity:0}}

/* -------------------------------------------------------------- impact ---- */
/* The ripple hangs off the hero, not off the stage. Inside the stage it lives
   in the clamped content column, so on a wide screen the splash stopped dead at
   the content edge. Anchored here on --ahb-ix / --ahb-iy (the mark's centre as a
   percentage of the hero, set from JS) it spreads the full width and is clipped
   only by the hero itself. */
.ahb__impact{
  position:absolute;
  left:var(--ahb-ix,50%);
  top:var(--ahb-iy,50%);
  width:0;height:0;
  pointer-events:none;
  z-index:0;
}
.ahb__impact > *{position:absolute;left:0;top:0;border-radius:50%;opacity:0}
/* Centred on the anchor by their own size, so growing width/height stays
   concentric. Particles use left/top instead, because their transform is
   already carrying the travel. */
.ahb__ring--impact{transform:translate(-50%,-50%)}

/* Growing width/height rather than scale, so the ring keeps a constant
   thickness instead of ballooning into a thick band. */
.ahb__ring--impact{width:0;height:0;border:2px solid rgba(255,255,255,.6)}
@keyframes ahb-ring{0%{width:0;height:0;opacity:0}7%{opacity:.9}100%{width:3200px;height:3200px;opacity:0}}
/* Fixed box, scaled. A soft gradient blob looks identical scaled and costs the
   compositor nothing, unlike growing width/height every frame. */
.ahb__wave{
  width:600px;height:600px;margin:-300px 0 0 -300px;
  background:radial-gradient(circle,rgba(255,255,255,.45) 0%,rgba(255,255,255,.14) 52%,transparent 70%);
}
@keyframes ahb-wave{0%{transform:scale(.02);opacity:.95}100%{transform:scale(4);opacity:0}}
.ahb__part{width:9px;height:9px;left:-4.5px;top:-4.5px;background:#fff;box-shadow:0 0 10px rgba(255,255,255,.9);display:none}
@keyframes ahb-part{0%{transform:translate(0,0) scale(1);opacity:0}12%{opacity:1}100%{transform:translate(var(--x),var(--y)) scale(.15);opacity:0}}

/* Hero-wide, anchored on the mark via --ahb-ix/--ahb-iy set from JS so it lands
   correctly in RTL and when the layout stacks on a phone. Opacity only:
   scaling this box shrinks it below the hero and its own edges appear as a hard
   rectangle, because the gradient's transparent stop sits at 55% of the
   farthest corner and is still opaque where the box ends vertically. */
.ahb__bloom,.ahb__flash{position:absolute;inset:0;pointer-events:none;z-index:0;opacity:0}
.ahb__bloom{background:radial-gradient(circle at var(--ahb-ix,50%) var(--ahb-iy,50%),rgba(255,255,255,.5) 0%,rgba(190,222,255,.22) 22%,transparent 55%)}
@keyframes ahb-bloom{0%{opacity:0}16%{opacity:1}100%{opacity:0}}
.ahb__flash{background:#eaf3ff}
@keyframes ahb-flash{0%{opacity:0}18%{opacity:.3}100%{opacity:0}}

.brxe-animated-hero[data-impact="ring"] .ahb__ring--impact:nth-of-type(1),
.brxe-animated-hero[data-impact="rings"] .ahb__ring--impact,
.brxe-animated-hero[data-impact="full"] .ahb__ring--impact{display:block;animation:ahb-ring 1.7s cubic-bezier(.12,.72,.25,1) var(--ahb-impact-at) forwards}
.brxe-animated-hero[data-impact="rings"] .ahb__ring--impact:nth-of-type(2),
.brxe-animated-hero[data-impact="full"] .ahb__ring--impact:nth-of-type(2){animation-delay:calc(var(--ahb-impact-at) + .14s)}
.brxe-animated-hero[data-impact="rings"] .ahb__ring--impact:nth-of-type(3),
.brxe-animated-hero[data-impact="full"] .ahb__ring--impact:nth-of-type(3){animation-delay:calc(var(--ahb-impact-at) + .28s)}

.brxe-animated-hero[data-impact="shockwave"] .ahb__wave,
.brxe-animated-hero[data-impact="full"] .ahb__wave{display:block;animation:ahb-wave 1.3s cubic-bezier(.1,.7,.3,1) var(--ahb-impact-at) forwards}

.brxe-animated-hero[data-impact="particles"] .ahb__part,
.brxe-animated-hero[data-impact="full"] .ahb__part{display:block;animation:ahb-part 1.5s cubic-bezier(.15,.7,.25,1) var(--ahb-impact-at) forwards}

.brxe-animated-hero[data-impact="bloom"] .ahb__bloom,
.brxe-animated-hero[data-impact="full"] .ahb__bloom{animation:ahb-bloom 1.6s ease-out var(--ahb-impact-at) forwards}

.brxe-animated-hero[data-impact="flash"] .ahb__flash{animation:ahb-flash 1s ease-out var(--ahb-impact-at) forwards}

/* ---------------------------------------------------------- idle rings ---- */
/* Anchored on the mark like the impact layer, and at the same z-index, so they
   sit behind the mark, its extruded side and its halo rather than over them. */
.ahb__idle-rings{
  position:absolute;
  left:var(--ahb-ix,50%);
  top:var(--ahb-iy,50%);
  width:0;height:0;
  pointer-events:none;
  z-index:0;
}
/* SVG circles scaled with transform, NOT divs grown with width/height.
   Growing a box relayouts and repaints the ring every single frame, which is
   affordable for a one-shot and ruinous for something looping forever - it was
   the reason this dropped frames on a phone. transform and opacity are the only
   two properties the compositor can animate without touching layout or paint.

   The reason width/height was used originally is that scaling a bordered box
   balloons the border from a hairline into a thick band. vector-effect
   non-scaling-stroke solves that properly: the stroke stays 1px on screen no
   matter how far the circle is scaled. */
/* Plain divs, not SVG circles.
   Round one of this fix used <circle> with vector-effect:non-scaling-stroke to
   keep a hairline stroke under scale. Correct in theory, useless in practice:
   Blink does not composite transforms on SVG children, it relayouts them, and
   it measured 300 layouts over 5 seconds - one per frame, exactly the cost the
   rewrite was meant to remove. An HTML box with a transform is promoted to its
   own layer, rasterised once and then only moved, which is free.

   The trade is that a scaled border thickens. It is bounded by starting from a
   large base (520px) so the scale factor stays near 3x rather than 17x, and a
   ring that softens slightly as it dissipates is the right look anyway. */
.ahb__idle-ring{
  position:absolute;left:0;top:0;
  width:520px;height:520px;
  margin:-260px 0 0 -260px;
  border-radius:50%;
  border:var(--ahb-ring-thickness) solid rgba(255,255,255,var(--ahb-ring-opacity));
  opacity:0;
  animation:ahb-idle-ring var(--ahb-ring-speed) linear infinite;
  /* Deliberately NO will-change. On a scaling element it makes the compositor
     rasterise the layer at its FINAL scale: 520px * 3.3 * DPR 3 is a 5148px
     square, 26 million pixels, ~106 MB of texture per ring. Without it the
     layer is rasterised near its current size and re-rasterised as it grows,
     which is a fraction of the memory. A 1px ring at 28% opacity does not need
     the sharpness the hint was buying. */
  contain:layout paint;
}
/* Fade is the opacity still left at mid travel: a keyframe OFFSET cannot be a
   custom property, but a keyframe VALUE can. */
@keyframes ahb-idle-ring{
  0%  {transform:scale(.06);opacity:0}
  8%  {opacity:1}
  55% {opacity:var(--ahb-ring-fade)}
  100%{transform:scale(var(--ahb-ring-scale,3.3));opacity:0}
}
/* Nothing animates while the hero is off screen or the tab is in the
   background. Costs one IntersectionObserver and saves a phone's battery. */
.brxe-animated-hero[data-visible="0"] .ahb__idle-ring,
.brxe-animated-hero[data-visible="0"] .ahb__wave{animation-play-state:paused}

/* -------------------------------------------------------------- reveal ---- */
/* The masked reveal needs overflow:hidden to hide the line before it rises.
   That clip also cuts every box-shadow inside it dead at the wrapper's edge,
   which is what drew a hard rectangle around the buttons and under the proof
   row. Text has no shadow, so it keeps the mask; anything with a shadow gets
   the unmasked variant below. */
.ahb__reveal{display:block;overflow:hidden}
.ahb__reveal > *{display:block;animation:ahb-rise .95s cubic-bezier(.16,1,.3,1) backwards}

.ahb__reveal--open{overflow:visible}
.ahb__reveal--open > *{animation:ahb-rise-soft .95s cubic-bezier(.16,1,.3,1) backwards}
@keyframes ahb-rise-soft{from{transform:translateY(18px);opacity:0}to{transform:none;opacity:1}}
.ahb__reveal--1 > *{animation-delay:.1s}
.ahb__reveal--2 > *{animation-delay:.22s}
.ahb__reveal--3 > *{animation-delay:.38s}
.ahb__reveal--4 > *{animation-delay:.52s}
.ahb__reveal--5 > *{animation-delay:.64s}
@keyframes ahb-rise{from{transform:translateY(105%);opacity:0}to{transform:none;opacity:1}}

/* Anyone whose OS asks for less movement gets the finished frame. */
@media (prefers-reduced-motion:reduce){
  .brxe-animated-hero *,
  .brxe-animated-hero *::before,
  .brxe-animated-hero *::after{animation:none !important;transition:none !important}
  .ahb__piece,.ahb__depth-layer{opacity:1 !important}
  .ahb__outline{stroke-dashoffset:0 !important;opacity:0 !important}
  .ahb__reveal > *{transform:none !important;opacity:1 !important}
  .ahb__impact,.ahb__bloom,.ahb__flash,.ahb__idle-rings{display:none !important}
}

/* Image mode: the lift wrapper is a div, not an SVG group, so it needs its own
   box. transform-box:fill-box is an SVG-only concept and would break it. */
div.ahb__lift{position:absolute;inset:0;transform-box:border-box}
