/* Benely redesign — only what Elementor's Site Settings cannot express.
   Tokens live in the Global Kit; this file consumes them. See spec section 7. */

/* Colour tokens alias the Elementor Global Kit's --e-global-color-* custom
   properties, with a literal fallback baked into each var(). The kit is
   the source of truth (Site Settings owns these colours; edit them there,
   not here); the literal is a safety net for the rare page where kit CSS
   isn't loaded — verified 2026-08 that all 49 published pages load it,
   but a fallback costs nothing and avoids a silent "unset" if that ever
   changes. The short local names exist purely so the utility classes
   below stay readable (`var(--ink)` vs `var(--e-global-color-ink)`).
   Updated 2026-08-19: the kit now carries `surface` and `deep`, so both
   alias the kit like everything else, and the `ink` fallback was corrected
   from the pre-revision #151F26 to #101D2B. */
:root {
  --ink: var(--e-global-color-ink, #101D2B);
  --ink-raised: var(--e-global-color-raised, #1E2A33);
  --brand: var(--e-global-color-primary, #0092D5);
  --navy: var(--e-global-color-secondary, #154478);
  --surface: var(--e-global-color-surface, #FFFFFF);
  --surface-alt: var(--e-global-color-surfacealt, #F4F4F4);
  --mist: var(--e-global-color-mist, #F1F6FA);
  --deep: var(--e-global-color-deep, #4A4A4A);
  --heading: var(--e-global-color-heading, #1C1D1F);
  --body: var(--e-global-color-text, #757575);
  --muted-dark: var(--e-global-color-muted, #A9ADB5);
  --edge: var(--e-global-color-edge, #7C8894);

  --space-1: 4px;  --space-2: 8px;  --space-3: 12px; --space-4: 16px;
  --space-5: 24px; --space-6: 32px; --space-7: 48px; --space-8: 64px;
  --space-9: 96px; --space-10: 128px;
}

/* Body copy sits at a readable measure rather than full container width.
   Verified against the live markup (49 pages, 2026-08): this site renders
   Elementor's legacy .elementor-section/.elementor-container/.elementor-column
   structure, not the .e-con flex-container markup (Flexbox Container
   experiment is off — .e-con only appears in Elementor's own boilerplate
   lazyload CSS/JS, never as a rendered class). The `.e-con > .elementor-
   widget-text-editor` selector from the original spec was dropped as dead
   code; the selector below is confirmed to match real <p> elements on the
   homepage and 21 other pages. */
.elementor-widget-text-editor .elementor-widget-container > p {
  max-width: 65ch;
  margin-left: auto;
  margin-right: auto;
}

/* Section rhythm — spec section 4.5 */
.section-ink { background: var(--ink); color: #fff; }
.section-ink h1, .section-ink h2, .section-ink h3, .section-ink h4 { color: #fff; }
.section-ink p, .section-ink li { color: var(--muted-dark); }
.section-ink a:not(.elementor-button) { color: var(--brand); }

.section-surface-alt { background: var(--surface-alt); }

/* Mist is the light-dominant ground. Body copy on any non-white ground must
   use Deep Text: the standard body grey measures 4.40 on Mist and fails AA. */
.section-mist { background: var(--mist); }
.section-mist p, .section-mist li,
.section-surface-alt p, .section-surface-alt li { color: var(--deep); }

/* Eyebrow label — spec section 4.6 */
.eyebrow {
  font-size: 12px; font-weight: 700; letter-spacing: 0.18em;
  text-transform: uppercase; color: var(--brand);
}
.section-ink .eyebrow { color: var(--brand); }

/* Monospace metadata, borrowed from the Editorial direction */
.meta-mono {
  font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
  font-size: 12px; letter-spacing: 0.03em; color: var(--muted-dark);
}

/* Buttons — the 3px rule that stood here was removed on 2026-08-27. It was
   dead (it never beat Elementor's per-element CSS) and it was the second,
   conflicting declaration of a value design-system.css already owned.
   The button radius now lives in the Global Kit's Button > Border Radius
   control, which is its editable, native home per Rule 1.
   Do not re-add a radius on `.elementor-button` here: every child stylesheet
   is enqueued *after* `post-6.css`, so such a rule ties the kit on
   specificity (0,0,2,0), wins on load order, and makes the Style panel lie
   about a value the editor can no longer change. */

/* Cards — hairline, no drop shadow */
.card-hairline {
  border: 1px solid #E6ECF1; border-radius: 8px; box-shadow: none;
  background: var(--surface);
}
.section-ink .card-hairline {
  border-color: rgba(255,255,255,0.14); background: var(--ink-raised);
}

/* The 8 white-background case-study graphics must sit on a light card. */
.section-ink .light-card {
  background: #fff; border-radius: 8px; padding: var(--space-3);
}

/* NOTE: the original spec's `@media (max-width: 767px) { .e-con { ... } }`
   mobile section-padding rule was dropped entirely — not rewritten for
   legacy markup — for two reasons: (1) .e-con never appears in this site's
   rendered HTML (see note above), so the rule as written is dead code;
   (2) a naive legacy equivalent (`.elementor-section { padding: ... }`)
   would very likely be dead code too — every customized section on this
   site already carries an explicit per-post, per-element padding override
   (e.g. `.elementor-22 .elementor-element.elementor-element-e606ef8
   { padding: 60px 0px 90px 0px; }`, specificity 0,0,3,0), which beats a
   bare `.elementor-section` rule (0,0,1,0) outright, independent of the
   equal-specificity load-order tie-break that sinks the button rule above.
   Section padding is something Elementor already lets authors set per
   section; a sitewide mobile rhythm reset belongs in the Global Kit or a
   deliberate high-specificity override, not a quietly-dead rule here. */

/* Skip link — WCAG 2.1 AA, SC 2.4.1 Bypass Blocks.
   Off-screen until focused rather than display:none, which would remove it
   from the tab order entirely and defeat the purpose. */
.benely-skip-link {
  position: absolute; left: -9999px; top: 0; z-index: 100000;
  background: var(--ink); color: #FFFFFF;
  padding: 12px 20px; border-radius: 0 0 3px 0;
  font-size: 15px; font-weight: 600; text-decoration: none;
}
.benely-skip-link:focus {
  left: 0;
  outline: 3px solid var(--amber);
  outline-offset: 2px;
}

/* Visible focus for every interactive element — WCAG 2.1 AA, SC 2.4.7.
   Amber is used because it clears 3:1 against both ink (11.32) and white
   (1.50 fails, so a dark ring is paired for light grounds). */
a:focus-visible,
button:focus-visible,
input:focus-visible,
select:focus-visible,
textarea:focus-visible,
.elementor-button:focus-visible,
[tabindex]:focus-visible {
  outline: 3px solid var(--brand);
  outline-offset: 2px;
}
.section-ink a:focus-visible,
.section-ink button:focus-visible,
.section-ink .elementor-button:focus-visible {
  outline-color: var(--amber);
}

/* Hero headline accent — the last line of the H1 turns navy, so the line
   breaks on emphasis rather than on size. It keeps the headline's own weight:
   the regular (400) weight read as a mismatched, thinner font next to the 700
   lines, and was dropped on 2026-09-15. The colour is a kit token; this rule
   only says which span gets it. Navy on Mist measures 9.06:1. */
.elementor-widget-heading .hero-accent {
  color: var(--navy);
}

/* ---------------------------------------------------------------------
   Testimonial carousel — global widget 7043, 18 published pages.
   Spec: docs/superpowers/specs/2026-08-27-testimonial-editorial-quote-design.md

   Only what the widget's own controls cannot express. Anything with a
   control is set on the widget instead: Elementor writes those to
   post-<id>.css at (0,0,4,0), and that file loads long after this one
   (measured on Home 2026-08-27 — redesign.css #19, post-22.css #45), so a
   rule here would lose however it were written.

   Selectors stay compound with .elementor-widget-testimonial-carousel where
   they have to outrank e-swiper.min.css, which also loads after this file.
   --------------------------------------------------------------------- */

.bnly-tq .elementor-testimonial {
  max-width: 820px;
  margin-inline: auto;
  text-align: center;
  padding-inline: clamp(0px, 4vw, 64px);
}

.bnly-tq .elementor-testimonial__text {
  max-width: 46ch;
  margin-inline: auto;
  text-wrap: balance;
}

/* Company: a peer of the quote, not an <h3> inside it. Mirrors the kit
   `accent` token, which has no control on this widget. */
.bnly-tq .bnly-tq__co {
  display: block;
  font-family: "Poppins", sans-serif;
  font-size: 12px;
  font-weight: 700;
  letter-spacing: .18em;
  text-transform: uppercase;
  line-height: 1;
  color: var(--navy);
  margin: 0 0 clamp(18px, 2vw, 26px);
}

/* flex-direction is stated, not assumed: style.css:235 (#17) sets
   `.elementor-testimonial__footer{flex-direction:row-reverse}` at (0,0,1,0),
   which put the headshot *after* the name/role. This is (0,0,2,0) and wins on
   specificity, so no !important is needed. */
.bnly-tq .elementor-testimonial__footer {
  display: flex;
  flex-direction: row;
  align-items: center;
  justify-content: center;
  gap: 14px;
  margin-top: clamp(26px, 3vw, 38px);
}

/* Elementor's stock 2px brand-blue border-right on the cite is removed;
   spacing carries the separation.

   The `cite` element selector and the !important are both load-bearing:
   style.css:236 (#17) is
     cite.elementor-testimonial__cite { margin-right:40px; padding-right:40px;
       border-right:2px solid #0092d5 !important; flex-direction:column !important }
   — (0,0,1,1) and important on the border, so a plain `.bnly-tq .cite`
   (0,0,2,0) lost the divider fight however late this file loads. Matching the
   element makes this (0,0,2,1), which outranks it important-to-important.
   margin-right/padding-right are not important and only ever needed the extra
   class, but they are restated here so the whole legacy rule is answered in
   one place. (At <=767 style.css:386 already zeroes the border; the divider
   only survived at 1024 and 1440.) */
.bnly-tq cite.elementor-testimonial__cite {
  border: 0 !important;
  padding: 0;
  padding-right: 0;
  margin: 0;
  margin-right: 0;
  text-align: left;
  font-style: normal;
  display: flex;
  flex-direction: column;
  gap: 2px;
}

/* style.css:238 pins `span.elementor-testimonial__name{margin-bottom:13px;
   line-height:1 !important}` at (0,0,1,1). The important line-height beat the
   1.3 the design asks for at every width, and the 13px margin fought the 2px
   cite gap above. (0,0,2,1) + !important answers both. The <=767 block below
   restates line-height alongside its own font-size for the same reason. */
.bnly-tq span.elementor-testimonial__name {
  line-height: 1.3 !important;
  margin-bottom: 0;
}

.bnly-tq .elementor-testimonial__image img {
  border-radius: 50%;
  object-fit: cover;
}

/* The arrows are positioned against this box, so it is capped rather than
   left full-width. At 800 they land ~80px outside the quote instead of
   ~165px away at the rail edge. */
.bnly-tq .elementor-swiper {
  position: relative;
  max-width: 800px;
  margin-inline: auto;
}

/* Arrows. The stock ones have a 0x0 box with the glyph painted by an
   absolutely-positioned ::before, giving a ~20x34 target under the 24x24
   floor. These are real buttons.

   Every arrow selector below carries the wrapper's own `.elementor-widget`
   class as well, making them (0,0,4,0). That third class is not decoration:
   e-swiper.min.css (#27, i.e. after this file) declares
     .elementor-element .swiper .elementor-swiper-button,
     .elementor-element .swiper~.elementor-swiper-button
       { position:absolute; top:50%; transform:translateY(-50%); z-index:1;
         display:inline-flex; font-size:25px; color:hsla(0,0%,93%,.9) }
     .elementor-element .swiper~.elementor-swiper-button-prev { left:10px }
     .elementor-element .swiper~.elementor-swiper-button-next { right:10px }
   at *exactly* (0,0,3,0). An equal-specificity tie goes to the later sheet, so
   the three-class form lost every one of those declarations: the arrows kept
   e-swiper's near-white colour, sat 10px in from the rail instead of 0, and —
   worst — never went position:static at <=767, so they stayed absolute and
   painted on top of the quote. Four classes beats three, which settles it on
   specificity alone and does not depend on load order. Verified in the
   rendered DOM: the wrapper class list is
   `elementor-element ... bnly-tq ... elementor-widget elementor-widget-global
   elementor-global-7043 elementor-widget-testimonial-carousel`. */
.elementor-widget.elementor-widget-testimonial-carousel.bnly-tq .elementor-swiper-button {
  position: absolute;
  top: 50%;
  transform: translateY(-50%);
  width: 46px;
  height: 46px;
  border-radius: 50%;
  display: grid;
  place-items: center;
  border: 1.5px solid var(--edge);
  background: var(--surface);
  color: var(--navy);
  cursor: pointer;
  z-index: 2;
  transition: background-color .2s ease, border-color .2s ease, color .2s ease;
}

.elementor-widget.elementor-widget-testimonial-carousel.bnly-tq .elementor-swiper-button-prev { left: 0 }
.elementor-widget.elementor-widget-testimonial-carousel.bnly-tq .elementor-swiper-button-next { right: 0 }

/* eicons never loads on this site (verified: document.fonts.check('20px
   eicons') is false and the <i> computes to display:none), so the glyph is
   drawn as text rather than depending on an icon font that is not there.
   Literal arrows, not \2190 escapes - see the snippet caveat in CLAUDE.md. */
.elementor-widget.elementor-widget-testimonial-carousel.bnly-tq .elementor-swiper-button i { display: none }

/* The ::before is the one fight no amount of class stacking can win.
   owl/custom.css:15-16 (#16) carries
     #testimonial .elementor-swiper-button-prev:before { content:''; left:0;
       width:55px; height:56px; background:url(https://benely.com/.../arrow-left.png);
       display:block; z-index:99; position:absolute; top:-95px }
   (and the -next twin). That is (1,0,2,0) — an ID — and the widget really does
   set _element_id "testimonial", so it matches on all 15 pages. No selector
   built from classes can outrank an ID, but an important declaration beats a
   non-important one whatever the specificity, and the legacy rule is not
   important. So !important is the correct weapon here, and it is the only
   place in this block that uses it for a non-important competitor.

   Every property the legacy rule sets is answered, not just `content`:
   neutralising the glyph alone would have left a 55x56 absolutely-positioned
   PNG box still floating 95px above the circle. width/height/display matter
   even once position is static, because the button is display:grid and the
   pseudo is one of its grid items. */
.elementor-widget.elementor-widget-testimonial-carousel.bnly-tq .elementor-swiper-button::before {
  background-image: none !important;
  position: static !important;
  top: auto !important;
  left: auto !important;
  right: auto !important;
  width: auto !important;
  height: auto !important;
  display: block !important;
  z-index: auto !important;
  font-family: "Poppins", sans-serif;
  line-height: 1;
  font-weight: 400;
  color: inherit;
}
.elementor-widget.elementor-widget-testimonial-carousel.bnly-tq .elementor-swiper-button-prev::before { content: "←" !important }
.elementor-widget.elementor-widget-testimonial-carousel.bnly-tq .elementor-swiper-button-next::before { content: "→" !important }

.elementor-widget.elementor-widget-testimonial-carousel.bnly-tq .elementor-swiper-button:hover {
  background: var(--navy);
  border-color: var(--navy);
  color: #fff;
}
.elementor-widget.elementor-widget-testimonial-carousel.bnly-tq .elementor-swiper-button:focus-visible {
  outline: 3px solid var(--navy);
  outline-offset: 2px;
}

/* Dead space under the quote. Measured on Home before these rules: a 93px
   gap between the attribution and the fraction, then another 40px below it.
   Four stacks, only one of which was ours:

     20px  Elementor's .swiper-slide { padding: 20px 0 }
     39px  slack — the wrapper takes the height of the TALLEST slide, so
           every shorter quote carries the difference. Inherent to a
           fixed-height carousel; not removable without auto-height, and
           the reason this gap can never go to zero.
     34px  our own fraction margin-top
     40px  Elementor's .elementor-pagination-position-outside padding-bottom

   The 39px is the only one that has to stay.

   Both rules need the (0,0,4,0) prefix rather than a plain .bnly-tq
   descendant selector: .elementor-widget-testimonial-carousel .swiper-slide
   {padding:20px} is (0,0,2,0) and loads at #43, so it beats an
   equal-specificity rule here at #19 on order — the same tie that cost the
   arrows earlier. */
.elementor-widget.elementor-widget-testimonial-carousel.bnly-tq .swiper-slide {
  padding-top: 0;
  padding-bottom: 0;
}
.elementor-widget.elementor-widget-testimonial-carousel.bnly-tq .elementor-main-swiper {
  padding-bottom: 0;
}

/* Fraction. Its colour is a control (pagination_color); its size and
   placement are not. */
.bnly-tq .swiper-pagination {
  position: static;
  margin-top: 20px;
  font-family: "Poppins", sans-serif;
  font-size: 13px;
  font-weight: 500;
  letter-spacing: .06em;
  text-align: center;
  width: auto;
}
.bnly-tq .swiper-pagination-current {
  color: var(--heading);
  font-weight: 700;
}

@media (max-width: 767px) {
  .bnly-tq .elementor-testimonial { padding-inline: 0 }
  .bnly-tq .elementor-testimonial__text { max-width: none }

  /* The child theme's own style.css:350 pins these inside its own
     max-width:767px block:

       span.elementor-testimonial__name,
       span.elementor-testimonial__title { font-size:16px !important;
                                           line-height:20px !important }

     That is (0,0,1,1) with !important. Elementor's post CSS is (0,0,4,0)
     but NOT important, so it loses regardless of load order — which means
     the 15px/13px set on the widget simply cannot reach mobile, and no
     widget control can fix it. Caught only because the checker measures at
     390px; both sizes pass at 1440 and 1024.

     redesign.css (#19) loads after style.css (#17), and this selector is
     (0,0,2,1) + !important, so it wins on specificity and order. It has to
     restate line-height too, because that rule pins it to 20px. */
  .bnly-tq span.elementor-testimonial__name {
    font-size: 15px !important;
    line-height: 1.3 !important;
  }
  .bnly-tq span.elementor-testimonial__title {
    font-size: 13px !important;
    line-height: 1.35 !important;
  }

  /* (0,0,4,0) again, and this is the rule the tie cost the most: e-swiper's
     `position:absolute; top:50%; transform:translateY(-50%)` won at (0,0,3,0)
     from a later sheet, so the arrows never joined the flex row below and were
     painted over the quote text at 390px. */
  .elementor-widget.elementor-widget-testimonial-carousel.bnly-tq .elementor-swiper-button {
    position: static;
    transform: none;
    width: 44px;
    height: 44px;
  }

  /* The reflow container is .elementor-main-swiper, NOT .elementor-swiper.
     This is the whole reason the mobile layout shipped broken. The rendered
     DOM is:

       .elementor-swiper                          <- ONE child, nothing else
         .elementor-main-swiper.swiper            <- the flex parent we want
           .swiper-wrapper                        <- the slides
           .elementor-swiper-button-prev
           .elementor-swiper-button-next
           .swiper-pagination.swiper-pagination-fraction
           .swiper-notification                   (absolute, out of flow)

     The arrows and the fraction are siblings of .swiper-wrapper, one level
     below .elementor-swiper. Declaring the flex container and the order
     values on .elementor-swiper therefore laid out a single child and left
     the arrows in .elementor-main-swiper's ordinary block flow: two 44px
     circles stacked vertically at the left edge, fraction beneath them.
     `position:static` was still true and they still missed the quote, which
     is exactly why the checker called it green — tools/testimonial.py now
     asserts the arrangement (one horizontal band, prev left of the fraction,
     next right of it) instead of those two properties alone.

     Flex, not grid: .elementor-main-swiper is the box Swiper measures slide
     widths against, so .swiper-wrapper keeps a full-width basis and
     min-width:0 rather than being sized by grid tracks. */
  .bnly-tq .elementor-main-swiper {
    display: flex;
    flex-wrap: wrap;
    justify-content: center;
    align-items: center;
    gap: 18px;
  }
  .bnly-tq .elementor-main-swiper > .swiper-wrapper {
    flex: 0 0 100%;
    min-width: 0;
    order: 1;
  }
  /* Kept at (0,0,4,0) to match the prev/next `left`/`right` rules above —
     e-swiper's `~.elementor-swiper-button-prev{left:10px}` is (0,0,3,0) and
     would otherwise re-offset a button that is now a static flex item. */
  .elementor-widget.elementor-widget-testimonial-carousel.bnly-tq .elementor-swiper-button-prev { order: 2 }
  .bnly-tq .swiper-pagination { order: 3; margin-top: 0 }
  .elementor-widget.elementor-widget-testimonial-carousel.bnly-tq .elementor-swiper-button-next { order: 4 }

  /* Attribution, stacked and centred on phones. At 390 the quote, the counter
     and the button all sit on the page axis, but the footer is a row -- a 48px
     headshot beside a left-aligned name -- so the text block reads right of
     that axis while everything above and below it is centred. Stacking the
     photo over centred text puts the whole card on one centre line.

     The cite rule restates `align-items` and `text-align`: the base rule at
     the top of this component sets `text-align: left` on the same selector,
     (0,0,2,1), so this must match it and sit later in the file, which it does.
     Desktop keeps the row: the pair is centred as a group there and the left
     edge gives the name something to sit against. */
  .bnly-tq .elementor-testimonial__footer {
    flex-direction: column;
    gap: 10px;
  }

  .bnly-tq cite.elementor-testimonial__cite {
    align-items: center;
    text-align: center;
  }
}

@media (prefers-reduced-motion: reduce) {
  .bnly-tq .elementor-swiper-button { transition-duration: .01ms }
}

/* One face, one headline weight — 2026-10-02, client request.
   Why CSS and not the kit: the parent theme resolves --global--font-primary /
   --global--font-secondary at :root from --font-headings / --font-base, and
   lists, tables, entry content and Gravity Forms read those derived vars.
   They are computed at :root, where the kit's --e-global-* vars (declared on
   .elementor-kit-6, i.e. <body>) do not exist yet, so this has to be a literal.
   Headline font and weight for everything Elementor owns live in Site
   Settings → Typography (body + H1–H6) and the kit tokens — edit them there. */
:root {
  --font-headings: "Poppins", sans-serif;
  --font-base: "Poppins", sans-serif;
}
/* Gravity Forms section headings: owl/custom.css sets `form#gform_1 h3` bold
   and the plugin sets .gform_title and .gsection labels 700. No Elementor control reaches
   markup a shortcode renders. */
body form#gform_1 h3,
body .gform_wrapper.gform_legacy_markup_wrapper .gform_heading h3.gform_title,
body .gform_legacy_markup_wrapper h2.gsection_title {
  font-weight: 500; /* headings follow the kit's H1-H6 weight (500, 2026-10-22) */
}
body .gform_legacy_markup_wrapper .gsection .gfield_label {
  font-weight: 400; /* a label, not a heading */
}
