/* ==========================================================================
   Spiria - site theme layer
   --------------------------------------------------------------------------
   Loads last, after style.css and mobile-fixes.css, so it wins the cascade
   without editing the purchased template's stylesheet. Everything the site
   overrides on top of the vendor theme belongs here.

   Contents
     1.  Design tokens
     2.  Typography
     3.  Focus & accessibility
     4.  Buttons
     5.  Cards & surfaces
     6.  Header & navigation
     7.  Forms & contact form states
     8.  Loading & empty / error states
     9.  Footer
     10. Page-level layout fixes (moved out of per-page <style> blocks)
     11. Mobile
   ========================================================================== */


/* ==========================================================================
   1. DESIGN TOKENS
   ========================================================================== */

:root {
    /* Brand. --brand is the purple the site already used (#873aa4); the rest
       of the ramp is derived from it so hovers and tints stay in family. */
    --brand:            #873aa4;
    --brand-dark:       #6c2f84;
    --brand-darker:     #55245f;
    --brand-tint:       rgba(135, 58, 164, 0.08);
    --brand-tint-strong:rgba(135, 58, 164, 0.16);
    /* Purple for text on the near-black bands (#141b1f). --brand itself is
       2.6:1 there and #a855f7 was 4.4:1 - both under the 4.5:1 AA floor.
       This one measures 6.2:1. */
    --brand-on-dark:    #b880f7;

    /* Neutrals, seeded from the template's near-black #262423. */
    --ink-900: #1a1817;
    --ink-800: #262423;
    --ink-700: #3d3a38;
    --ink-600: #5a5654;
    --ink-500: #7c7876;
    --ink-400: #a5a19f;
    --ink-300: #d4d1cf;
    --ink-200: #e8e6e4;
    --ink-100: #f4f3f2;
    --ink-000: #ffffff;

    /* Feedback */
    --ok:     #1f7a4d;
    --ok-bg:  #e7f4ed;
    --warn:   #8a6100;
    --error:  #b3261e;
    --error-bg: #fdecea;

    /* Spacing scale */
    --space-1: 4px;
    --space-2: 8px;
    --space-3: 12px;
    --space-4: 16px;
    --space-5: 24px;
    --space-6: 32px;
    --space-7: 48px;
    --space-8: 64px;

    /* Radii */
    --radius-sm:   4px;
    --radius-md:   8px;
    --radius-lg:  12px;
    --radius-pill: 999px;

    /* Elevation */
    --shadow-1: 0 1px 2px rgba(26, 24, 23, 0.06), 0 1px 3px rgba(26, 24, 23, 0.08);
    --shadow-2: 0 4px 12px rgba(26, 24, 23, 0.08), 0 2px 4px rgba(26, 24, 23, 0.04);
    --shadow-3: 0 12px 32px rgba(26, 24, 23, 0.12), 0 4px 8px rgba(26, 24, 23, 0.06);
    --shadow-brand: 0 12px 28px rgba(135, 58, 164, 0.18);

    /* Motion. One curve, one duration, used everywhere. */
    --ease: cubic-bezier(0.4, 0, 0.2, 1);
    --dur: 0.25s;

    /* Type stacks. These mirror what the Google Fonts request actually loads -
       style.css also names 'Roboto' and 'Crete Round', which are never fetched
       and silently fall back; those rules are repointed in section 2. */
    --font-display: 'Oswald', 'Poppins', sans-serif;
    --font-ui:      'Poppins', system-ui, -apple-system, 'Segoe UI', sans-serif;
    --font-body:    'Raleway', system-ui, -apple-system, 'Segoe UI', sans-serif;
}

@media (prefers-reduced-motion: reduce) {
    :root {
        --dur: 0.01ms;
    }

    *,
    *::before,
    *::after {
        animation-duration: 0.01ms !important;
        animation-iteration-count: 1 !important;
        transition-duration: 0.01ms !important;
        scroll-behavior: auto !important;
    }
}


/* ==========================================================================
   2. TYPOGRAPHY
   ========================================================================== */

/* style.css declares 'Roboto' and 'Crete Round' in a handful of places. The
   site never loads either, so those rules were falling back to whatever the
   browser had. Point them at the stacks that are actually fetched. */
body,
p,
.wt-post-text,
.widget_address {
    font-family: var(--font-body);
}

h1, h2, h3, h4, h5, h6,
.wt-title,
.wt-tilte,
.widget-title,
.post-title {
    font-family: var(--font-display);
    color: var(--ink-900);
    letter-spacing: 0.01em;
}

.site-button,
.site-button-link,
.nav.navbar-nav > li > a,
.form-control,
.btn {
    font-family: var(--font-ui);
}

/* A modular scale that only ever shrinks as the viewport narrows. The vendor
   stylesheet steps some headings back UP at small breakpoints, which is what
   mobile-fixes.css had to patch around one selector at a time. */
h1, .h1 { font-size: clamp(32px, 4.4vw, 52px); line-height: 1.14; }
h2, .h2 { font-size: clamp(27px, 3.4vw, 40px); line-height: 1.2; }
h3, .h3 { font-size: clamp(22px, 2.6vw, 30px); line-height: 1.26; }
h4, .h4 { font-size: clamp(18px, 1.9vw, 22px); line-height: 1.34; }
h5, .h5 { font-size: clamp(16px, 1.5vw, 18px); line-height: 1.4; }

body {
    font-size: 15px;
    line-height: 1.72;
    color: var(--ink-700);
    -webkit-font-smoothing: antialiased;
    -moz-osx-font-smoothing: grayscale;
}

p {
    margin-bottom: var(--space-4);
}

/* Long-form article body copy reads better slightly larger and looser. */
.wt-post-text p,
.blog-post .wt-post-text p {
    font-size: 16px;
    line-height: 1.8;
}

a {
    transition: color var(--dur) var(--ease);
}

/* Muted text on light backgrounds. The template used #718096 (3.74:1 on
   white), which fails AA at the sizes it is used at. */
.wt-tilte-light,
.text-gray,
.wt-post-meta li {
    color: var(--ink-600);
}


/* ==========================================================================
   3. FOCUS & ACCESSIBILITY
   --------------------------------------------------------------------------
   The template ships no visible focus state at all, so keyboard users had no
   idea where they were on the page.
   ========================================================================== */

:focus-visible {
    outline: 3px solid var(--brand);
    outline-offset: 2px;
    border-radius: var(--radius-sm);
}

/* Never remove the ring without replacing it. */
a:focus-visible,
button:focus-visible,
input:focus-visible,
select:focus-visible,
textarea:focus-visible,
[tabindex]:focus-visible {
    outline: 3px solid var(--brand);
    outline-offset: 2px;
}

/* Dark surfaces need a light ring to stay visible. */
.footer-dark a:focus-visible,
.bg-secondry a:focus-visible,
.site-header a:focus-visible,
.site-header button:focus-visible {
    outline-color: #ffffff;
}

.sr-only {
    position: absolute;
    width: 1px;
    height: 1px;
    padding: 0;
    margin: -1px;
    overflow: hidden;
    clip: rect(0, 0, 0, 0);
    white-space: nowrap;
    border: 0;
}


/* ==========================================================================
   4. BUTTONS
   ========================================================================== */

.site-button,
.site-button-secondry {
    font-family: var(--font-ui);
    font-weight: 600;
    letter-spacing: 0.04em;
    border: 0;
    border-radius: var(--radius-sm);
    padding: 13px 30px;
    transition: background-color var(--dur) var(--ease),
                box-shadow var(--dur) var(--ease),
                transform var(--dur) var(--ease);
}

.site-button {
    background-color: var(--brand);
    color: #fff;
}

.site-button:hover,
.site-button:focus {
    background-color: var(--brand-dark);
    color: #fff;
    box-shadow: var(--shadow-brand);
    transform: translateY(-2px);
}

.site-button:active {
    transform: translateY(0);
    box-shadow: var(--shadow-1);
}

.site-button[disabled],
.site-button.is-sending {
    opacity: 0.7;
    cursor: not-allowed;
    transform: none;
    box-shadow: none;
}

/* --- Brand fills ---------------------------------------------------------
   Bootstrap defines .bg-primary / .text-primary as #007bff !important, which
   outranks both the template's palette and any inline background - the CTA
   band on services.html rendered Bootstrap blue despite carrying its own
   colour. "Primary" on this site is the brand purple (see .bdr-primary,
   #873AA4), so use these where the template meant primary. */
.bg-brand {
    background-color: var(--brand);
}

.text-brand {
    color: var(--brand);
}

/* Bootstrap's whole "primary" family is #007bff, applied with !important on
   the utilities. Nothing in this design is blue, so map the family onto the
   brand purple rather than hunting the class down page by page. Equal
   specificity, and theme.css loads last, so these win. */
.bg-primary {
    background-color: var(--brand) !important;
}

.text-primary {
    color: var(--brand) !important;
}

.border-primary {
    border-color: var(--brand) !important;
}

a.text-primary:hover,
a.text-primary:focus {
    color: var(--brand-darker) !important;
}

.btn-primary {
    background-color: var(--brand);
    border-color: var(--brand);
}

.btn-primary:hover,
.btn-primary:focus,
.btn-primary:active,
.btn-primary:not(:disabled):not(.disabled):active {
    background-color: var(--brand-dark);
    border-color: var(--brand-dark);
}

.btn-primary:focus,
.btn-primary:not(:disabled):not(.disabled):active:focus {
    box-shadow: 0 0 0 3px var(--brand-tint-strong);
}

/* The template paints the logo container's text cyan. It only ever holds the
   logo image, so this shows solely as alt text when that image fails - which
   is exactly when it should not be a colour from nowhere. */
.logo-header,
.logo-header-inner {
    color: var(--brand);
}

/* The template's .radius-no wins over the shared radius on purpose. */
.site-button.radius-no {
    border-radius: 0;
}

.site-button-link {
    font-weight: 600;
    color: var(--brand);
    transition: color var(--dur) var(--ease), gap var(--dur) var(--ease);
}

.site-button-link:hover {
    color: var(--brand-darker);
}

/* Back-to-top control. 44px is the minimum comfortable touch target. */
.scroltop {
    width: 46px;
    height: 46px;
    border-radius: var(--radius-pill);
    background-color: var(--brand);
    box-shadow: var(--shadow-2);
    border: 0;
    transition: background-color var(--dur) var(--ease),
                transform var(--dur) var(--ease);
}

.scroltop:hover {
    background-color: var(--brand-dark);
    transform: translateY(-3px);
}


/* ==========================================================================
   5. CARDS & SURFACES
   ========================================================================== */

.wt-icon-box-wraper,
.blog-post,
.wt-team-arc2,
.project-img-effect-1 {
    border-radius: var(--radius-md);
    transition: transform var(--dur) var(--ease), box-shadow var(--dur) var(--ease);
}

.wt-icon-box-wraper.bg-white,
.blog-post {
    box-shadow: var(--shadow-1);
}

.wt-icon-box-wraper.bg-white:hover,
.blog-post:hover {
    box-shadow: var(--shadow-3);
    transform: translateY(-4px);
}

/* Two-up blog grid on the home page: the row stretches the columns, but each
   card kept its own content height, so a card with a longer excerpt left its
   neighbour short. Stretch the card to fill the column instead. */
.latest-blog-2-outer > [class*="col-"] {
    display: flex;
}

.latest-blog-2-outer > [class*="col-"] > .blog-post {
    width: 100%;
}

.project-img-effect-1 {
    overflow: hidden;
}

.project-img-effect-1 img {
    transition: transform 0.5s var(--ease);
}

.project-img-effect-1:hover img {
    transform: scale(1.04);
}

/* Media inside cards: consistent ratio instead of a hard pixel height, so
   portrait renders are not letterboxed on narrow screens. Client logos are
   excluded: they are small marks, not photography, and cover-cropping one
   stretches it past its natural size. */
.wt-media img,
.project-img-effect-1 img,
.wt-img-effect:not(.client-logo) img {
    display: block;
    width: 100%;
    height: 100%;
    object-fit: cover;
}

/* Client logo carousel: keep each mark at its own size, centred in the slide. */
.widget-client .ow-client-logo {
    display: flex;
    align-items: center;
    justify-content: center;
    min-height: 120px;
}

.widget-client .ow-client-logo img {
    display: block;
    width: auto;
    height: auto;
    max-width: 100%;
    max-height: 110px;
    object-fit: contain;
}


/* ==========================================================================
   6. HEADER & NAVIGATION
   --------------------------------------------------------------------------
   The header is deliberately NOT sticky - it is anchored to the top of the
   document and scrolls away with the page. Do not reintroduce position:fixed.
   ========================================================================== */

/*
 * The nav is hidden behind the hamburger at EVERY width, and what opens
 * differs either side of 991px:
 *
 *   >= 992px  a horizontal row inside the bar, sitting on the hero photograph
 *   <= 991px  a white panel that slides in from the left
 *
 * Those two need opposite text colours, and opposite current-page markers.
 * Everything below is scoped accordingly - a single rule cannot serve both.
 */

/* The bar is transparent over a photograph on every page. A scrim gives the
   logo, icons and links a consistent ground without adding a visible bar. */
.site-header .main-bar::before {
    content: "";
    position: absolute;
    inset: 0;
    /* Held at >= 0.6 through the band the logo and nav labels occupy, so white
       text clears 4.5:1 even over a blown-out highlight in the photo, then
       faded out so it reads as a gradient rather than a bar. */
    background: linear-gradient(to bottom,
        rgba(26, 24, 23, 0.80) 0%,
        rgba(26, 24, 23, 0.62) 62%,
        rgba(26, 24, 23, 0) 100%);
    pointer-events: none;
    z-index: 0;
}

.site-header .main-bar > .container {
    position: relative;
    z-index: 1;
}

.site-header .logo-header img {
    max-height: 50px;
    width: auto;
}

.header-nav .nav > li > a {
    font-weight: 500;
    letter-spacing: 0.04em;
    position: relative;
    /* inline-block so the marker below has a box to position against - the
       vendor sheet leaves these inline, which makes ::after unpredictable. */
    display: inline-block;
    transition: color var(--dur) var(--ease);
}

/* These sit on the scrim at every width. */
.site-header .extra-nav a {
    color: #fff;
}


/* --- >= 992px: horizontal row on the scrim -------------------------------- */
@media only screen and (min-width: 992px) {

    /* The vendor's drawer variant hides .header-nav at EVERY width and only
       restores it below 992px, so a 1265px desktop got a hamburger and no menu
       at all - and clicking it dropped the five links out as a loose row
       hanging 24px below the bar. Five items belong on the bar at this width,
       so show the nav and retire the toggle. */
    .mobile-sider-drawer-menu .header-nav,
    .mobile-sider-drawer-menu.active .header-nav {
        display: block;
    }

    #mobile-side-drawer {
        display: none;
    }

    /* The vendor spacing (li padding 25px + a padding 35px with a -10px
       margin) assumes a much taller bar. Against this 70px logo it made the
       nav row 121px tall, pushed .main-bar to 141px, and left the links
       hanging 36px below the logo. 24 + 22 + 24 matches the logo exactly. */
    .header-nav .nav > li {
        padding: 0;
    }

    .header-nav .nav > li > a {
        color: #fff;
        padding: 24px 12px;
        margin: 0;
        line-height: 22px;
        /* The scrim cannot know what is behind it. A tight shadow keeps the
           label readable even over a blown-out highlight in the photo. */
        text-shadow: 0 1px 3px rgba(0, 0, 0, 0.55);
    }

    .header-nav .nav > li:hover > a,
    .header-nav .nav > li.active > a {
        color: #fff;
    }

    /* Underline, which is the marker that reads correctly in a horizontal row.
       #a855f7 rather than the --brand #873aa4: the bar sits on a photograph,
       and the darker brand purple drops to around 2.6:1 against the dim parts
       of the hero. This is the same lighter purple the page already uses for
       purple-on-dark, and the shadow keeps it defined over bright areas. */
    .header-nav .nav > li > a::after {
        content: "";
        position: absolute;
        left: 12px;
        right: 12px;
        /* 24px of bottom padding, less 8px of breathing room under the text. */
        bottom: 16px;
        height: 2px;
        border-radius: 2px;
        background-color: #a855f7;
        box-shadow: 0 1px 3px rgba(0, 0, 0, 0.5);
        transform: scaleX(0);
        transform-origin: left center;
        transition: transform var(--dur) var(--ease);
    }

    .header-nav .nav > li:hover > a::after,
    .header-nav .nav > li > a:focus-visible::after,
    .header-nav .nav > li.active > a::after {
        transform: scaleX(1);
    }
}


/* --- <= 991px: white slide-out panel -------------------------------------- */
@media only screen and (max-width: 991px) {

    /* Lift the panel off the page it covers. Note the vendor's open position is
       left:15px, not 0 - .header-nav is fixed but resolves against an ancestor
       whose box starts at -15px, so 15px is what puts it flush. */
    .mobile-sider-drawer-menu.active .header-nav {
        box-shadow: 4px 0 24px rgba(0, 0, 0, 0.28);
    }

    /* Nothing dimmed the page behind an opaque full-height panel, so the two
       layers read as one broken screen.

       z-index 0, not 998: the nav's own 999 is scoped inside .container, which
       is position:relative;z-index:1 and so a stacking context of its own. Any
       value above 1 here paints over the panel as well as the page. At 0 the
       scrim sits under everything in .container - the panel, the logo and the
       close button - while still covering the page, because the whole header
       is a z-index:990 layer above it.

       pointer-events:none deliberately - the toggle only flips a class, there
       is no dismiss-on-tap handler, and a scrim you cannot tap away is a trap. */
    .mobile-sider-drawer-menu::before {
        content: "";
        position: fixed;
        inset: 0;
        background-color: rgba(10, 14, 17, 0.5);
        opacity: 0;
        visibility: hidden;
        pointer-events: none;
        z-index: 0;
        transition: opacity var(--dur) var(--ease), visibility var(--dur) var(--ease);
    }

    .mobile-sider-drawer-menu.active::before {
        opacity: 1;
        visibility: visible;
    }

    /* The list started hard against the top edge of the screen. Drop it clear
       of the header so it lines up with the logo. */
    .mobile-sider-drawer-menu .header-nav .nav {
        padding-top: 90px;
    }

    .header-nav .nav > li > a {
        /* Was #777 on white: 4.48:1, just under AA at 12px. */
        color: var(--ink-700);
        display: flex;
        align-items: center;
        /* 12px uppercase at 2px tracking is a caption, not a menu. */
        font-size: 14px;
        letter-spacing: 0.06em;
    }

    .mobile-sider-drawer-menu .header-nav .nav > li > a {
        padding: 16px 24px !important;
    }

    .header-nav .nav > li + li {
        border-top: 1px solid var(--ink-200);
    }

    .header-nav .nav > li:hover > a,
    .header-nav .nav > li.active > a {
        color: var(--brand);
    }

    /* Left accent bar, which is the marker that reads correctly in a
       vertical list. */
    .header-nav .nav > li > a::before {
        content: "";
        position: absolute;
        left: 0;
        top: 50%;
        height: 60%;
        width: 3px;
        background-color: var(--brand);
        transform: translateY(-50%) scaleY(0);
        transform-origin: center;
        transition: transform var(--dur) var(--ease);
    }

    .header-nav .nav > li:hover > a::before,
    .header-nav .nav > li.active > a::before {
        transform: translateY(-50%) scaleY(1);
    }
}

#mobile-side-drawer {
    min-width: 44px;
    min-height: 44px;
    background: transparent;
    border: 0;
}


/* ==========================================================================
   7. FORMS
   ========================================================================== */

.form-control {
    height: auto;
    min-height: 48px;
    padding: 12px 16px;
    border: 1px solid var(--ink-300);
    border-radius: var(--radius-sm);
    background-color: #fff;
    color: var(--ink-800);
    font-size: 15px;
    box-shadow: none;
    transition: border-color var(--dur) var(--ease), box-shadow var(--dur) var(--ease);
}

textarea.form-control {
    min-height: 130px;
    resize: vertical;
}

.form-control::placeholder {
    color: var(--ink-500);
}

.form-control:focus {
    border-color: var(--brand);
    box-shadow: 0 0 0 3px var(--brand-tint);
    outline: none;
}

/* Only flag a field the visitor has actually filled in wrongly - :invalid on
   its own turns every required field red before they have typed anything. */
.form-control:not(:placeholder-shown):invalid {
    border-color: var(--error);
}

.form-control:not(:placeholder-shown):invalid:focus {
    box-shadow: 0 0 0 3px rgba(179, 38, 30, 0.12);
}

.form-group {
    margin-bottom: var(--space-5);
}

/* Contact form result banner, inserted by js/custom.js. */
.contact-form-alert {
    border-radius: var(--radius-md);
    border: 1px solid transparent;
    padding: 14px 18px;
    margin-bottom: var(--space-5);
    font-size: 15px;
    line-height: 1.5;
}

.contact-form-alert.alert-success {
    background-color: var(--ok-bg);
    border-color: rgba(31, 122, 77, 0.3);
    color: var(--ok);
}

.contact-form-alert.alert-danger {
    background-color: var(--error-bg);
    border-color: rgba(179, 38, 30, 0.3);
    color: var(--error);
}


/* ==========================================================================
   8. LOADING & EMPTY / ERROR STATES
   ========================================================================== */

/* The template's full-screen white "loading-area" overlay is gone. It sat
   on top of the page until custom.js ran - which, with every script
   deferred, meant a visitor on a slow connection watched a spinner while
   the finished page was already painted underneath it. */

/* States rendered by js/dynamic.js when the API returns nothing or errors. */
.data-state {
    width: 100%;
    text-align: center;
    padding: var(--space-8) var(--space-5);
    border: 1px dashed var(--ink-300);
    border-radius: var(--radius-md);
    background-color: var(--ink-100);
    color: var(--ink-600);
}

.data-state i {
    font-size: 30px;
    color: var(--ink-400);
    margin-bottom: var(--space-3);
    display: block;
}

.data-state h3 {
    margin: 0 0 var(--space-2);
    font-size: 18px;
    color: var(--ink-800);
}

.data-state p {
    margin: 0;
    font-size: 14px;
}

.data-state--error {
    border-color: rgba(179, 38, 30, 0.35);
    background-color: var(--error-bg);
    color: var(--error);
}

.data-state--error i,
.data-state--error h3 {
    color: var(--error);
}

.data-state--loading i {
    color: var(--brand);
}


/* ==========================================================================
   9. FOOTER
   ========================================================================== */

.site-footer .widget-title {
    letter-spacing: 0.06em;
    margin-bottom: var(--space-5);
}

/* Footer body text was #7e878c on #141b1f - 4.75:1, scraping past AA and
   hard to read at 14px. This lands around 8:1. */
.footer-dark .widget_about p,
.footer-dark .widget_address li,
.footer-dark .widget_services a,
.footer-dark .recent-posts-entry .post-title a,
.footer-dark .copyrights-text {
    color: #c3cace;
}

.footer-dark .wt-post-meta li {
    color: #949da2;
}

.site-footer .widget_address a,
.site-footer .widget_services a {
    color: inherit;
}

.site-footer .widget_address a:hover,
.site-footer .widget_services a:hover {
    color: var(--brand);
}

/* Sizing only - the hover treatment is the icon turning brand purple, which
   style.css already handles on the li. A filled pill here as well rendered as
   a purple blob inside the cell, so it is deliberately not set. */
.footer-social-content ul li a {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    min-width: 44px;
    min-height: 44px;
    transition: color var(--dur) var(--ease), opacity var(--dur) var(--ease);
}

.recent-posts-entry .wt-post-media img {
    border-radius: var(--radius-sm);
}

.footer-logo-bar {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: var(--space-4);
    flex-wrap: wrap;
}


/* ==========================================================================
   10. PAGE-LEVEL LAYOUT
   --------------------------------------------------------------------------
   Moved here out of per-page <style> blocks (3.4 KB on the homepage, 2.9 KB
   on the journal). Rewritten as plain flex rules - the inline versions leaned
   on !important on every single declaration.
   ========================================================================== */

/* --- Split section heads -------------------------------------------------
   The head is a shrink-to-fit float (.wt-tilte-main) with the intro paragraph
   flowing beside it. A float takes its max-content width first, so a long
   title claims the whole row and squeezes the paragraph into a tall sliver -
   services.html sat at 76%/24%, the home page's services head at 74%/26%.
   Capping the title column makes the heading wrap instead. Short titles are
   still shrink-to-fit and render exactly as before. Below 992px style.css
   drops the float and the two stack, so this only applies above that. */
@media only screen and (min-width: 992px) {
    .section-head .wt-tilte-main {
        max-width: 60%;
    }
}

/* --- Equal-height card rows ---------------------------------------------
   One rule for every grid of cards that must line up, instead of a separate
   copy per section. */
.our-services-section .row,
.our-specialties-section .row,
.our-methodology-section .row,
.projects-carousel-two .owl-stage {
    display: flex;
    flex-wrap: wrap;
    align-items: stretch;
}

.our-services-section .row > [class*="col-"],
.our-specialties-section .row > [class*="col-"],
.our-methodology-section .row > [class*="col-"] {
    display: flex;
    margin-bottom: var(--space-6);
}

.our-services-section .wt-icon-box-wraper,
.our-specialties-section .wt-team-arc2,
.our-methodology-section .wt-icon-box-wraper {
    display: flex;
    flex-direction: column;
    width: 100%;
    margin-bottom: 0;
}

.our-services-section .wt-icon-box-wraper {
    justify-content: space-between;
    min-height: 440px;
}

.our-services-section .icon-content {
    flex: 1 1 auto;
    display: flex;
    flex-direction: column;
    justify-content: space-between;
}

.our-services-section .wt-icon-box-wraper p {
    flex: 1 1 auto;
    margin-bottom: var(--space-5);
}

.our-services-section .site-button-link {
    margin-top: auto;
    display: inline-block;
}

.our-specialties-section .wt-team-arc2 .wt-info {
    flex: 1 1 auto;
    display: flex;
    align-items: center;
    justify-content: center;
}

.our-specialties-section .wt-team-arc2 .wt-media img {
    aspect-ratio: 4 / 3;
}

/* The dark band moved down - it now sits behind the featured projects. */
.our-specialties-section::after {
    display: none;
}

/* --- Featured projects strip (home page) --------------------------------
   The same cards the portfolio page renders, on the dark band that runs into
   the footer. Only the section furniture needs recolouring for the dark
   ground; the cards carry their own overlay. */
.home-projects-section .wt-small-title {
    color: var(--brand-on-dark);
}

/* --- "Our philosophy" band (home page) ----------------------------------
   Was five style="" attributes in the markup. Same dark ground, same
   contrast fix. */
.philosophy-head {
    border-right-color: var(--brand-dark) !important;
}

.philosophy-head .wt-small-title {
    display: block;
    margin-bottom: 6px;
    font-weight: 600;
    letter-spacing: 3px;
    text-transform: uppercase;
    color: var(--brand-on-dark);
}

.philosophy-head h2 {
    font-size: 32px;
    font-weight: 700;
    letter-spacing: 1px;
    text-transform: uppercase;
    color: var(--ink-000);
}

.philosophy-accent {
    color: var(--brand-on-dark);
}

.home-projects-section .section-head h2 {
    color: var(--ink-000);
}

.home-projects-section .title-right-detail p {
    color: rgba(255, 255, 255, 0.75);
}

/* The empty / error panels default to a light card, which reads as a hole in
   the dark band. */
.home-projects-section .data-state {
    background-color: rgba(255, 255, 255, 0.04);
    border-color: rgba(255, 255, 255, 0.18);
    color: rgba(255, 255, 255, 0.75);
}

.home-projects-section .data-state h3 {
    color: var(--ink-000);
}

/* Dark block runs straight into the footer, with no grey strip between. */
.home-content-wrap {
    padding-bottom: 0;
}

/* --- Watermark numerals must not swallow clicks --------------------------
   style.css:12954 paints the big outlined "01" watermark on a service card
   with `content: attr(data-title)` on .wt-tilte-large:after, at 360px and
   `position: absolute; z-index: 2`. That box is roughly 155x150 CSS px and
   it lands directly ON TOP of the card's own content - including the
   "Read More" link at the bottom, which it covers all but a ~3px strip of.

   The symptom was a link that "sometimes works": clicking the left edge of
   the words hit the anchor, clicking anywhere else hit the watermark and
   did nothing. It also blocked selecting the card's body text.

   The numeral is pure decoration, so it should never be a hit-test target.
   The hover effects that fade it in (style.css:13050 and :13802) key off
   :hover on the CARD, not on the pseudo-element, so removing it from hit
   testing leaves them working - the pointer just falls through to the card
   underneath, which is the element those selectors watch anyway.

   Affects the six homepage "Our Services" cards, the four on about-1.html,
   and the services.html grid that js/dynamic.js builds with the same
   markup. */
.data-title-large .wt-tilte-large::after {
    pointer-events: none;
}

/* --- Homepage hero -------------------------------------------------------
   Replaces Slider Revolution. The old hero could not paint until jQuery plus
   ~300 KB of plugin JavaScript had downloaded and run, then held every text
   layer at opacity:0 for a further 1-2.5 s of scripted delay, and its
   "slidevertical" transition moved the slide in 15-20 steps that Lighthouse
   counted as layout shift (CLS 0.39 on an otherwise stable page). Largest
   Contentful Paint landed at ~3 s with the image already in cache.

   This version is markup and CSS: the first slide is in the HTML, its image
   is preloaded from <head>, and js/hero.js (60 lines, no jQuery) only swaps
   an .is-active class every five seconds. Slides cross-fade with opacity and
   the captions animate with transform, neither of which moves layout.

   Geometry mirrors what Revolution rendered at the 1240px design grid: text
   40px in from the grid edge, caption block ending ~20% up from the bottom,
   the 300px outlined numeral overlapping the eyebrow, arrows 25px from the
   bottom-right corner. */
.hero {
    position: relative;
    height: 100vh;
    height: 100svh;          /* stops the URL bar on phones resizing the hero */
    min-height: 560px;
    overflow: hidden;
    background-color: var(--ink-800);
    color: var(--ink-000);
}

.hero-slides,
.hero-slide,
.hero-media,
.hero-media img {
    position: absolute;
    top: 0;
    right: 0;
    bottom: 0;
    left: 0;
    width: 100%;
    height: 100%;
}

/* Inactive slides are visibility:hidden, not just transparent, so their
   "View Projects" link drops out of the tab order and the accessibility tree.
   The visibility change is delayed until the fade-out has finished. */
.hero-slide {
    opacity: 0;
    visibility: hidden;
    pointer-events: none;
    transition: opacity 0.9s var(--ease), visibility 0s linear 0.9s;
}

.hero-slide.is-active {
    opacity: 1;
    visibility: visible;
    pointer-events: auto;
    z-index: 1;
    transition-delay: 0s;
}

.hero-media img {
    object-fit: cover;
    object-position: center;
    border-radius: 0;        /* theme rules elsewhere round card images */
}

/* Revolution's layer 0: a 30% black wash so white type reads on any photo. */
.hero-media::after {
    content: '';
    position: absolute;
    top: 0;
    right: 0;
    bottom: 0;
    left: 0;
    background-color: rgba(0, 0, 0, 0.3);
}

.hero-content {
    position: relative;
    z-index: 2;
    box-sizing: border-box;
    max-width: 1240px;
    height: 100%;
    margin: 0 auto;
    padding: 110px 40px 18vh;
    display: flex;
    flex-direction: column;
    justify-content: flex-end;
    align-items: flex-start;
}

/* The outlined "01" watermark. Sits behind the eyebrow and first headline
   line, exactly as the old layer-2 caption did. */
.hero-index {
    display: block;
    margin: 0 0 -0.4em -0.04em;
    font-family: var(--font-display);
    font-size: 300px;
    font-weight: 700;
    line-height: 0.9;
    letter-spacing: -0.02em;
    color: transparent;
    -webkit-text-stroke: 2px rgba(255, 255, 255, 0.3);
    text-stroke: 2px rgba(255, 255, 255, 0.3);
    pointer-events: none;
    user-select: none;
}

.hero-eyebrow {
    position: relative;
    margin: 0 0 var(--space-3);
    font-family: var(--font-display);
    font-size: 18px;
    line-height: 28px;
    font-weight: 400;
    letter-spacing: 2px;
    text-transform: uppercase;
    color: var(--ink-000);
}

.hero-title {
    position: relative;
    max-width: 750px;
    margin: 0 0 var(--space-5);
    font-family: var(--font-display);
    font-size: 54px;
    line-height: 64px;
    font-weight: 400;
    text-transform: uppercase;
    color: var(--ink-000);
}

/* "View Projects" with the short brand-purple rule to its left. The old
   caption set the label itself in purple on top of the photo, which was
   close to invisible on the darker slides; the rule keeps the accent and
   the label goes white. */
.hero-cta {
    position: relative;
    display: inline-block;
    padding: var(--space-2) 0 var(--space-2) 80px;
    font-family: var(--font-display);
    font-size: 16px;
    line-height: 24px;
    font-weight: 600;
    letter-spacing: 1px;
    text-transform: uppercase;
    color: var(--ink-000);
    transition: color var(--dur) var(--ease);
}

.hero-cta::before {
    content: '';
    position: absolute;
    left: 0;
    top: 50%;
    width: 60px;
    height: 2px;
    background-color: var(--brand);
    transition: width var(--dur) var(--ease);
}

.hero-cta:hover,
.hero-cta:focus-visible {
    color: #c084fc;
}

.hero-cta:hover::before {
    width: 70px;
}

/* Caption entrance. Runs when a slide gains .is-active; removing the class
   resets it so the next pass replays. Transform + opacity only.

   Scoped to .hero.is-rotating, which hero.js adds on the first slide change.
   Without that guard the first slide's caption started at opacity 0 and
   faded in over 0.9 s on page load, and because the "01" watermark is the
   largest element in the viewport that pushed LCP out by a full second. */
.hero.is-rotating .hero-slide.is-active .hero-index,
.hero.is-rotating .hero-slide.is-active .hero-eyebrow,
.hero.is-rotating .hero-slide.is-active .hero-title,
.hero.is-rotating .hero-slide.is-active .hero-cta {
    animation: hero-rise 0.9s var(--ease) both;
}

.hero.is-rotating .hero-slide.is-active .hero-eyebrow { animation-delay: 0.15s; }
.hero.is-rotating .hero-slide.is-active .hero-title   { animation-delay: 0.3s; }
.hero.is-rotating .hero-slide.is-active .hero-cta     { animation-delay: 0.45s; }

@keyframes hero-rise {
    from {
        opacity: 0;
        transform: translateY(28px);
    }
    to {
        opacity: 1;
        transform: translateY(0);
    }
}

/* Arrows, bottom right, in the same spot as Revolution's "uranus" pair. */
.hero-nav {
    position: absolute;
    right: 25px;
    bottom: 25px;
    z-index: 3;
    display: flex;
    gap: 5px;
}

.hero-arrow {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 50px;
    height: 50px;
    padding: 0;
    border: 0;
    border-radius: 50%;
    background: transparent;
    color: var(--ink-000);
    cursor: pointer;
    transition: background-color var(--dur) var(--ease), opacity var(--dur) var(--ease);
}

.hero-arrow:hover {
    background-color: rgba(255, 255, 255, 0.12);
}

.hero-arrow:focus-visible {
    outline: 2px solid var(--ink-000);
    outline-offset: 2px;
}

/* Revolution's responsive levels were 1240 / 1024 / 778 / 480. */
@media only screen and (max-width: 1239px) {
    .hero-index { font-size: 200px; }
    .hero-title { font-size: 44px; line-height: 54px; }
}

@media only screen and (max-width: 1023px) {
    .hero-content { padding-bottom: 16vh; }
    .hero-index   { font-size: 160px; }
    .hero-eyebrow { font-size: 16px; line-height: 26px; }
    .hero-title   { font-size: 32px; line-height: 40px; }
}

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

    .hero-content {
        padding: 90px 24px 100px;   /* bottom clears the arrows */
        justify-content: center;
    }

    .hero-index {
        font-size: 96px;
        margin-bottom: -0.25em;
        -webkit-text-stroke-width: 1.5px;
    }

    .hero-eyebrow { font-size: 13px; line-height: 20px; letter-spacing: 1.5px; }
    .hero-title   { font-size: 26px; line-height: 34px; margin-bottom: var(--space-4); }

    /* The rule-and-label treatment reads as decoration at phone size; a
       filled button is the clearer tap target (matches what the old
       mobile-fixes.css did to the Revolution caption). */
    .hero-cta {
        padding: 12px 24px;
        font-size: 13px;
        line-height: 20px;
        background-color: var(--brand);
        border-radius: var(--radius-sm);
    }

    .hero-cta::before { display: none; }

    .hero-cta:hover,
    .hero-cta:focus-visible {
        color: var(--ink-000);
        background-color: var(--brand-dark);
    }

    .hero-nav { right: 16px; bottom: 16px; }
}

/* Short landscape viewports (a phone on its side): drop the watermark so the
   caption fits without colliding with the header. */
@media only screen and (max-height: 520px) {
    .hero-index { display: none; }
    .hero-content { padding-top: 80px; padding-bottom: 80px; }
}

/* --- Homepage "About us" block ------------------------------------------
   Was three style="" attributes on the markup; the eyebrow's #718096 came out
   at 3.74:1 on the grey panel, under AA at 13px. */
.about-eyebrow {
    font-size: 13px;
    line-height: 1.5;
    letter-spacing: 0.08em;
    color: var(--ink-600);
}

.about-heading {
    color: var(--brand-dark);
    margin-bottom: var(--space-5);
}

.about-lede {
    font-size: 16px;
    line-height: 1.8;
    font-weight: 500;
    color: var(--ink-800);
    margin-bottom: var(--space-4);
}

/* --- Homepage projects carousel ----------------------------------------- */
.projects-carousel-two .owl-item {
    display: flex;
    height: auto;
}

.projects-carousel-two .item {
    display: flex;
    flex: 1 1 100%;
}

.projects-two-info {
    display: flex;
    flex-direction: column;
    justify-content: space-between;
    width: 100%;
    box-sizing: border-box;
}

.projects-two-info p {
    min-height: 105px;
    margin-bottom: var(--space-5);
}

.projects-two-info .wt-media {
    margin-top: auto;
}

.projects-two-info .wt-media img {
    aspect-ratio: 4 / 3;
}

/* --- Journal / news grid ------------------------------------------------- */
.portfolio-wrap.news-grid {
    display: flex;
    flex-wrap: wrap;
    align-items: stretch;
}

.portfolio-wrap.news-grid .masonry-item {
    display: flex;
    flex-direction: column;
    margin-bottom: var(--space-6);
    float: none;
}

.portfolio-wrap.news-grid .blog-post {
    display: flex;
    flex-direction: column;
    height: 100%;
    width: 100%;
    background: #fff;
    overflow: hidden;
}

.portfolio-wrap.news-grid .wt-img-effect {
    width: 100%;
    aspect-ratio: 16 / 10;
    overflow: hidden;
    flex-shrink: 0;
}

.portfolio-wrap.news-grid .wt-img-effect img {
    transition: transform 0.5s var(--ease);
}

.portfolio-wrap.news-grid .blog-post:hover .wt-img-effect img {
    transform: scale(1.05);
}

.portfolio-wrap.news-grid .p-a20.bg-white {
    display: flex;
    flex-direction: column;
    flex-grow: 1;
    padding: var(--space-5);
}

.portfolio-wrap.news-grid .wt-post-info {
    display: flex;
    flex-direction: column;
    flex-grow: 1;
    justify-content: space-between;
}

.portfolio-wrap.news-grid .wt-post-title {
    margin-bottom: var(--space-4);
}

.portfolio-wrap.news-grid .wt-post-title .post-title {
    font-size: 17px;
    line-height: 1.45;
    font-weight: 600;
    display: -webkit-box;
    -webkit-line-clamp: 2;
    -webkit-box-orient: vertical;
    overflow: hidden;
}

.portfolio-wrap.news-grid .site-button-link {
    margin-top: auto;
}


/* --- Services page grid --------------------------------------------------
   #dynamic-services-grid only exists on services.html. The cards show a short
   preview and open the full copy in a dialog, so they can be a uniform height
   here in a way the home page's static, full-text cards cannot. */
#dynamic-services-grid > [class*="col-"] {
    display: flex;
}

#dynamic-services-grid .wt-icon-box-wraper {
    display: flex;
    flex-direction: column;
    justify-content: space-between;
    width: 100%;
    margin-bottom: 0;
}

#dynamic-services-grid .icon-content {
    flex: 1 1 auto;
    display: flex;
    flex-direction: column;
}

#dynamic-services-grid .icon-content p {
    flex: 1 1 auto;
}

/* "Read More" is an action, not a destination, so it is a <button>. Strip the
   control chrome so it still reads as the template's text link. */
.service-more-btn {
    margin-top: auto;
    align-self: flex-start;
    background: none;
    border: 0;
    padding: 0;
    font: inherit;
    letter-spacing: inherit;
    cursor: pointer;
}

/* --- Service dialog ------------------------------------------------------ */
.service-modal .modal-content {
    border: 0;
    border-radius: var(--radius-lg);
    box-shadow: var(--shadow-3);
}

.service-modal .modal-header,
.service-modal .modal-footer {
    border-color: var(--ink-200);
    padding: var(--space-5);
}

.service-modal .modal-title {
    font-size: 22px;
    line-height: 1.3;
}

.service-modal .modal-body {
    padding: var(--space-5);
}

.service-modal .modal-body p {
    margin: 0;
    font-size: 15px;
    line-height: 1.7;
}

.service-modal-media {
    width: 100%;
    aspect-ratio: 16 / 9;
    object-fit: cover;
    border-radius: var(--radius-md);
    margin-bottom: var(--space-5);
}

/* --- Cards rendered by js/dynamic.js -------------------------------------
   These used to carry a style="" attribute per element, rebuilt on every
   render. */
.service-card-media {
    width: 100%;
    aspect-ratio: 3 / 2;
    object-fit: cover;
    border-radius: var(--radius-md);
    margin-bottom: var(--space-4);
}

.project-card-media {
    width: 100%;
    aspect-ratio: 4 / 3;
    object-fit: cover;
}

.project-card-tag {
    display: inline-block;
    margin-top: var(--space-3);
    padding: 3px 10px;
    font-size: 12px;
    font-weight: 500;
    letter-spacing: 0.03em;
    border-radius: var(--radius-pill);
    background: rgba(255, 255, 255, 0.2);
}

.project-detail-media {
    width: 100%;
    height: auto;
    border-radius: var(--radius-md);
}

/* The home page drops its image column 200px so it reads as staggered against
   the About copy beside it. That only works while the two are side by side -
   once col-lg-6 stacks, the offset is just 200px of blank page between the
   text and the first image. It used to be an inline style, which no media
   query could reach. */
@media only screen and (min-width: 992px) {
    .arc-home-about-right.has-column-offset {
        margin-top: 200px;
    }
}

/* The home page keeps its still photos still: no glass-shine sweep on hover,
   in either the About image grid or the projects carousel. about-1.html still
   uses .img-reflection, so this is scoped rather than cut from the base rule. */
.arc-home-about-right .img-reflection::after,
.projects-two-info .img-reflection::after {
    content: none;
}


/* --- Article pages ------------------------------------------------------- */
/* The post templates pinned images to a fixed 350px, which crops portrait
   renders badly once the column narrows. */
.blog-post .wt-post-media img,
.wt-post-media.wt-img-effect img {
    aspect-ratio: 16 / 10;
}

.blog-post.date-style-2 .wt-post-info {
    padding-top: var(--space-5);
}

/* On the overlay cards the day is white but the month/year keeps the light
   theme's grey, so it disappears into the photo behind it. */
.post-overlay .post-date,
.post-overlay .post-date span,
.post-overlay .post-date strong {
    color: #fff;
}


/* ==========================================================================
   11. MOBILE
   ========================================================================== */

/* Nothing on the page may push the viewport sideways. Only the root element
   gets `hidden` - putting it on both makes the body its own scroll container,
   so the body grows a second scrollbar and every section stops ~15px short of
   the right edge. `clip` contains the overflow without that side effect. */
html {
    overflow-x: hidden;
    max-width: 100%;
}

body {
    overflow-x: clip;
    max-width: 100%;
}

img,
video,
iframe,
table {
    max-width: 100%;
}

@media only screen and (max-width: 991px) {
    .our-services-section .wt-icon-box-wraper {
        min-height: 0;
    }
}

@media only screen and (max-width: 767px) {
    body {
        font-size: 15px;
    }

    /* Comfortable tap targets throughout. */
    .header-nav .nav > li > a,
    .site-button,
    .footer-social-content ul li a,
    .widget_services a {
        min-height: 44px;
        display: flex;
        align-items: center;
    }

    .site-button {
        justify-content: center;
        width: 100%;
    }

    .footer-social-content ul li a,
    .widget_services a {
        display: inline-flex;
    }

    .projects-two-info p {
        min-height: 0;
    }

    .footer-logo-bar {
        justify-content: center;
        text-align: center;
    }

    /* The section paddings in the vendor sheet are tuned for desktop and
       leave huge gaps on a phone. */
    .section-full.p-t80,
    .section-full.p-tb80 {
        padding-top: var(--space-7);
    }

    .section-full.p-b80,
    .section-full.p-tb80 {
        padding-bottom: var(--space-7);
    }

    /* ...but the home wrapper is also a .section-full.p-b80, and the rule above
       outweighs the padding-bottom:0 it was given earlier, so the grey strip
       came back between the dark partner band and the dark footer on phones
       only. Matched on two classes here so it outranks that rule. */
    .section-full.home-content-wrap {
        padding-bottom: 0;
    }
}

@media only screen and (max-width: 480px) {
    .site-button {
        padding: 13px 20px;
    }
}
