/* Bird Morning — generated by bin/build-css.php. Do not edit. */

/* ===== assets/css/fonts.css ===== */
/* ============================================================
   Bird Morning — self-hosted webfonts
   ------------------------------------------------------------
   Replaces the two render-blocking Google Fonts requests. Latin
   subset only (the site is English); Oswald ships as a single
   variable file covering the 500/600/700 weights style.css uses.

   All four files are preloaded from inc/class-bmca-assets.php.
   Keep `font-display: swap` — it is what stops the webfonts from
   blocking First Contentful Paint.
   ============================================================ */

/* Oswald — headings (--font-head). Variable, 200-700. */
@font-face {
  font-family: "Oswald";
  font-style: normal;
  font-weight: 200 700;
  font-display: swap;
  src: url("../fonts/oswald-latin-var.woff2") format("woff2");
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA,
    U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193,
    U+2212, U+2215, U+FEFF, U+FFFD;
}

/* Ubuntu — body copy (--font-body). */
@font-face {
  font-family: "Ubuntu";
  font-style: normal;
  font-weight: 400;
  font-display: swap;
  src: url("../fonts/ubuntu-400-latin.woff2") format("woff2");
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA,
    U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193,
    U+2212, U+2215, U+FEFF, U+FFFD;
}

@font-face {
  font-family: "Ubuntu";
  font-style: normal;
  font-weight: 500;
  font-display: swap;
  src: url("../fonts/ubuntu-500-latin.woff2") format("woff2");
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA,
    U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193,
    U+2212, U+2215, U+FEFF, U+FFFD;
}

@font-face {
  font-family: "Ubuntu";
  font-style: normal;
  font-weight: 700;
  font-display: swap;
  src: url("../fonts/ubuntu-700-latin.woff2") format("woff2");
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA,
    U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193,
    U+2212, U+2215, U+FEFF, U+FFFD;
}


/* ===== assets/css/grid.css ===== */
/* ============================================================
   Bird Morning — Bootstrap 5.3 subset
   ------------------------------------------------------------
   Hand-cut replacement for the 232 KB Bootstrap CDN stylesheet.
   Contains ONLY the classes this theme's markup actually uses:
   container / row / col-* / g-* / align-items-* / d-* and the
   offcanvas + navbar shells.

   Semantics, breakpoints, gutter variables and container widths
   are identical to Bootstrap 5.3.3, so `assets/css/style.css`
   (which overrides .container, .navbar, .offcanvas-*) behaves
   exactly as it did against the full framework.

   Adding a Bootstrap class to a template that is NOT in this file
   will silently do nothing — add the rule here instead of pulling
   the CDN build back in.
   ============================================================ */

:root {
  --bs-gutter-x: 1.5rem;
  --bs-gutter-y: 0;
}

/* ------------------------------------------------------------
   Container
------------------------------------------------------------ */
.container {
  width: 100%;
  padding-right: calc(var(--bs-gutter-x) * .5);
  padding-left: calc(var(--bs-gutter-x) * .5);
  margin-right: auto;
  margin-left: auto;
}

@media (min-width: 576px)  { .container { max-width: 540px; } }
@media (min-width: 768px)  { .container { max-width: 720px; } }
@media (min-width: 992px)  { .container { max-width: 960px; } }
@media (min-width: 1200px) { .container { max-width: 1140px; } }
@media (min-width: 1400px) { .container { max-width: 1320px; } }

/* ------------------------------------------------------------
   Row + columns
------------------------------------------------------------ */
.row {
  --bs-gutter-x: 1.5rem;
  --bs-gutter-y: 0;
  display: flex;
  flex-wrap: wrap;
  margin-top: calc(-1 * var(--bs-gutter-y));
  margin-right: calc(-.5 * var(--bs-gutter-x));
  margin-left: calc(-.5 * var(--bs-gutter-x));
}

.row > * {
  flex-shrink: 0;
  width: 100%;
  max-width: 100%;
  padding-right: calc(var(--bs-gutter-x) * .5);
  padding-left: calc(var(--bs-gutter-x) * .5);
  margin-top: var(--bs-gutter-y);
}

.col { flex: 1 0 0%; }

.col-12 { flex: 0 0 auto; width: 100%; }

@media (min-width: 576px) {
  .col-sm-6 { flex: 0 0 auto; width: 50%; }
}

@media (min-width: 768px) {
  .col-md-4 { flex: 0 0 auto; width: 33.33333333%; }
  .col-md-6 { flex: 0 0 auto; width: 50%; }
}

@media (min-width: 992px) {
  .col-lg-2 { flex: 0 0 auto; width: 16.66666667%; }
  .col-lg-3 { flex: 0 0 auto; width: 25%; }
  .col-lg-4 { flex: 0 0 auto; width: 33.33333333%; }
  .col-lg-5 { flex: 0 0 auto; width: 41.66666667%; }
  .col-lg-6 { flex: 0 0 auto; width: 50%; }
  .col-lg-7 { flex: 0 0 auto; width: 58.33333333%; }
  .col-lg-8 { flex: 0 0 auto; width: 66.66666667%; }
}

@media (min-width: 1200px) {
  .col-xl-3 { flex: 0 0 auto; width: 25%; }
}

/* ------------------------------------------------------------
   Gutters
------------------------------------------------------------ */
.g-0 { --bs-gutter-x: 0; --bs-gutter-y: 0; }
.g-4 { --bs-gutter-x: 1.5rem; --bs-gutter-y: 1.5rem; }

@media (min-width: 992px) {
  .g-lg-5 { --bs-gutter-x: 3rem; --bs-gutter-y: 3rem; }
}

@media (min-width: 1200px) {
  .g-xl-5 { --bs-gutter-x: 3rem; --bs-gutter-y: 3rem; }
}

/* ------------------------------------------------------------
   Flex + display utilities
------------------------------------------------------------ */
.align-items-start  { align-items: flex-start !important; }
.align-items-center { align-items: center !important; }

.d-none { display: none !important; }

@media (min-width: 992px) {
  .d-lg-block { display: block !important; }
  .d-lg-flex  { display: flex !important; }
}

/* ------------------------------------------------------------
   Navbar shell
   (.navbar padding/width are re-set in style.css §3)
------------------------------------------------------------ */
.navbar {
  position: relative;
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
}

.navbar-brand {
  display: inline-block;
  text-decoration: none;
}

@media (min-width: 992px) {
  .navbar-expand-lg {
    flex-wrap: nowrap;
    justify-content: flex-start;
  }
}

/* ------------------------------------------------------------
   Offcanvas
   Driven by the vanilla implementation in main.js (initOffcanvas),
   not by Bootstrap's JS bundle. Class contract is unchanged:
   .offcanvas + .showing/.show/.hiding, plus .offcanvas-backdrop.
   .mobile-nav in style.css §4 overrides width, height and colour.
------------------------------------------------------------ */
.offcanvas {
  position: fixed;
  bottom: 0;
  z-index: 1045;
  display: flex;
  flex-direction: column;
  max-width: 100%;
  visibility: hidden;
  background-clip: padding-box;
  outline: 0;
  transition: transform .3s ease-in-out;
}

.offcanvas-end {
  top: 0;
  right: 0;
  width: 400px;
  transform: translateX(100%);
}

.offcanvas.showing,
.offcanvas.show:not(.hiding) { transform: none; }

.offcanvas.showing,
.offcanvas.hiding,
.offcanvas.show { visibility: visible; }

.offcanvas-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 1rem;
}

.offcanvas-body {
  flex-grow: 1;
  padding: 1rem;
  overflow-y: auto;
}

.offcanvas-backdrop {
  position: fixed;
  top: 0;
  left: 0;
  z-index: 1040;
  width: 100vw;
  height: 100vh;
  background-color: #000;
}

.offcanvas-backdrop.fade { opacity: 0; }
.offcanvas-backdrop.show { opacity: .5; }
.offcanvas-backdrop      { transition: opacity .15s linear; }

@media (prefers-reduced-motion: reduce) {
  .offcanvas,
  .offcanvas-backdrop { transition: none; }
}


/* ===== assets/css/icons.css ===== */
/* ============================================================
   Bird Morning — SVG icon sizing
   ------------------------------------------------------------
   The theme renders icons as <svg class="bi"><use href="#bi-name">
   against the sprite inlined by bmca_render_icon_sprite(), instead
   of loading the ~180 KB Bootstrap Icons webfont.

   These rules make an <svg class="bi"> occupy exactly the same box
   as the old <i class="bi bi-name"> did, so every font-size,
   colour and alignment rule already in style.css keeps working
   untouched.
   ============================================================ */

.bi {
  display: inline-block;
  width: 1em;
  height: 1em;
  fill: currentColor;
  vertical-align: -.125em;
  flex-shrink: 0;
  pointer-events: none;
}

/* The sprite itself must never take up space or be announced. */
.bmca-icon-sprite {
  position: absolute;
  width: 0;
  height: 0;
  overflow: hidden;
}


/* ===== assets/vendor/splide/splide-core.min.css ===== */
@keyframes splide-loading{0%{transform:rotate(0)}to{transform:rotate(1turn)}}.splide__track--draggable{-webkit-touch-callout:none;-webkit-user-select:none;-ms-user-select:none;user-select:none}.splide__track--fade>.splide__list>.splide__slide{margin:0!important;opacity:0;z-index:0}.splide__track--fade>.splide__list>.splide__slide.is-active{opacity:1;z-index:1}.splide--rtl{direction:rtl}.splide__track--ttb>.splide__list{display:block}.splide__container{box-sizing:border-box;position:relative}.splide__list{backface-visibility:hidden;display:-ms-flexbox;display:flex;height:100%;margin:0!important;padding:0!important}.splide.is-initialized:not(.is-active) .splide__list{display:block}.splide__pagination{-ms-flex-align:center;align-items:center;display:-ms-flexbox;display:flex;-ms-flex-wrap:wrap;flex-wrap:wrap;-ms-flex-pack:center;justify-content:center;margin:0;pointer-events:none}.splide__pagination li{display:inline-block;line-height:1;list-style-type:none;margin:0;pointer-events:auto}.splide:not(.is-overflow) .splide__pagination{display:none}.splide__progress__bar{width:0}.splide{position:relative;visibility:hidden}.splide.is-initialized,.splide.is-rendered{visibility:visible}.splide__slide{backface-visibility:hidden;box-sizing:border-box;-ms-flex-negative:0;flex-shrink:0;list-style-type:none!important;margin:0;position:relative}.splide__slide img{vertical-align:bottom}.splide__spinner{animation:splide-loading 1s linear infinite;border:2px solid #999;border-left-color:transparent;border-radius:50%;bottom:0;contain:strict;display:inline-block;height:20px;left:0;margin:auto;position:absolute;right:0;top:0;width:20px}.splide__sr{clip:rect(0 0 0 0);border:0;height:1px;margin:-1px;overflow:hidden;padding:0;position:absolute;width:1px}.splide__toggle.is-active .splide__toggle__play,.splide__toggle__pause{display:none}.splide__toggle.is-active .splide__toggle__pause{display:inline}.splide__track{overflow:hidden;position:relative;z-index:0}

/* ===== assets/css/style.css ===== */
/* ============================================================
   Bird Morning — Landing Page (Phase 1)
   Sections: Design Tokens · Base · Header · Offcanvas · Rails
             · Hero · Slider · Buttons · Animations · Responsive
   ============================================================ */

/* ============================================================
   1. DESIGN TOKENS
   ============================================================ */
:root {
  /* Brand palette */
  --c-primary: #18B6D9;
  --c-primary-600: #0FA0C2;
  --c-dark: #0F172A;
  --c-secondary: #334155;
  --c-bg: #F8FAFC;

  /* Accents derived from the logo gradient */
  --c-accent-a: #2ABCF8;
  --c-accent-b: #2CA464;
  --grad-primary: linear-gradient(135deg, #18B6D9 0%, #0FA0C2 55%, #185BD0 100%);
  --grad-accent: linear-gradient(120deg, #2ABCF8, #18B6D9, #2CA464);

  /* Surfaces */
  --glass-border: rgba(255, 255, 255, 0.22);
  --card-bg: linear-gradient(150deg, rgba(23, 158, 185, 0.96), rgba(15, 118, 143, 0.96));
  /* Opaque teal panel, one step lighter than --card-bg. --card-bg is a glass
     surface: it is translucent by design and sits over a photograph in the
     hero. Used flat on an opaque panel it reads noticeably darker than the
     brand teal, which is why the director card and the director's note — same
     section, side by side — did not match. Both use this now, so they cannot
     drift apart again. */
  --grad-panel: linear-gradient(150deg, #35B4C8 0%, #1BA6C6 55%, #1391B4 100%);

  /* Typography */
  --font-head: "Oswald", system-ui, sans-serif;
  --font-body: "Ubuntu", system-ui, -apple-system, sans-serif;

  /* Shadows */
  /* Two layers each, never one. A single blur is a flat slab of grey
     that stops dead at its own radius, which is what reads as a box
     rather than a shadow. A tight contact layer plus a wide, weak
     ambient one gives a falloff with somewhere to go. Tinted with
     the palette's navy, never pure black — black over a coloured
     surface goes grey-brown and looks like dirt on the panel. */
  --shadow-sm: 0 1px 3px rgba(15, 23, 42, 0.05), 0 6px 20px -6px rgba(15, 23, 42, 0.10);
  --shadow-md: 0 2px 8px rgba(15, 23, 42, 0.06), 0 24px 52px -18px rgba(15, 23, 42, 0.24);
  --shadow-glow: 0 0 32px rgba(24, 182, 217, 0.45);

  /* Metrics */
  /* Design rule: NO border-radius anywhere — sharp corners only */
  --radius: 0;
  --header-h: 84px;
  --rail-w: 26px;   /* +30% from 20px */
  --ease: cubic-bezier(0.22, 1, 0.36, 1);
}

/* ============================================================
   2. BASE / RESET
   ============================================================ */
*,
*::before,
*::after { box-sizing: border-box; }

/* Project rule: sharp corners only — never use border-radius anywhere.
   This overrides component + Bootstrap radii in one place. */
*,
*::before,
*::after { border-radius: 0 !important; }

html { scroll-behavior: smooth; -webkit-text-size-adjust: 100%; }

/* Anchor targets must clear the fixed header, or every in-page nav link
   lands with the section heading hidden underneath it. */
section[id],
main section[id] { scroll-margin-top: calc(var(--header-h) + 12px); }

body {
  margin: 0;
  font-family: var(--font-body);
  color: var(--c-dark);
  background: var(--c-bg);
  line-height: 1.6;
  overflow-x: hidden;
  -webkit-font-smoothing: antialiased;
}

img { max-width: 100%; height: auto; display: block; }
a { text-decoration: none; color: inherit; }
ul { list-style: none; margin: 0; padding: 0; }

:focus-visible {
  outline: 3px solid var(--c-primary);
  outline-offset: 3px;
  border-radius: 4px;
}

.skip-link {
  position: absolute;
  left: -999px;
  top: 0;
  z-index: 2000;
  background: transparent;
  color: #fff;
  padding: 12px 20px;
  border-radius: 0 0 10px 0;
}
.skip-link:focus { left: 0; }

/* ============================================================
   3. STICKY HEADER
   ============================================================ */
.site-header {
  position: fixed;
  inset: 0 0 auto 0;
  z-index: 1030;
  min-height: var(--header-h);
  display: flex;
  align-items: center;
  background: transparent;
  transition: background-color .4s var(--ease), box-shadow .4s var(--ease), min-height .4s var(--ease);
}
.site-header.is-scrolled {
  background: rgba(255, 255, 255, 0.92);
  backdrop-filter: saturate(180%) blur(12px);
  -webkit-backdrop-filter: saturate(180%) blur(12px);
  box-shadow: var(--shadow-sm);
  min-height: 70px;
}

.header-inner {
  display: flex;
  align-items: center;
  justify-content: space-between;
  width: 100%;
}

.navbar { width: 100%; padding: 0; }
.brand-logo {
  height: clamp(38px, 4vw, 46px);
  width: auto;
  transition: transform .3s var(--ease);
}
.navbar-brand:hover .brand-logo { transform: translateY(-1px) scale(1.02); }

/* Desktop nav */
.nav-list {
  display: flex;
  align-items: center;
  gap: clamp(14px, 2vw, 30px);
}
.nav-link-x {
  position: relative;
  font-family: var(--font-head);
  font-weight: 500;
  letter-spacing: .06em;
  text-transform: uppercase;
  font-size: clamp(.82rem, 1vw, .95rem);
  color: var(--c-secondary);
  padding: 6px 0;
  transition: color .3s var(--ease);
}
.site-header:not(.is-scrolled) .nav-link-x { color: rgba(15, 23, 42, 0.85); }

/* Hover underline animation */
.nav-link-x::after {
  content: "";
  position: absolute;
  left: 0; bottom: 0;
  width: 100%; height: 2px;
  background: var(--grad-primary);
  transform: scaleX(0);
  transform-origin: right;
  transition: transform .35s var(--ease);
}
.nav-link-x:hover,
.nav-link-x.active { color: var(--c-primary); }
.nav-link-x:hover::after,
.nav-link-x.active::after { transform: scaleX(1); transform-origin: left; }

/* ============================================================
   4. HAMBURGER + OFFCANVAS
   ============================================================ */
.hamburger {
  display: none;
  flex-direction: column;
  justify-content: center;
  align-items: flex-end;          /* uneven bars align to the right */
  gap: 6px;
  width: 44px; height: 44px;
  padding: 0 8px;
  background: transparent;
  border: 0;
  cursor: pointer;
}
.hamburger span {
  display: block;
  height: 2.5px;
  background: transparent;
  transition: transform .38s var(--ease), width .38s var(--ease);
}
.hamburger span:nth-child(1) { width: 26px; }   /* top bar — longer  */
.hamburger span:nth-child(2) { width: 17px; }   /* bottom bar — short */
/* premium hover: short bar extends to match */
.hamburger:hover span { width: 26px; }

/* Open state — both bars go full width and cross into an X */
body.nav-open .hamburger span { width: 26px; }
body.nav-open .hamburger span:nth-child(1) { transform: translateY(4.25px) rotate(45deg); }
body.nav-open .hamburger span:nth-child(2) { transform: translateY(-4.25px) rotate(-45deg); }

/* Full-screen brand panel. Overrides Bootstrap's .offcanvas-end width —
   same specificity, but style.css loads after bootstrap.min.css so it wins.
   100dvh keeps it exact while mobile browser chrome collapses; the 100% is
   the fallback for engines without dynamic viewport units. */
.mobile-nav {
  width: 100%;
  max-width: 100%;
  height: 100%;
  height: 100dvh;
  color: #fff;
  /* Brand cyan → brand blue, with the logo-gradient accents glowing in the
     corners. Deliberately NOT the navy used elsewhere. */
  background:
    radial-gradient(105% 70% at 100% 0%, rgba(42, 188, 248, .55), transparent 62%),
    radial-gradient(95% 65% at 0% 100%, rgba(44, 164, 100, .38), transparent 64%),
    linear-gradient(158deg, #18B6D9 0%, #0FA0C2 44%, #1257A8 100%);
  border-left: 0;
}
.mobile-nav-bg {
  position: absolute;
  inset: 0;
  z-index: 0;
  pointer-events: none;
  /* dot grid + a soft top-to-bottom scrim. The scrim is what keeps white
     text readable on mid-cyan — without it .mobile-nav-meta drops under
     3:1 against the lightest part of the gradient. */
  background-image:
    radial-gradient(rgba(255, 255, 255, .10) 1px, transparent 1.5px),
    linear-gradient(180deg, rgba(6, 38, 58, .18) 0%, rgba(6, 38, 58, .34) 100%);
  background-size: 22px 22px, 100% 100%;
}

/* constellation canvas injected by initMobileNavNet().
   Stack is bg(0) -> particles(1) -> content(2) -> brand edge(3); the canvas
   must clear .mobile-nav-bg or that layer's scrim washes the dots out. */
.mnav-net {
  position: absolute;
  inset: 0;
  z-index: 1;
  pointer-events: none;
  opacity: .85;
}
/* animated brand edge, mirroring the card/section accents.
   Needs its own keyframe: gradientMove pans background-position on X,
   which is invisible on a 3px-wide bar — this one pans on Y. */
.mobile-nav::before {
  content: "";
  position: absolute;
  top: 0; left: 0; bottom: 0;
  width: 3px;
  z-index: 3;
  background: var(--grad-accent);
  background-size: 100% 200%;
  animation: gradientMoveY 7s ease infinite;
}
@keyframes gradientMoveY {
  0%, 100% { background-position: 50% 0%; }
  50%      { background-position: 50% 100%; }
}

.mobile-nav .offcanvas-header,
.mobile-nav .offcanvas-body { position: relative; z-index: 2; }

/* Header row mirrors .site-header exactly — same min-height, same .container,
   same flex distribution — so the close button sits on the identical x/y as
   the hamburger it replaced instead of drifting inward. */
.mobile-nav .offcanvas-header {
  align-items: center;
  min-height: var(--header-h);
  padding: 0;
  border-bottom: 1px solid rgba(255, 255, 255, .16);
}
.mobile-nav-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  width: 100%;
}
.offcanvas-logo { height: 38px; width: auto; filter: brightness(0) invert(1); }

/* 44px box == the hamburger's 44px box, so swapping one for the other does
   not shift the tap target. Sits flush to the container edge, as it did. */
.mobile-nav-close {
  display: grid;
  place-items: center;
  flex: 0 0 auto;
  width: 44px;
  height: 44px;
  padding: 0;
  font-size: 1.05rem;
  color: #fff;
  background: rgba(255, 255, 255, .12);
  border: 1px solid rgba(255, 255, 255, .38);
  cursor: pointer;
  transition: background-color .3s var(--ease), color .3s var(--ease), border-color .3s var(--ease), transform .22s var(--ease);
}

/* It draws itself in with the panel rather than being simply present.
 *
 * `backwards`, not `both`: the fill has to stop applying the moment the
 * animation ends, or its final transform would outrank the :hover rotate
 * below for as long as the menu stays open — an animated declaration beats
 * a plain one. Keyed on .showing as well as .show for the same reason the
 * item stagger is; see the note further down. */
:is(.mobile-nav.showing, .mobile-nav.show) .mobile-nav-close {
  animation: mnClose .42s var(--ease) .1s backwards;
}
@keyframes mnClose {
  from { opacity: 0; transform: rotate(-90deg) scale(.6); }
  to   { opacity: 1; transform: none; }
}

/*
 * Colour only, no rotate. initOffcanvas() moves focus to the first focusable
 * element when the panel opens and that element is this button, so a rotate
 * here fires on open in any engine that treats programmatic focus as
 * focus-visible — the icon lands sideways before the reader has touched it.
 */
.mobile-nav-close:focus-visible {
  color: var(--c-dark);
  background: #fff;
  border-color: transparent;
}

/* Touch gets a press state instead of the pointer rotate. */
.mobile-nav-close:active { transform: scale(.88); }

/* Nine links, a button, two contact rows and four social icons have to fit
   between the header and the bottom of the viewport, or the panel scrolls and
   the CTA disappears below the fold. Every gap from here down is sized in vh
   with a tight floor so the whole thing collapses gracefully on a short
   phone instead of overflowing at the first one. */
.mobile-nav .offcanvas-body {
  display: flex;
  flex-direction: column;
  /* Top value is the panel's breathing room under the header; it used to be
     half this, because the leftover height it should have had was being
     collected into the middle of the panel instead. See .mobile-nav-inner. */
  padding: clamp(12px, 2.4vh, 26px) 0 clamp(14px, 2vh, 22px);
}
/* full-bleed panel would otherwise stretch the list across a 991px tablet */
.mobile-nav-inner {
  display: flex;
  flex-direction: column;
  width: 100%;

  /*
   * Centred, so whatever height is left over after the menu is split evenly
   * above and below it. It used to pool in a single gap above the CTA (see
   * .mobile-nav-foot), which on a tall phone was a hole through the middle
   * of the panel; pushing it all to the bottom instead would only move the
   * hole. Split, it reads as padding at both ends.
   *
   * flex-shrink is 0 on purpose. Centring a flex item that has been squeezed
   * below its content height overflows it in both directions, and the half
   * above the container cannot be scrolled to. Refusing to shrink means the
   * short-phone case has no free space to centre into: the column keeps its
   * content height and .offcanvas-body scrolls it, exactly as before.
   */
  flex: 1 0 auto;
  justify-content: center;
}

.mobile-nav-eyebrow {
  font-family: var(--font-head);
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 4px;
  font-size: .68rem;
  color: rgba(255, 255, 255, .45);
  margin: 0 0 14px;
}

/* ---- nav items: index number + label + arrow ---- */
.mobile-nav-list li + li { border-top: 1px solid rgba(255, 255, 255, .16); }
.mobile-nav-list a {
  position: relative;
  display: flex;
  align-items: center;
  gap: 14px;
  padding: clamp(8px, 1.35vh, 16px) 14px clamp(8px, 1.35vh, 16px) 0;
  font-family: var(--font-head);
  font-weight: 500;
  letter-spacing: .06em;
  text-transform: uppercase;
  font-size: clamp(.98rem, 3.9vw, 1.28rem);
  /* 1.6 comes from body; on a row that is always one line it just adds
     10px of dead space per item, which is 90px across the nine. */
  line-height: 1.25;
  color: rgba(255, 255, 255, .92);
  transition: color .3s var(--ease), padding-left .35s var(--ease);
}
/* accent bar wipes in from the left on hover/active. White, not the brand
   gradient — that gradient is now the panel background itself. */
.mobile-nav-list a::before {
  content: "";
  position: absolute;
  left: 0; top: 8px; bottom: 8px;
  width: 3px;
  background: #fff;
  transform: scaleY(0);
  transform-origin: top;
  transition: transform .35s var(--ease);
}
.mobile-nav-list a:focus-visible,
.mobile-nav-list a.active { color: #fff; padding-left: 14px; }
.mobile-nav-list a:focus-visible::before,
.mobile-nav-list a.active::before { transform: scaleY(1); }

/* cyan on a cyan panel is invisible — these were --c-primary before */
.mn-num {
  font-size: .74rem;
  font-weight: 600;
  letter-spacing: 1px;
  color: #fff;
  opacity: .6;
  transition: opacity .3s var(--ease);
}
.mn-label { flex: 1 1 auto; }
.mn-arrow {
  font-size: .85rem;
  color: #fff;
  opacity: 0;
  transform: translateX(-6px);
  transition: opacity .3s var(--ease), transform .3s var(--ease);
}
.mobile-nav-list a:focus-visible .mn-arrow,
.mobile-nav-list a.active .mn-arrow { opacity: 1; transform: translateX(0); }
.mobile-nav-list a.active .mn-num { opacity: 1; }

/* ---- pointer vs touch -------------------------------------------------
   Every :hover in this panel used to fire on a phone as well. A tap sets
   :hover on touch and it stays set until something else is tapped, so
   pressing a menu item slid it 14px right, wiped an accent bar in and
   floated an arrow across — a burst of animation on the way out of a menu
   that is already closing. None of it says anything a tap does not.

   Pointer devices keep the whole thing; :focus-visible and .active are
   outside the gate because neither fires on a touch tap. -------------- */
@media (hover: hover) {
  .mobile-nav-list a:hover { color: #fff; padding-left: 14px; }
  .mobile-nav-list a:hover::before { transform: scaleY(1); }
  .mobile-nav-list a:hover .mn-arrow { opacity: 1; transform: translateX(0); }
  .mobile-nav-list a:hover .mn-num { opacity: 1; }

  .mobile-nav-cta:hover { color: var(--c-dark); background: rgba(255, 255, 255, .88); }
  .mobile-nav-meta a:hover { color: #fff; }

  .mobile-nav-close:hover {
    color: var(--c-dark);
    background: #fff;
    border-color: transparent;
    transform: rotate(90deg);
  }

  .mobile-social a:hover {
    color: var(--c-dark);
    background: #fff;
    border-color: transparent;
    transform: translateY(-3px);
  }
}

/* On touch: no transitions to leave half-finished when the panel starts
   closing, and one instant press flash instead. iOS draws its own grey
   box on tap, which would be a second, differently-timed flash — hence
   the transparent tap highlight. */
@media (hover: none) {
  .mobile-nav a,
  .mobile-nav button {
    -webkit-tap-highlight-color: transparent;
  }

  .mobile-nav-list a,
  .mobile-nav-list a::before,
  .mobile-nav-list .mn-arrow,
  .mobile-nav-list .mn-num { transition: none; }

  .mobile-nav-list a:active { color: #fff; background: rgba(255, 255, 255, .12); }
  .mobile-social a:active,
  .mobile-nav-cta:active { opacity: .75; }
}

/* Staggered entry, replayed on every open because the class comes and goes.
 *
 * It has to be keyed on .showing AND .show, not .show alone. The panel wears
 * .showing for the 300ms of its slide and only swaps to .show at the end, so
 * gating on .show meant the items sat at full opacity through the whole slide
 * and were then yanked back to opacity:0 by this animation's backwards fill
 * and faded in a second time — a visible flash a third of a second after the
 * menu opened, worst on the first open when nothing is warm yet.
 *
 * The two states must produce an identical `animation` value. A class swap
 * that leaves the computed animation unchanged does not restart it, which is
 * what lets the stagger run straight through the showing -> show handover. */
:is(.mobile-nav.showing, .mobile-nav.show) .mobile-nav-list li { animation: mnIn .45s var(--ease) both; }
:is(.mobile-nav.showing, .mobile-nav.show) .mobile-nav-list li:nth-child(1) { animation-delay: .05s; }
:is(.mobile-nav.showing, .mobile-nav.show) .mobile-nav-list li:nth-child(2) { animation-delay: .09s; }
:is(.mobile-nav.showing, .mobile-nav.show) .mobile-nav-list li:nth-child(3) { animation-delay: .13s; }
:is(.mobile-nav.showing, .mobile-nav.show) .mobile-nav-list li:nth-child(4) { animation-delay: .17s; }
:is(.mobile-nav.showing, .mobile-nav.show) .mobile-nav-list li:nth-child(5) { animation-delay: .21s; }
:is(.mobile-nav.showing, .mobile-nav.show) .mobile-nav-list li:nth-child(6) { animation-delay: .25s; }
:is(.mobile-nav.showing, .mobile-nav.show) .mobile-nav-list li:nth-child(7) { animation-delay: .29s; }
:is(.mobile-nav.showing, .mobile-nav.show) .mobile-nav-list li:nth-child(8) { animation-delay: .33s; }
:is(.mobile-nav.showing, .mobile-nav.show) .mobile-nav-list li:nth-child(9) { animation-delay: .37s; }
:is(.mobile-nav.showing, .mobile-nav.show) .mobile-nav-foot { animation: mnIn .45s var(--ease) .42s both; }
@keyframes mnIn {
  from { opacity: 0; transform: translateX(18px); }
  to   { opacity: 1; transform: none; }
}

/* ---- footer block: CTA + contact + social ----

   No `margin-top: auto` here any more. .mobile-nav-inner is a flex column
   the full height of the panel, so an auto margin collected every pixel of
   leftover height into one gap directly above the CTA — on a tall phone
   that read as a hole in the middle of the menu, with the links crowded
   under the header and the button stranded at the bottom.

   The spacing is stated instead, and the slack it used to absorb now sits
   above the list as .offcanvas-body's top padding. On a short phone the
   list is taller than the panel, there is no slack either way, and this
   behaves exactly as it did before. */
.mobile-nav-foot {
  padding-top: clamp(16px, 2.6vh, 30px);
}
/* solid white — the default btn-x-primary is the same cyan gradient that is
   now the panel background, so it disappeared into it */
.mobile-nav-cta {
  display: flex;
  width: 100%;
  justify-content: center;
  padding: clamp(12px, 1.7vh, 15px) 22px;
  color: var(--c-dark);
  background: #fff;
  border-color: #fff;
}
.mobile-nav-cta:focus-visible {
  color: var(--c-dark);
  background: rgba(255, 255, 255, .88);
}
.mobile-nav-meta {
  margin: clamp(14px, 2vh, 22px) 0 0;
  padding-top: clamp(13px, 1.9vh, 20px);
  border-top: 1px solid rgba(255, 255, 255, .1);
  display: flex;
  flex-direction: column;
  gap: clamp(8px, 1.1vh, 12px);
}
.mobile-nav-meta li {
  display: flex;
  align-items: center;
  gap: 11px;
  min-width: 0;
  font-size: .9rem;
  line-height: 1.35;
  color: rgba(255, 255, 255, .85);
}
.mobile-nav-meta .bi { flex: 0 0 auto; color: #fff; opacity: .8; font-size: .95rem; }
.mobile-nav-meta a {
  min-width: 0;
  overflow-wrap: anywhere;
  color: rgba(255, 255, 255, .9);
  transition: color .3s var(--ease);
}
.mobile-nav-meta a:focus-visible { color: #fff; }

.mobile-social {
  display: flex;
  gap: 10px;
  margin-top: clamp(14px, 2vh, 22px);
  font-size: 1rem;
}
/* 44px is the WCAG 2.2 target minimum (SC 2.5.8) — do not shrink these to
   claw back height. Take it out of the gaps instead. */
.mobile-social a {
  display: grid;
  place-items: center;
  width: 44px;
  height: 44px;
  color: #fff;
  border: 1px solid rgba(255, 255, 255, .24);
  transition: color .3s var(--ease), background-color .3s var(--ease), border-color .3s var(--ease), transform .3s var(--ease);
}
.mobile-social a:focus-visible {
  color: var(--c-dark);
  background: #fff;
  border-color: transparent;
}

/* darker scrim so the dark panel still separates from the page */
.offcanvas-backdrop.show { opacity: .7; }

@media (prefers-reduced-motion: reduce) {
  :is(.mobile-nav.showing, .mobile-nav.show) .mobile-nav-list li,
  :is(.mobile-nav.showing, .mobile-nav.show) .mobile-nav-foot,
  :is(.mobile-nav.showing, .mobile-nav.show) .mobile-nav-close { animation: none; }
  .mobile-nav::before { animation: none; }
  .mobile-nav-close:hover,
  .mobile-nav-close:active { transform: none; }
}

/* ============================================================
   5. FIXED DECORATIVE RAILS
   ============================================================ */
.rail {
  position: fixed;
  top: 0; bottom: 0;
  z-index: 1020;
  width: var(--rail-w);
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 18px;
  pointer-events: none;
  /* clean, near-solid panel so it reads as a distinct border, not a wash */
  background: rgba(255, 255, 255, 0.86);
  backdrop-filter: saturate(150%) blur(6px);
  -webkit-backdrop-filter: saturate(150%) blur(6px);
}
.rail-left {
  left: 0;
  border-right: 1px solid rgba(15, 23, 42, 0.10);
  box-shadow: 8px 0 30px rgba(15, 23, 42, 0.05);
}
.rail-right {
  right: 0;
  border-left: 1px solid rgba(15, 23, 42, 0.10);
  box-shadow: -8px 0 30px rgba(15, 23, 42, 0.05);
}
.rail a { pointer-events: auto; }

.rail-social { display: flex; flex-direction: column; gap: 15px; }
.rail-social a {
  color: var(--c-primary);
  font-size: .9rem;
  line-height: 1;
  transition: color .3s var(--ease), transform .3s var(--ease);
}
.rail-social a:hover { color: var(--c-primary-600); transform: translateY(-2px) scale(1.12); }

/* Right rail: "scroll up" pinned to the bottom */
.rail-top {
  position: absolute;
  bottom: 22px;
  left: 50%;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 8px;
  color: var(--c-primary);
  /* hidden until scrolled past the first section */
  opacity: 0;
  visibility: hidden;
  transform: translateX(-50%) translateY(8px);
  transition: color .3s var(--ease), transform .35s var(--ease), opacity .35s var(--ease), visibility .35s var(--ease);
}
.rail-top.is-visible { opacity: 1; visibility: visible; transform: translateX(-50%); }
.rail-top .bi { font-size: 1rem; }
.rail-top span {
  writing-mode: vertical-rl;
  text-orientation: mixed;
  font-family: var(--font-head);
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 2px;
  font-size: .66rem;
}
.rail-top.is-visible:hover { color: var(--c-primary-600); transform: translateX(-50%) translateY(-3px); }

/* Right rail: section dots */
.rail-dots { display: flex; flex-direction: column; gap: 14px; }
.dot { display: grid; place-items: center; width: 18px; height: 18px; }
.dot span {
  width: 9px; height: 9px;
  border-radius: 50% !important;              /* round dots (override global) */
  background: transparent;
  border: 1.5px solid var(--c-primary);
  opacity: .5;
  transition: all .35s var(--ease);
}
.dot:hover span { opacity: 1; border-color: var(--c-primary); }
.dot.active span {
  background: var(--c-primary);
  border-color: var(--c-primary);
  opacity: 1;
  transform: scale(1.35);
  box-shadow: 0 0 0 4px rgba(24, 182, 217, 0.18);
}

/* ============================================================
   6. HERO SECTION
   ============================================================ */
.hero {
  position: relative;
  min-height: 100svh;
  display: flex;
  align-items: center;
  padding: calc(var(--header-h) + 24px) 0 60px;
  overflow: hidden;
  isolation: isolate;
}
.hero .container { position: relative; z-index: 3; }

/* Background image */
.hero-bg {
  position: absolute;
  inset: 0;
  z-index: 0;
  background: #e9eef3 url("../images/hero-bg.png") center / cover no-repeat;
}
.hero-bg::after {
  /* soft radial highlight + wash so the card pops */
  content: "";
  position: absolute;
  inset: 0;
  background:
    radial-gradient(120% 80% at 50% 0%, rgba(248, 250, 252, 0) 0%, rgba(248, 250, 252, .55) 100%),
    radial-gradient(60% 50% at 50% 45%, rgba(24, 182, 217, .10), transparent 70%);
}

/* Animated AI mesh background */
.hero-mesh {
  position: absolute;
  inset: -20%;
  z-index: 1;
  background:
    radial-gradient(38% 38% at 18% 22%, rgba(42, 188, 248, .16), transparent 60%),
    radial-gradient(42% 42% at 82% 30%, rgba(24, 182, 217, .14), transparent 60%),
    radial-gradient(45% 45% at 70% 85%, rgba(44, 164, 100, .12), transparent 60%);
  filter: blur(8px);
  animation: meshShift 18s ease-in-out infinite alternate;
}

/* CSS-only floating particles */
.hero-particles { position: absolute; inset: 0; z-index: 2; pointer-events: none; }
.hero-particles span {
  position: absolute;
  width: 8px; height: 8px;
  border-radius: 50%;
  background: radial-gradient(circle, rgba(42, 188, 248, .9), rgba(42, 188, 248, 0));
  animation: floatUp linear infinite;
}
.hero-particles span:nth-child(1) { left: 8%;  bottom: -10%; animation-duration: 15s; }
.hero-particles span:nth-child(2) { left: 22%; bottom: -10%; width: 5px; height: 5px; animation-duration: 19s; animation-delay: 2s; }
.hero-particles span:nth-child(3) { left: 35%; bottom: -10%; width: 11px; height: 11px; animation-duration: 22s; animation-delay: 4s; }
.hero-particles span:nth-child(4) { left: 48%; bottom: -10%; animation-duration: 17s; animation-delay: 1s; }
.hero-particles span:nth-child(5) { left: 60%; bottom: -10%; width: 6px; height: 6px; animation-duration: 20s; animation-delay: 5s; }
.hero-particles span:nth-child(6) { left: 70%; bottom: -10%; width: 9px; height: 9px; animation-duration: 16s; animation-delay: 3s; }
.hero-particles span:nth-child(7) { left: 80%; bottom: -10%; width: 5px; height: 5px; animation-duration: 21s; animation-delay: 6s; }
.hero-particles span:nth-child(8) { left: 88%; bottom: -10%; width: 12px; height: 12px; animation-duration: 24s; animation-delay: 2s; }
.hero-particles span:nth-child(9) { left: 15%; bottom: -10%; width: 6px; height: 6px; animation-duration: 23s; animation-delay: 7s; }
.hero-particles span:nth-child(10){ left: 92%; bottom: -10%; animation-duration: 18s; animation-delay: 4s; }

/* ---------- Floating glass slider card ---------- */
.hero-card {
  position: relative;
  border-radius: var(--radius);
  padding: clamp(24px, 4vw, 60px) clamp(22px, 4vw, 66px);
  background: var(--card-bg);
  border: 1px solid var(--glass-border);
  box-shadow: var(--shadow-md), inset 0 1px 0 rgba(255, 255, 255, .18);
  overflow: hidden;
  will-change: transform;
  transition: transform .3s var(--ease);
}
/* subtle glass sheen + radial highlight inside the card */
.hero-card::before {
  content: "";
  position: absolute;
  inset: 0;
  background:
    radial-gradient(80% 60% at 15% 0%, rgba(255, 255, 255, .12), transparent 55%),
    radial-gradient(70% 70% at 98% 100%, rgba(42, 188, 248, .14), transparent 55%);
  pointer-events: none;
}
/* Animated gradient shimmer sweeping across the card.
 *
 * It travels on transform, never on `left`. `left` is a layout property, so
 * animating it made Chrome record a layout shift on every frame of the sweep
 * — and because the animation is infinite, the page's CLS climbed for as long
 * as it stayed open rather than settling. This was the single largest CLS
 * source on the front page. transform is composited and is excluded from CLS
 * by definition; the sweep looks identical. See the keyframes in §9. */
.hero-card::after {
  content: "";
  position: absolute;
  top: 0; left: -60%;
  width: 60%; height: 100%;
  background: linear-gradient(100deg, transparent, rgba(255, 255, 255, .08), transparent);
  animation: shimmer 7s ease-in-out infinite;
  pointer-events: none;
}

/* ============================================================
   6b. SPLIDE BASE OVERRIDES
   ------------------------------------------------------------
   Applies to all three carousels. splide-core.min.css is loaded
   unmodified as part of the bundle; these are the two places its
   defaults do not suit this project.
   ============================================================ */

/* Splide hides .splide until it has mounted, which means a JS error
   would leave the hero — the LCP element — as an empty box. Keep the
   markup visible and reproduce the old first-slide-only fallback with
   the :not(.is-initialized) rules further down instead. */
.splide { visibility: visible; }

/* Slides stretch to the tallest in the row (flex default), which is what
   lets .blog-card / .tst-card use height:100% for equal-height cards. */
.splide__list { align-items: stretch; }

/* ---- Pagination ---------------------------------------------------
   One definition for all three carousels. §2 zeroes border-radius
   globally with !important, so a round dot was never actually round —
   these were rendering as small squares. Leaning into that: short bars
   that stretch and light up on the active slide, which also reads as a
   position indicator rather than a row of identical marks.

   Each carousel sets --dot-idle / --dot-active for its own background;
   nothing else differs between them.
------------------------------------------------------------------- */
.splide__pagination {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 8px;
  margin: 0;
  padding: 0;
}

.splide__pagination li { display: flex; }

.splide__pagination__page {
  position: relative;
  width: 20px;
  height: 3px;
  padding: 0;
  border: 0;
  opacity: 1;
  background: var(--dot-idle, rgba(15, 23, 42, .18));
  cursor: pointer;
  transition: width .45s var(--ease), background-color .45s var(--ease);
}

/* A 3px-tall bar is not a touch target. Widen the hit area to 44px
   without touching the visible mark (WCAG 2.2 SC 2.5.8). */
.splide__pagination__page::after {
  content: "";
  position: absolute;
  top: 50%;
  left: 50%;
  width: 100%;
  min-width: 30px;
  height: 44px;
  transform: translate(-50%, -50%);
}

.splide__pagination__page.is-active {
  width: 42px;
  background: var(--dot-active, var(--grad-primary));
}

.splide__pagination__page:hover:not(.is-active) {
  background: var(--dot-hover, rgba(15, 23, 42, .34));
}

/* ============================================================
   7. HERO SLIDER
   ============================================================ */
.hero-slider { position: relative; z-index: 2; }

/* One slide at a time, stacked — no track, no clones, no shared translation.
 *
 * Every slide sits in the same single grid cell, so the slider is always as
 * tall as its tallest slide and the card never resizes as it advances. Only
 * .is-active is painted; the pair mid-transition are painted too, and nothing
 * else ever is.
 *
 * The horizontal movement is per-slide and expressed in PERCENT, which is the
 * whole point: translateX(100%) resolves against the slide's own border box,
 * so the browser supplies the distance and no JavaScript ever measures
 * anything. Contrast Splide's `loop`, which this replaced — it slides ONE
 * flex track holding every slide, positions it in pixels read from
 * getBoundingClientRect() at mount, and fakes the wrap with cloned slides. A
 * bad measurement there parks the track between two slides (half the previous
 * beside half the next) and a clone set that drifts out of step with the
 * controller's index walks the carousel backwards past the last slide. There
 * is no track here to mis-seat and no clones to desynchronise, so the visual
 * effect is the same but neither failure is expressible. The blog and
 * testimonial carousels are still Splide: they scroll several items at once,
 * which is what a track is genuinely for. See blocks/hero/render.php.
 *
 * Animations, not transitions. A transition would need the incoming slide
 * parked off-screen and then moved in the same frame, which means a forced
 * reflow between the two writes to be reliable; keyframes just declare both
 * ends. `both` holds the end state so nothing snaps before JS clears the
 * class on animationend.
 *
 * Inactive slides are visibility:hidden, which is load-bearing — it keeps
 * their links and buttons out of the tab order for free. The two slides in
 * flight are necessarily visible, so JS also marks every non-active slide
 * `inert`; that is what covers the ~600ms window. */
.hero-slides { display: grid; }

.hero-slides > .slide {
  grid-area: 1 / 1;
  min-width: 0;
  opacity: 0;
  visibility: hidden;
  pointer-events: none;
}

.hero-slides > .slide.is-active {
  opacity: 1;
  visibility: visible;
  pointer-events: auto;
}

/* Both slides in flight must paint, including the one that is no longer
   .is-active. pointer-events stays off on the way out. */
.hero-slides > .slide.is-entering,
.hero-slides > .slide.is-leaving {
  visibility: visible;
  z-index: 1;
}
.hero-slides > .slide.is-leaving { pointer-events: none; }

/* Direction lives on the container, set by JS before the classes move, so
   "forward" and "back" read the same way here as they do in initHeroSlider(). */
.hero-slides[data-dir="next"] > .slide.is-entering { animation: heroInRight .6s var(--ease) both; }
.hero-slides[data-dir="next"] > .slide.is-leaving  { animation: heroOutLeft .6s var(--ease) both; }
.hero-slides[data-dir="prev"] > .slide.is-entering { animation: heroInLeft .6s var(--ease) both; }
.hero-slides[data-dir="prev"] > .slide.is-leaving  { animation: heroOutRight .6s var(--ease) both; }

@keyframes heroInRight  { from { transform: translateX(100%);  opacity: 0; } to { transform: none; opacity: 1; } }
@keyframes heroInLeft   { from { transform: translateX(-100%); opacity: 0; } to { transform: none; opacity: 1; } }
@keyframes heroOutLeft  { from { transform: none; opacity: 1; } to { transform: translateX(-100%); opacity: 0; } }
@keyframes heroOutRight { from { transform: none; opacity: 1; } to { transform: translateX(100%);  opacity: 0; } }

/* §6's prefersReduced gate is read once in JS and stops the autoplay; the
   movement itself is CSS, so it needs the media query too. Cutting the
   animation leaves the base rules, which is a clean instant swap. */
@media (prefers-reduced-motion: reduce) {
  .hero-slides > .slide.is-entering,
  .hero-slides > .slide.is-leaving { animation: none; }
}

/* Consistent height across all slides so the card never jumps up/down.
   min-height lives on the inner row, not on .slide — Splide writes its own
   width/height onto .splide__slide and re-applies them on every resize, so a
   dimension set there is either overwritten or fights the measurement.

   The row is stretched, NOT centred. Slides carry different numbers of
   heading lines (the seeded set has 4, 3 and 3) and descriptions that wrap to
   different depths, so centring each slide's content inside the same fixed
   height put the heading and the buttons at a different y on every slide —
   the content visibly jumped as the carousel advanced. Stretching the columns
   lets the text column pin its heading to the top and its buttons to the
   bottom, which is what actually lines up between slides. */
.slide > .row {
  width: 100%;
  margin: 0;
  min-height: clamp(340px, 30vw, 452px);
  align-items: stretch;
}

.slide-text {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  color: #fff;
}


.slide-heading {
  font-family: var(--font-head);
  font-weight: 700;
  line-height: 1.08;
  letter-spacing: .5px;
  font-size: clamp(1.9rem, 4.4vw, 3.35rem);
  color: #fff;
  margin: 0 0 20px;
  /* Height of the tallest heading in the set, so every slide's description
     and buttons start at the same y. --slide-lines is counted in PHP and
     printed on .hero-card; see the note in blocks/hero/render.php. A floor,
     not a cap — a line that wraps on a narrow screen still grows the box. */
  min-height: calc(var(--slide-lines, 1) * 1.08em);
}
.slide-heading span { display: block; }

.slide-desc {
  font-size: clamp(.98rem, 1.4vw, 1.12rem);
  color: rgba(255, 255, 255, .9);
  max-width: 46ch;
  margin: 0 0 30px;
}

/* margin-top:auto is what puts the buttons on the same baseline on every
   slide — the description absorbs the difference above them. */
.slide-cta { display: flex; flex-wrap: wrap; gap: 14px; margin-top: auto; }

/* Centred inside the stretched column, so the image does not grow with it. */
.slide-media {
  position: relative;
  display: flex;
  flex-direction: column;
  justify-content: center;
}
.slide-img {
  width: 100%;
  /* uniform frame for every slide so sizing + shadow stay consistent */
  aspect-ratio: 3 / 2;
  height: auto;
  object-fit: cover;

  /* A shadow that never reaches sideways past the image, because it cannot.
   *
   * The image has exactly zero horizontal slack inside the carousel. Splide
   * sizes .splide__slide to 100% of the track, .row cancels its own gutter
   * with a -.5 gutter margin, and .slide-media adds .5 gutter back as
   * padding — so the image's outer edge lands flush on the track edge, and
   * .splide__track is overflow:hidden. Any shadow reaching past that edge is
   * sliced off mid-fade, and a soft glow cut dead on both sides is a hard
   * rectangle. That is the "square" — it was never the colour or the blur.
   *
   * So both layers are tuned to stop at or inside the element horizontally.
   * Horizontal reach is `spread + blur/2`: -12 + 9 = -3px on the near layer,
   * -26 + 26 = 0px on the far one. Nothing crosses the clip, so there is no
   * cut edge to see. Vertically they are unconstrained — .slide-media centres
   * the image in a much taller column — so the shadow drops 30px below the
   * image and fades out properly, which is where it reads from anyway.
   *
   * Anything added here must keep spread + blur/2 <= 0 or the hard edges
   * come straight back.
   */
  box-shadow:
    0 10px 18px -12px rgba(15, 23, 42, .30),
    0 30px 52px -26px rgba(15, 23, 42, .45);
}

/* ---------- Slider arrows ---------- */
.slider-arrow {
  position: absolute;
  top: 50%;
  transform: translateY(-50%);
  z-index: 5;
  /* fixed, identical square for both arrows */
  width: 40px;
  height: 40px;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 0;
  border: 1px solid rgba(255, 255, 255, .35);
  background: rgba(255, 255, 255, .14);
  backdrop-filter: blur(6px);
  color: #fff;
  font-size: 1rem;
  line-height: 1;
  cursor: pointer;
  transition: background-color .3s var(--ease), transform .3s var(--ease), box-shadow .3s var(--ease);
}
/* icon perfectly centered inside the square */
.slider-arrow .bi { display: block; line-height: 1; }
.slider-arrow:hover {
  background: #fff;
  color: var(--c-primary);
  box-shadow: var(--shadow-glow);
}
/* Sit just inside the card edges so they never touch the side rails */
.slider-prev { left: 8px; }
.slider-next { right: 8px; }
.slider-prev:hover { transform: translateY(-50%) translateX(-2px); }
.slider-next:hover { transform: translateY(-50%) translateX(2px); }

/* Overlaid on the card rather than sitting under it.
   The hero draws its own dots now that it is not a Splide instance, but it
   keeps the same --dot-* tokens §6b's .splide__pagination__page reads, so the
   three carousels still share one definition of what a dot looks like. */
.hero-card {
  --dot-idle: rgba(255, 255, 255, .38);
  --dot-hover: rgba(255, 255, 255, .62);
  --dot-active: var(--grad-accent);
}
.hero-dots {
  position: absolute;
  left: 0; right: 0;
  bottom: clamp(14px, 2vw, 20px);
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 10px;
  margin: 0;
  z-index: 4;
}

/* Bars, not dots — §2 zeroes border-radius globally with !important, so a
   round dot was never actually round. Matches §6b exactly. */
.hero-dot {
  position: relative;
  width: 20px;
  height: 3px;
  padding: 0;
  border: 0;
  background: var(--dot-idle);
  cursor: pointer;
  transition: width .45s var(--ease), background-color .45s var(--ease);
}

/* A 3px-tall bar is not a touch target. Widen the hit area to 44px without
   touching the visible mark (WCAG 2.2 SC 2.5.8). */
.hero-dot::after {
  content: "";
  position: absolute;
  top: 50%;
  left: 50%;
  width: 100%;
  min-width: 30px;
  height: 44px;
  transform: translate(-50%, -50%);
}

.hero-dot.is-active { width: 42px; background: var(--dot-active); }
.hero-dot:hover:not(.is-active) { background: var(--dot-hover); }

/* ============================================================
   8. BUTTONS
   ============================================================ */
.btn-x {
  position: relative;
  overflow: hidden;
  display: inline-flex;
  align-items: center;
  gap: 8px;
  font-family: var(--font-head);
  font-weight: 600;
  letter-spacing: .08em;
  text-transform: uppercase;
  font-size: clamp(.8rem, 1.2vw, .9rem);
  padding: 14px 26px;
  border: 0;                       /* kill default <button> border */
  border-radius: 100px;
  cursor: pointer;
  isolation: isolate;
  transition: transform .3s var(--ease), box-shadow .3s var(--ease);
}
.btn-x:hover { transform: translateY(-3px); }
.btn-x:active { transform: translateY(-1px); }

.btn-x-dark {
  color: #fff;
  background: transparent;
  box-shadow: 0 8px 20px rgba(15, 23, 42, .3);
}
.btn-x-dark:hover { box-shadow: 0 14px 30px rgba(15, 23, 42, .45); color: #fff; }

/* animated gradient + glow primary button */
.btn-x-primary {
  color: #fff;
  background: var(--grad-accent);
  background-size: 200% 200%;
  animation: gradientMove 6s ease infinite;
  box-shadow: 0 8px 22px rgba(24, 182, 217, .4);
}
.btn-x-primary:hover {
  color: #fff;
  box-shadow: 0 14px 34px rgba(24, 182, 217, .6), var(--shadow-glow);
}

/* Ripple element injected by JS */
.ripple {
  position: absolute;
  border-radius: 50%;
  transform: scale(0);
  background: rgba(255, 255, 255, .5);
  animation: rippleFx .6s ease-out;
  pointer-events: none;
  z-index: -1;
}

/* ============================================================
   8b. TRUST BAR
   ============================================================ */
.trustbar {
  position: relative;
  overflow: hidden;
  isolation: isolate;
  color: #fff;
  padding: clamp(46px, 6vw, 82px) 0;
  background: linear-gradient(120deg, #12A6C6 0%, #0E8FB0 46%, #1257B8 100%);
}
/* AI dot-grid + soft corner glows */
.trustbar-bg {
  position: absolute;
  inset: 0;
  z-index: -1;
  background-image:
    radial-gradient(rgba(255, 255, 255, .10) 1px, transparent 1.6px),
    radial-gradient(52% 60% at 12% 0%, rgba(42, 188, 248, .30), transparent 60%),
    radial-gradient(52% 60% at 88% 100%, rgba(44, 164, 100, .24), transparent 60%),
    linear-gradient(120deg, #12A6C6 0%, #0E8FB0 46%, #1257B8 100%);
  background-size: 24px 24px, 100% 100%, 100% 100%, 100% 100%;
}
/* animated gradient accent line across the top */
.trustbar::before {
  content: "";
  position: absolute;
  top: 0; left: 0; right: 0;
  height: 3px;
  z-index: 1;
  background: var(--grad-accent);
  background-size: 200% 100%;
  animation: gradientMove 6s ease infinite;
}

.trust-row { position: relative; }
.trust-col { position: relative; }

/* vertical divider between pillars (desktop/tablet) */
@media (min-width: 768px) {
  .trust-col + .trust-col::before {
    content: "";
    position: absolute;
    left: 0; top: 14%; bottom: 14%;
    width: 1px;
    background: linear-gradient(180deg, transparent, rgba(255, 255, 255, .38), transparent);
  }
}
/* divider between stacked pillars (mobile) */
@media (max-width: 767.98px) {
  .trust-col + .trust-col { border-top: 1px solid rgba(255, 255, 255, .16); }
}

.trust-item {
  text-align: center;
  padding: clamp(20px, 2.4vw, 30px) clamp(16px, 3vw, 40px);
  transition: transform .4s var(--ease);
}
.trust-item:hover { transform: translateY(-6px); }

/* square, glowing icon tile (sharp corners) */
.trust-icon {
  display: inline-grid;
  place-items: center;
  width: 64px; height: 64px;
  margin-bottom: 18px;
  font-size: 1.7rem;
  color: #fff;
  background: rgba(255, 255, 255, .12);
  border: 1px solid rgba(255, 255, 255, .30);
  box-shadow: inset 0 0 18px rgba(255, 255, 255, .12), 0 10px 28px -8px rgba(6, 47, 62, .34);
  transition: border-color .4s var(--ease), box-shadow .4s var(--ease), transform .4s var(--ease);
}
.trust-item:hover .trust-icon {
  border-color: rgba(255, 255, 255, .6);
  box-shadow: inset 0 0 20px rgba(255, 255, 255, .2), 0 16px 34px rgba(24, 182, 217, .5);
  transform: scale(1.05);
}

.trust-title {
  font-family: var(--font-head);
  font-weight: 700;
  letter-spacing: 2px;
  text-transform: uppercase;
  font-size: clamp(1.55rem, 3vw, 2.15rem);
  line-height: 1.1;
  margin: 0 0 8px;
}
.trust-sub {
  font-family: var(--font-head);
  font-weight: 500;
  letter-spacing: 2.5px;
  text-transform: uppercase;
  font-size: clamp(.78rem, 1.3vw, .92rem);
  color: rgba(255, 255, 255, .9);
  margin: 0 0 14px;
}
.trust-hl {
  color: #7FE9FF;
  font-weight: 700;
  text-shadow: 0 0 14px rgba(127, 233, 255, .5);
}
.trust-desc {
  font-size: clamp(.92rem, 1.3vw, 1rem);
  line-height: 1.55;
  color: rgba(255, 255, 255, .82);
  max-width: 32ch;
  margin: 0 auto;
}

/* staggered scroll reveal for the three pillars */
.trust-col[data-reveal]:nth-child(2) { transition-delay: .12s; }
.trust-col[data-reveal]:nth-child(3) { transition-delay: .24s; }

/* ============================================================
   8b-i. REUSABLE SECTION HEADER (shared across sections)
   ============================================================ */
.section-head {
  max-width: 820px;
  margin: 0 auto;
  text-align: center;
  padding: clamp(48px, 7vw, 92px) 0 clamp(28px, 4vw, 50px);
}
/* These four sections set their own padding-top (58-108px), which stacked
   with the header's own 48-92px and left ~200px of dead space above the
   title. Drop the header's share and let the section's padding do the work.
   .about does the same thing locally — see section 9. Services and Portfolio
   are left alone: neither has a top padding of its own to stack with. */
.hiring .section-head,
section.blog .section-head,
.testimonials .section-head,
.faq .section-head { padding-top: clamp(6px, 1.5vw, 18px); }
.section-title {
  font-family: var(--font-head);
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: clamp(1px, .4vw, 4px);
  font-size: clamp(2.4rem, 6vw, 4.2rem);
  line-height: 1;
  color: var(--c-dark);
  margin: 0 0 14px;
}
.section-subtitle {
  font-family: var(--font-head);
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 2px;
  font-size: clamp(1rem, 2vw, 1.35rem);
  color: var(--c-primary);
  margin: 0 0 16px;
}
.section-lead {
  font-size: clamp(.95rem, 1.4vw, 1.08rem);
  line-height: 1.7;
  color: var(--c-secondary);
  margin: 0;
}

/* ============================================================
   8b-ii. SERVICES — dark premium glass cards
   ============================================================ */
.services {
  position: relative;
  isolation: isolate;
  overflow: hidden;
  /* clear whitespace so the dark band separates from the full-bleed
     portfolio grid above instead of blending into it */
  padding: 0 0 clamp(60px, 8vw, 110px);
  /* light band with a subtle teal (logo-colour) tint */
  background: linear-gradient(180deg, #FFFFFF 0%, #EEF7FB 100%);
}
/* thin gradient edge to make the top of the dark band deliberate */
.services::before {
  content: "";
  position: absolute;
  top: 0; left: 0; right: 0;
  height: 3px;
  z-index: 2;
  background: var(--grad-accent);
  background-size: 200% 100%;
  animation: gradientMove 7s ease infinite;
}
/* AI dot-grid + soft brand glows */
.services-bg {
  position: absolute;
  inset: 0;
  z-index: -1;
  background-image:
    radial-gradient(rgba(15, 23, 42, .05) 1px, transparent 1.4px),
    radial-gradient(46% 50% at 8% 4%, rgba(24, 182, 217, .12), transparent 60%),
    radial-gradient(44% 50% at 92% 96%, rgba(44, 164, 100, .10), transparent 60%);
  background-size: 26px 26px, 100% 100%, 100% 100%;
}

.service-card {
  --mx: 50%;
  --my: 0%;
  position: relative;
  height: 100%;
  display: flex;
  flex-direction: column;
  overflow: hidden;
  background: #fff;
  border: 1px solid rgba(15, 23, 42, .08);
  box-shadow: var(--shadow-sm);
  transition: transform .45s var(--ease), border-color .45s var(--ease), box-shadow .45s var(--ease);
}
/* animated top accent line */
.service-card::before {
  content: "";
  position: absolute;
  top: 0; left: 0; right: 0;
  height: 3px;
  z-index: 3;
  background: var(--grad-accent);
  background-size: 200% 100%;
  transform: scaleX(0);
  transform-origin: left;
  transition: transform .45s var(--ease);
}
/* cursor-following spotlight glow */
.service-card::after {
  content: "";
  position: absolute;
  inset: 0;
  z-index: 1;
  pointer-events: none;
  background: radial-gradient(240px circle at var(--mx) var(--my), rgba(24, 182, 217, .22), transparent 62%);
  opacity: 0;
  transition: opacity .35s var(--ease);
}
.service-card:hover {
  transform: translateY(-10px);
  border-color: rgba(24, 182, 217, .5);
  box-shadow: 0 26px 55px -22px rgba(15, 23, 42, .22), 0 16px 50px -26px rgba(24, 182, 217, .4);
}
.service-card:hover::before { transform: scaleX(1); }
.service-card:hover::after { opacity: 1; }

/* big watermark index */
.service-index {
  position: absolute;
  top: 6px;
  right: 14px;
  z-index: 3;
  font-family: var(--font-head);
  font-weight: 700;
  line-height: 1;
  font-size: clamp(2.6rem, 4vw, 3.6rem);
  color: rgba(255, 255, 255, .55);
  text-shadow: 0 2px 10px rgba(15, 23, 42, .25);
  transition: color .4s var(--ease);
}
.service-card:hover .service-index { color: #fff; }

.service-media {
  position: relative;
  z-index: 0;
  overflow: hidden;
  aspect-ratio: 16 / 10;
}
.service-media img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  transition: transform .6s var(--ease);
}
/* fade the image into the white card — no hard seam */
.service-media::after {
  content: "";
  position: absolute;
  inset: 0;
  background: linear-gradient(180deg, rgba(255, 255, 255, 0) 55%, rgba(255, 255, 255, .55) 84%, #fff 100%);
}
.service-card:hover .service-media img { transform: scale(1.08); }

.service-body {
  position: relative;
  z-index: 2;
  flex: 1 1 auto;
  display: flex;
  flex-direction: column;
  gap: 10px;
  padding: clamp(20px, 2vw, 28px);
  margin-top: -18px;               /* content lifts onto the faded image */
}
.service-title {
  font-family: var(--font-head);
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: .5px;
  line-height: 1.15;
  font-size: clamp(1.15rem, 1.6vw, 1.38rem);
  color: var(--c-dark);
  margin: 0;
}
.service-sub {
  font-family: var(--font-head);
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 1px;
  font-size: .76rem;
  line-height: 1.4;
  color: var(--c-primary);
  margin: 0;
}
.service-desc {
  font-size: .93rem;
  line-height: 1.65;
  color: var(--c-secondary);
  margin: 0;
}
/* staggered reveal */
.services-grid > [data-reveal]:nth-child(2) { transition-delay: .1s; }
.services-grid > [data-reveal]:nth-child(3) { transition-delay: .2s; }
.services-grid > [data-reveal]:nth-child(4) { transition-delay: .3s; }

/* ============================================================
   8c. PORTFOLIO GRID
   ============================================================ */
.portfolio { position: relative; }
/* Portfolio grid background */
.portfolio-grid {
  background-image:
    radial-gradient(rgba(255, 255, 255, .10) 1px, transparent 1.6px),
    radial-gradient(52% 60% at 12% 0%, rgba(42, 188, 248, .30), transparent 60%),
    radial-gradient(52% 60% at 88% 100%, rgba(44, 164, 100, .24), transparent 60%),
    linear-gradient(120deg, #12A6C6 0%, #0E8FB0 46%, #1257B8 100%);
  background-size: 24px 24px, 100% 100%, 100% 100%, 100% 100%;
}

.portfolio-grid {
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  gap: 0;
}
/* keep the full-bleed grid between the fixed side rails on desktop */
@media (min-width: 992px) {
  .portfolio { padding: 0 var(--rail-w); }
}
@media (max-width: 1199.98px) { .portfolio-grid { grid-template-columns: repeat(3, 1fr); } }
@media (max-width: 767.98px)  { .portfolio-grid { grid-template-columns: repeat(2, 1fr); } }
@media (max-width: 420px)     { .portfolio-grid { grid-template-columns: 1fr; } }

.pf-item {
  --pf-pad: clamp(12px, 1.15vw, 22px);

  position: relative;
  display: block;
  width: 100%;
  margin: 0;
  padding: var(--pf-pad);
  border: 0;
  cursor: pointer;
  overflow: hidden;
  background: transparent;
  /* Fixed tile frame — intrinsic image ratios must not stretch the grid. */
  aspect-ratio: 4 / 3;
  /* Hairline so equal cells read when mockups share the navy canvas. */
  box-shadow: inset 0 0 0 1px rgba(255, 255, 255, .08);
}
/* Absolute fill so tall/wide mockups crop instead of growing the button. */
.pf-item img {
  position: absolute;
  inset: var(--pf-pad);
  width: calc(100% - (var(--pf-pad) * 2));
  height: calc(100% - (var(--pf-pad) * 2));
  object-fit: cover;
  object-position: center;
  transition: transform .6s var(--ease), filter .5s var(--ease);
}
.pf-item:hover img,
.pf-item:focus-visible img { transform: scale(1.055); filter: saturate(1.12); }

.pf-overlay {
  position: absolute;
  inset: 0;
  display: flex;
  flex-direction: column;
  justify-content: flex-end;
  padding: clamp(16px, 2vw, 26px);
  color: #fff;
  text-align: left;
  background: linear-gradient(180deg, rgba(15, 23, 42, 0) 28%, rgba(13, 108, 132, .5) 62%, rgba(11, 92, 112, .92) 100%);
  opacity: 0;
  transition: opacity .45s var(--ease);
}
.pf-item:hover .pf-overlay,
.pf-item:focus-visible .pf-overlay { opacity: 1; }

.pf-cat {
  font-family: var(--font-head);
  text-transform: uppercase;
  letter-spacing: 2px;
  font-size: .72rem;
  color: #7FE9FF;
  transform: translateY(12px);
  transition: transform .45s var(--ease) .04s;
}
.pf-name {
  font-family: var(--font-head);
  font-weight: 700;
  font-size: clamp(1.05rem, 1.6vw, 1.4rem);
  line-height: 1.15;
  margin-top: 4px;
  transform: translateY(12px);
  transition: transform .45s var(--ease) .1s;
}
.pf-item:hover .pf-cat,
.pf-item:hover .pf-name,
.pf-item:focus-visible .pf-cat,
.pf-item:focus-visible .pf-name { transform: none; }

.pf-plus {
  position: absolute;
  top: clamp(14px, 2vw, 22px);
  right: clamp(14px, 2vw, 22px);
  width: 42px;
  height: 42px;
  display: grid;
  place-items: center;
  font-size: 1.15rem;
  color: #fff;
  background: rgba(255, 255, 255, .16);
  border: 1px solid rgba(255, 255, 255, .45);
  transform: scale(.5);
  opacity: 0;
  transition: transform .45s var(--ease), opacity .45s var(--ease), background-color .3s var(--ease);
}
.pf-item:hover .pf-plus,
.pf-item:focus-visible .pf-plus { transform: none; opacity: 1; }

/* ============================================================
   8d. PORTFOLIO MODAL
   ============================================================ */
body.modal-open { overflow: hidden; }

.pf-modal {
  position: fixed;
  inset: 0;
  z-index: 2000;
  display: none;
}
.pf-modal.is-open { display: block; }

.pf-modal-overlay {
  position: absolute;
  inset: 0;
  background: rgba(9, 15, 28, .92);
  backdrop-filter: blur(5px);
  -webkit-backdrop-filter: blur(5px);
}

.pf-modal-shell {
  position: relative;
  z-index: 1;
  height: 100%;
  display: flex;
  flex-direction: column;
  animation: pfIn .4s var(--ease);
}
@keyframes pfIn { from { opacity: 0; transform: scale(.98); } to { opacity: 1; transform: none; } }

/* top bar */
.pf-modal-bar {
  position: relative;
  z-index: 3;
  flex: 0 0 auto;
  display: flex;
  align-items: center;
  gap: 16px;
  padding: 13px clamp(16px, 3vw, 40px);
  background: linear-gradient(90deg, #18B6D9, #0FA0C2);
}
.pf-modal-nav { display: flex; gap: 10px; }
.pf-modal-arrow,
.pf-modal-close {
  width: 46px;
  height: 46px;
  display: grid;
  place-items: center;
  padding: 0;
  color: #fff;
  font-size: 1.1rem;
  line-height: 1;
  background: rgba(255, 255, 255, .12);
  border: 1px solid rgba(255, 255, 255, .5);
  cursor: pointer;
  transition: background-color .3s var(--ease), color .3s var(--ease), transform .3s var(--ease);
}
.pf-modal-arrow:hover,
.pf-modal-close:hover { background: #fff; color: var(--c-primary); }
.pf-modal-arrow:active { transform: scale(.94); }
.pf-modal-close { margin-left: auto; }
.pf-modal-count {
  font-family: var(--font-head);
  letter-spacing: 3px;
  font-size: .95rem;
  color: rgba(255, 255, 255, .95);
}

/* scrollable content */
.pf-modal-content {
  position: relative;
  z-index: 1;
  flex: 1 1 auto;
  overflow-y: auto;
  -webkit-overflow-scrolling: touch;
  display: flex;
  /* NOT align-items:center — a centred flex child overflows in BOTH
     directions once it's taller than the scroll box, making the top of
     the content unreachable. flex-start + auto margins on the child
     still centres it when it fits, but stays scrollable when it doesn't. */
  align-items: flex-start;
  padding: clamp(24px, 5vw, 56px) 0;
}
.pf-modal-content > .container { margin-top: auto; margin-bottom: auto; }
.pf-modal-img {
  width: 100%;
  aspect-ratio: 4 / 3;
  object-fit: cover;
  box-shadow:
    0 4px 12px -4px rgba(0, 0, 0, .45),
    0 34px 64px -24px rgba(0, 0, 0, .55);
}
.pf-modal-text { color: #fff; }
.pf-modal-cat {
  font-family: var(--font-head);
  text-transform: uppercase;
  letter-spacing: 3px;
  font-size: clamp(.78rem, 1.3vw, .9rem);
  color: #7FE9FF;
  margin: 0 0 10px;
}
.pf-modal-title {
  font-family: var(--font-head);
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 1px;
  line-height: 1.05;
  font-size: clamp(2rem, 5vw, 3.4rem);
  margin: 0 0 18px;
}
.pf-modal-short {
  font-size: clamp(1rem, 1.5vw, 1.15rem);
  color: rgba(255, 255, 255, .88);
  max-width: 52ch;
  margin: 0 0 20px;
}
.pf-modal-more { margin: 0 0 24px; }
.pf-modal-long {
  font-size: clamp(.95rem, 1.4vw, 1.05rem);
  color: rgba(255, 255, 255, .78);
  max-width: 54ch;
  margin: 0 0 20px;
}
.pf-meta {
  display: flex;
  flex-wrap: wrap;
  gap: 12px 40px;
}
.pf-meta li { display: flex; flex-direction: column; }
.pf-meta span {
  font-family: var(--font-head);
  text-transform: uppercase;
  letter-spacing: 2px;
  font-size: .7rem;
  color: #7FE9FF;
}
.pf-meta strong { font-weight: 600; color: #fff; }

/* smooth reveal of expanded content */
.pf-modal-more[hidden] { display: none; }

@media (prefers-reduced-motion: reduce) {
  .pf-modal-shell { animation: none; }
}

/* ============================================================
   8d-i. ABOUT — vision/mission/approach + director
   ============================================================ */
.about { padding: clamp(26px, 3.5vw, 46px) 0 clamp(56px, 8vw, 104px); background: var(--c-bg); }
/* tighter header top so there's less empty space above "About Us" */
.about .section-head { padding-top: clamp(6px, 1.5vw, 18px); }

/* ---- Director profile: image on top, solid info panel below ---- */
.director-card {
  position: relative;
  overflow: hidden;
  color: #fff;
  /* Was --card-bg, which is the translucent glass surface built for the hero;
     flat on an opaque panel it rendered a step darker than .director-note
     immediately below it. Same token as the note now — see §1. */
  background: var(--grad-panel);
  border: 1px solid rgba(15, 23, 42, .10);
  box-shadow: var(--shadow-md);
}
.director-card::before {
  content: "";
  position: absolute;
  top: 0; left: 0; right: 0;
  height: 4px;
  z-index: 3;
  background: var(--grad-accent);
  background-size: 200% 100%;
  animation: gradientMove 7s ease infinite;
}
.director-media {
  position: relative;
  overflow: hidden;
  font-size: 0;                          /* drop inline-image gap */
}
.director-media img {
  display: block;
  width: 100%;
  height: auto;                          /* full, uncropped photo */
  transition: transform .6s var(--ease);
}
.director-card:hover .director-media img { transform: scale(1.03); }
.director-body {
  padding: clamp(22px, 2.6vw, 32px);     /* details sit right under the photo */
  /* Particle host ([data-particles] in blocks/about/render.php). The canvas is
     absolutely positioned, so the panel has to establish a containing block,
     and the text has to be lifted above it — the canvas lands as the panel's
     first element child. White on teal, and dialled back like the hero card's
     so it stays texture rather than competing with the name. */
  position: relative;
  --net-color: 255, 255, 255;
}
.director-body > .section-net { opacity: .5; }
.director-name,
.director-role,
.director-social { position: relative; z-index: 1; }
.director-name {
  font-family: var(--font-head);
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 1px;
  font-size: clamp(1.5rem, 2.6vw, 2.1rem);
  margin: 0 0 2px;
}
.director-role {
  font-family: var(--font-head);
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 3px;
  font-size: .8rem;
  color: #fff;
  margin: 0 0 14px;
}
/* The row fills the panel and the icons share it equally, so the four tiles
   line up with the edges of the card rather than trailing off to the left.
   flex-basis 0 with grow 1 divides the space evenly no matter how many
   networks have a URL — three icons stay flush, they do not leave a gap. */
.director-social { display: flex; gap: 10px; width: 100%; }
.director-social a {
  display: grid;
  place-items: center;
  flex: 1 1 0;
  min-width: 0;                          /* let the tiles shrink below 42px */
  height: 42px;
  color: #fff;
  border: 1px solid rgba(255, 255, 255, .35);
  transition: color .3s var(--ease), background-color .3s var(--ease), border-color .3s var(--ease), transform .3s var(--ease);
}
.director-social a:hover {
  color: var(--c-primary);
  background: #fff;
  border-color: transparent;
  transform: translateY(-3px);
}

/* ---- Vision / Mission / Approach: connected timeline ---- */
.value-list {
  --vgap: 30px;
  position: relative;
  width: 100%;
  display: flex;
  flex-direction: column;
  justify-content: center;
  gap: var(--vgap);
}
.value-row {
  position: relative;
  display: flex;
  gap: 20px;
  align-items: flex-start;
}
/* connector runs from each icon down to the next — none after the last */
.value-row:not(:last-child)::before {
  content: "";
  position: absolute;
  left: 28px;                         /* centre of the 58px icon tile */
  top: 58px;                          /* start just below the icon */
  bottom: calc(-1 * var(--vgap));     /* reach the next icon */
  width: 2px;
  z-index: 0;
  background: linear-gradient(180deg, var(--c-primary), rgba(24, 182, 217, .25));
}
.value-icon {
  position: relative;
  z-index: 1;
  flex: 0 0 auto;
  display: grid;
  place-items: center;
  width: 58px;
  height: 58px;
  font-size: 1.45rem;
  color: var(--c-primary);
  background: #fff;
  border: 1px solid rgba(24, 182, 217, .35);
  box-shadow: 0 6px 16px rgba(15, 23, 42, .06);
  transition: transform .4s var(--ease), background-color .4s var(--ease), color .4s var(--ease), box-shadow .4s var(--ease), border-color .4s var(--ease);
}
.value-row:hover .value-icon {
  color: #fff;
  background: var(--grad-primary);
  border-color: transparent;
  box-shadow: 0 14px 28px -8px rgba(24, 182, 217, .6);
  transform: scale(1.06);
}
.value-title {
  font-family: var(--font-head);
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 1px;
  font-size: clamp(1.15rem, 1.6vw, 1.4rem);
  color: var(--c-dark);
  margin: 6px 0 8px;
}
.value-title span { color: var(--c-primary); }
.value-desc {
  font-size: .95rem;
  line-height: 1.7;
  color: var(--c-secondary);
  margin: 0;
}

/* ---- About key stats (fills the space beside the director card) ---- */
.about-stats {
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  gap: clamp(14px, 2vw, 26px);
  margin-top: clamp(26px, 3vw, 40px);
  padding-top: clamp(22px, 2.5vw, 32px);
  border-top: 1px solid rgba(15, 23, 42, .1);
}
@media (min-width: 992px) { .about-stats { margin-left: 78px; } } /* align with the timeline text */
/* mobile 2-up handled in section 10b */

.stat-num {
  font-family: var(--font-head);
  font-weight: 700;
  line-height: 1;
  font-size: clamp(1.9rem, 3.4vw, 2.9rem);
  background: var(--grad-primary);
  -webkit-background-clip: text;
  background-clip: text;
  -webkit-text-fill-color: transparent;
  color: var(--c-primary);
}
.stat-label {
  margin-top: 8px;
  font-family: var(--font-head);
  font-weight: 500;
  text-transform: uppercase;
  letter-spacing: 1px;
  font-size: .76rem;
  line-height: 1.35;
  color: var(--c-secondary);
}

/* ---- Director's note — full-width modern quote band ---- */
.director-note {
  position: relative;
  overflow: hidden;
  isolation: isolate;
  margin: clamp(34px, 4.5vw, 60px) 0 0;
  padding: clamp(28px, 3.6vw, 50px);
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: clamp(24px, 4vw, 56px);
  color: #fff;
  background: var(--grad-panel);         /* shared with .director-card — §1 */
  box-shadow: var(--shadow-md);
  transition: transform .4s var(--ease), box-shadow .4s var(--ease);
}
.director-note:hover {
  transform: translateY(-4px);
  box-shadow: 0 26px 55px -22px rgba(15, 23, 42, .5), 0 14px 40px -24px rgba(24, 182, 217, .5);
}
/* gradient accent bar */
.director-note::before {
  content: "";
  position: absolute;
  top: 0; left: 0; right: 0;
  height: 4px;
  z-index: 2;
  background: var(--grad-accent);
  background-size: 200% 100%;
  animation: gradientMove 7s ease infinite;
}
/* soft brand glow in a corner */
.director-note::after {
  content: "";
  position: absolute;
  right: -60px; bottom: -80px;
  width: 300px; height: 300px;
  z-index: -1;
  background: radial-gradient(circle, rgba(255, 255, 255, .18), transparent 70%);
  pointer-events: none;
}
/* big watermark quote mark */
.director-note-mark {
  position: absolute;
  top: 6px;
  right: clamp(20px, 4vw, 56px);
  font-size: clamp(6rem, 12vw, 9rem);
  line-height: 1;
  color: rgba(255, 255, 255, .2);
  pointer-events: none;
  z-index: 0;
}

/* quote */
.director-note-content {
  flex: 1 1 360px;
  position: relative;
  z-index: 1;
}
.director-note-label {
  display: inline-block;
  font-family: var(--font-head);
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 3px;
  font-size: .74rem;
  color: #fff;
  margin-bottom: 10px;
}
.director-note-text {
  font-size: clamp(1.02rem, 1.5vw, 1.2rem);
  line-height: 1.7;
  font-style: italic;
  color: rgba(255, 255, 255, .92);
  margin: 0;
  max-width: 90ch;
}

/* ============================================================
   8d-ii. HIRING MODEL — engagement cards
   ============================================================ */
.hiring {
  padding: clamp(58px, 8vw, 108px) 0;
  background: linear-gradient(180deg, #FFFFFF 0%, #EEF2F6 100%);
}
.hire-card {
  position: relative;
  height: 100%;
  overflow: hidden;
  padding: clamp(26px, 3vw, 42px);
  background: #fff;
  border: 1px solid rgba(15, 23, 42, .08);
  box-shadow: var(--shadow-sm);
  transition: transform .42s var(--ease), box-shadow .42s var(--ease), border-color .42s var(--ease);
}
/* left accent bar grows in on hover */
.hire-card::before {
  content: "";
  position: absolute;
  left: 0; top: 0; bottom: 0;
  width: 4px;
  background: var(--grad-accent);
  transform: scaleY(0);
  transform-origin: top;
  transition: transform .45s var(--ease);
}
.hire-card:hover {
  transform: translateY(-8px);
  border-color: rgba(24, 182, 217, .4);
  box-shadow: var(--shadow-md);
}
.hire-card:hover::before { transform: scaleY(1); }

.hire-icon {
  position: relative;
  z-index: 1;
  display: grid;
  place-items: center;
  width: 64px;
  height: 64px;
  margin-bottom: 22px;
  font-size: 1.75rem;
  color: #fff;
  background: var(--grad-primary);
  box-shadow: 0 12px 26px -8px rgba(24, 182, 217, .6);
  transition: transform .42s var(--ease);
}
.hire-card:hover .hire-icon { transform: translateY(-4px) rotate(-4deg) scale(1.05); }

.hire-index {
  position: absolute;
  top: 16px;
  right: 22px;
  font-family: var(--font-head);
  font-weight: 700;
  line-height: 1;
  font-size: clamp(2.2rem, 3vw, 3rem);
  color: rgba(15, 23, 42, .06);
  transition: color .42s var(--ease);
}
.hire-card:hover .hire-index { color: rgba(24, 182, 217, .16); }

.hire-title {
  font-family: var(--font-head);
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: .5px;
  line-height: 1.15;
  font-size: clamp(1.2rem, 1.7vw, 1.45rem);
  color: var(--c-dark);
  margin: 0 0 6px;
}
.hire-sub {
  font-family: var(--font-head);
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 1px;
  font-size: .76rem;
  color: var(--c-primary);
  margin: 0 0 14px;
}
.hire-desc {
  font-size: .95rem;
  line-height: 1.7;
  color: var(--c-secondary);
  margin: 0;
}

/* staggered reveal */
.hiring-grid > [data-reveal]:nth-child(2) { transition-delay: .1s; }
.hiring-grid > [data-reveal]:nth-child(3) { transition-delay: .2s; }

/* ============================================================
   8d-iii. OUR EXPERTISE — statement band + capability chips
   ============================================================ */
.expertise {
  position: relative;
  isolation: isolate;
  overflow: hidden;
  text-align: center;
  color: #fff;
  padding: clamp(62px, 9vw, 122px) 0;
  background: linear-gradient(150deg, #35B4C8 0%, #18A6C4 55%, #0F8FB2 100%);
}
.expertise-bg {
  position: absolute;
  inset: 0;
  z-index: -1;
  background-image:
    radial-gradient(rgba(255, 255, 255, .10) 1px, transparent 1.5px),
    radial-gradient(48% 60% at 12% 0%, rgba(42, 188, 248, .28), transparent 60%),
    radial-gradient(48% 60% at 88% 100%, rgba(44, 164, 100, .20), transparent 60%);
  background-size: 24px 24px, 100% 100%, 100% 100%;
}
.expertise-inner { max-width: 940px; margin: 0 auto; }

.expertise-title {
  font-family: var(--font-head);
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: clamp(1px, .4vw, 3px);
  line-height: 1;
  font-size: clamp(2.2rem, 6.5vw, 4.6rem);
  margin: 0 0 22px;
  text-shadow: 0 6px 30px rgba(0, 0, 0, .16);
}
.expertise-tagline {
  font-family: var(--font-head);
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 1.4px;
  line-height: 1.6;
  font-size: clamp(.8rem, 1.5vw, 1rem);
  color: rgba(255, 255, 255, .95);
  max-width: 62ch;
  margin: 0 auto 18px;
}
.expertise-desc {
  font-size: clamp(.95rem, 1.5vw, 1.12rem);
  line-height: 1.75;
  color: rgba(255, 255, 255, .9);
  max-width: 70ch;
  margin: 0 auto clamp(26px, 3vw, 38px);
}

/* glass capability chips */
.expertise-tags {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: 12px;
}
.expertise-tags li {
  font-family: var(--font-head);
  font-weight: 500;
  text-transform: uppercase;
  letter-spacing: 1px;
  font-size: .76rem;
  padding: 10px 18px;
  color: #fff;
  background: rgba(255, 255, 255, .12);
  border: 1px solid rgba(255, 255, 255, .28);
  backdrop-filter: blur(6px);
  -webkit-backdrop-filter: blur(6px);
  transition: background-color .3s var(--ease), border-color .3s var(--ease), transform .3s var(--ease);
}
.expertise-tags li:hover {
  background: rgba(255, 255, 255, .22);
  border-color: rgba(255, 255, 255, .55);
  transform: translateY(-3px);
}

/* ============================================================
   8e. STATEMENT — rotating headline band
   ============================================================ */
.statement {
  position: relative;
  isolation: isolate;
  overflow: hidden;
  text-align: center;
  color: #fff;
  padding: clamp(64px, 10vw, 132px) 0;
  background: linear-gradient(120deg, #17A9CC 0%, #0E8FB0 52%, #1257B8 100%);
}
.statement-bg {
  position: absolute;
  inset: 0;
  z-index: -1;
  background-image:
    radial-gradient(rgba(255, 255, 255, .10) 1px, transparent 1.5px),
    radial-gradient(50% 60% at 15% 0%, rgba(42, 188, 248, .28), transparent 60%),
    radial-gradient(50% 60% at 85% 100%, rgba(44, 164, 100, .22), transparent 60%);
  background-size: 24px 24px, 100% 100%, 100% 100%;
}

.statement-rotator {
  font-family: var(--font-head);
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: clamp(1px, .5vw, 4px);
  font-size: clamp(2rem, 6vw, 4.4rem);
  line-height: 1.06;
  min-height: 1.15em;                 /* prevent layout shift between phrases */
  margin: 0 0 clamp(14px, 2vw, 24px);
  overflow: hidden;                   /* mask the sliding word */
}
.statement-word {
  display: inline-block;
  text-shadow: 0 6px 30px rgba(0, 0, 0, .18);
}
/* each letter animates independently for a staggered wave */
.statement-word .ltr {
  display: inline-block;
  opacity: 0;
  white-space: pre;                   /* preserve spaces between words */
  will-change: opacity, transform;
}
@keyframes ltrIn {
  from { opacity: 0; transform: translateX(26px); }
  to   { opacity: 1; transform: translateX(0); }
}
@keyframes ltrOut {
  from { opacity: 1; transform: translateX(0); }
  to   { opacity: 0; transform: translateX(-26px); }
}

.statement-desc {
  max-width: 840px;
  margin: 0 auto;
  font-weight: 500;
  font-size: clamp(.95rem, 1.6vw, 1.16rem);
  letter-spacing: .3px;
  color: rgba(255, 255, 255, .92);
}

@media (prefers-reduced-motion: reduce) {
  .statement-word .ltr { opacity: 1 !important; transform: none !important; animation: none !important; }
}

/* ============================================================
   8e-ii. TESTIMONIALS
   ============================================================ */
.testimonials {
  position: relative;
  overflow: hidden;
  padding: clamp(58px, 8vw, 106px) 0;
  /* decorative dot-grid + soft brand glows so it isn't flat white */
  background:
    radial-gradient(rgba(15, 23, 42, .05) 1px, transparent 1.4px) 0 0 / 24px 24px,
    radial-gradient(46% 55% at 88% 2%, rgba(24, 182, 217, .16), transparent 60%),
    radial-gradient(46% 55% at 10% 100%, rgba(44, 164, 100, .12), transparent 60%),
    linear-gradient(180deg, #FFFFFF 0%, #E9F3F7 100%);
}

.tst-slider-wrap { position: relative; }
@media (min-width: 768px) { .tst-slider-wrap { padding: 0 60px; } }
/* the slide stays transparent (holds Tiny Slider's gutter) */
.tst-card { position: relative; height: 100%; }

/* the visible card; side slides sit smaller & faded, centre slide is full size */
.tst-inner {
  position: relative;
  height: 100%;
  min-height: clamp(300px, 32vw, 340px);
  overflow: hidden;
  display: flex;
  flex-direction: column;
  padding: clamp(26px, 3vw, 38px);
  background: #fff;
  border: 1px solid rgba(15, 23, 42, .08);
  box-shadow: var(--shadow-sm);
  transform: scale(.92);
  opacity: .8;
  transition: transform .7s var(--ease), opacity .7s var(--ease), box-shadow .5s var(--ease), border-color .5s var(--ease);
}
/* animated top accent */
.tst-inner::before {
  content: "";
  position: absolute;
  top: 0; left: 0; right: 0;
  height: 4px;
  background: var(--grad-accent);
  transform: scaleX(0);
  transform-origin: left;
  transition: transform .5s var(--ease);
}
/* centre slide — bigger, fully opaque, accent shown.
 *
 * The shadow has to finish inside the track's padding or it is sliced mid-fade
 * and the soft glow becomes a hard dark rectangle — the same failure the hero
 * image's shadow is tuned around in §7, and what "the centre card has a dark
 * square behind it" actually was.
 *
 * Two things eat the budget, and the second is easy to miss: transform scales
 * the *rendered* shadow too, so every reach below is multiplied by 1.08 on top
 * of the box growing.
 *
 *   vertical growth   = 340px (max min-height) x .08 / 2  = 13.6px each side
 *   bottom reach      = offsetY + blur/2 - spread
 *     layer 1         = 24 + 25 - 20                      = 29px
 *     layer 2         = 12 + 19 - 22                      =  9px
 *   worst case below  = 13.6 + (29 x 1.08)                = 45.9px  <  48px pad
 *   horizontal reach  = blur/2 - spread = 5px, scaled 5.4 + 15.2 growth
 *                     = 20.6px  <  24px gap between cards
 *
 * Anything retuned here has to keep both of those under the padding and the
 * gap, or the hard edges come straight back. */
.tst-card.is-center .tst-inner {
  transform: scale(1.08);
  opacity: 1;
  border-color: rgba(24, 182, 217, .45);
  box-shadow:
    0 24px 50px -20px rgba(15, 23, 42, .22),
    0 12px 38px -22px rgba(24, 182, 217, .45);
}
.tst-card.is-center .tst-inner::before { transform: scaleX(1); }

/* Vertical room so the scaled centre card and its shadow are not clipped by
   .splide__track's overflow:hidden. Padding is inside the track's box, so it
   is room the clip does not take — see the sums above for the 48px. */
.blog-slider-wrap .splide__track { padding: 16px 0 32px; }
.tst-slider-wrap .splide__track { padding: 24px 0 48px; }

.tst-mark {
  position: absolute;
  top: 14px;
  right: 20px;
  font-size: 4.5rem;
  line-height: 1;
  color: rgba(24, 182, 217, .12);
  pointer-events: none;
}
.tst-stars {
  display: flex;
  gap: 3px;
  color: #F5A623;
  font-size: .95rem;
  margin-bottom: 16px;
}
.tst-quote {
  position: relative;
  z-index: 1;
  flex: 1 1 auto;
  font-size: 1rem;
  line-height: 1.7;
  color: var(--c-secondary);
  margin: 0 0 22px;
}
.tst-author {
  display: flex;
  align-items: center;
  gap: 14px;
  padding-top: 20px;
  border-top: 1px solid rgba(15, 23, 42, .08);
}
.tst-avatar {
  width: 54px;
  height: 54px;
  object-fit: cover;
  border: 2px solid rgba(24, 182, 217, .4);
}
.tst-who { display: flex; flex-direction: column; line-height: 1.35; }
.tst-who strong {
  font-family: var(--font-head);
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: .5px;
  font-size: .98rem;
  color: var(--c-dark);
}
.tst-who small {
  font-size: .78rem;
  color: var(--c-primary);
}

/* arrows */
.tst-arrow {
  position: absolute;
  top: 50%;
  transform: translateY(-50%);
  z-index: 5;
  width: 46px;
  height: 46px;
  display: grid;
  place-items: center;
  padding: 0;
  font-size: 1.2rem;
  line-height: 1;
  color: #fff;
  background: var(--c-primary);
  border: 0;
  box-shadow: 0 10px 24px -10px rgba(24, 182, 217, .8);
  cursor: pointer;
  transition: background-color .3s var(--ease), transform .3s var(--ease);
}
.tst-arrow:hover { background: transparent; }
.tst-prev { left: 4px; }
.tst-next { right: 4px; }
.tst-prev:hover { transform: translateY(-50%) translateX(-2px); }
.tst-next:hover { transform: translateY(-50%) translateX(2px); }

/* Dots. 4px sat them almost against the cards; this matches the blog
   carousel's 26px so the two read as the same component. */
.tst-slider-wrap .splide__pagination {
  display: flex;
  justify-content: center;
  gap: 9px;
  margin-top: 26px;
}

/* prevent flash before Tiny Slider inits */
.tst-slider:not(.is-initialized) .splide__list { display: flex; gap: 24px; overflow: hidden; }
.tst-slider:not(.is-initialized) .tst-card { flex: 0 0 calc((100% - 48px) / 3); }

/* ============================================================
   8h. CONTACT
   ============================================================ */
.contact {
  position: relative;
  isolation: isolate;
  overflow: hidden;
  padding: clamp(58px, 8vw, 110px) 0;
  background: linear-gradient(180deg, #FFFFFF 0%, #E9F1F6 100%);
}
.contact-bg {
  position: absolute;
  inset: 0;
  z-index: -1;
  background-image:
    radial-gradient(rgba(15, 23, 42, .05) 1px, transparent 1.4px),
    radial-gradient(46% 55% at 6% 10%, rgba(24, 182, 217, .14), transparent 60%),
    radial-gradient(46% 55% at 96% 92%, rgba(44, 164, 100, .1), transparent 60%);
  background-size: 24px 24px, 100% 100%, 100% 100%;
}

/* ---- Left column ---- */
.contact-badge {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  margin-bottom: 20px;
  padding: 9px 16px;
  font-family: var(--font-head);
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 1px;
  font-size: .76rem;
  color: var(--c-primary);
  background: rgba(24, 182, 217, .1);
  border: 1px solid rgba(24, 182, 217, .28);
}
/* The heading is measured against this column, not the viewport — see the
   font-size note on .contact-title. inline-size containment is safe here:
   nothing in this column is absolutely positioned against an outer ancestor. */
.contact-pitch { container-type: inline-size; }

.contact-title {
  font-family: var(--font-head);
  font-weight: 700;
  text-transform: uppercase;
  /* .section-title's floor. It used to be .5px, which read as a different
     typeface next to every other section heading on the page. The ramp
     itself only kicks in below lg — see the mobile block in §10b — because
     at lg+ this heading is measured against a 5/12 column and the cqi sum
     below has no room for 4px of tracking. */
  letter-spacing: 1px;
  line-height: 1.08;
  /* vw fallback for engines without container query units. Below lg this
     heading is full-width; at lg+ it drops into a 5/12 column, so a single
     vw-based clamp can't serve both — it grows faster than the column does
     and "Let's Build It Together." spills onto a third line. Two ranges,
     split where the column proportion changes. */
  font-size: clamp(1.7rem, 5.2vw, 2.9rem);
  /* If it ever does wrap, wrap evenly instead of orphaning "Together." */
  text-wrap: balance;
  overflow-wrap: break-word;
  color: var(--c-dark);
  margin: 0 0 18px;
}
@media (min-width: 992px) {
  .contact-title { font-size: clamp(1.9rem, 3vw, 2.6rem); }
}

/* The real fix. 1cqi == 1% of .contact-pitch's width, so the type tracks the
   column at every breakpoint instead of the viewport, and the two-line design
   holds everywhere. "LET'S BUILD IT TOGETHER." is ~12em wide in Oswald 700
   plus 1px x 24 glyphs of tracking, so it needs ~8.0cqi to sit on one line;
   7.2cqi leaves ~10% headroom for the glyph-width estimate being off.
   Must come after the media query above — equal specificity, so source order
   decides which one wins for browsers that support both. */
@supports (font-size: 1cqi) {
  .contact-title { font-size: clamp(1.55rem, 7.2cqi, 2.6rem); }
}

.contact-title .grad {
  display: inline;
  background: var(--grad-primary);
  -webkit-background-clip: text;
  background-clip: text;
  -webkit-text-fill-color: transparent;
  color: var(--c-primary);
  /* underline via text-decoration so it tracks the text on every line —
     an absolutely-positioned bar spans the whole box and detaches from
     the copy as soon as the heading wraps */
  text-decoration: underline;
  text-decoration-color: rgba(24, 182, 217, .55);
  text-decoration-thickness: 4px;
  text-underline-offset: 8px;
  text-decoration-skip-ink: none;
}
.contact-lead {
  font-size: clamp(1rem, 1.5vw, 1.14rem);
  line-height: 1.7;
  color: var(--c-secondary);
  max-width: 42ch;
  margin: 0 0 clamp(26px, 3vw, 38px);
}
.contact-lead .hl { color: var(--c-primary); font-weight: 700; }

.contact-features { display: flex; flex-direction: column; gap: clamp(16px, 2vw, 24px); }
.contact-features li { display: flex; align-items: center; gap: 16px; }
.cf-icon {
  flex: 0 0 auto;
  display: grid;
  place-items: center;
  width: 52px;
  height: 52px;
  font-size: 1.25rem;
  color: var(--c-primary);
  background: rgba(24, 182, 217, .1);
  border: 1px solid rgba(24, 182, 217, .25);
  transition: transform .35s var(--ease), background-color .35s var(--ease), color .35s var(--ease);
}
.contact-features li:hover .cf-icon { color: #fff; background: var(--grad-primary); transform: translateY(-3px); }
.cf-text { display: flex; flex-direction: column; }
.cf-text strong {
  font-family: var(--font-head);
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: .5px;
  font-size: 1rem;
  color: var(--c-dark);
}
.cf-text small { font-size: .88rem; color: var(--c-secondary); }

/* ---- animated contact vector ---- */
.contact-vector { margin-top: clamp(28px, 4vw, 48px); max-width: 300px; }
.contact-vector svg { width: 100%; height: auto; overflow: visible; }
.cv-mail { animation: cvBob 5s ease-in-out infinite; transform-origin: center; }
.cv-plane { animation: cvFloat 4s ease-in-out infinite; }
.cv-trail { animation: cvDash 1.3s linear infinite; }
.cv-dot { animation: cvBlink 3s ease-in-out infinite; }
.cv-dot2 { animation-delay: .6s; }
.cv-dot3 { animation-delay: 1.2s; }

@keyframes cvBob   { 50% { transform: translateY(-6px); } }
@keyframes cvFloat { 50% { transform: translate(6px, -10px); } }
@keyframes cvDash  { to { stroke-dashoffset: -14; } }
@keyframes cvBlink { 0%, 100% { opacity: .35; transform: translateY(0); } 50% { opacity: 1; transform: translateY(-5px); } }

@media (prefers-reduced-motion: reduce) {
  .cv-mail, .cv-plane, .cv-trail, .cv-dot { animation: none; }
}

/* ---- Form card ---- */
.contact-card {
  position: relative;
  background: #fff;
  padding: clamp(24px, 3.2vw, 44px);
  border: 1px solid rgba(15, 23, 42, .06);
  box-shadow: var(--shadow-md);
}
.contact-card::before {
  content: "";
  position: absolute;
  top: 0; left: 0; right: 0;
  height: 4px;
  background: var(--grad-accent);
  background-size: 200% 100%;
  animation: gradientMove 7s ease infinite;
}
.contact-card-head { display: flex; align-items: center; gap: 16px; margin-bottom: clamp(20px, 2.5vw, 30px); }
.cc-icon {
  display: grid;
  place-items: center;
  width: 54px;
  height: 54px;
  font-size: 1.35rem;
  color: #fff;
  background: var(--grad-primary);
  box-shadow: 0 12px 26px -8px rgba(24, 182, 217, .6);
}
.cc-title {
  position: relative;
  font-family: var(--font-head);
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: .5px;
  font-size: clamp(1.3rem, 2vw, 1.7rem);
  color: var(--c-dark);
  margin: 0;
  padding-bottom: 8px;
}
.cc-title::after {
  content: "";
  position: absolute;
  left: 0; bottom: 0;
  width: 48px; height: 3px;
  background: var(--grad-primary);
}

.form-group { margin-bottom: clamp(16px, 2vw, 22px); }
/* two fields side by side */
.form-row {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: clamp(14px, 1.8vw, 22px);
}
@media (max-width: 575.98px) { .form-row { grid-template-columns: 1fr; } }
.form-label {
  display: flex;
  align-items: center;
  gap: 8px;
  font-family: var(--font-head);
  font-weight: 500;
  text-transform: uppercase;
  letter-spacing: .5px;
  font-size: .82rem;
  color: var(--c-dark);
  margin-bottom: 8px;
}
.form-label .bi { color: var(--c-primary); font-size: .95rem; }
.form-label .req { color: #E23B4E; }

.c-input {
  width: 100%;
  padding: 14px 16px;
  font-family: var(--font-body);
  font-size: .98rem;
  color: var(--c-dark);
  background: #F8FAFC;
  border: 1px solid rgba(15, 23, 42, .12);
  transition: border-color .3s var(--ease), box-shadow .3s var(--ease), background-color .3s var(--ease);
}
.c-input::placeholder { color: #94A3B8; }
.c-input:focus {
  outline: none;
  background: #fff;
  border-color: var(--c-primary);
  box-shadow: 0 0 0 3px rgba(24, 182, 217, .16);
}
.c-textarea { resize: vertical; min-height: 120px; display: block; }
/* counter sits BELOW the field: at bottom-right it collided with the
   native resize grabber, which is unavoidable on touch */
.c-textarea-wrap { position: relative; padding-bottom: 22px; }
.c-count {
  position: absolute;
  right: 0;
  bottom: 0;
  font-size: .78rem;
  color: #94A3B8;
  pointer-events: none;
}

/* privacy checkbox */
.c-check {
  display: flex;
  align-items: flex-start;
  gap: 10px;
  margin: 4px 0 8px;
  cursor: pointer;
  font-size: .88rem;
  line-height: 1.5;
  color: var(--c-secondary);
}
.c-check input {
  flex: 0 0 auto;
  width: 18px;
  height: 18px;
  margin-top: 2px;
  accent-color: var(--c-primary);
  cursor: pointer;
}

.c-submit {
  width: 100%;
  justify-content: center;
  margin-top: 6px;
  padding: 16px 26px;
  font-size: .95rem;
}
.c-trust {
  display: flex;
  flex-wrap: wrap;
  gap: 10px 22px;
  margin: 18px 0 0;
  padding-top: 18px;
  border-top: 1px solid rgba(15, 23, 42, .08);
}
.c-trust li {
  display: flex;
  align-items: center;
  gap: 7px;
  font-size: .85rem;
  color: var(--c-secondary);
}
.c-trust .bi { color: var(--c-primary); }

/* ============================================================
   8g. FAQ — accordion
   ============================================================ */
.faq {
  position: relative;
  padding: clamp(58px, 8vw, 104px) 0;
  /* teal band (same as Blog / Expertise) + dot-grid + soft glows */
  background:
    radial-gradient(rgba(255, 255, 255, .08) 1px, transparent 1.5px) 0 0 / 24px 24px,
    radial-gradient(48% 60% at 12% 0%, rgba(42, 188, 248, .2), transparent 60%),
    radial-gradient(48% 60% at 88% 100%, rgba(44, 164, 100, .14), transparent 60%),
    linear-gradient(150deg, #35B4C8 0%, #18A6C4 55%, #0F8FB2 100%);
}
/* header + copy go light on the teal band */
.faq .section-title { color: #fff; }
.faq .section-subtitle { color: rgba(255, 255, 255, .92); }
.faq .section-lead { color: rgba(255, 255, 255, .9); }

.faq-list { max-width: 860px; margin: 0 auto; }

.faq-item {
  position: relative;
  overflow: hidden;
  margin-bottom: 14px;
  /* glass panel so items sit on the teal background */
  background: rgba(255, 255, 255, .1);
  border: 1px solid rgba(255, 255, 255, .2);
  backdrop-filter: blur(8px);
  -webkit-backdrop-filter: blur(8px);
  box-shadow: 0 14px 36px -18px rgba(6, 47, 62, .38);
  transition: box-shadow .35s var(--ease), border-color .35s var(--ease), background-color .35s var(--ease);
}
/* left accent bar grows in when open */
/* active item becomes a crisp solid-white card that pops on the teal */
.faq-item.is-open {
  border-color: #fff;
  background: #fff;
  box-shadow:
    0 2px 6px -2px rgba(6, 47, 62, .16),
    0 24px 50px -20px rgba(6, 47, 62, .40);
}

.faq-q {
  width: 100%;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 18px;
  padding: clamp(18px, 2vw, 24px) clamp(20px, 2.4vw, 28px);
  background: transparent;
  border: 0;
  cursor: pointer;
  text-align: left;
  font-family: var(--font-head);
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: .5px;
  line-height: 1.35;
  font-size: clamp(1rem, 1.4vw, 1.16rem);
  color: #fff;
  transition: color .3s var(--ease);
}
.faq-q:hover { color: rgba(255, 255, 255, .82); }
/* open card is white → dark question text */
.faq-item.is-open .faq-q { color: var(--c-dark); }
.faq-item.is-open .faq-q:hover { color: var(--c-primary); }

.faq-icon {
  position: relative;
  flex: 0 0 auto;
  width: 34px;
  height: 34px;
  border: 1px solid rgba(255, 255, 255, .5);
  transition: background-color .35s var(--ease), border-color .35s var(--ease);
}
/* simple + / − drawn with two bars — no rotation */
.faq-icon::before,
.faq-icon::after {
  content: "";
  position: absolute;
  top: 50%;
  left: 50%;
  width: 13px;
  height: 2px;
  background: #fff;
  transform: translate(-50%, -50%);
  transition: opacity .3s var(--ease), background-color .35s var(--ease);
}
.faq-icon::after { transform: translate(-50%, -50%) rotate(90deg); }  /* vertical bar */
.faq-item.is-open .faq-icon {
  background: var(--grad-primary);
  border-color: transparent;
}
.faq-item.is-open .faq-icon::before,
.faq-item.is-open .faq-icon::after { background: #fff; }
.faq-item.is-open .faq-icon::after { opacity: 0; }   /* hide vertical → minus */

/* smooth height via grid-rows (no JS measuring) */
.faq-a {
  display: grid;
  grid-template-rows: 0fr;
  transition: grid-template-rows .4s var(--ease);
}
.faq-item.is-open .faq-a { grid-template-rows: 1fr; }
.faq-a-inner { overflow: hidden; }
.faq-a p {
  margin: 0;
  padding: 0 clamp(20px, 2.4vw, 28px) clamp(20px, 2vw, 26px);
  font-size: clamp(.95rem, 1.3vw, 1.02rem);
  line-height: 1.7;
  color: var(--c-secondary);   /* answer shows only on the open white card */
}

/* ============================================================
   8f. BLOG — Tiny Slider carousel

   Every selector for this section is qualified with `section`
   on purpose, and must stay that way. WordPress puts `blog` in
   body_class() on the posts index, so a bare `.blog` here also
   matched <body> on /blog/ — which meant `overflow: hidden`
   landed on the document element and froze the whole page:
   no scrolling, no way to reach the footer.
   ============================================================ */
section.blog {
  position: relative;
  isolation: isolate;
  overflow: hidden;
  padding: clamp(58px, 8vw, 104px) 0;
  background: linear-gradient(150deg, #35B4C8 0%, #18A6C4 55%, #0F8FB2 100%);
}
.blog-bg {
  position: absolute;
  inset: 0;
  z-index: -1;
  background-image:
    radial-gradient(rgba(255, 255, 255, .10) 1px, transparent 1.5px),
    radial-gradient(48% 60% at 12% 0%, rgba(42, 188, 248, .28), transparent 60%),
    radial-gradient(48% 60% at 88% 100%, rgba(44, 164, 100, .2), transparent 60%);
  background-size: 24px 24px, 100% 100%, 100% 100%;
}
section.blog .section-title { color: #fff; }
section.blog .section-lead { color: rgba(255, 255, 255, .9); }

.blog-slider-wrap { position: relative; }
@media (min-width: 768px) {
  .blog-slider-wrap { padding: 0 60px; }   /* room for the side arrows */
}

/* the slide itself stays transparent — Tiny Slider's gutter padding
   sits here, so the teal shows through the gap between cards */
.blog-card { position: relative; height: 100%; }

/* the visible white card lives on the inner wrapper */
.blog-inner {
  position: relative;
  height: 100%;
  display: flex;
  flex-direction: column;
  overflow: hidden;
  background: #fff;
  border: 1px solid rgba(15, 23, 42, .08);
  box-shadow: var(--shadow-sm);
  transition: transform .42s var(--ease), box-shadow .42s var(--ease), border-color .42s var(--ease);
}
/* animated gradient top accent (same as other cards) */
.blog-inner::before {
  content: "";
  position: absolute;
  top: 0; left: 0; right: 0;
  height: 3px;
  z-index: 3;
  background: var(--grad-accent);
  transform: scaleX(0);
  transform-origin: left;
  transition: transform .45s var(--ease);
}
.blog-card:hover .blog-inner {
  transform: translateY(-8px);
  border-color: rgba(24, 182, 217, .4);
  box-shadow: var(--shadow-md);
}
.blog-card:hover .blog-inner::before { transform: scaleX(1); }

.blog-media {
  position: relative;
  width: 100%;
  aspect-ratio: 16 / 10;
  overflow: hidden;
}
/* absolute fill guarantees the image always matches the card width/height */
.blog-img {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  transition: transform .6s var(--ease);
}
.blog-card:hover .blog-img { transform: scale(1.07); }
.blog-cat {
  position: absolute;
  top: 14px;
  left: 14px;
  z-index: 2;
  font-family: var(--font-head);
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 2px;
  font-size: .68rem;
  color: #fff;
  padding: 7px 13px;
  background: rgba(24, 182, 217, .92);
}

.blog-body {
  flex: 1 1 auto;
  display: flex;
  flex-direction: column;
  padding: clamp(20px, 2vw, 26px);
}
.blog-date {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  font-size: .76rem;
  letter-spacing: .5px;
  color: var(--c-primary);
  margin-bottom: 10px;
}
.blog-title {
  font-family: var(--font-head);
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: .5px;
  line-height: 1.25;
  font-size: clamp(1.05rem, 1.4vw, 1.24rem);
  margin: 0 0 10px;
  /* fix to 2 lines so every card matches */
  display: -webkit-box;
  -webkit-line-clamp: 2;
  -webkit-box-orient: vertical;
  overflow: hidden;
  min-height: 2.5em;
}
.blog-title a { color: var(--c-dark); transition: color .3s var(--ease); }
.blog-title a:hover { color: var(--c-primary); }

/* The whole card is the click target, not just "Read More".
 *
 * The title's own link is stretched across the card with a pseudo-element
 * rather than wrapping the card in an <a>. Wrapping would pull the image, the
 * date and the category badge inside the link, and a screen reader reads all
 * of that out as the link's name; this way the accessible name stays the
 * title alone and no new interactive element is introduced.
 *
 * It anchors to .blog-inner, the nearest positioned ancestor, so it covers the
 * visible card exactly. .blog-body and .blog-title must stay unpositioned or
 * the overlay collapses to them instead. Used by both the homepage carousel
 * and the archive grid, since they share template-parts/content/card-post.php.
 */
.blog-title a::after {
  content: "";
  position: absolute;
  inset: 0;
  z-index: 4;
}
.blog-excerpt {
  font-size: .92rem;
  line-height: 1.6;
  color: var(--c-secondary);
  margin: 0 0 18px;
  /* fix to 3 lines so every card matches */
  display: -webkit-box;
  -webkit-line-clamp: 3;
  -webkit-box-orient: vertical;
  overflow: hidden;
}
.blog-link {
  margin-top: auto;
  display: inline-flex;
  align-items: center;
  gap: 7px;
  /* Above the title's stretched overlay so it keeps its own cursor and hover
     state. It points at the same permalink, so whichever the reader hits is
     the same journey. */
  position: relative;
  z-index: 5;
  font-family: var(--font-head);
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 1.5px;
  font-size: .78rem;
  color: var(--c-primary);
  transition: gap .3s var(--ease);
}
.blog-link .bi { transition: transform .3s var(--ease); }
.blog-card:hover .blog-link { gap: 12px; }
.blog-card:hover .blog-link .bi { transform: translateX(4px); }

/* arrows */
.blog-arrow {
  position: absolute;
  top: 50%;
  transform: translateY(-50%);
  z-index: 5;
  width: 46px;
  height: 46px;
  display: grid;
  place-items: center;
  padding: 0;
  font-size: 1.2rem;
  line-height: 1;
  color: var(--c-primary);
  background: #fff;
  border: 0;
  box-shadow: var(--shadow-sm);
  cursor: pointer;
  transition: background-color .3s var(--ease), color .3s var(--ease), transform .3s var(--ease);
}
.blog-arrow:hover { background: transparent; color: #fff; }
.blog-prev { left: 4px; }
.blog-next { right: 4px; }
.blog-prev:hover { transform: translateY(-50%) translateX(-2px); }
.blog-next:hover { transform: translateY(-50%) translateX(2px); }

/* Tiny Slider dots */
.blog-slider-wrap .splide__pagination {
  display: flex;
  justify-content: center;
  gap: 9px;
  margin-top: 26px;
}
/* The carousel sits on the teal panel; the archive grid does not. */
section.blog:not(.is-archive) .blog-slider-wrap {
  --dot-idle: rgba(255, 255, 255, .42);
  --dot-hover: rgba(255, 255, 255, .68);
  --dot-active: #fff;
}

/* Show All */
.blog-cta { text-align: center; margin-top: clamp(30px, 4vw, 46px); }
.btn-x-white {
  color: var(--c-primary);
  background: #fff;
  box-shadow: 0 10px 26px -10px rgba(15, 23, 42, .5);
}
.btn-x-white:hover { color: #fff; background: transparent; }

/* prevent flash before Tiny Slider inits */
.blog-slider:not(.is-initialized) .splide__list { display: flex; gap: 24px; overflow: hidden; }
.blog-slider:not(.is-initialized) .blog-card { flex: 0 0 calc((100% - 48px) / 3); }

/* ============================================================
   8i. FOOTER
   ============================================================ */
.site-footer {
  position: relative;
  isolation: isolate;
  overflow: hidden;
  color: #fff;
  padding: clamp(54px, 7vw, 90px) 0 0;
  background: var(--grad-panel);         /* the shared brand panel — §1 */
}
/* animated gradient accent marking the top of the footer */
.site-footer::before {
  content: "";
  position: absolute;
  top: 0; left: 0; right: 0;
  height: 3px;
  z-index: 2;
  background: var(--grad-accent);
  background-size: 200% 100%;
  animation: gradientMove 7s ease infinite;
}
/* AI dot-grid + soft brand glows */
.footer-bg {
  position: absolute;
  inset: 0;
  z-index: -1;
  background-image:
    radial-gradient(rgba(255, 255, 255, .1) 1px, transparent 1.5px),
    radial-gradient(52% 60% at 85% 0%, rgba(42, 188, 248, .22), transparent 60%),
    radial-gradient(48% 60% at 4% 100%, rgba(44, 164, 100, .16), transparent 60%);
  background-size: 24px 24px, 100% 100%, 100% 100%;
}

.footer-logo {
  height: clamp(40px, 4vw, 48px);
  width: auto;
  margin-bottom: 18px;
  /* render the brand mark in white on the dark band */
  filter: brightness(0) invert(1);
}
.footer-about {
  font-size: .95rem;
  line-height: 1.7;
  color: rgba(255, 255, 255, .88);
  max-width: 34ch;
  margin: 0 0 22px;
}
.footer-badge {
  display: inline-block;
  transition: transform .35s var(--ease);
}
/*
 * drop-shadow, not box-shadow, because the badge is a circle on a
 * transparent canvas — box-shadow would trace the square element box
 * and put a hard rectangle behind a round badge.
 *
 * Two passes, tinted to the footer's own deep teal. Pure black over
 * that gradient reads as a dirty patch rather than depth, and one
 * pass at 22px gave a tight ring that stopped abruptly instead of
 * fading out. The second filter blurs the first one's output as well
 * as the badge, which is what softens the outer edge.
 */
.footer-badge img {
  width: 116px;
  height: auto;
  filter:
    drop-shadow(0 3px 6px rgba(4, 40, 54, .22))
    drop-shadow(0 12px 26px rgba(4, 40, 54, .26));
}
.footer-badge:hover { transform: translateY(-3px); }

/* column headings */
.footer-h {
  position: relative;
  font-family: var(--font-head);
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 2px;
  font-size: 1.02rem;
  color: #fff;
  margin: 0 0 22px;
  padding-bottom: 12px;
}
.footer-h::after {
  content: "";
  position: absolute;
  left: 0; bottom: 0;
  width: 44px; height: 3px;
  background: rgba(255, 255, 255, .6);
}

/* link lists */
.footer-links li { margin-bottom: 6px; }
.footer-contact li { margin-bottom: 12px; }
.footer-links a {
  display: inline-flex;
  align-items: center;
  gap: 10px;
  /* vertical padding lifts the tap target off text height (~24px) so
     stacked links can't be mistapped */
  padding: 7px 0;
  font-size: .95rem;
  color: rgba(255, 255, 255, .82);
  transition: color .3s var(--ease), transform .3s var(--ease);
}
.footer-links a .bi { font-size: .85rem; color: rgba(255, 255, 255, .9); transition: transform .3s var(--ease); }
.footer-links a:hover { color: #fff; transform: translateX(3px); }
.footer-links a:hover .bi { transform: translateX(3px); }

.footer-contact li {
  display: flex;
  align-items: center;
  gap: 12px;
  /* min-width:0 lets the flex item shrink; without it an unbreakable
     string (the email address) overflows the narrow mobile column and is
     silently clipped by body{overflow-x:hidden} */
  min-width: 0;
  font-size: .95rem;
  color: rgba(255, 255, 255, .78);
}
.footer-contact li > .bi { flex: 0 0 auto; font-size: 1rem; color: rgba(255, 255, 255, .9); }
.footer-contact a,
.footer-contact span {
  min-width: 0;
  overflow-wrap: anywhere;
}
.footer-contact a { color: rgba(255, 255, 255, .85); transition: color .3s var(--ease); }
.footer-contact a:hover { color: #fff; }

/* address */
.footer-address {
  display: flex;
  gap: 12px;
  font-size: .95rem;
  line-height: 1.7;
  color: rgba(255, 255, 255, .78);
  margin: 0 0 22px;
}
.footer-address .bi { flex: 0 0 auto; margin-top: 4px; color: rgba(255, 255, 255, .9); font-size: 1.05rem; }

/* social */
.footer-social { display: flex; gap: 12px; }
.footer-social a {
  display: grid;
  place-items: center;
  width: 42px;
  height: 42px;
  font-size: 1.05rem;
  color: #fff;
  border: 1px solid rgba(255, 255, 255, .4);
  transition: color .3s var(--ease), background-color .3s var(--ease), border-color .3s var(--ease), transform .3s var(--ease);
}
.footer-social a:hover {
  color: var(--c-primary);
  background: #fff;
  border-color: transparent;
  transform: translateY(-3px);
}

/* bottom bar */
.footer-bottom {
  margin-top: clamp(36px, 5vw, 56px);
  padding: 20px 0;
  border-top: 1px solid rgba(255, 255, 255, .18);
  text-align: center;
}
.footer-bottom p {
  margin: 0;
  font-size: .86rem;
  color: rgba(255, 255, 255, .8);
}

/* ============================================================
   8j. SECTION PARTICLES (injected by main.js)
   ============================================================ */
.has-particles { position: relative; }
.has-particles > .container { position: relative; z-index: 2; }
.section-net {
  position: absolute;
  inset: 0;
  z-index: 0;
  pointer-events: none;
}
/* node/line colour (r,g,b) tuned to each section's background */
.trustbar, .statement, .expertise, section.blog, .faq, .site-footer { --net-color: 255, 255, 255; }
.services, .hiring, .about, .testimonials, .contact { --net-color: 24, 182, 217; }

/*
 * The hero card is an opt-in host ([data-particles] in blocks/hero/render.php)
 * rather than a section, so it needs its own colour: white, on the teal glass
 * of --card-bg. The canvas lands as the card's first element child, which puts
 * it above the ::before sheen and below .hero-slider's z-index 2 — no other
 * z-index needed. Dialled back from the section default so it stays texture
 * behind the headline instead of competing with it.
 */
.hero-card {
  --net-color: 255, 255, 255;
}
.hero-card > .section-net { opacity: .55; }

/* ============================================================
   9. ANIMATIONS
   ============================================================ */
@keyframes meshShift {
  0%   { transform: translate3d(0, 0, 0) scale(1); }
  100% { transform: translate3d(2%, -2%, 0) scale(1.08); }
}
@keyframes floatUp {
  0%   { transform: translateY(0) translateX(0); opacity: 0; }
  10%  { opacity: .9; }
  90%  { opacity: .9; }
  100% { transform: translateY(-115vh) translateX(30px); opacity: 0; }
}
/* The sweep used to run `left: -60%` -> `left: 130%`, a travel of 1.9x the
   card's width. The element is .6x that width, and translateX percentages
   resolve against the element's own width, so 1.9 / .6 = 316.667% reproduces
   the same path exactly — starting from the static left: -60% and ending with
   its left edge at 130%. Same motion, no layout. */
@keyframes shimmer {
  0%   { transform: translateX(0); }
  55%, 100% { transform: translateX(316.667%); }
}
@keyframes gradientMove {
  0%, 100% { background-position: 0% 50%; }
  50%      { background-position: 100% 50%; }
}
@keyframes rippleFx {
  to { transform: scale(2.6); opacity: 0; }
}
@keyframes floatY {
  0%, 100% { transform: translateY(0); }
  50%      { transform: translateY(-14px); }
}
.float-anim { animation: floatY 6s ease-in-out infinite; will-change: transform; }

/* ---------- Scroll / load reveal (driven by JS) ---------- */
[data-reveal] {
  opacity: 0;
  transform: translateY(24px);
  transition: opacity .7s var(--ease), transform .7s var(--ease);
}
[data-reveal].is-visible { opacity: 1; transform: none; }

/* Line-by-line heading reveal */
[data-reveal-lines] span {
  display: block;
  opacity: 0;
  transform: translateY(110%);
  transition: opacity .6s var(--ease), transform .6s var(--ease);
}
[data-reveal-lines].is-visible span { opacity: 1; transform: none; }
[data-reveal-lines].is-visible span:nth-child(1) { transition-delay: .05s; }
[data-reveal-lines].is-visible span:nth-child(2) { transition-delay: .16s; }
[data-reveal-lines].is-visible span:nth-child(3) { transition-delay: .27s; }
[data-reveal-lines].is-visible span:nth-child(4) { transition-delay: .38s; }

/* ============================================================
   10. RESPONSIVE
   ============================================================ */

/* Give the card breathing room from fixed rails on large screens */
@media (min-width: 992px) {
  .hero .container { padding-left: var(--rail-w); padding-right: var(--rail-w); }
  /* Tablet-to-desktop text/media balance handled by Bootstrap grid (col-lg-6) */
}

/* Tablet: 60 / 40 split feel via slightly tighter media column */
@media (min-width: 768px) and (max-width: 991.98px) {
  .slide-text  { flex: 0 0 60%; max-width: 60%; }
  .slide-media { flex: 0 0 40%; max-width: 40%; }
}

/* Mobile: stacked, media first hidden priority kept simple */
@media (max-width: 767.98px) {
  .hamburger { display: flex; }
  .hero { padding-top: calc(var(--header-h) + 8px); }
  .slide-media { order: -1; margin-bottom: 4px; }
  .slide-cta .btn-x { flex: 1 1 auto; justify-content: center; }
}

/* Show hamburger below lg (offcanvas breakpoint) */
@media (max-width: 991.98px) {
  .hamburger { display: flex; }
}

/* Ultra-wide: cap card width and center */
@media (min-width: 1600px) {
  .hero .container { max-width: 1440px; }
  .hero-card { padding: 66px 80px; }
}

/* ============================================================
   10b. MOBILE REFINEMENTS
   Consolidated phone/tablet pass: safe areas, touch targets, overflow
   guards and content density. Deliberately last in the file so it wins
   over the component rules above without inflating specificity.
   ============================================================ */

/* ---- iOS notch + home indicator ----------------------------------
   index.html declares viewport-fit=cover, so without these the header,
   footer and modal run underneath the notch and home indicator (most
   visible in landscape). max() keeps the existing padding elsewhere. */
@supports (padding: max(0px)) {
  .container {
    padding-left: max(12px, env(safe-area-inset-left));
    padding-right: max(12px, env(safe-area-inset-right));
  }
  .mobile-nav .offcanvas-body { padding-bottom: max(26px, env(safe-area-inset-bottom)); }
  .footer-bottom { padding-bottom: max(20px, env(safe-area-inset-bottom)); }
  .pf-modal-bar { padding-top: max(13px, env(safe-area-inset-top)); }
}

/* ---- Header height ------------------------------------------------
   84px is 12.6% of a 667px-tall iPhone SE viewport. --header-h also
   drives .hero padding and section scroll-margin, so both follow. */
@media (max-width: 767.98px) {
  :root { --header-h: 64px; }
  .site-header.is-scrolled { min-height: 56px; }
}

/* ---- Form fields: 16px floor --------------------------------------
   Below 16px, iOS Safari zooms the viewport on focus and does not zoom
   back — the user is stranded mid-form in a horizontally-scrolling page. */
@media (max-width: 767.98px) {
  .c-input { font-size: 16px; }
  .c-textarea { min-height: 132px; }
}

/* ---- Slider controls ----------------------------------------------
   Below 768px the arrows go and the pagination carries navigation on
   its own. An arrow overlaid on a phone-width slide covers the thing
   the reader is looking at, and moved below the slider it competes
   with the section's own call to action for the same strip of screen.
   Swipe plus a visible position indicator is the lighter answer.

   The bars themselves already carry a 44px hit area from their ::after
   — see the pagination block above — so all that is needed here is a
   little more room between neighbouring targets.
------------------------------------------------------------------- */
@media (max-width: 767.98px) {
  .slider-arrow,
  .blog-arrow,
  .tst-arrow { display: none; }

  .splide__pagination { gap: 12px; }
  .splide__pagination__page { width: 24px; }
  .splide__pagination__page.is-active { width: 46px; }

  .tst-slider-wrap .splide__pagination { margin-top: 10px; }
}

/* ---- Testimonials -------------------------------------------------
   Tiny Slider runs items:1 on mobile, so the centre card already fills
   its slot; scaling it 1.08 pushed it past the overflow:hidden wrapper
   and sliced off both edges plus the gradient accent. */
@media (max-width: 767.98px) {
  .tst-card .tst-inner,
  .tst-card.is-center .tst-inner { transform: none; opacity: 1; }
}

/* ---- Hero: keep the CTA reachable ---------------------------------
   Stacked content already sets the card height; the 340px floor just
   pushed the buttons further below the fold. */
@media (max-width: 767.98px) {
  .slide > .row { min-height: 0; }
  .slide-heading { margin-bottom: 14px; }
  .slide-desc { max-width: none; margin-bottom: 22px; }
}
@media (max-width: 575.98px) {
  .slide-img { aspect-ratio: 16 / 10; }
}

/* ---- About: stat + timeline density --------------------------------
   On a phone the four counters were four bare numbers in a 2x2 grid under
   a hairline rule — left-aligned in their cells, with nothing to say where
   one stat ended and the next began, and a label small enough to read as
   a caption for whatever happened to sit above it.

   Each one becomes a tile instead: its own tinted panel, a gradient rule
   across the top, and the number centred over its label so the pair reads
   as a unit rather than drifting to the left edge of an invisible cell.
   The container rule goes — four framed tiles do not need a fifth line
   above them. -------------------------------------------------------- */
@media (max-width: 767.98px) {
  .about-stats {
    grid-template-columns: repeat(2, 1fr);
    gap: 12px;
    border-top: 0;
    padding-top: 4px;
  }

  .about-stats .stat {
    position: relative;
    display: flex;
    flex-direction: column;
    justify-content: center;
    min-height: 104px;
    padding: 16px 12px 15px;
    text-align: center;
    background:
      linear-gradient(180deg, rgba(24, 182, 217, .10), rgba(24, 182, 217, .015));
    border: 1px solid rgba(24, 182, 217, .18);
  }

  .about-stats .stat::before {
    content: "";
    position: absolute;
    top: 0; left: 0; right: 0;
    height: 3px;
    background: var(--grad-primary);
  }

  /* Bigger than the fluid clamp would give at this width — the number is
     the whole point of the tile and there is room for it now. */
  .stat-num { font-size: 2.3rem; }

  .stat-label {
    margin-top: 7px;
    font-size: .72rem;
    letter-spacing: .09em;
    color: var(--c-secondary);
  }
}
@media (max-width: 575.98px) {
  .value-row { gap: 14px; }
  .value-icon { width: 46px; height: 46px; font-size: 1.2rem; }
  /* keep the connector centred under the smaller tile */
  .value-row:not(:last-child)::before { left: 22px; top: 46px; }
}

/* ---- Services: trim the wall of text -------------------------------
   Four ~400-character descriptions stacked full-width. Same line-clamp
   treatment the blog cards already use. */
@media (max-width: 991.98px) {
  .service-desc {
    display: -webkit-box;
    -webkit-line-clamp: 6;
    -webkit-box-orient: vertical;
    overflow: hidden;
  }
}

/* ---- Contact: match the other section heads ------------------------
   Below lg the pitch column is not a column any more — it is full width,
   stacked above the form, and it is the only thing on screen. At that
   point it is doing the job every other section's .section-head does, so
   it is centred here, and the weight, transform, tracking and colour on
   the base rule are .section-title's.

   The size deliberately is not. Forcing .section-title's 38.4rem-floor
   clamp on a phone put "LET'S BUILD IT TOGETHER." — 24 characters — onto
   two lines of its own, so the heading became three lines and the
   highlight's underline ran straight through the line beneath it.

   24 characters of Oswald 700 only fit one phone line up to about 26px,
   which is what 7.2cqi already resolves to, so the cqi rule in §8h is
   left to size this and nothing here overrides it. On a tablet the same
   rule reaches its 2.6rem cap and the heading is close to section scale
   anyway; on a phone the copy is the constraint, not the CSS. A shorter
   highlight is the only thing that would buy display size here.
   -------------------------------------------------------------------- */
@media (max-width: 991.98px) {
  .contact-pitch { text-align: center; }

  /* Room for the underline. The base 1.08 is sized for the two unwrapped
     lines this heading has at lg+; once the underlined span itself wraps,
     ink 8px below the baseline lands on the cap-height of the next line.
     A looser leading plus a tighter, thinner rule clears it — and the
     phrase is all-caps, so there are no descenders for the rule to crowd. */
  .contact-title {
    line-height: 1.24;
  }

  .contact-title .grad {
    text-decoration-thickness: 3px;
    text-underline-offset: 5px;
  }

  /* max-width: 42ch centres as a block, not just as text */
  .contact-lead { margin-inline: auto; }

  /* The three trust points centre too, but not as they were built. They are
     icon-beside-text rows, and centring one of those leaves each row a
     different width, so the three icons land at three different x positions
     — the ragged edge reads as a mistake rather than as centred.

     Stacking the icon over its text fixes that: every item becomes its own
     centred column, the icons line up because the grid tracks do, and the
     row of three uses the width a tablet has instead of running down the
     left edge of it. */
  .contact-features {
    display: grid;
    grid-template-columns: repeat(3, 1fr);
    gap: clamp(18px, 3vw, 28px);
    text-align: center;
  }

  .contact-features li {
    flex-direction: column;
    align-items: center;
    gap: 10px;
  }

  .cf-text {
    align-items: center;
    gap: 3px;
  }
}

/* Three tracks need ~180px each to hold "Free Consultation" on one line.
   A phone has ~120px, so below sm the three columns become three rows —
   still centred, still icon over text. */
@media (max-width: 575.98px) {
  .contact-features {
    grid-template-columns: 1fr;
    gap: 20px;
  }
}

@media (max-width: 767.98px) {
  /* the badge is a single long string — let it break instead of forcing
     the row wider than the column on 320px devices. justify-content keeps
     the wrapped second line centred under the first, matching the head. */
  .contact-badge { flex-wrap: wrap; justify-content: center; line-height: 1.35; }
  /* The icon sits above the text here rather than beside it, so this gap is
     vertical — just enough to read as one unit with the label under it. */
  .contact-features li { gap: 8px; }
  .cf-icon { width: 46px; height: 46px; font-size: 1.1rem; }
  .cf-text strong { font-size: .95rem; }
  .c-trust { gap: 8px 18px; }
}
@media (max-width: 400px) {
  .contact-card { padding: 20px 18px; }
  /* icon + title no longer fit side by side at this width */
  .contact-card-head { gap: 12px; }
  .cc-icon { width: 46px; height: 46px; font-size: 1.15rem; }
}

/* ---- Remaining touch targets --------------------------------------- */
@media (max-width: 767.98px) {
  .director-social a,
  .footer-social a { width: 44px; height: 44px; }
  .pf-meta { gap: 12px 24px; }
}

/* ============================================================
   11. ACCESSIBILITY — reduced motion
   ============================================================ */
@media (prefers-reduced-motion: reduce) {
  *,
  *::before,
  *::after {
    animation-duration: .001ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: .001ms !important;
    scroll-behavior: auto !important;
  }
  [data-reveal],
  [data-reveal-lines] span { opacity: 1 !important; transform: none !important; }
  .hero-particles { display: none; }
}

/* ---- Footer density on phones -------------------------------------
   Stacked, the four footer columns run to well over a full screen —
   three section headings with 34px of margin+rule each, link rows
   padded for 44px touch targets, and a 56px gap before the copyright
   line. None of that is wrong on a desktop column; on a phone it is
   most of the page.

   Every value below only tightens whitespace. Tap targets are left
   alone: .footer-links a keeps 5px of padding on top of a ~24px line
   box, and the 44px social buttons are untouched (WCAG 2.2 SC 2.5.8).
------------------------------------------------------------------- */
@media (max-width: 767.98px) {
  .site-footer { padding-top: 38px; }

  /* Gap between the stacked columns, from .g-4's 1.5rem. */
  .site-footer .row { --bs-gutter-y: 1.25rem; }

  .footer-logo { height: 38px; margin-bottom: 12px; }
  .footer-about { font-size: .9rem; margin-bottom: 14px; }
  .footer-badge img { width: 96px; }

  .footer-h {
    font-size: .95rem;
    margin-bottom: 14px;
    padding-bottom: 9px;
  }
  .footer-h::after { width: 36px; height: 2px; }

  .footer-links li { margin-bottom: 0; }
  .footer-links a { padding: 5px 0; font-size: .92rem; }
  .footer-contact li { margin-bottom: 9px; }
  .footer-address { margin-bottom: 14px; }
  .footer-social { gap: 10px; }

  .footer-bottom { margin-top: 22px; padding: 14px 0; }
  .footer-bottom p { font-size: .8rem; }
}

/* ---- Footer: mobile shape ------------------------------------------
   One column each, the four blocks were four full-width rows of
   left-aligned content — and none of that content is wide. Six one-word
   links, four contact lines, a 34ch paragraph. What you saw was a thin
   rail of text down the left edge with most of every row empty beside it.

   Two changes fix the shape rather than the spacing. The brand block and
   the address block are the two that read as prose, so they centre, one
   at each end — the footer gets bookends instead of a ragged left edge.
   And the two link lists, the only blocks with enough items to fill a
   column, go side by side.

   The centred blocks stay centred from here up to lg, where the real
   four-column layout takes over. The side-by-side split only applies
   below sm, because .col-sm-6 already pairs those two above it.
   -------------------------------------------------------------------- */
@media (max-width: 767.98px) {
  /* Logo with the blurb under it on the left, Clutch badge pinned right.
     The badge is a fixed 96px of artwork and the blurb is a short
     paragraph, so side by side they fill a row that each of them left
     half empty when all three were stacked. Explicit placement rather
     than auto-flow: the badge has to span both rows of the left column,
     and either of the three can be absent if its Site Setting is. */
  .footer-brand {
    display: grid;
    grid-template-columns: 1fr auto;
    column-gap: 18px;
    text-align: left;
  }

  /* justify-self matters: a grid item defaults to stretch, and an <img>
     with width:auto stretches to fill its track. The logo has a fixed
     height and no width, so it would have been pulled to the full column
     width and squashed out of aspect. */
  .footer-logo {
    grid-area: 1 / 1;
    justify-self: start;
  }

  .footer-about { grid-area: 2 / 1; max-width: none; }

  .footer-badge {
    grid-column: 2;
    grid-row: 1 / 3;
    align-self: center;
  }

  .footer-col-address { text-align: center; }

  /* the rule under a footer heading is pinned left by default */
  .footer-col-address .footer-h::after {
    left: 50%;
    transform: translateX(-50%);
  }

  /* Flex row -> normal text flow, so the pin sits inline with the first
     line and the address centres as the paragraph it reads as. Centring
     the flex row instead would have left the icon hanging off the left
     of a centred block. */
  .footer-address {
    display: block;
    max-width: 34ch;
    margin-inline: auto;
  }

  .footer-address .bi {
    margin: 0 7px 0 0;
    vertical-align: -.15em;
  }

  .footer-social { justify-content: center; }
}

@media (max-width: 575.98px) {
  /* .row > * already carries the gutter as its own padding, so these two
     add up to 100% and land side by side with the gutter between them.
     42/58 rather than half and half because the lists are not the same
     width: "Portfolio" is nine characters, "info@birdmorning.com" is
     twenty and should not have to wrap to fit. */
  .footer-col-menu { width: 42%; }
  .footer-col-quick { width: 58%; }
}

/* ============================================================
   12. TOUCH LAYER
   Two things that only exist below the desktop breakpoint: the
   floating back-to-top button, and the gate that keeps the
   pointer hover effects off touch screens.

   Last in the file on purpose, like §10b — these have to win
   over the component rules above without inflating specificity.
   ============================================================ */

/* ---- Back to top --------------------------------------------------
   The desktop equivalent is .rail-top inside .rail-right, which is
   d-none d-lg-flex; this takes over below 992px so the two are never
   on screen at once. Shown by initHeader() once the page has scrolled
   past 70% of a viewport — the same threshold .rail-top uses.

   z-index sits above the page but below .offcanvas-backdrop (1040 in
   grid.css), so opening the mobile menu covers it rather than leaving
   a button floating on the scrim. -------------------------------- */
.to-top {
  position: fixed;
  right: 16px;
  /* Clear of the iOS home indicator. env() resolves to 0 elsewhere. */
  bottom: calc(16px + env(safe-area-inset-bottom, 0px));
  z-index: 1035;

  /* 48px, comfortably over the 44px WCAG 2.2 SC 2.5.8 minimum. */
  display: grid;
  place-items: center;
  width: 48px;
  height: 48px;
  padding: 0;

  color: #fff;
  background: var(--grad-primary);
  border: 0;
  box-shadow: 0 8px 20px -6px rgba(15, 23, 42, .35), 0 2px 6px rgba(15, 23, 42, .12);
  cursor: pointer;

  /* Hidden until scrolled. visibility, not display, so the fade has
     something to animate and the button stays out of the tab order
     while it is invisible. */
  opacity: 0;
  visibility: hidden;
  transform: translateY(12px);
  transition: opacity .3s var(--ease), visibility .3s var(--ease), transform .3s var(--ease);
}

.to-top.is-visible {
  opacity: 1;
  visibility: visible;
  transform: none;
}

/* .bi is 1em square (icons.css), so font-size is the size control. */
.to-top .bi {
  display: block;
  font-size: 1.15rem;
  vertical-align: 0;
}

/* Pressed state instead of a hover lift — see the gate below. */
.to-top:active { transform: scale(.92); }

/* The rail owns this job from lg up. */
@media (min-width: 992px) {
  .to-top { display: none; }
}

/* ---- Hover effects, pointer devices only --------------------------
   A touch screen has no hover: the browser fires :hover on tap and
   leaves it set until something else is tapped. Every lift, zoom and
   slide in the design therefore played on tap and then stuck — a blog
   card jumped 8px and stayed there while the tap was still resolving
   into a page load, which reads as a glitch rather than as feedback.

   So the movement is gated to (hover: hover) and touch gets the base
   state. Only the transforms are restated here; colour and shadow
   changes do not move anything and are harmless where they land.

   Two deliberate omissions:
   • :focus-visible pairs (.pf-item:focus-visible img and friends) are
     untouched — a keyboard is not a mouse, and those still have to
     work on a touch-capable laptop.
   • .mobile-nav-*, .rail-* and the three carousels' arrows are absent:
     the first is already gated in §4, and the rest are display:none
     below lg, so there is nothing to suppress.
------------------------------------------------------------------- */
@media (hover: none) {
  /* Header */
  .navbar-brand:hover .brand-logo { transform: none; }

  /* Buttons */
  .btn-x:hover { transform: none; }

  /* Trust bar */
  .trust-item:hover { transform: none; }
  .trust-item:hover .trust-icon { transform: none; }

  /* Portfolio — the tile's own overlay reveal is left alone; only the
     image zoom behind it is suppressed. */
  .pf-item:hover img { transform: none; filter: none; }

  /* Services */
  .service-card:hover { transform: none; }
  .service-card:hover::before { transform: scaleX(0); }
  .service-card:hover .service-media img { transform: none; }

  /* Hiring */
  .hire-card:hover { transform: none; }
  .hire-card:hover::before { transform: scaleY(0); }
  .hire-card:hover .hire-icon { transform: none; }

  /* Expertise */
  .expertise-tags li:hover { transform: none; }

  /* About */
  .director-card:hover .director-media img { transform: none; }
  .director-note:hover { transform: none; }
  .director-social a:hover { transform: none; }
  .value-row:hover .value-icon { transform: none; }

  /* Blog */
  .blog-card:hover .blog-inner { transform: none; }
  .blog-card:hover .blog-inner::before { transform: scaleX(0); }
  .blog-card:hover .blog-img { transform: none; }
  .blog-card:hover .blog-link .bi { transform: none; }

  /* Contact */
  .contact-features li:hover .cf-icon { transform: none; }

  /* Carousel arrows. Hidden below 768px, but a touch tablet is wider
     than that and still has no pointer — so the base translateY, which
     is what centres them, is restated rather than cleared. */
  .slider-prev:hover,
  .slider-next:hover,
  .blog-prev:hover,
  .blog-next:hover,
  .tst-prev:hover,
  .tst-next:hover { transform: translateY(-50%); }

  /* Footer */
  .footer-badge:hover { transform: none; }
  .footer-links a:hover { transform: none; }
  .footer-links a:hover .bi { transform: none; }
  .footer-social a:hover { transform: none; }

  /* iOS paints its own grey box over the top of all this. */
  .btn-x,
  .blog-card,
  .service-card,
  .hire-card,
  .pf-item,
  .to-top { -webkit-tap-highlight-color: transparent; }
}


/* ===== assets/css/cf7.css ===== */
/* ============================================================
   Bird Morning — Contact Form 7 normalisation
   ------------------------------------------------------------
   The form controls carry this design system's own classes
   (c-input, c-textarea, btn-x …), so §8h of style.css already
   styles them. What CF7 adds around those controls is what
   needs neutralising:

     .wpcf7-form-control-wrap   an inline <span> around every
                                field, which collapses the
                                two-column .form-row layout
     .wpcf7-not-valid-tip       inline validation text
     .wpcf7-response-output     a bordered status box
     .wpcf7-spinner             a loading dot

   Everything here is either layout neutralisation or restyling
   CF7's feedback to match the flat, borderless status treatment
   the static build used for the same messages.
   ============================================================ */

/* ------------------------------------------------------------
   Layout neutralisation
------------------------------------------------------------ */
.contact-form .wpcf7-form-control-wrap {
  display: block;
  width: 100%;
}

.contact-form p {
  margin: 0;
}

/* CF7 marks the whole form while submitting. */
.contact-form.submitting .c-submit {
  opacity: .7;
  pointer-events: none;
}

/* ------------------------------------------------------------
   The submit control
   CF7 renders <input type="submit">, which cannot contain the
   paper-plane icon the design puts inside the button — so it is
   drawn as a mask instead, inheriting currentColor like the
   sprite icons do.
------------------------------------------------------------ */
.contact-form .c-submit {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 10px;
  border: 0;
  cursor: pointer;
}

.contact-form .c-submit::before {
  content: "";
  width: 1em;
  height: 1em;
  flex-shrink: 0;
  background-color: currentColor;
  -webkit-mask: var(--icon-send) center / contain no-repeat;
  mask: var(--icon-send) center / contain no-repeat;
}

:root {
  --icon-send: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3E%3Cpath d='M15.854.146a.5.5 0 0 1 .11.54l-5.819 14.547a.75.75 0 0 1-1.329.124l-3.178-4.995L.643 7.184a.75.75 0 0 1 .124-1.33L15.314.037a.5.5 0 0 1 .54.11ZM6.636 10.07l2.761 4.338L14.13 2.576zm6.787-8.201L1.591 6.602l4.339 2.76z'/%3E%3C/svg%3E");
}

/* ------------------------------------------------------------
   Consent checkbox
   CF7 wraps acceptance in its own spans; flatten them so the
   .c-check layout in style.css applies.
------------------------------------------------------------ */
.contact-form .c-check .wpcf7-form-control-wrap,
.contact-form .c-check .wpcf7-acceptance,
.contact-form .c-check .wpcf7-list-item {
  display: inline;
  margin: 0;
}

.contact-form .c-check .wpcf7-list-item-label {
  display: inline;
}

/* ------------------------------------------------------------
   Validation and status messages
   Restyled to match the flat status treatment the static build
   used, instead of CF7's default bordered box.
------------------------------------------------------------ */
.contact-form .wpcf7-not-valid-tip {
  display: block;
  margin-top: 6px;
  font-size: .82rem;
  line-height: 1.4;
  color: #B4231F;
}

.contact-form .c-input.wpcf7-not-valid {
  border-color: #B4231F;
}

.contact-form .wpcf7-response-output {
  margin: 18px 0 0;
  padding: 12px 16px;
  border: 0;
  border-left: 3px solid var(--c-primary);
  background: rgba(24, 182, 217, .08);
  font-size: .92rem;
  line-height: 1.5;
  color: var(--c-dark);
}

.contact-form.sent .wpcf7-response-output {
  border-left-color: var(--c-accent-b);
  background: rgba(44, 164, 100, .10);
}

.contact-form.invalid .wpcf7-response-output,
.contact-form.failed .wpcf7-response-output,
.contact-form.spam .wpcf7-response-output {
  border-left-color: #B4231F;
  background: rgba(180, 35, 31, .08);
}

/* ------------------------------------------------------------
   Spinner
------------------------------------------------------------ */
.contact-form .wpcf7-spinner {
  margin: 0 0 0 12px;
  background-color: var(--c-primary);
}

@media (prefers-reduced-motion: reduce) {
  .contact-form .wpcf7-spinner,
  .contact-form .wpcf7-spinner::before {
    animation: none;
  }
}

/* ------------------------------------------------------------
   Honeypot for the character counter's sibling spacing —
   the counter is plain markup inside .c-textarea-wrap and must
   stay above CF7's validation tip.
------------------------------------------------------------ */
.contact-form .c-textarea-wrap {
  position: relative;
}


/* ===== assets/css/services.css ===== */
/* ============================================================
   Bird Morning — Services: per-card accent theme
   ------------------------------------------------------------
   The Services block gained one thing the original static build
   never had: an accent colour chosen per service.

   It also gained a "Learn more" dialog, which has since been
   removed along with the link that opened it — the card carries
   the whole description now. The dialog's styles lived here too
   and went with it.

   Built from the §1 tokens. No border-radius, per the project
   rule enforced globally in §2.
   ============================================================ */

/* ------------------------------------------------------------
   Per-service accent
   `is-theme-*` is written by bmca_service_theme(); every rule
   below reads --svc-accent rather than a literal colour, so a new
   theme is one block here and one entry in that helper.
------------------------------------------------------------ */
.service-card {
  --svc-accent: var(--c-primary);
  --svc-accent-rgb: 24, 182, 217;
}

.service-card.is-theme-blue {
  --svc-accent: #185BD0;
  --svc-accent-rgb: 24, 91, 208;
}

.service-card.is-theme-green {
  --svc-accent: #2CA464;
  --svc-accent-rgb: 44, 164, 100;
}

.service-card.is-theme-navy {
  --svc-accent: #0F172A;
  --svc-accent-rgb: 15, 23, 42;
}

/* The card's hover state, spotlight and accent line all follow the
   chosen theme instead of the fixed brand cyan they used to. */
.service-card::after {
  background: radial-gradient(240px circle at var(--mx) var(--my), rgba(var(--svc-accent-rgb), .22), transparent 62%);
}

.service-card:hover {
  border-color: rgba(var(--svc-accent-rgb), .5);
  box-shadow: 0 26px 55px -22px rgba(15, 23, 42, .22), 0 16px 50px -26px rgba(var(--svc-accent-rgb), .4);
}

.service-card .service-sub { color: var(--svc-accent); }


/* ===== assets/css/article.css ===== */
/* ============================================================
   Bird Morning — Blog detail page
   ------------------------------------------------------------
   The static build had no article template at all, so everything
   here is new: the two-column reading layout, the sticky table of
   contents, the key-takeaways box, the author card, the closing
   call to action and the previous/next cards.

   Base article typography (.entry-content) stays in wp.css. This
   file is only the furniture around it.

   Built from the §1 tokens. No border-radius — §2 enforces it.
   ============================================================ */

/* ------------------------------------------------------------
   Breadcrumbs
------------------------------------------------------------ */
.breadcrumbs {
  margin-bottom: clamp(18px, 2.5vw, 30px);
  font-size: .86rem;
  color: var(--c-secondary);
}

.breadcrumbs ol {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 6px 10px;
  /*
   * style.css §2 resets `ul` but never `ol`, so this list was still carrying
   * the user agent's padding-inline-start: 40px — the trail sat indented from
   * the container edge while the heading under it did not. It is the only
   * <ol> in the theme's own chrome, which is how that went unnoticed.
   */
  margin: 0;
  padding: 0;
  list-style: none;
}

.breadcrumbs li {
  display: inline-flex;
  align-items: center;
  gap: 10px;
}

.breadcrumbs li + li::before {
  content: "/";
  color: rgba(15, 23, 42, .35);
}

.breadcrumbs a {
  color: var(--c-secondary);
  transition: color .25s var(--ease);
}

.breadcrumbs a:hover { color: var(--c-primary-600); }
.breadcrumbs [aria-current="page"] { color: var(--c-dark); font-weight: 500; }

/* ------------------------------------------------------------
   Article header
------------------------------------------------------------ */
.entry-meta {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 10px 18px;
}

.entry-meta-item {
  display: inline-flex;
  align-items: center;
  gap: 7px;
}

.entry-cat {
  font-family: var(--font-head);
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 2px;
  font-size: .68rem;
  color: #fff;
  padding: 7px 13px;
  background: var(--c-primary);
  transition: background-color .25s var(--ease);
}

.entry-cat:hover { background: transparent; }

.entry-media figcaption { text-align: left; }

/* ------------------------------------------------------------
   Two-column reading layout
   The contents list is a sibling of the body rather than a child,
   so it can stick without the article's own overflow trapping it.
------------------------------------------------------------ */
.entry-layout {
  display: grid;
  gap: clamp(28px, 4vw, 56px);
  align-items: start;
}

@media (min-width: 992px) {
  .entry-layout {
    grid-template-columns: 260px minmax(0, 1fr);
  }

  .entry-layout.has-no-toc { grid-template-columns: minmax(0, 1fr); }
}

.entry-main { min-width: 0; }

/* ------------------------------------------------------------
   Table of contents
------------------------------------------------------------ */
@media (min-width: 992px) {
  .entry-aside {
    position: sticky;
    /* Clears the fixed header, which is what --header-h exists for. */
    top: calc(var(--header-h) + 20px);
    max-height: calc(100vh - var(--header-h) - 48px);
    overflow-y: auto;
  }
}

.toc {
  padding: clamp(18px, 2vw, 24px);
  background: #fff;
  border: 1px solid rgba(15, 23, 42, .10);
  border-top: 3px solid var(--c-primary);
  box-shadow: var(--shadow-sm);
}

.toc-title {
  font-family: var(--font-head);
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 1.5px;
  font-size: .82rem;
  color: var(--c-dark);
  margin: 0 0 14px;
}

/*
 * Same story as .breadcrumbs ol above: §2's reset skips `ol`, so this was
 * indented 40px inside an already narrow aside and rendering native decimal
 * markers. The markers fight the design — .toc-item a carries a 2px left rule
 * that turns cyan on the current section, which is the position indicator this
 * list is meant to have — so they go.
 *
 * The `counter-reset: toc` that used to sit here went with them: nothing ever
 * incremented or printed that counter.
 */
.toc-list {
  margin: 0;
  padding: 0;
  list-style: none;
  font-size: .92rem;
  line-height: 1.5;
}

.toc-item + .toc-item { margin-top: 10px; }
.toc-item.is-level-3 { padding-left: 16px; font-size: .87rem; }

.toc-item a {
  display: block;
  color: var(--c-secondary);
  border-left: 2px solid transparent;
  padding-left: 12px;
  transition: color .25s var(--ease), border-color .25s var(--ease);
}

.toc-item a:hover { color: var(--c-primary-600); }

.toc-item a.is-current {
  color: var(--c-dark);
  font-weight: 500;
  border-left-color: var(--c-primary);
}

/* ------------------------------------------------------------
   Key takeaways
------------------------------------------------------------ */
.takeaways {
  position: relative;
  margin: 0 0 clamp(30px, 4vw, 44px);
  padding: clamp(22px, 3vw, 32px);
  color: #fff;
  background: linear-gradient(150deg, #18B6D9 0%, #0FA0C2 55%, #185BD0 100%);
  box-shadow: var(--shadow-md);
}

.takeaways-title {
  display: flex;
  align-items: center;
  gap: 10px;
  font-family: var(--font-head);
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 1.5px;
  font-size: clamp(1rem, 1.8vw, 1.2rem);
  margin: 0 0 16px;
}

.takeaways-list {
  display: grid;
  gap: 12px;
  font-size: 1rem;
  line-height: 1.6;
}

.takeaways-list li {
  display: flex;
  align-items: flex-start;
  gap: 11px;
}

.takeaways-list .bi {
  flex: 0 0 auto;
  margin-top: .3em;
  color: #BDF3FF;
}

/* ------------------------------------------------------------
   Author card
   Same brand panel as .director-note in §8d-i — gradient, white
   type and the animated accent bar — so the two "who wrote this"
   cards on the site read as one component.
------------------------------------------------------------ */
.entry-author {
  position: relative;
  overflow: hidden;
  isolation: isolate;
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: clamp(18px, 3vw, 28px);
  margin-top: clamp(36px, 5vw, 56px);
  padding: clamp(24px, 3.2vw, 36px);
  color: #fff;
  background: var(--grad-panel);         /* the shared brand panel — style.css §1 */
  box-shadow: var(--shadow-md);
}

/* gradient accent bar, as on .director-note */
.entry-author::before {
  content: "";
  position: absolute;
  top: 0; left: 0; right: 0;
  height: 4px;
  z-index: 2;
  background: var(--grad-accent);
  background-size: 200% 100%;
  animation: gradientMove 7s ease infinite;
}

/* dot grid, matching the other brand panels */
.entry-author::after {
  content: "";
  position: absolute;
  inset: 0;
  z-index: -1;
  background-image: radial-gradient(rgba(255, 255, 255, .10) 1px, transparent 1.5px);
  background-size: 22px 22px;
}

.entry-author-avatar {
  position: relative;
  flex: 0 0 auto;
  width: 96px;
  height: 96px;
  overflow: hidden;
  border: 2px solid rgba(255, 255, 255, .5);
}

.entry-author-avatar img {
  width: 100%;
  height: 100%;
  object-fit: cover;
}

.entry-author-body {
  flex: 1 1 260px;
  min-width: 0;
}

.entry-author-eyebrow {
  font-family: var(--font-head);
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 2px;
  font-size: .68rem;
  color: #BDF3FF;
  margin: 0 0 6px;
}

.entry-author-name {
  font-family: var(--font-head);
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: .5px;
  font-size: clamp(1.1rem, 2vw, 1.35rem);
  color: #fff;
  margin: 0 0 10px;
}

.entry-author-bio {
  font-size: .96rem;
  line-height: 1.7;
  color: rgba(255, 255, 255, .88);
  margin: 0 0 12px;
}

.entry-author-links {
  display: flex;
  flex-wrap: wrap;
  gap: 8px 22px;
  margin: 0;
}

.entry-author-links a {
  display: inline-flex;
  align-items: center;
  gap: 7px;
  font-family: var(--font-head);
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 1.5px;
  font-size: .76rem;
  color: #fff;
  transition: color .25s var(--ease), gap .25s var(--ease);
}

.entry-author-links a:hover { color: #BDF3FF; gap: 11px; }

/* ------------------------------------------------------------
   Closing call to action
------------------------------------------------------------ */
/*
 * A light brand surface, not the near-black --c-dark it used to be. Two
 * reasons it changed: that navy belongs to no other panel on the article and
 * read as a foreign block, and the author card directly above it is already
 * the brand teal — repeating that teal here would have given the end of every
 * post two identical slabs in a row. Light, with the brand gradient as the
 * accent rule and the button, is on-brand and still separates the two.
 */
.entry-cta {
  position: relative;
  overflow: hidden;
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: clamp(18px, 3vw, 32px);
  margin-top: clamp(30px, 4vw, 48px);
  padding: clamp(26px, 4vw, 42px);
  color: var(--c-dark);
  background:
    radial-gradient(70% 120% at 100% 0%, rgba(24, 182, 217, .13), transparent 62%),
    radial-gradient(60% 100% at 0% 100%, rgba(44, 164, 100, .10), transparent 60%),
    var(--c-bg);
  border: 1px solid rgba(24, 182, 217, .24);
  box-shadow: var(--shadow-sm);
}

/* The same animated accent rule the author card and director's note carry. */
.entry-cta::before {
  content: "";
  position: absolute;
  top: 0; left: 0; right: 0;
  height: 4px;
  background: var(--grad-accent);
  background-size: 200% 100%;
  animation: gradientMove 7s ease infinite;
}

.entry-cta-body { flex: 1 1 320px; min-width: 0; }

.entry-cta-eyebrow {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  font-family: var(--font-head);
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 2px;
  font-size: .72rem;
  color: var(--c-primary-600);
  margin: 0 0 10px;
}

.entry-cta-eyebrow .bi { font-size: .95rem; }

.entry-cta-title {
  font-family: var(--font-head);
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 1px;
  line-height: 1.15;
  font-size: clamp(1.3rem, 2.8vw, 1.9rem);
  margin: 0;
}

.entry-cta-text {
  font-size: 1rem;
  line-height: 1.65;
  color: var(--c-secondary);
  max-width: 52ch;
  margin: 10px 0 0;
}

/* ------------------------------------------------------------
   Previous / next
------------------------------------------------------------ */
/* Two columns at every width. They used to stack below 768px, which put
   "previous" and "next" on two separate rows and made the pair read as two
   unrelated cards instead of one control. */
.post-nav {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 16px;
  margin-top: clamp(36px, 5vw, 60px);
  padding-top: clamp(24px, 3vw, 34px);
  border-top: 1px solid rgba(15, 23, 42, .12);
}

/* A post with no newer sibling keeps its card on the left. */
.post-nav-card.is-next:only-child { grid-column: 2; }

/* Half a phone is ~170px, and an 84px thumbnail would leave the title about
   seventy of them. The image goes, the title gets the whole card and is
   clamped to two lines so the two cards stay the same height. */
@media (max-width: 767.98px) {
  .post-nav { gap: 10px; }

  .post-nav-media { display: none; }

  .post-nav-card {
    gap: 0;
    padding: 11px 12px;
    align-items: stretch;
  }

  .post-nav-label {
    font-size: .6rem;
    letter-spacing: 1.4px;
    margin-bottom: 4px;
  }

  .post-nav-title {
    display: -webkit-box;
    -webkit-line-clamp: 2;
    -webkit-box-orient: vertical;
    overflow: hidden;
    font-size: .86rem;
    line-height: 1.35;
  }
}

.post-nav-card {
  display: flex;
  align-items: center;
  gap: 16px;
  padding: 14px;
  background: #fff;
  border: 1px solid rgba(15, 23, 42, .10);
  transition: transform .35s var(--ease), border-color .35s var(--ease), box-shadow .35s var(--ease);
}

.post-nav-card:hover {
  transform: translateY(-4px);
  border-color: rgba(24, 182, 217, .45);
  box-shadow: var(--shadow-sm);
}

.post-nav-card.is-next { text-align: right; flex-direction: row-reverse; }

.post-nav-media {
  flex: 0 0 auto;
  width: 84px;
  height: 64px;
  overflow: hidden;
}

.post-nav-media img {
  width: 100%;
  height: 100%;
  object-fit: cover;
}

.post-nav-body { min-width: 0; }

.post-nav-label {
  display: flex;
  align-items: center;
  gap: 7px;
  font-family: var(--font-head);
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 2px;
  font-size: .68rem;
  color: var(--c-primary-600);
  margin-bottom: 5px;
}

.post-nav-card.is-next .post-nav-label { justify-content: flex-end; }

.post-nav-title {
  display: block;
  font-family: var(--font-head);
  font-weight: 500;
  line-height: 1.3;
  font-size: .98rem;
  color: var(--c-dark);
}

/* ------------------------------------------------------------
   Author archive intro
------------------------------------------------------------ */
.author-intro {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 20px;
  margin-bottom: clamp(24px, 3vw, 40px);
  color: var(--c-secondary);
}

.author-intro img { width: 84px; height: 84px; object-fit: cover; }
.author-intro p { margin: 0; max-width: 60ch; }

/* ------------------------------------------------------------
   Heading anchors
   The TOC links to headings inside the article, so they need the
   same fixed-header clearance every section already gets from
   §2 — that rule only matches `section[id]`.
------------------------------------------------------------ */
.entry-content h2[id],
.entry-content h3[id] {
  scroll-margin-top: calc(var(--header-h) + 20px);
}

/* The one-pager Blog section is a teal panel, where the default slate
   breadcrumb text has no contrast. The archive is light — see wp.css —
   so it keeps the default colours. */
section.blog:not(.is-archive) .breadcrumbs,
section.blog:not(.is-archive) .breadcrumbs a { color: rgba(255, 255, 255, .82); }
section.blog:not(.is-archive) .breadcrumbs a:hover { color: #fff; }
section.blog:not(.is-archive) .breadcrumbs li + li::before { color: rgba(255, 255, 255, .5); }
section.blog:not(.is-archive) .breadcrumbs [aria-current="page"] { color: #fff; }

/* ------------------------------------------------------------
   Touch: no hover lift
   §12 of style.css gates the rest of the site's hover movement,
   but this file loads after it, so the article's one lift has to
   be gated here or the cascade puts it back.

   Rationale is the same: a tap sets :hover and leaves it set, so
   the card jumped 4px and stayed there on the way to the next
   article. Colour and shadow are left alone — they do not move.
------------------------------------------------------------ */
@media (hover: none) {
  .post-nav-card:hover { transform: none; }
  .post-nav-card { -webkit-tap-highlight-color: transparent; }
}

/* ------------------------------------------------------------
   Related articles
   ------------------------------------------------------------
   Sits outside .entry-layout, so it spans the container rather
   than the reading column — see the note in single.php. The
   cards are template-parts/content/card-post.php, identical to
   the archive grid, so everything they need is already styled
   in §8f of style.css and nothing is restated here.

   A rule above the strip separates it from the article without
   adding another filled panel; the end of a post already has
   the author card and the CTA, and a third slab reads as clutter.
------------------------------------------------------------ */
.entry-related {
  margin-top: clamp(44px, 6vw, 78px);
  padding-top: clamp(30px, 4vw, 48px);
  border-top: 1px solid rgba(15, 23, 42, .10);
}

/* The shared .section-head centres and sizes for a full section
   opener. As a sub-heading inside an article it wants to be
   smaller and left-aligned, in line with the headings the reader
   has been following down the page. */
.entry-related .section-head {
  margin-bottom: clamp(20px, 2.6vw, 30px);
  text-align: left;
}
.entry-related .section-title {
  font-size: clamp(1.35rem, 2.4vw, 1.85rem);
  margin: 0;
}


/* ===== assets/css/wp.css ===== */
/* ============================================================
   Bird Morning — WordPress layer
   ------------------------------------------------------------
   The static build was one page, so it never needed styles for
   an article, an archive, a 404, pagination or the alignment
   and caption classes WordPress emits from the editor.

   Everything here is built from the §1 design tokens and obeys
   the project's rules: no border-radius, Oswald for headings,
   Ubuntu for body copy.
   ============================================================ */

/* ------------------------------------------------------------
   Accessibility helpers
   .screen-reader-text is WordPress's standard visually-hidden
   class and is used by the templates to give icon-only links
   and "Read More" links their full context.
------------------------------------------------------------ */
.screen-reader-text {
  position: absolute !important;
  width: 1px;
  height: 1px;
  padding: 0;
  margin: -1px;
  overflow: hidden;
  clip: rect(0, 0, 0, 0);
  clip-path: inset(50%);
  white-space: nowrap;
  border: 0;
}

/* Except when focused — a skip link has to become visible. */
.screen-reader-text:focus {
  position: static !important;
  width: auto;
  height: auto;
  margin: 0;
  overflow: visible;
  clip: auto;
  clip-path: none;
  white-space: normal;
}

/*
 * A visible focus ring everywhere, at 3:1 against both the light
 * and dark section backgrounds (WCAG 2.2 SC 2.4.11/2.4.13).
 * Only :focus-visible, so it does not fire on mouse clicks.
 */
a:focus-visible,
button:focus-visible,
input:focus-visible,
select:focus-visible,
textarea:focus-visible,
summary:focus-visible,
[tabindex]:focus-visible {
  outline: 3px solid var(--c-accent-a);
  outline-offset: 2px;
}

/* On the dark sections the cyan ring needs a light halo to stay visible. */
.trustbar :focus-visible,
.statement :focus-visible,
.expertise :focus-visible,
section.blog :focus-visible,
.faq :focus-visible,
.site-footer :focus-visible,
.mobile-nav :focus-visible {
  outline-color: #fff;
  box-shadow: 0 0 0 5px rgba(24, 182, 217, .5);
}

/* ------------------------------------------------------------
   Editor alignment + caption classes
------------------------------------------------------------ */
.alignleft {
  float: left;
  margin: 0 1.5rem 1rem 0;
}

.alignright {
  float: right;
  margin: 0 0 1rem 1.5rem;
}

.aligncenter {
  display: block;
  margin: 0 auto 1.5rem;
}

/*
 * Scoped to the article body on purpose. Sections carry .alignfull too now
 * that the blocks expose the alignment control, and they are already full
 * bleed — an unscoped margin here would open a gap between every section on
 * the front page.
 */
.entry-content .alignwide,
.entry-content .alignfull {
  margin: 1.5rem 0;
}

.wp-caption {
  max-width: 100%;
}

.wp-caption-text,
.gallery-caption,
figcaption {
  font-size: .86rem;
  line-height: 1.5;
  color: var(--c-secondary);
  padding-top: 8px;
}

.wp-block-image img,
.entry-content img {
  max-width: 100%;
  height: auto;
}

/* ------------------------------------------------------------
   Article + interior pages
   Reuses .section-head from §8b-i for the title block.
------------------------------------------------------------ */
.entry-single,
section.blog.is-archive {
  padding: calc(var(--header-h) + 40px) 0 clamp(56px, 8vw, 110px);
}

.entry-head {
  text-align: left;
  max-width: 68ch;
  margin-inline: 0;
}

.entry-head .section-subtitle {
  display: inline-flex;
  align-items: center;
  gap: 8px;
}

.entry-media {
  margin: 0 0 clamp(28px, 4vw, 48px);
}

.entry-media img {
  width: 100%;
  height: auto;
  display: block;
}

.entry-content {
  max-width: 68ch;
  font-size: 1.02rem;
  line-height: 1.75;
  color: var(--c-secondary);
}

.entry-content > * + * {
  margin-top: 1.25em;
}

.entry-content h2,
.entry-content h3,
.entry-content h4 {
  font-family: var(--font-head);
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 1px;
  color: var(--c-dark);
  margin-top: 1.8em;
}

.entry-content h2 { font-size: clamp(1.5rem, 3vw, 2.1rem); }
.entry-content h3 { font-size: clamp(1.25rem, 2.4vw, 1.6rem); }
.entry-content h4 { font-size: 1.1rem; }

.entry-content a {
  color: var(--c-primary-600);
  text-decoration: underline;
  text-underline-offset: 2px;
}

.entry-content a:hover {
  color: var(--c-dark);
}

.entry-content blockquote {
  margin: 1.6em 0;
  padding: 4px 0 4px 22px;
  border-left: 3px solid var(--c-primary);
  font-size: 1.08rem;
  color: var(--c-dark);
}

.entry-content ul,
.entry-content ol {
  padding-left: 1.4em;
}

/*
 * §2 sets `ul { list-style: none }` so the site's own nav bars, card lists and
 * footer columns are not covered in bullets. Editor content is the one place
 * that wants real markers back — without this a bulleted list inside a post
 * renders as indented text with nothing marking the items. `ol` is spelled out
 * alongside it so both lists are stated in one place rather than one relying
 * on the reset having skipped it.
 */
.entry-content ul { list-style: disc; }
.entry-content ol { list-style: decimal; }

.entry-content li + li {
  margin-top: .5em;
}

.entry-content code {
  font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
  font-size: .9em;
  background: rgba(15, 23, 42, .06);
  padding: .1em .35em;
}

.entry-content pre {
  overflow-x: auto;
  padding: 18px 20px;
  background: transparent;
  color: #E2E8F0;
  font-size: .9rem;
  line-height: 1.6;
}

/* ------------------------------------------------------------
   Pagination
------------------------------------------------------------ */
.navigation.pagination {
  margin-top: clamp(36px, 5vw, 60px);
}

.nav-links {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
  justify-content: center;
}

.nav-links .page-numbers {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: 44px;
  min-height: 44px;
  padding: 0 12px;
  font-family: var(--font-head);
  font-weight: 500;
  color: var(--c-dark);
  border: 1px solid rgba(15, 23, 42, .16);
  text-decoration: none;
  transition: background-color .25s var(--ease), color .25s var(--ease);
}

.nav-links .page-numbers:hover,
.nav-links .page-numbers.current {
  background: transparent;
  color: #fff;
  border-color: var(--c-dark);
}

.page-links {
  display: flex;
  gap: 8px;
  margin-top: 2em;
  font-family: var(--font-head);
}

/* ------------------------------------------------------------
   Search form + 404
------------------------------------------------------------ */
.search-form {
  display: flex;
  flex-wrap: wrap;
  gap: 12px;
  align-items: flex-end;
  max-width: 520px;
}

.search-form .form-label {
  width: 100%;
}

.search-form .c-input {
  flex: 1 1 220px;
}

/* ------------------------------------------------------------
   404

   The whole design is the numeral, so it is sized off the
   viewport rather than off the heading scale, and everything
   else on the page is one sentence and one button.

   §8j gives every `main > section` a particle canvas but only
   names the front-page sections when it sets --net-color; this
   one is a light surface, so it takes the cyan value.
------------------------------------------------------------ */
.error-404 {
  --net-color: 24, 182, 217;

  /*
   * Vertically centred in what is left below the fixed header. Flex, not
   * `place-items: center` on a grid — `justify-items: center` would shrink
   * .container to max-content and throw away its max-width.
   */
  display: flex;
  flex-direction: column;
  justify-content: center;
  min-height: calc(100vh - var(--header-h));
  padding: calc(var(--header-h) + 40px) 0 clamp(56px, 8vw, 110px);
  text-align: center;
}

.error-404 > .container {
  width: 100%;
}

.e404 {
  max-width: 34ch;
  margin-inline: auto;
}

.e404-code {
  margin: 0;
  font-family: var(--font-head);
  font-weight: 700;
  font-size: clamp(6.5rem, 27vw, 16rem);
  /* Tight enough that the digits read as one mark rather than a line of text. */
  line-height: .85;
  letter-spacing: 2px;
  background: var(--grad-primary);
  -webkit-background-clip: text;
  background-clip: text;
  -webkit-text-fill-color: transparent;
  /* Fallback for the handful of engines that ignore background-clip: text —
     without it the numeral inherits the body colour and still reads fine. */
  color: var(--c-primary);
}

.e404-title {
  margin: clamp(12px, 2.2vw, 20px) 0 0;
  font-family: var(--font-head);
  font-weight: 600;
  font-size: clamp(1.1rem, 3.4vw, 1.7rem);
  line-height: 1.35;
  text-transform: uppercase;
  letter-spacing: 2px;
  color: var(--c-dark);
}

.e404-actions {
  margin: clamp(26px, 4vw, 38px) 0 0;
}

/* ------------------------------------------------------------
   Blog archive grid
   The carousel styles in §8f assume a slider track; on the
   archive the cards sit in a plain grid instead.

   `section.blog`, never `.blog` — WordPress adds `blog` to
   body_class() on the posts index. See the banner above §8f.
------------------------------------------------------------ */
/*
 * The archive is a page, not a section of the one-pager, so it does not
 * inherit §8f's teal panel — that gradient is there to separate the Blog
 * section from the white sections around it, and on a full page it just
 * makes a wall of colour. Light surface, dark type, and the particle
 * canvas re-tinted to brand cyan so it is actually visible on it.
 *
 * --net-color is read by initSectionNetworks() through getComputedStyle
 * when it builds the canvas, so setting it here is enough; §8f sets white
 * for the one-pager version of this section.
 */
section.blog.is-archive {
  --net-color: 24, 182, 217;
  background: var(--c-bg);
}

section.blog.is-archive .blog-bg {
  background-image:
    radial-gradient(rgba(15, 23, 42, .05) 1px, transparent 1.4px),
    radial-gradient(46% 50% at 8% 4%, rgba(24, 182, 217, .12), transparent 60%),
    radial-gradient(44% 50% at 92% 96%, rgba(44, 164, 100, .10), transparent 60%);
  background-size: 26px 26px, 100% 100%, 100% 100%;
}

section.blog.is-archive .section-title { color: var(--c-dark); }
section.blog.is-archive .section-lead { color: var(--c-secondary); }

/* §8f's white focus halo is for the teal panel; this one is light again. */
section.blog.is-archive :focus-visible {
  outline-color: var(--c-accent-a);
  box-shadow: none;
}

section.blog.is-archive .blog-card {
  height: 100%;
}

section.blog.is-archive .section-head {
  padding-top: 0;
}

/*
 * The placeholder is an SVG drawn at the card crop, so object-fit has
 * nothing to crop — but .blog-img sets it for photographs and the value
 * has to be neutralised here or the artwork is scaled oddly on the few
 * aspect ratios that do not match exactly.
 */
.blog-img.is-placeholder {
  object-fit: cover;
  background: var(--c-primary);
}

/* ------------------------------------------------------------
   Required by the WordPress theme review guidelines
------------------------------------------------------------ */
.sticky,
.bypostauthor {
  display: block;
}

/* ------------------------------------------------------------
   Admin bar
   The header is fixed; the admin bar pushes it down. The offset
   itself is printed by BMCA_Setup::admin_bar_offset() because it
   depends on --header-h.
------------------------------------------------------------ */
.admin-bar .offcanvas.offcanvas-end {
  top: 32px;
}

@media screen and (max-width: 782px) {
  .admin-bar .offcanvas.offcanvas-end {
    top: 46px;
  }
}

