/*--------------------------------------------------------------
# custom.css
#
# Site-specific overrides. Loaded last by css/global.css.php so these rules win
# over style.css without editing the upstream theme file.
#
# Note: the desktop header only exists at >=1025px. Below that, style.css hides
# .octf-main-header and .header-topbar and the .header_mobile block takes over,
# so every rule here is scoped to the desktop header.
--------------------------------------------------------------*/

/*--------------------------------------------------------------
## Design tokens
#
# Same brand colors already in use throughout style.css (navy #2e3e98, orange
# #f3491c, blue accent #43baff, purple #7141b1) - collected here as custom
# properties so new rules can reference them by name instead of re-deriving hex
# values, and so a future palette tweak only needs one edit.
--------------------------------------------------------------*/
:root {
  --brand-navy: #2e3e98;
  --brand-orange: #f3491c;
  --brand-orange-hover: #d63d14;
  --brand-blue-accent: #43baff;
  --brand-purple: #7141b1;
  --ink: #1b1d21;
  --muted-on-light: #5c6270;
  --space-xs: 8px;
  --space-sm: 16px;
  --space-md: 24px;
  --space-lg: 40px;
  --space-xl: 64px;
  --radius-sm: 4px;
  --radius-md: 6px;
  --shadow-card: 0 6px 18px rgba(14, 22, 66, 0.12);

  /* Typography */
  --font-heading: "Sora", "Montserrat", sans-serif;
  --font-body: "Inter", "Nunito Sans", sans-serif;

  /* Links: one color pair used everywhere on light backgrounds, so a link
     reads as a link no matter which section it's in. Dark-surface sections
     (header CTA bar, footer, newsletter) keep their own white-on-navy rules
     below since --link's contrast fails against --brand-navy. */
  --link: var(--brand-navy);
  --link-hover: var(--brand-orange);
}

/*--------------------------------------------------------------
## Typography
#
# Swaps the theme's Montserrat/Nunito Sans for Sora/Inter (linked in
# header.base.php's <head>) - a pairing more typical of contemporary managed
# IT / SaaS sites, with better small-text legibility than Nunito Sans.
--------------------------------------------------------------*/
body, button, input, select, optgroup, textarea {
  font-family: var(--font-body);
}

h1, h2, h3, h4, h5, h6,
.main-heading,
.ot-heading span {
  font-family: var(--font-heading);
}

/*--------------------------------------------------------------
## Links: one consistent color/underline/hover treatment
#
# style.css leaves plain <a> tags (inside paragraphs, lists, cards) at the
# body text color with no distinguishing treatment, so it's not obvious
# they're clickable until hovered. This gives every in-content link the same
# identity: brand navy, no underline, orange on hover/focus. Nav links,
# buttons (.octf-btn) and anything already given an explicit color (header
# CTA, footer, newsletter, .terms-consent) are unaffected - this only
# targets plain inline/content links.
--------------------------------------------------------------*/
.site-content a:not(.octf-btn):not([class*="btn"]) {
  color: var(--link);
  text-decoration: none;
  transition: color 0.2s linear;
}

.site-content a:not(.octf-btn):not([class*="btn"]):hover,
.site-content a:not(.octf-btn):not([class*="btn"]):focus {
  color: var(--link-hover);
}

/*--------------------------------------------------------------
## Header: keep the main bar on a single line
#
# Reclaim horizontal space before anything is forced to wrap.
--------------------------------------------------------------*/
@media only screen and (min-width: 1025px) and (max-width: 1500px) {
  /* Main bar: tighten the nav and CTA so logo + menu + CTA stay on one row. */
  .main-navigation > ul > li {
    margin: 0 12px;
  }

  .octf-btn-cta .btn-cta-group {
    margin-left: 20px;
  }

  .octf-btn-cta .contact-header {
    padding-left: 20px;
  }
}

/* The main bar is a nowrap flex row already; make sure the nav can shrink into
   it rather than pushing the CTA onto a second row. */
.octf-main-header .octf-mainbar-row {
  flex-wrap: nowrap;
}

.octf-mainbar-row .menu-col {
  min-width: 0;
}

/* The CTA phone number and its label are two lines by design, but neither line
   may wrap within itself. */
.octf-btn-cta .contact-header .cinfo-header span {
  white-space: nowrap;
}

.octf-btn-cta .contact-header .cinfo-header {
  display: flex;
  flex-direction: column;
}

.octf-btn-cta .contact-header .cinfo-header .main-text,
.octf-btn-cta .contact-header .cinfo-header .main-text a {
  display: block;
  color: var(--brand-navy);
  font-size: 16px;
  line-height: 1.35;
  visibility: visible;
  opacity: 1;
}

.octf-btn-cta .btn-cta-header .octf-btn {
  white-space: nowrap;
}

/*--------------------------------------------------------------
## Contact Us button: always present on desktop
#
# style.css:1835 hid .btn-cta-header below 1440px. The desktop header itself only
# exists at >=1025px, so on every viewport between 1025px and 1440px - which is
# most laptops, and the majority of desktop sessions - the header had no Contact
# Us button at all. The single most important conversion control on the page was
# invisible to the people most likely to click it.
#
# It is restored at every width the desktop header exists at. The phone block
# (.contact-header) keeps its own 1265px cutoff from style.css:1840: when space is
# tight the two-line phone number is the thing that gives way, not the button.
--------------------------------------------------------------*/
@media (max-width: 1440px) {
  .octf-btn-cta .btn-cta-header {
    display: block;
  }
}

/* Below 1265px the phone block is gone, so the button no longer needs the gutter
   that separated it from the phone number. */
@media (max-width: 1265px) {
  .octf-btn-cta .btn-cta-header {
    margin-left: 0;
  }
}

/* base/nav.base.php renders a Contact Us <li> for the mobile hamburger, which has no
   CTA column of its own. The desktop nav already has the button beside it, so hide
   the duplicate there. */
.main-navigation .nav-contact-item {
  display: none;
}

/* Keep the header's contact links readable against the navy header surfaces. */
.octf-btn-cta .contact-header span a,
.octf-btn-cta .contact-header span a:visited,
.octf-btn-cta .contact-header span a:hover,
.octf-btn-cta .contact-header span a:focus {
  color: #fff;
}

/*--------------------------------------------------------------
## Contact page
#
# The consent text and its Privacy Policy link sit on a #f4f6f9 panel. The link
# previously carried an inline `color: white !important`, which rendered it
# invisible against that background.
--------------------------------------------------------------*/
/* Fine print under the submit button. It carries the consent wording that used to sit
   beside a checkbox, so it must stay legible - small, but not a disclaimer nobody can
   read. Colour is deliberately not set here: inside the form it is white on the
   gradient panel (see "Contact form field contrast"), and this base rule is only the
   size and spacing shared by any other placement. */
.terms-consent {
  margin-top: 4px;
  font-size: 12.5px;
  line-height: 1.6;
}

.terms-consent a {
  color: #2e3e98;
  text-decoration: none;
}

.terms-consent a:hover,
.terms-consent a:focus {
  color: #f3491c;
}

/* Honeypot. Must stay reachable in the DOM for bots but never be perceivable by a
   person, so it is moved off-screen rather than display:none - some bots skip fields
   that are explicitly hidden. Paired with aria-hidden and tabindex="-1" in the markup
   so keyboard and screen-reader users never land on it. */
.hp-field {
  position: absolute;
  left: -9999px;
  top: auto;
  width: 1px;
  height: 1px;
  overflow: hidden;
}

/* Submission feedback. The form previously gave no confirmation at all. */
.contact-notice {
  padding: 14px 18px;
  margin-bottom: 22px;
  border-radius: 4px;
  border-left: 4px solid transparent;
  font-size: 15px;
  line-height: 1.5;
}

.contact-notice-success {
  background: #e8f6ee;
  border-left-color: #2e8b57;
  color: #1c5c39;
}

.contact-notice-error {
  background: #fdecea;
  border-left-color: #f3491c;
  color: #8c2410;
}

/*--------------------------------------------------------------
## Contact form field contrast
#
# The panel itself is a cyan-to-purple gradient (style.css:5709), so the white copy
# sitting on it is correct and stays white. The bug was one level in: the theme also
# paints the INPUTS white-on-translucent (style.css:5724-5733), while
# .conversion-panel-form gives those same inputs a near-white #f8fafc background.
# White text and a white placeholder on a #f8fafc field is invisible - visitors could
# not read the field hints or proof-read what they had typed.
#
# So: light fields with dark text and a dark placeholder, inside a panel whose
# surrounding copy stays white. Overriding here rather than editing style.css keeps
# the vendor theme file untouched - custom.css is concatenated last by
# css/global.css.php, so it wins without !important.
--------------------------------------------------------------*/
.contact-form .main-form input:not([type="checkbox"]):not([type="submit"]),
.contact-form .main-form textarea {
  color: var(--ink);
  background: #f8fafc;
}

/* ::placeholder and the legacy vendor-prefixed selectors are listed separately.
   A browser that does not understand one selector in a group discards the whole
   rule, so combining them would silently drop the fix in older engines. */
.contact-form .main-form input::placeholder,
.contact-form .main-form textarea::placeholder {
  color: #55607a;
  opacity: 1; /* Firefox dims placeholders to 0.54 by default. */
}

.contact-form .main-form input::-webkit-input-placeholder,
.contact-form .main-form textarea::-webkit-input-placeholder {
  color: #55607a;
}

.contact-form .main-form input::-moz-placeholder,
.contact-form .main-form textarea::-moz-placeholder {
  color: #55607a;
  opacity: 1;
}

.contact-form .main-form input:-ms-input-placeholder,
.contact-form .main-form textarea:-ms-input-placeholder {
  color: #55607a;
}

/* Consent fine print under the submit button. It sits directly on the gradient
   panel, not inside a field, so it is white - and at full opacity rather than a
   faded grey, because it is the wording that makes submitting the form an act of
   consent and it has to be readable to do that job. */
.contact-form .main-form .terms-consent {
  color: #fff;
}

.contact-form .main-form .terms-consent a {
  color: #fff;
  text-decoration: underline;
}

.contact-form .main-form .terms-consent a:hover,
.contact-form .main-form .terms-consent a:focus {
  color: #ffd9cf;
}

/*--------------------------------------------------------------
## Rich text message editor
#
# Enhancement over the plain <textarea> in base/contact-form.base.php. The
# .richtext-ready class is added by JavaScript once the editor is in place, so
# every rule below is inert until the enhancement has actually succeeded - if the
# script never runs the textarea stays visible and styled as normal.
--------------------------------------------------------------*/
.richtext-toolbar {
  display: none;
}

.richtext-ready .richtext-toolbar {
  display: flex;
  flex-wrap: wrap;
  gap: 4px;
  padding: 6px;
  border: 1px solid #cbd5df;
  border-bottom: 0;
  border-radius: var(--radius-sm) var(--radius-sm) 0 0;
  background: #eef2f7;
}

.richtext-btn {
  width: 32px;
  height: 32px;
  padding: 0;
  border: 1px solid transparent;
  border-radius: var(--radius-sm);
  background: transparent;
  color: var(--ink);
  font-size: 14px;
  line-height: 1;
  cursor: pointer;
}

.richtext-btn:hover {
  background: #dde5ef;
}

/* Keyboard users must be able to see which toolbar button they are on. */
.richtext-btn:focus-visible {
  outline: 2px solid var(--brand-navy);
  outline-offset: 1px;
}

.richtext-btn.is-active {
  border-color: var(--brand-navy);
  background: #fff;
  color: var(--brand-navy);
}

/* The textarea is still the submitted control and is still in the form; it is
   moved out of sight rather than display:none'd so assistive technology and
   autofill keep working against a real field. */
.richtext-ready textarea {
  position: absolute;
  width: 1px;
  height: 1px;
  padding: 0;
  border: 0;
  overflow: hidden;
  clip: rect(0 0 0 0);
  clip-path: inset(50%);
  white-space: nowrap;
}

.richtext-input {
  min-height: 150px;
  max-height: 420px;
  overflow-y: auto;
  padding: 12px 14px;
  border: 1px solid #cbd5df;
  border-radius: 0 0 var(--radius-sm) var(--radius-sm);
  background: #f8fafc;
  color: var(--ink);
  font-family: var(--font-body);
  font-size: 15px;
  line-height: 1.6;
}

.richtext-input:focus {
  background: #fff;
  border-color: var(--brand-navy);
  outline: 2px solid rgba(46, 62, 152, 0.35);
  outline-offset: 1px;
}

/* Failed the required check on submit. */
.richtext-input.is-invalid {
  border-color: var(--brand-orange);
}

/* Placeholder. contenteditable has no placeholder attribute, so the text comes
   from data-placeholder and is shown only while the editor is genuinely empty.
   :empty alone is not enough - a browser leaves a stray <br> behind after the
   visitor deletes everything, which stops matching :empty while still looking
   blank - so the JS clears innerHTML in that case. */
.richtext-input:empty::before {
  content: attr(data-placeholder);
  color: #55607a;
  pointer-events: none;
}

/* The editor renders the visitor's own markup, so it needs the basic block
   spacing the surrounding form CSS does not provide. */
.richtext-input p {
  margin: 0 0 10px;
  color: inherit;
}

.richtext-input p:last-child {
  margin-bottom: 0;
}

.richtext-input ul,
.richtext-input ol {
  margin: 0 0 10px;
  padding-left: 22px;
  list-style: revert;
}

.richtext-input a {
  color: var(--brand-navy);
  text-decoration: underline;
}

.contact-form .main-form input:focus,
.contact-form .main-form textarea:focus {
  background: #fff;
  border-color: var(--brand-navy);
  outline: 2px solid rgba(46, 62, 152, 0.35);
  outline-offset: 1px;
}

/*--------------------------------------------------------------
## Footer
#
# The footer sits on #2e3e98, so white here is legitimate and readable. It was
# previously an inline `color: white !important`, which is the same shape as the
# hidden-text trick used for cloaking - moved into CSS so intent is unambiguous.
--------------------------------------------------------------*/
.footer-v2 a,
.footer-v2 a:visited,
.footer-v2 a:hover,
.footer-v2 a:focus,
.footer-v2 a:active {
  color: #fff;
}

/*--------------------------------------------------------------
## Newsletter signup
#
# Deliberately an inline block in the footer only. No modal, no overlay, no
# exit-intent, no scroll trigger, no timed popup - it never interrupts a visitor
# and is simply there if they want it.
--------------------------------------------------------------*/
.newsletter-signup {
  max-width: 620px;
  margin: 0 auto;
  text-align: center;
}

/* Every colour below is measured against the footer's #2e3e98, not inherited from
   the light-background body rules in style.css. */
.newsletter-signup .newsletter-title {
  color: #fff;
  font-size: 18px;
  line-height: 1.4;
  margin: 0 0 8px;
}

.newsletter-signup .newsletter-intro {
  color: rgba(255, 255, 255, 0.78);
  font-size: 15px;
  line-height: 1.6;
  margin: 0 auto 20px;
  max-width: 520px;
}

.newsletter-form {
  margin: 0;
}

/* The field and its button sit inside one white control so they read as a single
   inline element instead of two detached boxes. */
.newsletter-control {
  display: flex;
  align-items: center;
  max-width: 480px;
  margin: 0 auto;
  padding: 5px;
  background: #fff;
  border: 1px solid rgba(255, 255, 255, 0.35);
  border-radius: 6px;
  box-shadow: 0 6px 18px rgba(14, 22, 66, 0.18);
  transition: border-color 0.2s linear, box-shadow 0.2s linear;
}

.newsletter-control:focus-within {
  border-color: #43baff;
  box-shadow: 0 6px 18px rgba(14, 22, 66, 0.22), 0 0 0 3px rgba(67, 186, 255, 0.35);
}

.newsletter-form input[type="email"] {
  flex: 1 1 auto;
  min-width: 0;
  height: 42px;
  padding: 0 14px;
  border: 0;
  background: transparent;
  color: #1c2440;
  font-size: 15px;
  line-height: 42px;
}

.newsletter-form input[type="email"]::placeholder {
  color: #8a90a6;
}

.newsletter-form input[type="email"]:focus {
  outline: none;
}

.newsletter-form .newsletter-submit {
  flex: 0 0 auto;
  height: 42px;
  padding: 0 24px;
  border: 0;
  border-radius: 4px;
  background: #f3491c;
  color: #fff;
  font-weight: 700;
  font-size: 14px;
  letter-spacing: 0.02em;
  cursor: pointer;
  transition: background 0.3s linear;
}

.newsletter-form .newsletter-submit:hover,
.newsletter-form .newsletter-submit:focus {
  background: #d63d14;
  color: #fff;
}

.newsletter-form .newsletter-submit:focus-visible {
  outline: 2px solid #fff;
  outline-offset: 2px;
}

.newsletter-signup .newsletter-privacy {
  font-size: 13px;
  margin: 14px 0 0;
  color: rgba(255, 255, 255, 0.65);
}

.newsletter-signup .newsletter-privacy a {
  color: #fff;
  text-decoration: none;
}

.newsletter-signup .newsletter-privacy a:hover,
.newsletter-signup .newsletter-privacy a:focus {
  color: #43baff;
}

/* Below the joined layout's comfortable width, stack the two controls and drop the
   shared white shell so each keeps its own readable edge. */
@media only screen and (max-width: 575px) {
  .newsletter-control {
    display: block;
    padding: 0;
    background: transparent;
    border: 0;
    box-shadow: none;
  }

  .newsletter-control:focus-within {
    box-shadow: none;
  }

  .newsletter-form input[type="email"] {
    display: block;
    width: 100%;
    height: 48px;
    line-height: 48px;
    padding: 0 14px;
    background: #fff;
    border-radius: 6px;
  }

  .newsletter-form input[type="email"]:focus {
    box-shadow: 0 0 0 3px rgba(67, 186, 255, 0.4);
  }

  .newsletter-form .newsletter-submit {
    display: block;
    width: 100%;
    height: 48px;
    margin-top: 10px;
  }
}

/*--------------------------------------------------------------
## Skip link
--------------------------------------------------------------*/
.skip-link {
  position: absolute;
  left: -9999px;
  top: 0;
  z-index: 10000;
  padding: 12px 20px;
  background: var(--brand-navy);
  color: #fff;
  text-decoration: none;
  border-radius: 0 0 var(--radius-sm) 0;
}

.skip-link:focus {
  left: 8px;
  top: 8px;
}

/* Bootstrap 5's .visually-hidden hides content until it (or a focused
   descendant) receives focus - the skip link needs the -focusable variant so
   it becomes visible on focus instead of staying visually-hidden always. */
.visually-hidden-focusable:not(:focus):not(:focus-within) {
  position: absolute !important;
  left: -9999px !important;
  top: 0 !important;
}

/*--------------------------------------------------------------
## Focus indicators
#
# style.css sets outline:none/0 on many interactive elements with no
# :focus-visible replacement anywhere, including the mobile-menu toggle. This
# restores a visible focus ring for keyboard users without adding an outline
# to every mouse click.
--------------------------------------------------------------*/
a:focus-visible,
button:focus-visible,
input:focus-visible,
textarea:focus-visible,
select:focus-visible,
[tabindex]:focus-visible,
#mmenu_toggle button:focus-visible {
  outline: 3px solid var(--brand-blue-accent);
  outline-offset: 2px;
}

/* Safari didn't support :focus-visible until 15.4; this keeps older
   engines from ending up with literally no focus indicator at all. */
@supports not selector(:focus-visible) {
  a:focus,
  button:focus,
  input:focus,
  textarea:focus,
  select:focus,
  [tabindex]:focus {
    outline: 3px solid var(--brand-blue-accent);
    outline-offset: 2px;
  }
}

/*--------------------------------------------------------------
## Reduced motion
#
# Owl Carousel's loop/autoplay is already gated in js/scripts.js via
# matchMedia. This is the CSS-side safety net for everything else (hover
# transforms, theme transitions). Near-zero durations rather than `none` so
# any JS listening for transitionend/animationend still fires.
--------------------------------------------------------------*/
@media (prefers-reduced-motion: reduce) {
  *,
  *::before,
  *::after {
    animation-duration: 0.001ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.001ms !important;
    scroll-behavior: auto !important;
  }
}

/*--------------------------------------------------------------
## Color contrast
#
# style.css sets typed-text color to #b5b5b5 on a #f6f6f6 background for every
# text input/textarea sitewide (contact form, newsletter signup) - roughly
# 1.7:1, far short of the 4.5:1 WCAG AA requires. --muted-on-light isn't used
# here since this is text the visitor is actively reading back as they type;
# it gets the same near-black tone as body copy instead.
--------------------------------------------------------------*/
input[type="text"],
input[type="email"],
input[type="url"],
input[type="password"],
input[type="search"],
input[type="number"],
input[type="tel"],
textarea {
  color: var(--ink);
}

/*--------------------------------------------------------------
## Modernized buttons & cards
#
# Same brand colors and layout as before - just softer corners, a subtle
# shadow, and a hover lift on the service icon-boxes so the page reads less
# flat. Respects the reduced-motion override above (it zeroes these
# transition-durations for visitors who've asked for it).
--------------------------------------------------------------*/
.octf-btn {
  border-radius: var(--radius-md);
}

.icon-box-s2 {
  background: #fff;
  border-radius: var(--radius-md);
  box-shadow: var(--shadow-card);
  padding: 30px 24px;
  margin-bottom: var(--space-md);
  height: calc(100% - var(--space-md));
  box-sizing: border-box;
  transition: transform 0.2s ease, box-shadow 0.2s ease;
}

.icon-box-s2:hover,
.icon-box-s2:focus-within {
  transform: translateY(-4px);
  box-shadow: 0 10px 28px rgba(14, 22, 66, 0.18);
}

.conversion-panel {
  position: relative;
  overflow: hidden;
  padding: 88px 0;
  background: linear-gradient(135deg, #eef3f8 0%, #f8fafc 58%, #e5edf5 100%);
  border-top: 1px solid #dbe4ed;
  border-bottom: 1px solid #dbe4ed;
}

.conversion-panel:after {
  position: absolute;
  right: -90px;
  bottom: -150px;
  width: 360px;
  height: 360px;
  border: 70px solid rgba(67, 186, 255, 0.12);
  border-radius: 50%;
  content: "";
}

.conversion-panel-grid {
  position: relative;
  z-index: 1;
  display: grid;
  grid-template-columns: minmax(0, 0.9fr) minmax(360px, 0.75fr);
  gap: 72px;
  align-items: center;
}

.conversion-panel-copy {
  max-width: 570px;
}

.conversion-panel-copy .page-hero-eyebrow {
  display: block;
  margin-bottom: 18px;
  color: var(--brand-orange);
  font-size: 13px;
  font-weight: 800;
  letter-spacing: 0.08em;
  text-transform: uppercase;
}

.conversion-panel-copy h2 {
  margin-bottom: 18px;
  color: var(--brand-navy);
  font-size: clamp(28px, 3vw, 42px);
  line-height: 1.14;
}

.conversion-panel-copy p {
  color: var(--muted-on-light);
  font-size: 17px;
  line-height: 1.75;
}

/* "Prefer the phone?" line under the checklist in base/conversion-panel.base.php.
   A phone number is the highest-intent contact route we have, so it is offered
   alongside the form rather than buried in the footer. */
.conversion-panel-alt {
  margin-top: 24px;
  font-size: 15px;
}

.conversion-panel-alt a {
  color: var(--brand-navy);
  font-weight: 700;
  white-space: nowrap;
}

.conversion-checklist {
  margin: 28px 0 0;
  padding: 0;
  list-style: none;
}

.conversion-checklist li {
  display: flex;
  gap: 12px;
  align-items: flex-start;
  margin-bottom: 14px;
  color: var(--ink);
  font-weight: 600;
}

.conversion-checklist i {
  flex: 0 0 auto;
  margin-top: 4px;
  color: var(--brand-orange);
}

.conversion-panel-form {
  padding: 34px;
  background: #fff;
  border: 1px solid #dce5ee;
  border-radius: var(--radius-md);
  box-shadow: 0 18px 46px rgba(14, 22, 66, 0.14);
}

.conversion-panel-form .main-form p {
  margin-bottom: 14px;
}

.conversion-panel-form .main-form input:not([type="checkbox"]),
.conversion-panel-form .main-form textarea {
  width: 100%;
  box-sizing: border-box;
  border: 1px solid #cbd5df;
  border-radius: var(--radius-sm);
  background: #f8fafc;
}

.conversion-panel-form .main-form textarea {
  min-height: 130px;
  resize: vertical;
}

/* Size only. The colour is set once, in the "Contact form field contrast" section
   above - this used to say `color: var(--muted-on-light)`, which put grey text on the
   gradient panel and was the least readable combination on the page. */
.conversion-panel-form .terms-consent {
  font-size: 12px;
  line-height: 1.55;
}

.conversion-panel-form .g-recaptcha {
  margin: 18px 0;
  overflow: hidden;
}

.conversion-panel-form .contact-submit {
  width: 100%;
  justify-content: center;
  border: 0;
}

.service-feature-image {
  width: 100%;
  aspect-ratio: 16 / 6;
  margin: 0 0 42px;
  overflow: hidden;
  border-radius: var(--radius-md);
  background: #e8eef4;
}

.service-feature-image img {
  width: 100%;
  height: 100%;
  display: block;
  object-fit: cover;
  object-position: center;
}

.faq-intro {
  margin-bottom: 24px;
  color: var(--muted-on-light);
}

.faq-item {
  margin: 12px 0;
  border: 1px solid #d9e1e9;
  border-radius: var(--radius-sm);
  background: #fff;
  box-shadow: 0 3px 12px rgba(14, 22, 66, 0.06);
}

.faq-item summary {
  position: relative;
  padding: 20px 54px 20px 22px;
  color: var(--brand-navy);
  cursor: pointer;
  font-family: var(--font-heading);
  font-size: 16px;
  font-weight: 700;
  list-style: none;
}

.faq-item summary::-webkit-details-marker {
  display: none;
}

.faq-item summary:after {
  position: absolute;
  top: 21px;
  right: 23px;
  width: 9px;
  height: 9px;
  border-right: 2px solid var(--brand-orange);
  border-bottom: 2px solid var(--brand-orange);
  content: "";
  transform: rotate(45deg);
  transition: transform 0.2s ease;
}

.faq-item[open] summary:after {
  top: 25px;
  transform: rotate(225deg);
}

.faq-item summary:focus-visible {
  outline: 3px solid rgba(67, 186, 255, 0.45);
  outline-offset: -3px;
}

.faq-answer {
  padding: 0 22px 20px;
  color: var(--muted-on-light);
}

.faq-answer p {
  margin: 0;
  line-height: 1.7;
}

/*--------------------------------------------------------------
## Overflow and typography safety net
#
# The purchased theme sizes buttons and fields for its own demo copy - fixed
# heights, nowrap labels, and no minimum width on grid columns. Real copy is
# longer than demo copy ("Book My Free Assessment", "Get My Monthly Price"), and
# real visitors have narrow phones, so text was escaping its container.
#
# These are defensive rules, not design. Each one fixes a specific way content
# can break out of its box; none of them changes how anything looks when the
# content already fits.
--------------------------------------------------------------*/

/* Buttons: let a long label wrap onto a second line and grow the button, instead
   of running past its own background. The theme sets a fixed line-height and
   relies on labels being short. */
.octf-btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  max-width: 100%;
  height: auto;
  min-height: 48px;
  white-space: normal;
  text-align: center;
  overflow-wrap: break-word;
}

/* Two CTAs side by side must be able to stack rather than force the row wider
   than the viewport. */
.hero-cta-group,
.page-hero-cta,
.btn-cta-group {
  min-width: 0;
}

.hero-cta-group,
.page-hero-cta {
  flex-wrap: wrap;
}

/* A grid or flex child will not shrink below its content's intrinsic width
   unless told to - this is the single most common cause of a page scrolling
   sideways on a phone. */
.conversion-panel-grid > *,
.octf-mainbar-row > [class^="octf-col"],
.row > [class*="col-"] {
  min-width: 0;
}

/* Form controls: honour the container width rather than the browser's default
   `size` attribute width, which is what pushes a form past its panel. */
input,
select,
textarea,
button {
  max-width: 100%;
  font-family: inherit;
}

.contact-form .main-form input,
.contact-form .main-form textarea,
.newsletter-control input[type="email"] {
  box-sizing: border-box;
}

/* Long unbroken strings - a pasted URL, an email address, a part number - have no
   break opportunity and will otherwise widen whatever contains them. */
p,
li,
dd,
h1, h2, h3, h4, h5, h6,
.faq-item summary,
.richtext-input,
.contact-notice {
  overflow-wrap: break-word;
}

/* Headings only. Hyphenating body copy hurts readability; hyphenating a long
   compound heading on a narrow screen is the lesser evil versus overflow. */
h1, h2, h3 {
  hyphens: auto;
}

/* Menu items must not wrap mid-label into a ragged two-line nav. Below 1025px the
   desktop nav is replaced by the hamburger anyway, so this only applies where
   there is room for it. */
.main-navigation ul.menu > li > a {
  white-space: nowrap;
}

/* The mobile menu is a vertical stack with the full container width available, so
   there long labels should wrap rather than be clipped. */
.header_mobile .mobile_mainmenu a {
  white-space: normal;
  overflow-wrap: break-word;
}

/* Last-resort guard. If any single element still overflows, the page as a whole
   must not scroll sideways - horizontal scroll on a phone makes a site feel
   broken even when everything else is right. Wide content that legitimately needs
   to scroll (tables, code) should be given its own overflow-x container. */
html,
body {
  overflow-x: hidden;
}

/*--------------------------------------------------------------
## Vendor logo carousel
#
# The theme shipped these logos desaturated - `contrast(0%)` plus `opacity: .7`
# (style.css:5959) - and only returned them to full colour on hover. That is a
# common editorial pattern, but it is the wrong one here: these are the brands we
# are accredited with and resell, so they are evidence, not decoration. Greying
# them out hides the recognisable thing (the Microsoft, Cisco and VMware colours)
# behind an interaction most visitors never perform, and never on touch, where
# there is no hover at all.
#
# So: full colour at rest, and hover does something subtler instead - a slight
# lift via drop-shadow.
#
# drop-shadow() rather than box-shadow: these are PNG/WebP logos on transparency.
# box-shadow would trace the rectangular image box and draw a visible card edge
# around a shape that has none; drop-shadow follows the alpha channel, so the
# shadow falls behind the logo itself.
--------------------------------------------------------------*/
.partners-slide img,
.partners-slide-image {
  filter: none;
  -webkit-filter: none;
  opacity: 1;
  /* Transition only the property that changes. The theme's `all 0.3s linear`
     also animates the filter swap, which is expensive to composite. */
  transition: filter 0.2s ease;
}

.partners-slide img:hover,
.partners-slide-image:hover {
  filter: drop-shadow(0 3px 5px rgba(14, 22, 66, 0.28));
  -webkit-filter: drop-shadow(0 3px 5px rgba(14, 22, 66, 0.28));
  opacity: 1;
}

/* Respect a reduced-motion preference: the shadow still appears, it just does
   not animate in. */
@media (prefers-reduced-motion: reduce) {
  .partners-slide img,
  .partners-slide-image {
    transition: none;
  }
}

/*--------------------------------------------------------------
## Breadcrumbs
#
# Only /services and the location pages previously emitted BreadcrumbList JSON-LD,
# and
# neither showed breadcrumbs on the page. Google expects the markup to describe
# something the visitor can actually see, and on a 31-page location cluster the
# visible trail is genuinely useful navigation, not decoration.
--------------------------------------------------------------*/
.breadcrumbs {
  margin-bottom: 18px;
  font-size: 13px;
}

.breadcrumbs ol {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
  margin: 0;
  padding: 0;
  list-style: none;
}

/* Separator via ::before on the non-first items, so it is generated content that
   screen readers skip rather than a literal character in the accessible name. */
.breadcrumbs li + li::before {
  margin-right: 8px;
  content: "/";
}

/* White throughout. The breadcrumb sits inside .page-hero, which is a dark banner -
   the navy link colour used elsewhere on light surfaces is close to unreadable
   against it. The trail is set as one white unit, with the current page dimmed
   slightly rather than recoloured, so it still reads as "you are here" without
   dropping below the contrast the hero copy already meets. */
.breadcrumbs,
.breadcrumbs li + li::before {
  color: #fff;
}

/* The selector has to carry the same weight as the sitewide link rule near the top
   of this file - `.site-content a:not(.octf-btn):not([class*="btn"])` - because that
   one is (0,3,1) and would otherwise repaint these navy against the dark hero. The
   :not() pairs are matched here purely to win on specificity, not because a button
   is expected inside a breadcrumb. Plain `.breadcrumbs a` is (0,1,1) and loses. */
.site-content .breadcrumbs a:not(.octf-btn):not([class*="btn"]),
.site-content .breadcrumbs a:not(.octf-btn):not([class*="btn"]):visited {
  color: #fff;
  font-weight: 600;
  text-decoration: none;
}

.site-content .breadcrumbs a:not(.octf-btn):not([class*="btn"]):hover,
.site-content .breadcrumbs a:not(.octf-btn):not([class*="btn"]):focus {
  color: #fff;
  text-decoration: underline;
}

.breadcrumbs [aria-current="page"] {
  color: rgba(255, 255, 255, 0.85);
}

/*--------------------------------------------------------------
## Page hero CTA row
--------------------------------------------------------------*/
.page-hero-cta {
  display: flex;
  flex-wrap: wrap;
  gap: 14px;
  margin-top: 28px;
}

/* The sector list on a location page is a plain fact list, not a sales checklist,
   so it drops the heavier weight the conversion-panel version uses. */
.city-sectors li {
  font-weight: 500;
}

/*--------------------------------------------------------------
## Video embed (click-to-load facade)
#
# See base/video-embed.base.php. The iframe does not exist until the visitor
# presses play, so the box below is a button with a thumbnail in it. The 16:9
# aspect-ratio is what stops the page jumping when the iframe replaces it - the
# space is reserved from first paint.
--------------------------------------------------------------*/
.video-embed {
  margin: 0 0 34px;
}

.video-embed-trigger {
  position: relative;
  display: block;
  width: 100%;
  aspect-ratio: 16 / 9;
  padding: 0;
  overflow: hidden;
  border: 0;
  border-radius: var(--radius-md);
  background: #0d1330;
  cursor: pointer;
}

.video-embed-poster {
  width: 100%;
  height: 100%;
  object-fit: cover;
  /* YouTube's hqdefault thumbnail is 4:3 with black bars top and bottom. Scaling
     it slightly past the frame crops those off rather than showing them. */
  transform: scale(1.35);
  opacity: 0.85;
  transition: opacity 0.2s ease, transform 0.3s ease;
}

.video-embed-trigger:hover .video-embed-poster {
  opacity: 1;
  transform: scale(1.4);
}

.video-embed-play {
  position: absolute;
  top: 50%;
  left: 50%;
  display: flex;
  align-items: center;
  justify-content: center;
  width: 74px;
  height: 74px;
  border-radius: 50%;
  background: var(--brand-orange);
  color: #fff;
  font-size: 24px;
  transform: translate(-50%, -50%);
  transition: background 0.2s ease;
  /* Nudge the glyph right: a triangle is optically off-centre in a circle. */
  padding-left: 5px;
}

.video-embed-trigger:hover .video-embed-play {
  background: var(--brand-orange-hover);
}

.video-embed-label {
  position: absolute;
  right: 20px;
  bottom: 18px;
  left: 20px;
  color: #fff;
  font-family: var(--font-heading);
  font-size: 17px;
  font-weight: 700;
  line-height: 1.3;
  text-align: left;
  text-shadow: 0 2px 12px rgba(0, 0, 0, 0.7);
}

.video-embed-trigger:focus-visible {
  outline: 3px solid var(--brand-blue-accent);
  outline-offset: 3px;
}

/* Once clicked, JS swaps the button for an iframe carrying this class. */
.video-embed-frame {
  display: block;
  width: 100%;
  aspect-ratio: 16 / 9;
  border: 0;
  border-radius: var(--radius-md);
}

.video-embed-caption {
  margin-top: 12px;
  color: var(--muted-on-light);
  font-size: 14px;
  line-height: 1.6;
}

.video-embed-credit {
  display: block;
  margin-top: 4px;
  font-size: 12.5px;
  opacity: 0.85;
}

@media only screen and (max-width: 575px) {
  .video-embed-play {
    width: 58px;
    height: 58px;
    font-size: 19px;
  }

  .video-embed-label {
    font-size: 15px;
  }
}

@media only screen and (max-width: 767px) {
  .conversion-panel {
    padding: 56px 0;
  }

  .conversion-panel-grid {
    grid-template-columns: 1fr;
    gap: 36px;
  }

  .conversion-panel-copy h2 {
    font-size: 30px;
  }

  .conversion-panel-form {
    padding: 24px 18px;
  }

  .service-feature-image {
    aspect-ratio: 16 / 8;
    margin-bottom: 30px;
  }
}

/*--------------------------------------------------------------
## Mobile submenu toggle
#
# The submenu opener was a <span>, unreachable and inoperable by keyboard.
# Swapped for a real <button> in header-mobile.js; this strips the browser's
# default button chrome so it still looks like the original arrow.
--------------------------------------------------------------*/
.header_mobile .mobile_nav .mobile_mainmenu > li.menu-item-has-children .arrow {
  background: transparent;
  border: 0;
  margin: 0;
  font: inherit;
}

/*--------------------------------------------------------------
## Desktop submenu indicators

# The theme's Flaticon glyph is not available in the current font bundle,
# so it renders as a missing-glyph square. Draw the indicator in CSS instead
# so every current and future menu-item-has-children item gets a reliable icon.
--------------------------------------------------------------*/
.main-navigation ul > li.menu-item-has-children > a:after,
.main-navigation ul > li li.menu-item-has-children > a:after {
  content: "";
  width: 6px;
  height: 6px;
  border-right: 1.5px solid currentColor;
  border-bottom: 1.5px solid currentColor;
  font-family: inherit;
  color: #c3c7c9;
  transform: rotate(45deg);
}

.main-navigation ul > li li.menu-item-has-children > a:after {
  right: 1px;
  transform: rotate(-45deg);
}

.main-navigation ul > li.menu-item-has-children > a:hover:after,
.main-navigation ul > li li.menu-item-has-children > a:hover:after {
  color: #43baff;
}

/*--------------------------------------------------------------
## Heading hierarchy fixes
#
# services.php had no <h1> on the page at all (its main heading was an <h2>).
# Changed to a real <h1> for accessibility/SEO; style.css's sitewide h1 is
# 48px vs h2's 36px, so this preserves the original visual size.
--------------------------------------------------------------*/
.main-heading {
  font-size: 36px;
}

/*--------------------------------------------------------------
## Homepage hero background
#
# style.css's gradient (lines 6144) hard-stops from transparent to opaque
# between 20%-40%, which reads as a visible seam against the banner image.
# Softened to a longer ease over the same two colors - no new asset, no new
# color.
--------------------------------------------------------------*/
.home2-top {
  background-image: linear-gradient(to right, rgba(255, 255, 255, 0) 10%, rgba(255, 255, 255, 0.85) 45%, rgba(255, 255, 255, 1) 60%), url(../images/bg-banner-home2.png) !important;
}

/*--------------------------------------------------------------
## Testimonial star rating + source credit
#
# Only rendered when data/testimonials.php sets 'rating'/'source' for an
# entry - legacy testimonials (collected before this existed) have neither
# and render exactly as before.
--------------------------------------------------------------*/
.testi-rating {
  margin-bottom: 6px;
  color: var(--brand-orange);
  font-size: 14px;
  letter-spacing: 2px;
}

.testi-rating .testi-star-empty {
  color: #d8dae2;
}

.testi-source {
  color: var(--muted-on-light);
  font-weight: 400;
  font-size: 13px;
}

/*--------------------------------------------------------------
## Interior page hero
#
# Every interior page previously rendered its <h1> as plain text on a white
# background with no banner at all (or, for monthly-managed/voip/city, a
# background photo with no subheading). This gives every non-home page the
# same banner treatment: eyebrow + title + one-line subheading on a branded
# gradient, so each page states who it's for and what it offers before the
# visitor scrolls.
--------------------------------------------------------------*/
.page-hero {
  position: relative;
  padding: 90px 0 64px;
  overflow: hidden;
  background: linear-gradient(120deg, #1c2456 0%, var(--brand-navy) 55%, var(--brand-purple) 140%);
}

.page-hero::before {
  content: "";
  position: absolute;
  inset: 0;
  background-image:
    radial-gradient(circle at 88% 15%, rgba(67, 186, 255, 0.28), transparent 55%),
    radial-gradient(circle at 8% 95%, rgba(243, 73, 28, 0.2), transparent 45%);
  pointer-events: none;
}

.page-hero-inner {
  position: relative;
  z-index: 1;
  max-width: 760px;
}

.page-hero-eyebrow {
  display: inline-block;
  font-family: var(--font-heading);
  color: var(--brand-blue-accent);
  font-weight: 700;
  font-size: 14px;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  margin-bottom: 14px;
}

.page-hero-title {
  color: #fff;
  margin: 0 0 16px;
  font-size: clamp(1.75rem, 1.3rem + 1.8vw, 2.75rem);
  line-height: 1.2;
}

.page-hero-subtitle {
  color: rgba(255, 255, 255, 0.85);
  font-size: 17px;
  line-height: 1.7;
  margin: 0;
}

@media only screen and (max-width: 575px) {
  .page-hero {
    padding: 60px 0 40px;
  }
}

/* Topic-matched photo variants: same brand-tinted gradient as the plain
   .page-hero (opaque enough to keep the white heading/subtitle readable
   over any photo), layered over a locally hosted Unsplash photo instead of
   flat color. Photos live in /images/unsplash and are licensed for free
   commercial use under the Unsplash License. */
.page-hero.page-hero--photo {
  background-size: cover;
  background-position: center;
}

.page-hero--about {
  background-image: linear-gradient(120deg, rgba(28, 36, 86, 0.92) 0%, rgba(46, 62, 152, 0.9) 55%, rgba(113, 65, 177, 0.85) 140%), url(../images/unsplash/about-team.jpg);
}

.page-hero--services {
  background-image: linear-gradient(120deg, rgba(28, 36, 86, 0.92) 0%, rgba(46, 62, 152, 0.9) 55%, rgba(113, 65, 177, 0.85) 140%), url(../images/unsplash/services-meeting.jpg);
}

.page-hero--it-services {
  background-image: linear-gradient(120deg, rgba(28, 36, 86, 0.92) 0%, rgba(46, 62, 152, 0.9) 55%, rgba(113, 65, 177, 0.85) 140%), url(../images/unsplash/it-services-server.jpg);
}

.page-hero--managed {
  background-image: linear-gradient(120deg, rgba(28, 36, 86, 0.92) 0%, rgba(46, 62, 152, 0.9) 55%, rgba(113, 65, 177, 0.85) 140%), url(../images/unsplash/managed-dashboard.jpg);
}

.page-hero--voip {
  background-image: linear-gradient(120deg, rgba(28, 36, 86, 0.92) 0%, rgba(46, 62, 152, 0.9) 55%, rgba(113, 65, 177, 0.85) 140%), url(../images/unsplash/voip-phone.jpg);
}

.page-hero--app-dev {
  background-image: linear-gradient(120deg, rgba(28, 36, 86, 0.92) 0%, rgba(46, 62, 152, 0.9) 55%, rgba(113, 65, 177, 0.85) 140%), url(../images/unsplash/app-development-code.jpg);
}

.page-hero--products {
  background-image: linear-gradient(120deg, rgba(28, 36, 86, 0.92) 0%, rgba(46, 62, 152, 0.9) 55%, rgba(113, 65, 177, 0.85) 140%), url(../images/unsplash/products-laptop.jpg);
}

/*--------------------------------------------------------------
## Homepage hero background video
#
# CC0/royalty-free loop (Pixabay Content License, no attribution required),
# downloaded locally to /videos so the site doesn't depend on a third-party
# CDN staying up. Sits behind the same gradient used above (now a real
# element so it can layer over the video) and behind the existing
# .home2-top background-image, which remains the poster/fallback until the
# video loads and for browsers where it never runs (reduced motion,
# autoplay blocked).
--------------------------------------------------------------*/
.hero-video-section {
  position: relative;
  overflow: hidden;
  min-height: 480px;
}

.hero-bg-video {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  object-position: center;
  z-index: 0;
  pointer-events: none;
}

/* The overlay was rgba(255,255,255,0.88), rising to 0.94 at the bottom on mobile.
   At that opacity the video underneath is not washed out, it is erased - which is
   why the hero looked like a plain white panel at small widths and mostly like one
   at large widths too. 0.74 keeps the video legible as motion behind the copy.

   Contrast is still safe: the hero heading and body copy are var(--ink) (#1b1d21),
   and 74% white over the darkest frame of the loop stays far above the 4.5:1 that
   WCAG 1.4.3 asks for. Do not push this below about 0.65 without re-checking the
   darkest frame of /videos/hero-network-loop.mp4. */
.hero-video-overlay {
  position: absolute;
  inset: 0;
  z-index: 1;
  background: rgba(255, 255, 255, 0.74);
}

.hero-video-section > .container {
  position: relative;
  z-index: 2;
}

/* Reduced motion: stop the movement, but keep the element on screen so its poster
   frame still shows. This previously used display:none, which removed the poster
   too and left the hero as an empty white band - a worse result than the still
   image the poster attribute already provides for free. js/scripts.js makes the
   matching decision and never starts playback in this case. */
@media (prefers-reduced-motion: reduce) {
  .hero-bg-video {
    display: block;
  }
}

/* Tablet/mobile: the copy is centered full-width below 992px rather than sitting in
   the left ~60%, so the overlay has to be even rather than one-sided - but it stays
   at the same strength as desktop. The video is shown at every viewport size; there
   is no width at which it is suppressed. */
@media only screen and (max-width: 991px) {
  .hero-video-overlay {
    background-image: linear-gradient(to bottom, rgba(255, 255, 255, 0.76) 0%, rgba(255, 255, 255, 0.82) 100%);
  }
}

@media only screen and (max-width: 767px) {
  .hero-video-section {
    min-height: 0;
  }
}

/*--------------------------------------------------------------
## Homepage hero
#
# style.css sizes the hero heading via `.home2-top-right h2` (lines 6160, 7875,
# 8134), a leftover from before the markup was changed to a real <h1> for
# heading-hierarchy/SEO reasons. That left the hero heading falling back to the
# flat, non-fluid sitewide `h1` rule (48px at every width). This is the
# corrected, fluid rule for the tag actually in the markup.
--------------------------------------------------------------*/
.home2-top-right h1 {
  font-family: var(--font-heading);
  font-weight: 900;
  color: var(--ink);
  margin-bottom: 18px;
  font-size: clamp(2.125rem, 1.4rem + 3.2vw, 4.5rem); /* ~34px -> 72px, fluid */
  line-height: 1.15;
  overflow-wrap: break-word;
}

/* Was an inline style="padding-left:65px" with no responsive reduction. */
.hero-content-col {
  padding-left: 65px;
}

@media only screen and (max-width: 575px) {
  .hero-content-col {
    padding-left: 20px;
  }
}

@media only screen and (max-width: 767px) {
  .hero-content-col {
    padding-left: 30px;
  }
}

/* Dual CTA: a click-to-call anchor plus a named, low-friction offer, replacing
   the old single "Get Started" bubble button (which also had a duplicate
   `href` attribute and was not keyboard-operable - see hero-cta-call/offer
   below for the real anchors). */
.hero-cta-group {
  display: flex;
  flex-wrap: wrap;
  gap: 16px;
  margin-top: 8px;
}

.hero-cta-group .octf-btn {
  border-radius: var(--radius-md);
}

.hero-cta-call {
  background: var(--brand-orange-hover);
}

.hero-cta-call:hover,
.hero-cta-call:focus {
  background: var(--brand-orange);
}

.hero-cta-offer {
  background: var(--brand-navy);
}

.hero-cta-offer:hover,
.hero-cta-offer:focus {
  background: var(--brand-purple);
}

@media only screen and (max-width: 575px) {
  .hero-cta-group {
    flex-direction: column;
    align-items: stretch;
  }

  .hero-cta-group .octf-btn {
    text-align: center;
  }
}
