/* Beonyx — Engineering Benefits
   Geometry is expressed as percentages of the frame, so the whole composition
   scales as one piece. The trapezoid was measured off the Figma export: its
   left edge runs from 32.52% at the top to 0% at the bottom, and its right edge
   stays vertical for the first 33% before leaning in to 82.35%.

   Every number that decides the layout is a custom property rather than a fixed
   value, because Elementor sets those per breakpoint. Nothing here is allowed to
   hard-code a shape the tablet or phone might need to change.

   Everything paints the FINISHED state. The script adds .is-armed to hide the
   bars before it plays them, which means a visitor with no JavaScript — or a
   crawler — still gets the full composition instead of an empty frame. */

/* Pinned rather than inherited. A bar's width is a percentage of the frame, and
   with content-box its 1px outline is added on top of that — so the same widget
   would come out two pixels wider on a theme that does not set border-box
   globally than on one that does. Small on a desktop, visible once a phone has
   squeezed the bars down to a couple of dozen pixels. */
.bnxb,
.bnxb *,
.bnxb *::before,
.bnxb *::after { box-sizing: border-box; }

.bnxb {
  --bnxb-bar: #87b73a;
  --bnxb-bg: transparent;
  --bnxb-fill-op: 0.55;
  --bnxb-outline-op: 0.16;
  /* how far down the bar the fill reaches, as a share of its visible height */
  --bnxb-fill-drop: 0.5;
  --bnxb-grow: 900ms;
  --bnxb-reveal: 700ms;
  --bnxb-ease: cubic-bezier(0.16, 1, 0.3, 1);
  --bnxb-pos-x: 50%;
  --bnxb-pos-y: 50%;

  /* frame height as a fraction of its width — 584 / 818 */
  --bnxb-ratio-n: 0.7139;
  /* how far the bars drop below the frame, as a plain number of percent */
  --bnxb-overhang-n: 4;

  --bnxb-clip-tl: 32.52%;
  --bnxb-clip-kink: 33%;
  --bnxb-clip-br: 82.35%;

  --bnxb-band-start: 38%;
  --bnxb-band-end: 90%;
  --bnxb-gap-n: 0.34;
  --bnxb-inv-count: 0.1428571;

  position: relative;
  width: 100%;
  max-width: 818px;
  /* centre in containers wider than the cap, instead of leaving all the
     leftover space on the right */
  margin-left: auto;
  margin-right: auto;

  /* Reserve the strip the bars hang down into, so whatever sits below cannot
     collide with them. A percentage padding resolves against WIDTH while the
     overhang is a share of HEIGHT, so it has to go through the frame ratio.
     Doing that multiplication in CSS rather than in PHP is what lets the ratio
     and the overhang be set independently at each breakpoint. */
  padding-bottom: 2.855%;
  padding-bottom: calc(var(--bnxb-overhang-n) * var(--bnxb-ratio-n) * 1%);
}

/* ---------- stage ---------- */
/* The stage is the full rectangle; the photo is clipped inside it, so the two
   corners the trapezoid cuts away show whatever is behind the widget.

   That background is transparent by default and should usually stay that way.
   The section this sits in carries a photographic rock-and-contour texture, and
   anything opaque here lands on top of it as a flat rectangle. Only set a colour
   if the widget is going somewhere with nothing behind it worth showing. */

.bnxb-stage {
  position: relative;
  width: 100%;
  background: var(--bnxb-bg);
}

/* ---------- the trapezoid frame ---------- */
/* The frame stays in normal flow, and this is load-bearing. Elementor lays a
   widget out as a flex item sized to its content, and a box whose children are
   all absolutely positioned has a content width of zero — so the whole widget
   collapsed to 0 x 0 on the front end while looking fine in the editor. Keeping
   the frame (and the photo inside it) in flow gives the widget a real intrinsic
   size, exactly the way Elementor's own Image widget gets one. It also means the
   frame, not the stage, is what carries the aspect ratio. */

.bnxb-frame {
  position: relative;
  width: 100%;
  /* literal first, so a browser that will not take the calc keeps the design
     shape rather than collapsing the box to nothing */
  aspect-ratio: 818 / 584;
  aspect-ratio: calc(1 / var(--bnxb-ratio-n));
  overflow: hidden;
  -webkit-clip-path: polygon(var(--bnxb-clip-tl) 0, 100% 0, 100% var(--bnxb-clip-kink), var(--bnxb-clip-br) 100%, 0 100%);
  clip-path: polygon(var(--bnxb-clip-tl) 0, 100% 0, 100% var(--bnxb-clip-kink), var(--bnxb-clip-br) 100%, 0 100%);
}

.bnxb-photo {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: cover;
  object-position: var(--bnxb-pos-x) var(--bnxb-pos-y);
}

/* ---------- bars ---------- */
/* The bar box is the frame box pushed down, so a bar height stays a percentage
   of the frame the designer measured against even though the bars sit lower.
   A percentage translate resolves against the element's own height, which is
   exactly the frame height — no conversion needed here. */

.bnxb-bars {
  position: absolute;
  inset: 0;
  transform: translateY(4%);
  transform: translateY(calc(var(--bnxb-overhang-n) * 1%));
  pointer-events: none;
}

/* A bar is a rectangle with two moving edges: it starts as a sliver on the
   baseline and grows upward while its bottom rises to --b. It is therefore
   sized and positioned directly, rather than clipped out of a taller box.

   An earlier version did clip a full-height box, which was cheaper — a clip
   stays on the compositor. But a clip cuts through the border as well as the
   fill, so every bar whose bottom was lifted lost its bottom edge while the
   ones standing on the baseline kept theirs. Animating `bottom` and `height` is
   layout work instead, and with seven absolutely positioned elements — which
   cannot reflow anything around them — that costs nothing worth measuring. */

.bnxb-bar {
  position: absolute;
  left: var(--l, 0%);
  width: var(--w, 5%);

  /* Where the bar comes to rest: top at the peak, bottom at --b. A bar with
     --b:0 stands on the baseline; a higher value leaves it floating. This is
     also the no-JavaScript state. */
  bottom: var(--b, 0%);
  height: calc(var(--h, 40%) - var(--b, 0%));

  /* All four sides, at even strength. The fill carries the gradient, so the
     outline can stay uniform and the bottom edge reads as clearly as the top. */
  border: 1px solid var(--bnxb-bar);
}

/* Automatic spacing. The band is cut into one slot per bar and each bar is
   centred in its slot. This is done here rather than in PHP so that the band
   and the gap can be widened on a phone, where the desktop band leaves the bars
   too narrow to read. --i and --bnxb-inv-count are the only parts PHP supplies. */

.bnxb-bars--auto .bnxb-bar {
  --bnxb-slot: calc((var(--bnxb-band-end) - var(--bnxb-band-start)) * var(--bnxb-inv-count));
  --bnxb-barw: calc(var(--bnxb-slot) * (1 - var(--bnxb-gap-n)));

  left: calc(
    var(--bnxb-band-start) + (var(--i, 0) * var(--bnxb-slot)) +
    ((var(--bnxb-slot) - var(--bnxb-barw)) / 2)
  );
  width: var(--bnxb-barw);
}

.bnxb-bar-fill {
  position: absolute;
  inset: 0;
  display: block;
  background: var(--bnxb-bar);
  /* The fill hangs from the top of the bar and is gone by --bnxb-fill-drop of
     the way down, leaving the rest as bare outline. The element is the visible
     bar itself, so this is simply a share of its own height: every bar gets the
     same treatment whatever its height and wherever its bottom sits. */
  -webkit-mask-image: linear-gradient(to top, transparent calc(100% * (1 - var(--bnxb-fill-drop))), #000 100%);
  mask-image: linear-gradient(to top, transparent calc(100% * (1 - var(--bnxb-fill-drop))), #000 100%);
}

.bnxb-bar--fill .bnxb-bar-fill { opacity: var(--bnxb-fill-op); }
.bnxb-bar--outline .bnxb-bar-fill { opacity: var(--bnxb-outline-op); }

/* ---------- the reveal ---------- */
/* .is-armed is added by the script; without it everything above is already in
   its final state. */

/* Arming runs with transitions OFF, so the bars snap to nothing instead of
   animating down into it. The script switches them on afterwards by adding
   .is-ready in a separate style flush. Without that split, a visitor who lands
   already scrolled to this section would watch the bars shrink away before they
   grew — the arming change would animate too. */

.bnxb.is-armed .bnxb-bar {
  bottom: 0;
  height: 0;
  /* A bordered box cannot be shorter than its own top and bottom borders, so
     "height: 0" still leaves a 2px dash sitting on the baseline. Seven of them
     would wait there through the start delay before anything moved. */
  opacity: 0;
  transition: none;
}

.bnxb.is-armed.is-ready .bnxb-bar {
  /* Opacity flips at 1ms once the bar's own delay has elapsed — exactly as it
     starts to grow, while it is still 2px tall and the change cannot be seen. */
  transition: bottom var(--bnxb-grow) var(--bnxb-ease) var(--d, 0ms),
              height var(--bnxb-grow) var(--bnxb-ease) var(--d, 0ms),
              opacity 1ms linear var(--d, 0ms);
}

/* Both edges travel at once: the top rises from the baseline to the peak while
   the bottom lifts to --b. Every bar starts as a sliver on the baseline, the
   floating ones shrink away from below as they climb, and because the element
   is the bar rather than a window onto it, all four borders are drawn the whole
   way up. */

.bnxb.is-armed.is-shown .bnxb-bar {
  bottom: var(--b, 0%);
  height: calc(var(--h, 40%) - var(--b, 0%));
  opacity: 1;
}

.bnxb.is-armed.is-reveal .bnxb-frame {
  opacity: 0;
  transition: none;
}

.bnxb.is-armed.is-reveal.is-ready .bnxb-frame {
  transition: opacity var(--bnxb-reveal) ease;
}

.bnxb.is-armed.is-reveal.is-shown .bnxb-frame { opacity: 1; }

/* ---------- accessibility ---------- */
/* The script does not arm at all when the OS asks for reduced motion, so this
   is only a backstop for a stylesheet that outlives it. */

@media (prefers-reduced-motion: reduce) {
  .bnxb.is-armed .bnxb-bar,
  .bnxb.is-armed.is-shown .bnxb-bar {
    bottom: var(--b, 0%);
    height: calc(var(--h, 40%) - var(--b, 0%));
    opacity: 1;
    transition: none;
  }

  .bnxb.is-armed.is-reveal .bnxb-frame {
    opacity: 1;
    transition: none;
  }
}
