/* ==========================================================================
   Lumicore Sinergi Awan - brand overrides on top of the Digix template.
   Loaded after assets/css/style.css so every rule here wins.
   Keep template files untouched; put all brand tweaks in this file.
   ========================================================================== */

/* --- Type system ---------------------------------------------------------
   Two families, taken from the hero redesign and applied site-wide so the top
   of the page does not speak a different language from the rest of it.

   Outfit is geometric and carries display sizes; Manrope is humanist and was
   drawn for reading at body sizes. The template used Inter Tight for both jobs,
   which is why headings and paragraphs read at the same pitch.

   Outfit's geometry is already optically heavy, so headings are left at the
   weight the template gives them rather than pushed to bold, and the negative
   tracking closes the gaps that open between capitals as the size grows.

   Reverting is a two-line edit: point both variables back at "Inter Tight". */
:root {
    --lumicore-font-display: "Outfit", sans-serif;
    --lumicore-font-body: "Manrope", system-ui, sans-serif;
}

body,
var {
    font-family: var(--lumicore-font-body);
}

h1, h2, h3, h4, h5, h6,
.h1, .h2, .h3, .h4, .h5, .h6 {
    font-family: var(--lumicore-font-display);
    letter-spacing: -.02em;
}

/* Tracked capitals, counters and button labels are display work too — they are
   set in small caps or figures, where Manrope's wider spacing goes loose. */
.pre,
.pre-title,
.odometer,
.rts-btn,
.btn-line {
    font-family: var(--lumicore-font-display);
}

/* --- Primary buttons -----------------------------------------------------
   The template paints every solid button with --color-primary, the same blue
   that carries the badges, checkmarks, links and slider arrows. Repointing
   that variable would take all of them to navy and leave the page without an
   accent, so the colour is set on the button alone.

   The value is the header's rgba(7, 20, 49, .96) flattened over white, which
   is how the bar actually reads on the page. If the header navy below ever
   moves, this moves with it. */
:root {
    --lumicore-btn-navy: #111D39;
}

.rts-btn.btn-primary {
    background: var(--lumicore-btn-navy);
}

/* Navy on navy is an invisible button, so the three surfaces that are already
   dark keep the accent blue: the header bar, the call-to-action panel's
   artwork, and the footer. */
header .rts-btn.btn-primary,
.call-to-action-wrapper-three .rts-btn.btn-primary,
.rts-footer-one .rts-btn.btn-primary {
    background: var(--color-primary);
}

/* --- Sticky header -------------------------------------------------------
   The stock template leaves the header `absolute` and swaps it to `fixed`
   only past 150px, so it scrolls away with the hero and then teleports back
   to the top. `.sticky` also carries no transition, which is why the
   background snaps from transparent straight to navy.

   Keeping the header `fixed` at all times removes the jump entirely, and
   leaves background, height and logo size free to animate together as one
   transition. The `.sticky` class is still toggled by assets/js/main.js. */
header.header-one.header--sticky {
    position: fixed;
    top: 0;
    background-color: rgba(7, 20, 49, 0);
    box-shadow: 0 7px 18px rgba(24, 16, 16, 0);
    transition: background-color .4s ease, box-shadow .4s ease, padding .4s ease;
}

header.header-one.header--sticky.sticky {
    background-color: rgba(7, 20, 49, .96);
    box-shadow: 0 7px 18px rgba(24, 16, 16, .18);
    -webkit-backdrop-filter: blur(10px);
    backdrop-filter: blur(10px);
}

/* Header height lives on the nav links' vertical padding in this template,
   so growing or shrinking the bar means changing those.

   The stock 25px of padding gives a 76px bar, which reads as cramped next to
   the 38px logo and the 48px CTA — there is barely a finger of air above and
   below them. 37px opens it to 100px, closer to the proportions the reference
   sites use, and the sticky state settles back at the old 76px so scrolling
   still hands the page back a chunk of viewport. */
header.header-one .nav-area ul li a {
    padding-top: var(--lumicore-nav-padding);
    padding-bottom: var(--lumicore-nav-padding);
}

header.header-one.header--sticky .nav-area ul li a {
    transition: padding .4s ease, color .3s ease, opacity .3s ease;
}

header.header-one.header--sticky.sticky .nav-area ul li a {
    padding-top: var(--lumicore-nav-padding-sticky);
    padding-bottom: var(--lumicore-nav-padding-sticky);
}

/* --- Clearance below the fixed header ------------------------------------
   Because the header is fixed it takes no space in the layout, so anything
   that starts at the top of the page slides underneath it. The template's
   heroes were padded for a header that did the same, and the numbers were
   already too tight: 80px of padding against a 76px header left 4px of air,
   and on a phone 60px against a 60px header left none at all.

   Declaring the header's height once lets each hero ask for "the header plus
   a gap" instead of carrying a hard-coded number that has to be re-checked
   whenever the bar changes. */
:root {
    /* A nav link is 16px/26px text, so the bar is that line box plus the
       padding above and below it. Deriving the height instead of restating it
       means tuning the bar is a one-number edit. */
    --lumicore-nav-line: 26px;
    --lumicore-nav-padding: 37px;
    --lumicore-nav-padding-sticky: 25px;
    --lumicore-header-height: calc(var(--lumicore-nav-padding) * 2 + var(--lumicore-nav-line));
    --lumicore-header-gap: 56px;
}

/* The nav collapses to a burger here, so the links no longer set the height.
   With the CTA button removed from the bar, the tallest thing left is the
   language dropdown at 44px, which puts the bar at 74px. Measured, not
   assumed — and it is the same figure all the way down to the smallest
   phone, because the dropdown keeps that size. */
@media only screen and (max-width: 1199px) {
    :root {
        --lumicore-header-height: 74px;
        --lumicore-header-gap: 40px;
    }
}

@media only screen and (max-width: 575px) {
    :root {
        --lumicore-header-gap: 32px;
    }
}

/* Centred and left-aligned breadcrumb heroes: blog, contact, FAQ, article,
   service detail, generic pages. */
.rts-service-details-breadcrumb-area {
    padding-top: calc(var(--lumicore-header-height) + var(--lumicore-header-gap));
}

/* The split hero — about and services index — is a fixed 541px box that
   centres its row. The headline column is short enough to sit clear of the
   bar, which is why this looked fine, but the photo beside it is nearly 400px
   tall: centred in 541px it starts around 75px down and slides under the nav.

   Padding the header's height onto the top and letting `min-height` carry the
   541px through means the row still centres in exactly the box the template
   designed, only now that box begins where the bar ends. Everything is
   border-box here, so the padding is inside the min-height and the visible
   composition keeps its proportions. */
@media only screen and (min-width: 992px) {
    .rts-about-breadcrumb-area {
        height: auto;
        min-height: calc(541px + var(--lumicore-header-height));
        padding-top: var(--lumicore-header-height);
    }
}

/* Below 991px it collapses to 50px of padding and stacks, so it needs the
   plain clearance the other heroes get. */
@media only screen and (max-width: 991px) {
    .rts-about-breadcrumb-area {
        padding-top: calc(var(--lumicore-header-height) + var(--lumicore-header-gap));
    }
}

/* --- Header over a light hero --------------------------------------------
   The template paints the nav links, the dropdown chevrons and the mobile
   toggle white (`header .main-nav > a`, `.has-dropdown::after`,
   `.menu-btn-toggle.white rect`). That only reads on the homepage banner —
   every other page opens with a white breadcrumb hero, so the menu was
   invisible until the bar went sticky at 70px.

   `.header--on-light` is set by the header partial on those pages and flips
   the menu to the brand navy while the bar is still transparent. Past 70px
   `.sticky` paints the bar navy, and the template's white is correct again. */
header.header--on-light:not(.sticky) .nav-area .main-nav > a,
header.header--on-light:not(.sticky) .nav-area .has-dropdown.mega-menu > a {
    color: #071431;
}

header.header--on-light:not(.sticky) .nav-area .has-dropdown::after,
header.header--on-light:not(.sticky) .nav-area .has-dropdown.mega-menu > a::after {
    color: #071431;
}

header.header--on-light:not(.sticky) .menu-btn-toggle.white rect {
    fill: #071431;
}

/* --- Logo sizing ---------------------------------------------------------
   The stock template ships SVG logos with intrinsic sizes (131x43 header,
   119x39 footer). The Lumicore logo is a wide PNG, so it needs an explicit
   height or it renders at full resolution.

   The header logo also scales down with the bar as it becomes sticky. */
.header-wrapper-main .logo-area a img,
.rts-sidebar-menu-desktop .logo-area a img {
    height: 38px;
    width: auto;
}

header.header--sticky .logo-area a img {
    transition: height .4s ease, filter .4s ease;
}

header.header--sticky.sticky .logo-area a img {
    height: 31px;
}

/* --- Logo on a dark bar --------------------------------------------------
   The brand logo is navy-blue wordmark artwork, which disappears against a
   navy background. Two situations put it there:

   1. The homepage banner. Every page that opens on a white breadcrumb hero
      carries `.header--on-light`, so the absence of that class is exactly
      "the header is sitting on the dark hero".
   2. Any page once the bar goes `.sticky` and paints itself navy — including
      the inner pages, where the logo is navy-on-white before that point and
      must stay untouched.

   `brightness(0)` crushes every colour channel to black and `invert(1)` flips
   it to white, leaving the alpha channel alone so the transparent background
   of the PNG stays transparent. On the inner pages the flip rides the same
   .4s transition as the bar's background, so the two land together. */
header.header-one:not(.header--on-light) .logo-area a img,
header.header-one.sticky .logo-area a img {
    filter: brightness(0) invert(1);
}

/* Someone who asked for less motion gets the colour change without the
   size and position animation. */
@media (prefers-reduced-motion: reduce) {

    header.header-one.header--sticky,
    header.header-one.header--sticky .nav-area ul li a,
    header.header--sticky .logo-area a img {
        transition: background-color .4s ease, filter .4s ease;
    }
}

.rts-footer-one .left-wiget .logo img {
    height: 34px;
    width: auto;
}

/* Same treatment as the header, for the same reason: the footer is navy on
   every page. Tied to `.bg_dark` rather than the footer alone, so if a page
   ever asks for the light footer again the wordmark goes back to its own
   colours instead of vanishing into the pale background. */
.rts-footer-one.bg_dark .left-wiget .logo img {
    filter: brightness(0) invert(1);
}

@media only screen and (max-width: 575px) {

    .header-wrapper-main .logo-area a img,
    .rts-sidebar-menu-desktop .logo-area a img {
        height: 30px;
    }

    /* At this size the bar is already compact — shrinking it further just
       makes the logo hard to read, so hold it at the resting size. */
    header.header--sticky.sticky .logo-area a img {
        height: 30px;
    }
}

/* The template caps the logo link at 90px below 480px, sized for the narrow
   SVG mark it shipped with. Ours is a 1062x235 wordmark, and because the base
   stylesheet also gives every image `max-width: 100%`, that cap became the
   image's width while our rule above held the height at 30px — 90x30 renders
   the wordmark at a 3.0 ratio instead of its true 4.52, squashing it by a
   third. Measured with the page open, not inferred.

   Lifting the cap lets the height drive both dimensions again: 30px tall
   becomes 136px wide, which still leaves well over half the bar free on a
   320px screen once the language switcher moves into the menu. */
@media only screen and (max-width: 479px) {
    .header-wrapper-main .logo-area a {
        max-width: none;
    }
}

/* --- Footer widgets ------------------------------------------------------
   The stock footer relies on `space-between` with no explicit widths, so a
   long postal address squeezes the other columns to nothing. Give the columns
   a shared basis and let the contact column keep a bit more room. */
.rts-footer-one .footer-wized-wrapper {
    gap: 40px;
}

.rts-footer-one .footer-wized-wrapper .single {
    flex: 1 1 0;
    min-width: 0;
}

.rts-footer-one .footer-wized-wrapper .single.contact {
    flex: 1.5 1 0;
}

.rts-footer-one .footer-wized-wrapper .single.contact ul li {
    line-height: 1.7;
}

@media only screen and (max-width: 767px) {
    .rts-footer-one .footer-wized-wrapper {
        flex-wrap: wrap;
        gap: 30px;
    }

    .rts-footer-one .footer-wized-wrapper .single {
        flex: 1 1 45%;
    }
}

/* --- Homepage "about" collage --------------------------------------------
   The stock images are portrait and fill the column on their own. Lumicore's
   photos are landscape, so crop them to the same proportions instead of
   letting them shrink to their intrinsic size. */
.about-area-inner-wrapper-thumbnail .left-image {
    flex: 1 1 0;
    min-width: 0;
}

.about-area-inner-wrapper-thumbnail .left-image img {
    width: 100%;
    height: 510px;
    object-fit: cover;
    border-radius: 4px;
}

.about-area-inner-wrapper-thumbnail .left-image.last img {
    height: 430px;
}

@media only screen and (max-width: 767px) {
    .about-area-inner-wrapper-thumbnail .left-image img {
        height: 320px;
    }

    .about-area-inner-wrapper-thumbnail .left-image.last img {
        height: 260px;
    }
}

/* Vision / smart-manufacturing block.
   Unlike the collage above, this is one photo standing alone in its column,
   and the admin uploads whatever shape the subject came in — a square process
   diagram, a portrait shot of a line, a wide factory floor. A fixed pixel
   height cropped all of them the moment the column stopped being that wide:
   at col-lg-5 the box is roughly 510x520, near enough to square that little
   was lost, but on a phone the column collapses to ~345px while the height
   stayed 520, turning the box portrait and cutting a third off the width of a
   square image — about a sixth from each side, which is where the labels
   ringing the Industry 4.0 wheel live.

   So the photo keeps its own proportions instead of being poured into a box:
   the height follows the width at whatever ratio the file has, and the cap
   below only stops an unusually tall portrait from taking over the section.
   `contain` is what the cap falls back to, so a clamped image letterboxes
   rather than squashing — either way nothing is cut off. */
.vision-left-main-image .thumbnail-vision img {
    display: block;
    width: 100%;
    height: auto;
    max-height: 520px;
    object-fit: contain;
    border-radius: 6px;
}

@media only screen and (max-width: 767px) {

    /* 520px of photo is most of a phone screen; a lower cap keeps the copy
       beside it within reach of the same scroll. */
    .vision-left-main-image .thumbnail-vision img {
        max-height: 420px;
    }
}

/* --- "Why choose us" collage ---------------------------------------------
   Two images sit side by side in a flex row that the template never gave a
   width to — it relied on its own art being the right size: 281x471 and
   300x365, which add up with the 30px gap to just under the column.

   The CMS hands the block whatever the editor uploaded. A pair of 1600px
   photographs asks for 3230px inside a ~570px column, and because the row is
   justified to the end, the five-fold overflow runs off to the LEFT — straight
   over the heading and the reason list beside it, which is what made the
   section look scrambled rather than merely too wide.

   Letting them share the row fixes it for any upload. `min-width: 0` is the
   part that actually does the work: a flex item defaults to min-width:auto and
   would otherwise refuse to shrink below its intrinsic width, no matter what
   basis it is given. Heights stay automatic, so nothing is cropped. */
.why-choose-iamge-two img {
    flex: 1 1 0;
    min-width: 0;
    width: 100%;
    height: auto;
}

/* --- Client logo grid ----------------------------------------------------
   Client marks come in mixed sizes and background colours; normalise them to
   a common box so the row reads as one band. */
.lumicore-client-grid {
    --lumicore-client-height: 54px;
}

.lumicore-client-item {
    display: flex;
    align-items: center;
    justify-content: center;
    height: 110px;
    padding: 18px 24px;
    background: #fff;
    border: 1px solid #e9ecef;
    border-radius: 8px;
    transition: border-color .3s ease, transform .3s ease, box-shadow .3s ease;
}

.lumicore-client-item:hover {
    border-color: var(--color-primary, #2c5cff);
    transform: translateY(-4px);
    box-shadow: 0 12px 30px rgba(0, 0, 0, .06);
}

.lumicore-client-item img {
    max-height: var(--lumicore-client-height);
    max-width: 100%;
    width: auto;
    object-fit: contain;
    filter: grayscale(100%);
    opacity: .65;
    transition: filter .3s ease, opacity .3s ease;
}

.lumicore-client-item:hover img {
    filter: grayscale(0);
    opacity: 1;
}

/* --- Achievement ---------------------------------------------------------
   Rebuilt on the reference layout in digix-main/index.html: a dark band, a
   full-width photo, and the lead counter dropped onto its bottom-right corner
   in brand blue. The template already carries `.achievment-large-image-video`
   and `.right-bottom-area`, so the only thing missing was a crop box.

   The reference art is 1290x600 and the template rule only says `width: 100%`,
   which works when the image is cut to that shape and fails on anything else —
   the photo this falls back to is 1920x1280, and left alone it would stand
   870px tall and swallow the section. Stating the reference's ratio here means
   any photo the admin uploads lands in the same band. */
.achievment-large-image-video .achiv-m-img {
    display: block;
    aspect-ratio: 1290 / 600;
    /* Faces and skylines both live in the upper half of a photo, and the
       counter block covers the bottom-right corner regardless. */
    object-fit: cover;
    object-position: center 35%;
}

@media only screen and (max-width: 767px) {

    /* A 2.15:1 slice of a phone-width photo is barely 170px tall — too thin to
       read as an image. Squarer proportions give it something to show. */
    .achievment-large-image-video .achiv-m-img {
        aspect-ratio: 4 / 3;
    }
}

/* Stat icons are brand-blue-on-white artwork, chosen back when they sat on the
   light counter band — and the block behind them is now that exact blue, so the
   glyph sinks into it. Crushing every channel to white is the same move the
   header and footer logos make above, and it holds whichever icon the admin
   assigns to whichever counter ends up leading. The reference art is 98px; the
   icons here are drawn at 60, small enough next to a 60px numeral to read as an
   afterthought, so they are sized up to sit level with it. */
.achievment-large-image-video .right-bottom-area .icon img {
    width: 72px;
    height: auto;
    filter: brightness(0) invert(1);
}

/* The counters that do not lead. The template's `.single-fun-facts` is built
   for the light band this section used to be, so these get their own rules
   rather than an override war with it. */
.lumicore-achievement-stats {
    display: flex;
    flex-wrap: wrap;
    margin-top: 60px;
}

.lumicore-achievement-stat {
    flex: 1 1 200px;
    padding: 0 30px;
    border-left: 1px solid rgba(255, 255, 255, .12);
}

/* The rule belongs between the counters, not down the outside of the row. */
.lumicore-achievement-stat:first-child {
    padding-left: 0;
    border-left: 0;
}

.lumicore-achievement-stat__value {
    margin: 0 0 8px;
    color: #fff;
    font-size: 48px;
    font-weight: 500;
    line-height: 1;
}

.lumicore-achievement-stat__label {
    margin: 0;
    color: rgba(255, 255, 255, .62);
    font-size: 16px;
    line-height: 1.5;
}

@media only screen and (max-width: 767px) {

    .lumicore-achievement-stats {
        margin-top: 40px;
        gap: 30px 0;
    }

    /* Two per row on a phone, so the divider has to reset on every other one
       rather than only on the first. */
    .lumicore-achievement-stat {
        flex: 0 0 50%;
        padding: 0 16px;
    }

    .lumicore-achievement-stat:nth-child(odd) {
        padding-left: 0;
        border-left: 0;
    }

    .lumicore-achievement-stat__value {
        font-size: 36px;
    }
}

/* --- Team and testimonial rails ------------------------------------------
   Both rails are driven by their own Swiper instances rather than the ones
   assets/js/main.js sets up, because those point `navigation` at the bare
   `.swiper-button-next` / `.swiper-button-prev` selectors. Swiper resolves a
   string selector against the whole document, so the banner at the top of the
   page already claims every element carrying those classes — arrows named that
   way anywhere below would drive the hero instead of their own rail.

   Which leaves these arrows without the template's styling, since that too is
   written against `.team-swiper-area .swiper-button-next`. Restated here, same
   look: a grey disc at the edge of the rail that fills with brand blue. */
.team-swiper-area {
    /* The arrows sit against this box; nothing in the template positions it. */
    position: relative;
}

/* Photos are uploaded at whatever ratio the cut-out arrived in — a 1122x1402
   portrait sits next to a 1448x1086 landscape. The template never constrains
   the thumbnail, so each slide renders its image at the slide's width times
   the image's own ratio and every photo ends up a different height, taking the
   name below it along. Fix the box rather than the assets: one ratio for every
   slide, and the image contained inside it. Contained, not covered — these are
   people cut out against a plain backdrop, and cropping to fill would slice
   heads off the portraits. Standing them on the bottom edge gives the subjects
   a shared floor instead of leaving each one floating at its own height. */
.team-swiper-area .single-team-style-one .thumbnail {
    display: flex;
    align-items: flex-end;
    justify-content: center;
    aspect-ratio: 3 / 4;
    /* Narrower than the slide it sits in. The cut-outs are on white and the
       section behind them is #F9FAFF, so photos running edge to edge left no
       visible seam and the row read as one unbroken band. Holding them in from
       the sides is what makes the gap show, without putting a card around the
       picture. */
    width: 85%;
    margin-inline: auto;
}

.team-swiper-area .single-team-style-one .thumbnail img {
    width: 100%;
    height: 100%;
    object-fit: contain;
    object-position: bottom center;
}

/* Names run long enough to wrap, and a two-line name would otherwise push its
   designation a line below its neighbours'. Hold two lines for every card. */
.team-swiper-area .single-team-style-one .inner-content .title {
    line-height: 1.35;
    min-height: 2.7em;
}

.lumicore-rail-arrow {
    position: absolute;
    top: 50%;
    z-index: 2;
    display: flex;
    align-items: center;
    justify-content: center;
    width: 50px;
    height: 50px;
    padding: 0;
    transform: translateY(-50%);
    border: none;
    border-radius: 50%;
    background: rgba(128, 128, 128, .55);
    color: #fff;
    font-size: 16px;
    cursor: pointer;
    transition: background-color .3s ease, opacity .3s ease;
}

.lumicore-rail-arrow:hover {
    background: var(--color-primary);
}

.lumicore-rail-arrow--prev {
    left: -24px;
}

.lumicore-rail-arrow--next {
    right: -24px;
}

/* Swiper sets this at either end of a rail that does not loop. Without a rule
   the button looks live at a point where it does nothing. */
.lumicore-rail-arrow.swiper-button-disabled,
.testimonials-left-one .swiper-btn-area .swiper-btn.swiper-button-disabled {
    opacity: .35;
    cursor: default;
    pointer-events: none;
}

/* The template's own quote mark is a 24x17 PNG and its intrinsic size is the
   only thing holding it down — there is no width rule anywhere. The vector
   copy is 245x279, so left alone it covers the whole card. Stating a height
   also means a replacement asset cannot blow the card apart. */
.single-testimonails-style-one .quote img {
    width: auto;
    height: 34px;
}

/* Below 991px the rail runs to the container's edge, so arrows hanging 24px
   outside it end up off-screen. */
@media only screen and (max-width: 991px) {

    .lumicore-rail-arrow--prev {
        left: 0;
    }

    .lumicore-rail-arrow--next {
        right: 0;
    }
}

/* --- Photo strips in article bodies --------------------------------------
   Rich-text bodies drop rows of thumbnails that open in the light-box. The
   photos arrive at mixed sizes, so crop them to a uniform tile. */
.lumicore-gallery-item {
    display: block;
    position: relative;
    overflow: hidden;
    border-radius: 10px;
    background: #f3f5f8;
}

.lumicore-gallery-item img {
    width: 100%;
    height: 260px;
    object-fit: cover;
    transition: transform .5s ease;
}

.lumicore-gallery-item:hover img,
.lumicore-gallery-item:focus-visible img {
    transform: scale(1.06);
}

/* --- Project cards -------------------------------------------------------
   The home page gallery is screenshots of products we shipped, and it was
   drawn with the article-strip rules above. Two things made that read badly.

   The shots are ~1.95:1 laptop captures, and the tile they were poured into
   was about 1.5:1 — so `cover` scaled each one until its height fit and threw
   away a tenth of the width off either side. That is exactly the strip a web
   page keeps its logo and its nav in, so the products came out reading as
   "1ub" and "etafio".

   And bled edge to edge with no frame, six products with six different
   background colours sat against each other as a patchwork. The caption only
   existed on hover, so at rest the grid said nothing about what any of it was.

   The fix is the same three moves: a browser chrome so each tile reads as a
   product rather than a photo, a crop box that matches the shape the
   screenshots actually are, and the title and blurb kept on the card.

   The card itself borrows its anatomy from `.single-blog-style-one` in the
   Latest Insights row: square corners, no outline, the same soft ambient
   shadow, and the same 20px mat holding the image away from the card's edge.
   An outlined box drawn flush to its picture is what read as flat — depth here
   comes from the inset and the shadow, exactly as it does over there. */
.lumicore-projects .lumicore-gallery-item {
    display: flex;
    flex-direction: column;
    /* Bootstrap columns already stretch, so this is what levels the cards. */
    height: 100%;
    padding: 20px 20px 33px;
    background: #fff;
    box-shadow: 0 4px 20px rgba(0, 0, 0, .05);
    transition: transform .45s cubic-bezier(.22, .61, .36, 1),
        box-shadow .45s ease;
}

.lumicore-projects .lumicore-gallery-item:hover,
.lumicore-projects .lumicore-gallery-item:focus-visible {
    transform: translateY(-6px);
    box-shadow: 0 18px 40px rgba(7, 20, 49, .12);
}

/* The browser chrome. Deliberately grey and dotted rather than a coloured
   traffic light — it should frame the screenshot, not compete with it.

   Its own shadow is what sells the window as sitting on the card rather than
   being printed into it, and it is the one rounded thing on a square card:
   a browser with sharp corners stops reading as a browser. */
.lumicore-project__frame {
    display: block;
    overflow: hidden;
    border-radius: 6px;
    background: #eef1f7;
    box-shadow: 0 6px 18px rgba(7, 20, 49, .1);
    transition: box-shadow .45s ease;
}

.lumicore-projects .lumicore-gallery-item:hover .lumicore-project__frame,
.lumicore-projects .lumicore-gallery-item:focus-visible .lumicore-project__frame {
    box-shadow: 0 12px 26px rgba(7, 20, 49, .16);
}

.lumicore-project__bar {
    display: flex;
    align-items: center;
    gap: 6px;
    height: 34px;
    padding: 0 14px;
}

.lumicore-project__bar i {
    width: 9px;
    height: 9px;
    border-radius: 50%;
    background: rgba(7, 20, 49, .16);
}

.lumicore-project__url {
    flex: 1;
    max-width: 210px;
    height: 13px;
    margin-left: 10px;
    border-radius: 999px;
    background: rgba(7, 20, 49, .07);
}

/* 1.9:1 is what the screenshots already are, so `cover` has next to nothing
   left to crop and no logo loses its first letter. */
.lumicore-project__shot {
    display: block;
    position: relative;
    overflow: hidden;
    aspect-ratio: 1.9 / 1;
    background: #f3f5f8;
}

.lumicore-projects .lumicore-gallery-item img {
    width: 100%;
    height: 100%;
    /* Anchored to the top: a page's header and hero is the part worth
       showing, and a taller shot then loses its footer rather than its
       sides. */
    object-fit: cover;
    object-position: top center;
    transition: transform .6s cubic-bezier(.22, .61, .36, 1);
}

.lumicore-projects .lumicore-gallery-item:hover img,
.lumicore-projects .lumicore-gallery-item:focus-visible img {
    transform: scale(1.04);
}

/* At this size the UI inside a screenshot is unreadable, so the card has to
   say that the full-size version is one click away. */
.lumicore-project__zoom {
    position: absolute;
    inset: 0;
    display: flex;
    align-items: center;
    justify-content: center;
    background: rgba(7, 20, 49, .32);
    opacity: 0;
    transition: opacity .35s ease;
}

.lumicore-project__zoom i {
    display: flex;
    align-items: center;
    justify-content: center;
    width: 52px;
    height: 52px;
    border-radius: 50%;
    background: rgba(255, 255, 255, .94);
    color: #071431;
    font-size: 17px;
    transform: scale(.85);
    transition: transform .35s cubic-bezier(.22, .61, .36, 1);
}

.lumicore-projects .lumicore-gallery-item:hover .lumicore-project__zoom,
.lumicore-projects .lumicore-gallery-item:focus-visible .lumicore-project__zoom {
    opacity: 1;
}

.lumicore-projects .lumicore-gallery-item:hover .lumicore-project__zoom i,
.lumicore-projects .lumicore-gallery-item:focus-visible .lumicore-project__zoom i {
    transform: scale(1);
}

/* The mat already holds the text off the card's edge, so the body only owes
   the row its top gap — the same 30px the Latest Insights card leaves. */
.lumicore-project__body {
    display: flex;
    flex: 1;
    flex-direction: column;
    gap: 10px;
    padding: 30px 0 0;
}

.lumicore-project__title {
    display: block;
    color: var(--color-heading-1);
    font-size: 20px;
    font-weight: 500;
    line-height: 1.4;
    transition: color .3s ease;
}

.lumicore-projects .lumicore-gallery-item:hover .lumicore-project__title,
.lumicore-projects .lumicore-gallery-item:focus-visible .lumicore-project__title {
    color: var(--color-primary);
}

/* The blog card ends on a "Read More"; this one ends on the same line, pinned
   to the bottom so the row shares a baseline whatever the blurb's length.
   `.btn-line` is scoped to `a` in the template and the card is already a link,
   so the look is restated here rather than nested illegally. */
.lumicore-project__more {
    display: flex;
    align-items: center;
    gap: 7px;
    margin-top: auto;
    padding-top: 22px;
    color: var(--color-heading-1);
    font-size: 16px;
    font-weight: 600;
    line-height: 1;
    transition: color .3s ease;
}

.lumicore-project__more span {
    text-decoration: underline;
    text-underline-offset: 5px;
    text-decoration-thickness: 2px;
}

.lumicore-projects .lumicore-gallery-item:hover .lumicore-project__more,
.lumicore-projects .lumicore-gallery-item:focus-visible .lumicore-project__more {
    color: var(--color-primary);
}

/* Blurbs are written to whatever length the author felt like. Two lines keeps
   the row of cards on one rhythm; the full text is in the light-box caption. */
.lumicore-project__text {
    display: -webkit-box;
    overflow: hidden;
    color: rgba(7, 20, 49, .6);
    font-size: 15px;
    line-height: 1.6;
    -webkit-box-orient: vertical;
    -webkit-line-clamp: 2;
    line-clamp: 2;
}

/* --- Project slider ------------------------------------------------------
   Six cards as a static two-row grid asked the page for a lot of height and
   gave nothing back, and the section could only ever hold as many projects as
   fit above the fold. On a rail they cost one row, the set can grow without
   the page growing with it, and the movement matches the banner, testimonials
   and blog strips that already slide elsewhere on this page. */
.lumicore-projects-slider .swiper {
    /* Cards lift and cast a shadow on hover, and `.swiper` has to clip its
       overflow for the rail to work at all — so pad the clip box outward and
       pull it back with a matching negative margin. The slides still line up
       with the section title; only the edge that crops the shadow moves. */
    padding: 14px 24px 34px;
    margin: -14px -24px -34px;
}

.lumicore-projects .swiper-slide {
    /* Swiper hands slides `height: 100%`, which leaves a card with a short
       blurb short. `auto` lets the flex row stretch them all to the tallest. */
    height: auto;
}

/* Until Swiper initialises and writes its inline widths, every slide is 100%
   wide — one giant card, then a snap to three. Restating the breakpoints here
   keeps the first paint honest; the class Swiper adds retires these. */
.lumicore-projects-slider .swiper:not(.swiper-initialized) .swiper-slide {
    width: calc(100% / 1.08 - 16px);
    margin-right: 16px;
}

@media (min-width: 576px) {

    .lumicore-projects-slider .swiper:not(.swiper-initialized) .swiper-slide {
        width: calc(100% / 1.6 - 20px);
        margin-right: 20px;
    }
}

@media (min-width: 768px) {

    .lumicore-projects-slider .swiper:not(.swiper-initialized) .swiper-slide {
        width: calc(100% / 2.2 - 24px);
        margin-right: 24px;
    }
}

@media (min-width: 992px) {

    .lumicore-projects-slider .swiper:not(.swiper-initialized) .swiper-slide {
        width: calc((100% - 48px) / 3);
        margin-right: 24px;
    }
}

/* Progress line on the left, arrows on the right. Bullets would only ever
   read as "two of four" here; a line shows how much of the set is left. */
.lumicore-projects-nav {
    display: flex;
    align-items: center;
    gap: 24px;
    margin-top: 34px;
}

/* A gallery short enough to fit in one screen has nothing to page through, and
   Swiper locks both controls out of sight. Collapse the row with them rather
   than leave a band of empty space under the cards. */
.lumicore-projects-nav:has(.swiper-pagination-lock) {
    display: none;
}

.lumicore-projects-nav .lumicore-projects-progress {
    /* Swiper's own progressbar is absolutely positioned across the top of the
       slider. This one sits in the flow of the row instead. */
    position: relative;
    top: auto;
    left: auto;
    flex: 1;
    max-width: 360px;
    height: 3px;
    overflow: hidden;
    border-radius: 999px;
    background: rgba(7, 20, 49, .1);
}

.lumicore-projects-nav .lumicore-projects-progress .swiper-pagination-progressbar-fill {
    border-radius: 999px;
    background: var(--color-primary);
}

.lumicore-projects-arrows {
    display: flex;
    gap: 10px;
    margin-left: auto;
}

.lumicore-projects-arrow {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 48px;
    height: 48px;
    padding: 0;
    border: 1px solid rgba(7, 20, 49, .14);
    border-radius: 50%;
    background: #fff;
    color: #071431;
    font-size: 14px;
    cursor: pointer;
    transition: background-color .3s ease, border-color .3s ease, color .3s ease;
}

.lumicore-projects-arrow:hover {
    border-color: var(--color-primary);
    background: var(--color-primary);
    color: #fff;
}

/* Swiper sets this at either end of the rail. Without a rule of its own the
   button would look live at a point where it does nothing. */
.lumicore-projects-arrow.swiper-button-disabled {
    opacity: .35;
    cursor: default;
    pointer-events: none;
}

/* The template parks its back-to-top button in the bottom-right corner, fixed
   to the viewport — so arrows sitting there end up underneath it at whatever
   scroll position lines the two up, and the next arrow stops taking taps.
   Reversing the row on a phone moves them out from under it. */
@media (max-width: 575.98px) {

    .lumicore-projects-nav {
        flex-direction: row-reverse;
    }

    .lumicore-projects-arrows {
        margin-left: 0;
        margin-right: auto;
    }
}

/* A touch screen has no hover, so the zoom badge would never appear and the
   lift would never fire. Drop both rather than leave them half-working. */
@media (hover: none) {

    .lumicore-project__zoom {
        display: none;
    }

    .lumicore-projects .lumicore-gallery-item:hover {
        transform: none;
    }
}

@media (prefers-reduced-motion: reduce) {

    .lumicore-gallery-item,
    .lumicore-gallery-item img,
    .lumicore-project__zoom i {
        transition: none;
        transform: none;
    }

    .lumicore-projects .lumicore-gallery-item:hover,
    .lumicore-projects .lumicore-gallery-item:hover img,
    .lumicore-projects .lumicore-gallery-item:focus-visible,
    .lumicore-projects .lumicore-gallery-item:focus-visible img {
        transform: none;
    }
}

/* ==========================================================================
   Article pages (insight detail, service detail)
   ========================================================================== */

/* --- Article masthead ----------------------------------------------------
   The shared hero centres a short label like "Contact" inside 765px. An
   article headline is a sentence with a byline attached, and centred text
   with a ragged left edge is slow to scan — so article pages opt into a
   left-aligned masthead instead: breadcrumb, badge and date, headline,
   author, rule, then the lead image. Everything else keeps the centred
   default. */
.rts-service-details-breadcrumb-area.breadcrumb--left .breadcrumb-area {
    text-align: left;
    max-width: none;
    margin-left: 0;
    margin-right: 0;
}

/* The rule closing the masthead reads as the article's own divider, so it
   has to stop where the article column does. Below xl that column is full
   width and so is this. */
@media only screen and (min-width: 1200px) {
    .rts-service-details-breadcrumb-area.breadcrumb--left .breadcrumb-area {
        max-width: 66.6667%;
    }
}

/* The hero's 80px bottom padding was sized for a title floating above the
   page. With a rule underneath the byline the break is already drawn. */
.rts-service-details-breadcrumb-area.breadcrumb--left {
    padding-bottom: 40px;
}

.breadcrumb--left .breadcrumb-area ul {
    justify-content: flex-start;
}

/* Restores the inline styles the subtitle used to carry, so the centred
   pages that pass one are unaffected by the markup change. */
.breadcrumb-subtitle {
    max-width: 520px;
    margin: 25px auto 0;
}

.breadcrumb--left .breadcrumb-subtitle {
    margin-left: 0;
}

/* Badge + date row above the headline. */
.breadcrumb-eyebrow {
    display: flex;
    align-items: center;
    flex-wrap: wrap;
    gap: 14px;
    margin: 22px 0 14px;
}

.breadcrumb--left .breadcrumb-eyebrow {
    justify-content: flex-start;
}

.rts-service-details-breadcrumb-area .breadcrumb-eyebrow .breadcrumb-badge {
    display: inline-block;
    padding: 5px 12px;
    border-radius: 4px;
    background: var(--color-primary, #2c5cff);
    color: #fff;
    font-size: 12px;
    font-weight: 600;
    letter-spacing: .06em;
    text-transform: uppercase;
    line-height: 1.5;
}

.rts-service-details-breadcrumb-area .breadcrumb-eyebrow .breadcrumb-meta {
    font-size: 15px;
    color: #5d6473;
}

/* Byline, separated from the headline by the rule that closes the masthead. */
.breadcrumb-byline {
    display: flex;
    flex-direction: column;
    gap: 2px;
    margin-top: 22px;
    padding-bottom: 28px;
    border-bottom: 1px solid #e6e9ef;
}

.rts-service-details-breadcrumb-area .breadcrumb-byline .breadcrumb-byline__name {
    font-weight: 600;
    color: var(--color-heading-1, #1f1f25);
    line-height: 1.5;
}

.rts-service-details-breadcrumb-area .breadcrumb-byline .breadcrumb-byline__note {
    font-size: 14px;
    color: #5d6473;
    line-height: 1.5;
}

.breadcrumb-area h1.breadcrumb-title--article {
    font-size: 42px;
    line-height: 1.25;
}

@media only screen and (max-width: 767px) {
    .breadcrumb-area h1.breadcrumb-title--article {
        font-size: 30px;
    }

    .rts-service-details-breadcrumb-area.breadcrumb--left .breadcrumb-area {
        max-width: none;
    }
}

/* The hero contributes 80px of bottom padding and the article another 80 on
   top, leaving 160px of empty page between the title and the first line of
   the piece. Dropping the article's side leaves the hero's 80px, which is
   break enough. Shared by the insight, service and page detail templates. */
.rts-blog-list-area.pt--80 {
    padding-top: 0;
}

/* --- Article meta line ---------------------------------------------------
   Author, date and category are separate facts, so each is its own span and
   the bullets are drawn between them. Previously two spans were joined with
   a hard-coded "•", which left a 25px hole before the first separator. */
.rts-blog-detials-area-start .inner-content-blog-details .top-area {
    flex-wrap: wrap;
    gap: 0;
    margin-bottom: 34px;
}

/* Separator trails the item it follows rather than leading the next one, so
   when the line wraps on a phone the bullet stays at the end of the first
   line instead of dangling at the start of the second. */
.inner-content-blog-details .top-area span:not(:last-child)::after {
    content: "•";
    display: inline-block;
    margin: 0 10px;
    opacity: .45;
}

/* --- Article body --------------------------------------------------------
   Service and insight pages carry prose imported from the previous site:
   bulleted lists and inline images the Digix article styles don't cover. */

/* Measure: the 845px column ran to roughly 106 characters a line, well past
   the 45-75 that reads comfortably. Constraining the text — but not the
   images, gallery or pull-quote — brings it to about 78 without narrowing
   the layout itself.

   One shared pixel value rather than `ch`, which resolves against each
   element's own font-size and so would let a 30px heading run 400px wider
   than the paragraph beneath it. */
.inner-content-blog-details {
    --article-measure: 640px;
}

.inner-content-blog-details > p,
.inner-content-blog-details > ul,
.inner-content-blog-details > ol,
.inner-content-blog-details > h2,
.inner-content-blog-details > h3,
.inner-content-blog-details > h4 {
    max-width: var(--article-measure);
}

.inner-content-blog-details > p {
    margin-bottom: 24px;
    line-height: 1.75;
}

/* Headings carried only a 10px top margin, so the space above one depended
   entirely on what came before it — 40px after a paragraph, 30px after a
   list. A margin larger than any of those collapses against them, which
   makes the gap above every heading identical. */
.inner-content-blog-details > h2,
.inner-content-blog-details > h3,
.inner-content-blog-details > h4 {
    margin-top: 48px;
    margin-bottom: 16px;
}

.inner-content-blog-details > *:first-child {
    margin-top: 0;
}
.inner-content-blog-details .lumicore-article-list {
    list-style: none;
    margin: 0 0 30px;
    padding: 0;
}

.inner-content-blog-details .lumicore-article-list li {
    position: relative;
    padding-left: 30px;
    margin-bottom: 12px;
    line-height: 1.7;
}

.inner-content-blog-details .lumicore-article-list li::before {
    content: "\f00c";
    font-family: "Font Awesome 6 Pro", "Font Awesome 6 Free";
    font-weight: 900;
    position: absolute;
    left: 0;
    top: 1px;
    font-size: 13px;
    color: var(--color-primary, #2c5cff);
}

.inner-content-blog-details img.bottom-image {
    width: 100%;
    height: auto;
    border-radius: 10px;
    margin: 10px 0 30px;
}

.inner-content-blog-details .lumicore-gallery-wrapper {
    margin-bottom: 30px;
}

.inner-content-blog-details .lumicore-gallery-item img {
    height: 200px;
}

/* Body headings carry a `.title` class, which outranks the element selectors
   in the "Article pages" block above — so the rhythm has to be restated at
   this specificity to take effect. */
.inner-content-blog-details h2.title,
.inner-content-blog-details h3.title,
.inner-content-blog-details h4.title {
    margin-top: 48px;
    margin-bottom: 16px;
}

.inner-content-blog-details > *:first-child,
.inner-content-blog-details > *:first-child.title {
    margin-top: 0;
}

/* --- Contact details block ----------------------------------------------- */
.lumicore-contact-list li {
    display: flex;
    align-items: flex-start;
    gap: 12px;
    margin-bottom: 14px;
    line-height: 1.6;
}

.lumicore-contact-list li i {
    margin-top: 5px;
    color: var(--color-primary, #2c5cff);
}

/* --- Map embed ------------------------------------------------------------ */
.lumicore-map-wrapper {
    border-radius: 10px;
    overflow: hidden;
    line-height: 0;
}

.lumicore-map-wrapper iframe {
    width: 100%;
    height: 460px;
    border: 0;
    display: block;
}

@media only screen and (max-width: 767px) {
    .lumicore-map-wrapper iframe {
        height: 320px;
    }
}

/* --- Service cards -------------------------------------------------------
   The template's service icons are SVG glyphs; Lumicore uses Font Awesome
   marks instead so they need the same visual weight. */
.lumicore-service-icon {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 60px;
    height: 60px;
    border-radius: 8px;
    background: rgba(44, 92, 255, .08);
    color: var(--color-primary, #2c5cff);
    font-size: 26px;
}

/* ==========================================================================
   Homepage layout
   ========================================================================== */

/* --- Section rhythm ------------------------------------------------------
   Every band carries 120px of padding top and bottom, so consecutive
   sections put 240px of empty space between two pieces of content — roughly
   a quarter of a 900px viewport. Tightening it to 90px keeps the alternating
   light/white bands intact while cutting the dead space by a third. Scoped to
   the homepage, which is the only page that stacks nine of them. */
.lumicore-home .rts-section-gap {
    padding: 90px 0;
}

@media only screen and (max-width: 991px) {
    .lumicore-home .rts-section-gap {
        padding: 64px 0;
    }
}

/* ==========================================================================
   Homepage hero
   Rebuilt from the redesign in "Lumicore Hero Slider.html" rather than layered
   on top of the template's `.rts-banner-area-one`. Its rules run through
   several files and assume a caption block, a boxed kicker and arrows pinned
   to the viewport edges — none of which this composition has, so overriding
   them would have cost more than starting from the markup up.
   ========================================================================== */
/* Deliberately a plain block, not a column flex container with the rail as a
   `flex: 1` child. Swiper sizes its slides from the width of `.swiper`, and as
   a flex item that width is resolved late — slides came out 2^25px wide, and
   Bootstrap's `margin: 0 auto` on the inner container then centred the copy
   16 million pixels off-screen. The rail measures itself correctly as a normal
   block, so the foot is positioned over it instead. */
.lumicore-hero {
    position: relative;
    overflow: hidden;
    background: #070B14;
}

.mySwiper-lumicore-hero .swiper-slide {
    position: relative;
    display: flex;
    align-items: center;
    /* Fills the first screen, minus the fixed header the hero sits under, and
       stops shrinking once a laptop viewport gets short. */
    min-height: max(560px, 100vh);
    /* Bottom clearance is for the foot, which is laid over the photo rather
       than below it — the backdrop runs the full height of the hero. */
    padding: calc(var(--lumicore-header-height) + 40px) 0 150px;
}

.lumicore-hero__media {
    position: absolute;
    inset: 0;
}

.lumicore-hero__media img,
.lumicore-hero__media video {
    display: block;
    width: 100%;
    height: 100%;
    object-fit: cover;
    /* Photography and footage arrive from the CMS at whatever contrast they
       happen to have. Draining half the saturation and most of the brightness
       puts every upload on the same footing behind white text — and it is what
       keeps a moving backdrop from pulling the eye off the sentence. */
    filter: saturate(.5) brightness(.6);
}

/* Two washes, not one. The horizontal pass darkens the side the copy sits on;
   the vertical pass anchors the frame top and bottom so the image reads as lit
   rather than as a photo with a navy panel laid over it. */
.lumicore-hero__media::after {
    content: "";
    position: absolute;
    inset: 0;
    background:
        linear-gradient(90deg,
            rgba(6, 10, 18, .95) 0%,
            rgba(6, 10, 18, .88) 40%,
            rgba(6, 10, 18, .5) 78%,
            rgba(6, 10, 18, .38) 100%),
        linear-gradient(180deg,
            rgba(6, 10, 18, .6) 0%,
            rgba(6, 10, 18, 0) 28%,
            rgba(4, 7, 13, .9) 100%);
}

/* The mock-up runs its copy to a clamped edge padding. Here it goes in the
   page container instead, so the headline's left edge lands on the same line as
   every section heading below it — a hero that does not line up with the page
   under it is the thing that reads as bolted on. */
.lumicore-hero__inner {
    position: relative;
    z-index: 1;
    display: flex;
    flex-direction: column;
    gap: 26px;
}

.lumicore-hero__eyebrow {
    display: inline-flex;
    align-items: center;
    gap: 14px;
    color: rgba(255, 255, 255, .75);
    font-family: var(--lumicore-font-display);
    font-size: 12px;
    font-weight: 600;
    letter-spacing: .2em;
    text-transform: uppercase;
}

.lumicore-hero__rule {
    width: 34px;
    height: 1px;
    background: #fff;
    opacity: .7;
}

/* Outfit is geometric enough to read heavy at 500; at 700 it tips into looking
   cheap, which is why the weight is deliberately low for the size. The negative
   tracking closes the gaps that open up between capitals at display sizes. */
.lumicore-hero__title {
    max-width: 760px;
    margin: 0;
    color: #fff;
    font-family: var(--lumicore-font-display);
    font-size: clamp(40px, 5vw, 66px);
    font-weight: 500;
    line-height: 1.04;
    letter-spacing: -.03em;
    text-wrap: balance;
}

.lumicore-hero__text {
    /* ~65 characters at this size, which is where a line stops needing the eye
       to hunt for the start of the next one. */
    max-width: 470px;
    margin: 0;
    color: rgba(255, 255, 255, .7);
    font-size: 17px;
    line-height: 1.65;
    text-wrap: pretty;
}

.lumicore-hero__actions {
    display: flex;
    align-items: center;
    flex-wrap: wrap;
    gap: 16px 28px;
    margin-top: 6px;
}

/* One solid door and one quiet one. Two buttons of equal weight make the
   visitor choose before they know enough to. */
.lumicore-hero__cta {
    padding: 16px 32px;
    border-radius: 3px;
    background: #fff;
    color: #070B14;
    font-family: var(--lumicore-font-display);
    font-size: 15px;
    font-weight: 600;
    white-space: nowrap;
    transition: background-color .3s ease;
}

.lumicore-hero__cta:hover {
    background: #E4E9F7;
    color: #070B14;
}

.lumicore-hero__link {
    display: inline-flex;
    align-items: center;
    gap: 10px;
    padding-bottom: 4px;
    border-bottom: 1px solid rgba(255, 255, 255, .3);
    color: rgba(255, 255, 255, .85);
    font-size: 15px;
    white-space: nowrap;
    transition: color .3s ease, border-color .3s ease, gap .3s ease;
}

.lumicore-hero__link:hover {
    gap: 14px;
    border-bottom-color: #fff;
    color: #fff;
}

/* --- Foot of the hero ----------------------------------------------------
   Laid over the foot of the rail rather than stacked under it, so the photo
   runs the full height of the hero and the rule sits on the image. */
.lumicore-hero__bar {
    position: absolute;
    right: 0;
    bottom: 0;
    left: 0;
    z-index: 2;
    display: flex;
    align-items: center;
    justify-content: space-between;
    flex-wrap: wrap;
    gap: 20px 40px;
    padding-top: 22px;
    padding-bottom: 30px;
    border-top: 1px solid rgba(255, 255, 255, .12);
}

.lumicore-hero__track {
    display: flex;
    align-items: center;
    gap: 26px;
}

.lumicore-hero__numbers {
    display: flex;
    align-items: center;
    gap: 26px;
}

/* Swiper stamps its pagination classes onto this element, and they carry
   `position: absolute; left: 0; width: 100%` for a bullet strip floating over
   a slide. Here it is a flex item in the foot: left alone it claimed the whole
   row and squeezed the progress bar to nothing on a phone.

   Scoped through the parent rather than through `.swiper-pagination`, because
   Swiper only stamps on the modifier classes here — `swiper-pagination-bullets`
   and `swiper-pagination-horizontal` — not the base one. */
.lumicore-hero__track .lumicore-hero__numbers {
    position: static;
    right: auto;
    bottom: auto;
    left: auto;
    width: auto;
    text-align: left;
}

/* The template gives every bare `<button>` `width: 100%` (style.css:993), so
   each number claimed the whole strip and the three of them spread across the
   foot with the progress bar drawn through them. Every other button added here
   states a pixel width and never hit it. */
.lumicore-hero__num {
    flex: none;
    width: auto;
}

.lumicore-hero__num {
    padding: 0;
    border: 0;
    background: none;
    color: rgba(255, 255, 255, .4);
    font-family: var(--lumicore-font-display);
    font-size: 14px;
    font-weight: 600;
    letter-spacing: .08em;
    cursor: pointer;
    transition: color .3s ease;
}

.lumicore-hero__num:hover,
.lumicore-hero__num.swiper-pagination-bullet-active {
    color: #fff;
}

.lumicore-hero__progress {
    position: relative;
    display: block;
    width: clamp(120px, 18vw, 220px);
    height: 2px;
    overflow: hidden;
    background: rgba(255, 255, 255, .15);
}

/* Driven from Swiper's `autoplayTimeLeft`, which reports how much of the delay
   is left rather than how much has run — hence the subtraction in the init. */
.lumicore-hero__progress-fill {
    position: absolute;
    inset: 0;
    transform: scaleX(var(--lumicore-hero-progress, 0));
    transform-origin: left center;
    background: #fff;
}

.lumicore-hero__arrows {
    display: flex;
    align-items: center;
    gap: 10px;
}

.lumicore-hero__arrow {
    display: flex;
    align-items: center;
    justify-content: center;
    width: 46px;
    height: 46px;
    padding: 0;
    border: 1px solid rgba(255, 255, 255, .28);
    background: none;
    color: #fff;
    font-size: 15px;
    cursor: pointer;
    transition: background-color .3s ease, border-color .3s ease;
}

.lumicore-hero__arrow:hover {
    border-color: rgba(255, 255, 255, .5);
    background: rgba(255, 255, 255, .12);
}

/* The copy lifts into place as its slide takes over. Swiper cross-fades the
   slides themselves; this is what stops the text arriving as a flat dissolve. */
.mySwiper-lumicore-hero .swiper-slide .lumicore-hero__inner > * {
    transform: translateY(18px);
    opacity: 0;
    transition: opacity .55s ease, transform .7s cubic-bezier(.22, .61, .36, 1);
}

.mySwiper-lumicore-hero .swiper-slide-active .lumicore-hero__inner > * {
    transform: translateY(0);
    opacity: 1;
}

/* Each line follows the one above rather than the whole block moving at once. */
.mySwiper-lumicore-hero .swiper-slide-active .lumicore-hero__title {
    transition-delay: .08s;
}

.mySwiper-lumicore-hero .swiper-slide-active .lumicore-hero__text {
    transition-delay: .16s;
}

.mySwiper-lumicore-hero .swiper-slide-active .lumicore-hero__actions {
    transition-delay: .24s;
}

@media only screen and (max-width: 991px) {

    .lumicore-hero {
        min-height: 0;
    }

    .lumicore-hero__inner {
        padding-top: 72px;
        padding-bottom: 72px;
    }

    /* The horizontal wash assumes the copy sits on the left half. Once the
       column fills the width, the darkness has to come from the top down. */
    .lumicore-hero__media::after {
        background:
            linear-gradient(180deg,
                rgba(6, 10, 18, .8) 0%,
                rgba(6, 10, 18, .72) 45%,
                rgba(4, 7, 13, .92) 100%);
    }
}

@media only screen and (max-width: 575px) {

    .lumicore-hero__bar {
        gap: 18px;
    }

    .lumicore-hero__track {
        gap: 18px;
        width: 100%;
    }

    .lumicore-hero__progress {
        flex: 1;
        width: auto;
    }
}

@media (prefers-reduced-motion: reduce) {

    .mySwiper-lumicore-hero .swiper-slide .lumicore-hero__inner > * {
        transform: none;
        opacity: 1;
        transition: none;
    }
}

/* --- Service cards on the homepage ---------------------------------------
   The decorative shape is pinned at right:170px/bottom:180px, which lands on
   top of the fourth card. Push it behind the grid instead of moving it, so
   the composition the template intended survives. */
.rts-service-area-one .shape-img {
    z-index: 0;
    opacity: .3;
}

.rts-service-area-one > .container {
    position: relative;
    z-index: 1;
}

/* Cards sit in a stretch row, so matching their heights lines the titles up
   whether or not an excerpt wraps to a third line. */
.lumicore-home .single-service-style-one {
    height: 100%;
}

/* The icons are separate drawings, and the spread in their heights pushed each
   card's title to a different baseline. A fixed box absorbs it.

   96px rather than the 60 this started at. The uploads sitting here now carry
   a wide empty margin baked into the file — the drawing fills only 54-68% of
   the frame's width and 65-85% of its height — so a 60px box was rendering a
   glyph barely 40px tall, which is what made them look undersized next to a
   24px title. The same margin is why they still read as slightly different
   sizes; that part cannot be fixed from CSS, only by trimming the files. */
.lumicore-home .single-service-style-one .icon {
    display: flex;
    align-items: center;
    height: 96px;
}

.lumicore-home .single-service-style-one .icon img {
    max-height: 96px;
    width: auto;
}

/* --- Client logo grid ----------------------------------------------------
   Nine logos in a six-column grid leave the last row of three hanging on the
   left. Centring the row turns the remainder into a deliberate cap. */
.lumicore-client-grid {
    justify-content: center;
}

/* --- FAQ shortlist on the home page --------------------------------------
   The site draws this component three times. The FAQ and contact pages share
   one partial and get the template's `.accordon-pricing-wrapper-three`
   treatment; the home page has its own markup on `.accordion-faq-one`, and
   that variant was never finished — it inherits three things from Bootstrap
   that the other two override. */

/* Bootstrap paints accordion items white. On the FAQ page that goes unnoticed
   because the page behind them is white too. Here the section is #F9FAFF, so
   the shortlist showed up as a white slab with no border, no radius and no
   shadow — a container that does not look like one, which is the whole reason
   the block read as untidy. Transparent turns it back into what it is: a list
   of questions separated by rules. */
.lumicore-home .accordion-faq-one .accordion-item {
    background: transparent;
}

/* A rule belongs between two questions. On the last one it is a line hanging
   under the list with nothing beneath it. */
.lumicore-home .accordion-faq-one .accordion-item:last-child {
    border-bottom: 0;
}

.lumicore-home .accordion-faq-one .accordion-item .accordion-header .accordion-button {
    /* 16px matched the paragraph directly above it, so the questions read as
       more body copy rather than as things to click. The FAQ page sets 20px;
       this sits between the two and keeps the column's own hierarchy. */
    font-size: 18px;
    padding: 20px 0;
}

/* Bootstrap's chevron is the one place this component speaks a different
   language — the FAQ and contact pages both carry the template's plus/minus.
   Same mark here, so all three read as the same control. Bootstrap also spins
   its chevron 180deg when open, which has to go with the glyph it belonged
   to. */
.lumicore-home .accordion-faq-one .accordion-item .accordion-header .accordion-button::after {
    width: auto;
    height: auto;
    /* `auto` is what pushes the mark to the right-hand edge of the row —
       Bootstrap sets it, and a fixed margin here would park the icon against
       the end of the question text instead. */
    margin-left: auto;
    padding-left: 16px;
    transform: none;
    background-image: none;
    color: var(--color-primary);
    content: "\f056";
    font-family: "Font Awesome 6 Pro";
    font-size: 20px;
    font-weight: 500;
    line-height: 1;
}

.lumicore-home .accordion-faq-one .accordion-item .accordion-header .accordion-button.collapsed::after {
    color: #050D20;
    content: "\f055";
}

/* --- Blog cards ----------------------------------------------------------
   A three-line headline made the third card taller than its neighbours and
   dropped its "Read More" below the others. Equal heights with the link
   pinned to the bottom keeps the row on one baseline. */
.lumicore-home .single-blog-style-one {
    height: 100%;
    display: flex;
    flex-direction: column;
}

.lumicore-home .single-blog-style-one .inner-content-blog {
    display: flex;
    flex-direction: column;
    flex: 1 1 auto;
}

.lumicore-home .single-blog-style-one .inner-content-blog .btn-line {
    margin-top: auto;
}

/* --- Service list on the services page ----------------------------------- */
.lumicore-service-catalog {
    list-style: none;
    margin: 0;
    padding: 0;
}

.lumicore-service-link {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 16px;
    padding: 18px 24px;
    border: 1px solid #e9ecef;
    border-radius: 8px;
    background: #fff;
    transition: border-color .3s ease, transform .3s ease;
}

.lumicore-service-link:hover {
    border-color: var(--color-primary, #2c5cff);
    transform: translateX(4px);
}

.lumicore-service-link span {
    font-weight: 600;
    color: var(--color-heading-1, #1f1f25);
}

.lumicore-service-link i {
    color: var(--color-primary, #2c5cff);
}

/* --- Language switcher ---------------------------------------------------
   Sits beside the header call-to-action. Colours follow the nav: white over
   the dark homepage banner, navy over the light breadcrumb heroes, and white
   again once the sticky bar paints itself. */
.lumicore-lang-switch {
    display: inline-flex;
    align-items: center;
    border: 1px solid rgba(255, 255, 255, .28);
    border-radius: 6px;
    overflow: hidden;
    margin-right: 18px;
}

.lumicore-lang-switch a {
    display: block;
    padding: 6px 11px;
    font-size: 13px;
    font-weight: 600;
    line-height: 1.4;
    color: rgba(255, 255, 255, .72);
    transition: background-color .25s ease, color .25s ease;
}

.lumicore-lang-switch a:hover {
    color: #fff;
}

.lumicore-lang-switch a.is-active {
    background: var(--color-primary, #2c5cff);
    color: #fff;
}

/* Over a white breadcrumb hero the bar is transparent, so the switcher has
   to darken with the rest of the menu. */
header.header--on-light:not(.sticky) .lumicore-lang-switch {
    border-color: rgba(7, 20, 49, .2);
}

header.header--on-light:not(.sticky) .lumicore-lang-switch a {
    color: rgba(7, 20, 49, .65);
}

header.header--on-light:not(.sticky) .lumicore-lang-switch a:hover {
    color: #071431;
}

header.header--on-light:not(.sticky) .lumicore-lang-switch a.is-active {
    color: #fff;
}

/* Once the burger appears the nav lives in the offcanvas panel, and a language
   control floating beside it in the bar reads as a second, competing menu — on
   a 375px screen it also eats a third of the row. Below 1200px the panel's copy
   is the only one, which is why it sits inside the same partial.

   Targets .lumicore-lang-drop, the <details> the bar variant actually renders.
   This rule used to name .lumicore-lang-switch--bar, a class the markup stopped
   producing, so it quietly matched nothing and the dropdown stayed on phones. */
@media only screen and (max-width: 1199px) {
    .lumicore-lang-drop {
        display: none;
    }
}

/* The panel is a dark translucent sheet, so the switcher's default white-on-
   transparent treatment already reads correctly — it only needs room and a
   left-aligned start under the logo. */
.lumicore-lang-switch--panel {
    display: inline-flex;
    margin: 24px 0 0;
}

/* --- Language dropdown (header bar) --------------------------------------
   Matches the "Engage Our Team" button it sits beside: 48px tall, square,
   same 16px/600 type. Only the width differs — a language code needs nowhere
   near the button's 202px, and stretching it to match would look padded.

   Built on <details>, so `open` is the state and no class toggling is needed. */
.lumicore-lang-drop {
    position: relative;
    margin-right: 14px;
}

.lumicore-lang-drop > summary {
    display: flex;
    align-items: center;
    justify-content: center;
    gap: 9px;
    height: 48px;
    padding: 0 18px;
    border: 1px solid rgba(255, 255, 255, .28);
    font-size: 16px;
    font-weight: 600;
    line-height: 1;
    color: rgba(255, 255, 255, .85);
    cursor: pointer;
    user-select: none;
    list-style: none;
    transition: border-color .3s ease, color .3s ease, background-color .3s ease;
}

/* Safari draws its own disclosure triangle unless this is removed. */
.lumicore-lang-drop > summary::-webkit-details-marker,
.lumicore-lang-drop > summary::marker {
    display: none;
    content: "";
}

.lumicore-lang-drop > summary:hover,
.lumicore-lang-drop[open] > summary {
    color: #fff;
    border-color: rgba(255, 255, 255, .6);
}

.lumicore-lang-drop > summary:focus-visible {
    outline: 2px solid var(--color-primary, #2c5cff);
    outline-offset: 2px;
}

.lumicore-lang-drop > summary i {
    font-size: 11px;
    transition: transform .3s ease;
}

.lumicore-lang-drop[open] > summary i {
    transform: rotate(180deg);
}

.lumicore-lang-drop__menu {
    position: absolute;
    top: calc(100% + 8px);
    right: 0;
    min-width: 180px;
    padding: 6px;
    background: #fff;
    border: 1px solid #e6e9ef;
    border-radius: 6px;
    box-shadow: 0 18px 40px rgba(7, 20, 49, .16);
    z-index: 20;
}

.lumicore-lang-drop__menu a {
    display: flex;
    align-items: center;
    gap: 12px;
    padding: 10px 12px;
    border-radius: 4px;
    font-size: 15px;
    color: var(--color-heading-1, #1f1f25);
    transition: background-color .2s ease, color .2s ease;
}

.lumicore-lang-drop__menu a:hover {
    background: #f4f6fa;
}

.lumicore-lang-drop__menu a.is-active {
    background: var(--color-primary, #2c5cff);
    color: #fff;
}

.lumicore-lang-drop__code {
    min-width: 26px;
    font-size: 12px;
    font-weight: 700;
    letter-spacing: .08em;
    opacity: .65;
}

.lumicore-lang-drop__menu a.is-active .lumicore-lang-drop__code {
    opacity: .9;
}

.lumicore-lang-drop__name {
    font-weight: 500;
}

/* Over a white breadcrumb hero the bar is transparent, so the trigger has to
   darken with the rest of the menu. */
header.header--on-light:not(.sticky) .lumicore-lang-drop > summary {
    border-color: rgba(7, 20, 49, .2);
    color: rgba(7, 20, 49, .7);
}

header.header--on-light:not(.sticky) .lumicore-lang-drop > summary:hover,
header.header--on-light:not(.sticky) .lumicore-lang-drop[open] > summary {
    color: #071431;
    border-color: rgba(7, 20, 49, .45);
}

/* The bar shrinks when it goes sticky; the trigger follows so it stays level
   with the CTA rather than overflowing it. */
header.header--sticky.sticky .lumicore-lang-drop > summary {
    height: 42px;
}

/* No small-screen sizing for the dropdown: below 1200px it is not rendered at
   all, and the panel switcher inside the burger menu takes over. */

@media (prefers-reduced-motion: reduce) {
    .lumicore-lang-drop > summary i {
        transition: none;
    }
}
