/* ==========================================================================
   RD Transport - rebrand overrides
   Loaded after css/style.css so these rules win without touching the
   minified template stylesheet (which would be lost on a template update).
   ========================================================================== */

/* --------------------------------------------------------------------------
   Header logo

   The RD lockup is ~2:1 (1774x887) where the stock TransHub mark was ~3.8:1,
   so the template's width-based sizing does not translate. Size by height
   instead and let the width follow.

   The selector deliberately matches the template's own
   `header .navbar-brand img` specificity: a plain `.navbar-brand img` rule
   here would lose the max-width battle (180px desktop / 155px tablet) and
   the mark would get capped and squashed as it grows.
   -------------------------------------------------------------------------- */
header .navbar-brand img {
   height: 82px;
   width: auto;
   max-width: none;
}

/* The nav bar is a pill (border-radius:100px) with only 20px of side padding.
   Against a curved edge that reads as cramped, so nudge the mark inward. */
header .navbar-brand {
   margin-left: 22px;
}

@media (max-width: 1199.98px) {
   header .navbar-brand img {
      height: 64px;
   }

   header .navbar-brand {
      margin-left: 12px;
   }
}

@media (max-width: 575.98px) {
   header .navbar-brand img {
      height: 54px;
   }

   header .navbar-brand {
      margin-left: 6px;
   }
}

/* Logo inside the mobile slide-in nav panel */
.offcanvas-nav .offcanvas-header img {
   height: 58px;
   width: auto;
   max-width: none;
}

/* --------------------------------------------------------------------------
   Hero slide 2 background dimming

   Slide 1 uses assets/bg-1.jpg, which was shot/graded dark -- mean luminance
   over the left 45% (where the headline sits) is 22/255, so white type reads
   cleanly with no overlay at all.

   assets/fleet-hero.png is a bright daylight shot at 148/255 there. The
   template's .parallax-overly is one way to tame it, but it is a 90%-opacity
   multiply and crushed the trucks into near-black -- visibly heavier than
   slide 1. Dimming the image itself instead reproduces slide 1's treatment:
   the photo stays legible, the type stays readable.

   0.34 lands fleet-hero at ~50/255, in the same band as slide 1.

   Painted as a black overlay rather than filter: brightness(). The two are
   arithmetically the same -- brightness(x) multiplies each channel by x, and
   black at alpha a leaves c * (1 - a), so 0.34 is alpha 0.66 exactly -- but a
   filter is not free the way a flat colour is. Swiper's creative effect scales
   this element between 1.1 and 1.3 on every slide change, and a filtered layer
   has to be re-rasterised and re-filtered at each new scale, across the full
   width of the viewport, for every frame of the 900ms transition.
   -------------------------------------------------------------------------- */
.slider-sec2 .slide-item .item-bg.bg-dim::after {
   content: "";
   position: absolute;
   inset: 0;
   background: rgba(0, 0, 0, 0.66);
}

/* --------------------------------------------------------------------------
   About section imagery

   The template's art was cut to exact pixel sizes and relies on the files'
   intrinsic dimensions for layout; the RD photographs are much larger and a
   different shape, so both need constraining or they blow the section apart.

   Main image: about-m.jpg was 557x582 (~0.96, near square). loading.jpeg is
   1600x1200 (1.33 landscape), which would render markedly shorter and pull
   the column out of alignment with the text beside it. Pin the original
   aspect and let object-fit crop.

   Circle inset: .about-sm is absolutely positioned with no width, so it takes
   its size from the image itself -- about-m2.png was 350x350. ace-hero.png is
   1254x1254, which would render the circle 3.5x oversized and cover the
   section. Pin it back to 350px.
   -------------------------------------------------------------------------- */
.about-sec2 .ab-main-img > img {
   width: 100%;
   aspect-ratio: 557 / 582;
   object-fit: cover;
   object-position: center;
}

.about-sec2 .about-media-box .about-sm img {
   width: 350px;
   height: 350px;
   max-width: none;
   object-fit: cover;
}

@media (max-width: 575.98px) {
   .about-sec2 .about-media-box .about-sm img {
      width: 200px;
      height: 200px;
   }
}

/* --------------------------------------------------------------------------
   Hero headline height

   The template gives .slide-content a 700px box and 74px type, which is a
   budget of roughly two lines. Reserving that height keeps the two slides
   from jogging vertically as the carousel rotates, and stops a slightly
   longer headline from shunting the body copy and buttons down the page.

   Scoped to >=1200px on purpose: below that the template drops the headline
   to 48px/38px with a unitless line-height, where a fixed px floor would
   just add dead space.
   -------------------------------------------------------------------------- */
@media (min-width: 1200px) {
   .slide-content h1 {
      min-height: 168px; /* 2 x the template's 84px line-height */
   }
}

/* Keep the hero buttons on one line -- "Call Us" is short, but the tel:
   label should never wrap the way the full number did. */
.slide-content .slide-cta .btn {
   white-space: nowrap;
}

/* --------------------------------------------------------------------------
   Services carousel (index.html "Our Services")

   The template's own thumb art (images/serv-s1..3.png) is exactly 328x100 --
   the same size the box renders at (388px slide - 2x30px card padding), and
   the height comes from the 100px icon sitting inside it. Because the art
   matched the box 1:1, the stylesheet never set a background-size, so the
   default `auto` was harmless.

   That stops being harmless the moment a real photograph goes in: a
   1254x1254 or 1600x1200 asset painted at `auto` renders at natural size and
   the box shows only its top-left 328x100 corner -- which is why the client
   photos read as random slivers of sky and torso rather than vehicles.

   The art in assets/services/ is cut to 656x200 -- 2x the box, the same
   3.28:1 band -- so at the carousel's 388px slide it lands exactly 1:1 and
   stays sharp on HiDPI screens.

   `contain` rather than `cover`, because that 1:1 fit only holds at one width.
   The services-page grid gives the card about 304px and a phone about 260px,
   and at those widths cover keeps the band full by scaling the photo up and
   slicing the sides off -- which cuts the vehicle in half, the whole subject
   of the picture. Contain keeps every photo whole at every width and lets the
   band letterbox instead; the bars land on --dark, the same navy as the icon
   tile beside them, so the shortfall reads as part of the tile rather than as
   a gap. Nothing is lost at the 388px slide, where the two are identical.
   -------------------------------------------------------------------------- */
/* The template filled this tile with a white PNG icon; these are Font Awesome
   glyphs instead, which take their colour from the card's body text -- near
   black on a navy tile, so the icon all but disappeared. The tile also turns
   orange on hover, and white is the one colour that holds up on both.

   Sized and drawn to match the services page, which puts a 60px line-art PNG
   in this same 100px tile. 16px is the base .icon size and reads lost at that
   scale; 46px gives a glyph of about the same footprint as those PNGs.
   -------------------------------------------------------------------------- */
.service-card2 .serv-thumb .icon i {
   color: var(--white);
   font-size: 46px;
   line-height: 1;
}

/* The line-art look comes from stroking the glyph and hollowing out the fill,
   the same technique the theme uses on alternate words in the scrolling text
   band. Font Awesome Free ships no outline weight for vehicles -- fa-regular
   covers about 160 UI shapes and no trucks -- so there is no icon to swap to;
   the outline has to be drawn from the solid glyph.

   Guarded, because the fill is what makes this work: a browser that ignores
   -webkit-text-stroke would apply `color: transparent` and paint nothing at
   all. Inside @supports, a browser that cannot draw the stroke never sees the
   transparent fill either, and keeps the solid glyph above. */
@supports (-webkit-text-stroke: 1px #fff) {
   .service-card2 .serv-thumb .icon i {
      color: transparent;
      -webkit-text-stroke: 1.5px var(--white);
   }
}

/* Service icons are uploaded artwork now, not glyphs. The tile is 100px and
   the template's own services page fills it with a 60px mark, so match that
   -- an <img> has no font-size to inherit, and left at its natural size a
   120px source would overflow the tile while a 32px one would swim in it. */
.service-card2 .serv-thumb .icon img {
   width: 60px;
   height: 60px;
   object-fit: contain;
}

.service-card2 .serv-thumb {
   background-size: contain;
   background-position: center;
   background-repeat: no-repeat;
   background-color: var(--dark);
}

/* --------------------------------------------------------------------------
   Photo scrim: at rest, not on hover

   The template dims every thumb by 40% and clears it on hover, so the photo a
   visitor reads while scanning the list is the dimmed one and the true colour
   only appears on the single card under the cursor -- and never at all on a
   touch screen. Reversed here: the photo shows as shot, and the scrim becomes
   the hover response instead.

   Same specificity as the template's pair, so this wins on source order
   alone -- rd-custom.css is loaded after style.css. It has to stay above the
   .serv-cta block below for the same reason: that rule holds the scrim at 0
   for the brand-panel card, and only stays in force while it comes last.
   -------------------------------------------------------------------------- */
.service-card2 .serv-thumb:before {
   opacity: 0;
}

.service-card2:hover .serv-thumb:before {
   opacity: .4;
}

/* Below 576px the template drops the fixed 388px slide width and leaves
   slidesPerView:"auto" to size from content -- but .service-card2 is a plain
   block with no width, so the slide collapses to its text. Give the phone
   breakpoint an explicit width just under the viewport so one card reads at a
   time with the next one peeking. */
@media (max-width: 575.98px) {
   .service-slider2 .swiper-slide {
      width: min(320px, calc(100vw - 72px));
   }
}

/* The closing "Need a Custom Plan?" slide is a call to action, not a service,
   so it gets the brand panel instead of a photograph -- both because no asset
   in the set depicts "planning", and because it should read as the end of the
   list rather than an eleventh service. Same 3.28:1 band as the photo thumbs. */
.service-card2.serv-cta .serv-thumb {
   aspect-ratio: 328 / 100;
   background-image: linear-gradient(100deg, var(--dark) 0%, var(--dark) 34%, var(--primary) 100%);
}

/* The photo thumbs carry a 40% dark scrim that lifts on hover; over a flat
   brand panel that just reads as muddy, so drop it. */
.service-card2.serv-cta .serv-thumb:before {
   opacity: 0;
}

.service-card2.serv-cta .serv-thumb .icon {
   background-color: rgba(var(--white-rgb), .16);
}

/* The phone number is the point of this card -- keep it on one line even
   though it is longer than the "View Details" label it replaces. */
.service-card2.serv-cta .service-footer .custom-btn {
   white-space: nowrap;
}

@media (max-width: 575.98px) {
   /* The section's container carries Bootstrap's `p-0`, which is deliberate on
      desktop -- the carousel is meant to bleed off the right edge. On a phone
      it just puts the eyebrow, heading and cards hard against both screen
      edges, so give the gutter back at this breakpoint only. */
   .services-sec2 .container.p-0 {
      padding-left: 16px !important;
      padding-right: 16px !important;
   }

   /* "A Complete Small-Vehicle" is wider than a phone viewport at the
      template's 38px, and .sec-title is overflow:hidden at this breakpoint --
      so the tail of the line is silently clipped instead of wrapping rather
      than pushed out where it would at least be visible. Scale with the
      viewport so the longest word fits at 320px and the heading still reaches
      the template's size by the time the breakpoint ends. */
   .services-sec2 .sec-title {
      font-size: clamp(23px, 7.1vw, 38px);
   }
}

@media (max-width: 575.98px) {
   /* The template right-aligns the carousel arrows (justify-content:end) to sit
      under the desktop layout's wide left column. Once the section has a real
      gutter the pair no longer clears the screen edge and the "next" arrow is
      cut off -- which is the one control a phone visitor needs to see the
      other ten services. Anchor them to the left gutter instead, and clear
      Swiper's own left/right offsets, which assume absolute positioning. */
   .services-sec2 .header-slider-nav {
      justify-content: flex-start;
   }

   .services-sec2 .header-slider-nav .swiper-button-next,
   .services-sec2 .header-slider-nav .swiper-button-prev {
      left: auto;
      right: auto;
   }
}

/* --------------------------------------------------------------------------
   Why Choose Us (index.html "choose-sec2")

   The template shipped this as an HVAC panel -- six stock labels radiating
   from a globe-and-freighter montage. Rebuilt around the company profile's
   ten advantages (p.9), with the West Bengal coverage map from p.8 as the
   centrepiece, since "we cover one state properly" is the actual pitch.
   -------------------------------------------------------------------------- */

/* The template's labels carry an <img> icon; these use Font Awesome glyphs,
   which inherit .icon's 16px and would sit lost in the 68px tile. */
.choose-sec2 .single-choose span.icon i,
.choose-sec2 .choose-extra-item span.icon i {
   font-size: 26px;
   color: var(--white);
   display: block;
}

/* The stock labels were two short words ("Floor Heating"), so the heading was
   capped at 130px. The profile's advantages are longer -- "Dedicated
   Corporate Support" needs the room or it breaks to four lines. */
.choose-sec2 .single-choose h3 {
   max-width: 165px;
}

/* The montage this replaces was landscape (672x514) and filled the column;
   the map is portrait, so at img-fluid's width:100% it would tower over the
   three labels either side of it.

   Sizing it as a percentage of its own column (rather than a fixed height)
   is what keeps the connector lines aligned at every breakpoint: the centre
   column is exactly twice a label column, so a 66% map leaves a gap of
   0.34 x (label column) on each side -- a constant the lines below can be
   written against, instead of a pixel gap that drifts as the grid resizes. */
.choose-sec2 .choose-map {
   display: flex;
   justify-content: center;
}

.choose-sec2 .choose-map img {
   width: 66%;
   max-width: 100%;
   height: auto;
}

@media (max-width: 991.98px) {
   /* Full-width column below lg, so 72% would leave it needlessly small. */
   .choose-sec2 .choose-map img {
      width: min(100%, 420px);
   }
}

.choose-sec2 .choose-lead {
   max-width: 620px;
   margin: 18px auto 0;
}

/* The four advantages that do not fit the six-point radial layout. Kept
   visually subordinate to it -- a quiet footer row rather than a second
   competing grid. */
.choose-sec2 .choose-extra {
   margin-top: 70px;
}

.choose-sec2 .choose-extra-item {
   display: flex;
   align-items: flex-start;
   gap: 16px;
   height: 100%;
   padding: 26px 22px;
   background-color: rgba(var(--white-rgb), .72);
   border: 1px solid var(--theme-border);
   border-radius: 12px;
}

.choose-sec2 .choose-extra-item span.icon {
   width: 52px;
   height: 52px;
   border-radius: 10px;
}

.choose-sec2 .choose-extra-item span.icon i {
   font-size: 20px;
}

.choose-sec2 .choose-extra-item h4 {
   font-size: 18px;
   font-weight: 700;
   margin-bottom: 6px;
}

.choose-sec2 .choose-extra-item p {
   margin-bottom: 0;
   font-size: 15px;
   line-height: 24px;
}

@media (max-width: 991.98px) {
   .choose-sec2 .choose-extra {
      margin-top: 40px;
   }
}

/* The connector lines were hand-tuned to the montage's irregular silhouette:
   the left column runs them to 100%/60%/50% and the right column to
   -50%/-30%/-100%, so each one stopped somewhere on the globe or the ship.
   Against the map's clean rectangle that staggering just reads as sloppy --
   left-hand dots landed inside the panel while right-hand ones stopped short.
   Give all six the same length so they meet the panel edge symmetrically.
   Only applies from lg up; the template hides these below 992px. */
@media (min-width: 992px) {
   .choose-sec2 .single-choose:after,
   .choose-sec2 .single-choose:nth-child(2):after,
   .choose-sec2 .single-choose:nth-child(3):after {
      left: 100%;
      right: auto;
      width: 34%;
   }

   .choose-sec2 .reverse-choose .single-choose:after,
   .choose-sec2 .reverse-choose .single-choose:first-child:after,
   .choose-sec2 .reverse-choose .single-choose:nth-child(2):after {
      left: auto;
      right: 100%;
      width: 34%;
   }
}

@media (max-width: 991.98px) {
   /* The 80px rhythm between labels exists to give the connector lines room to
      travel. Those lines are display:none here, so the gap is just dead space. */
   .choose-sec2 .single-choose:not(:last-child) {
      margin-bottom: 20px;
   }

   /* The template hid the centre image from 576px up (d-sm-none d-lg-block),
      which would drop the coverage map -- the whole point of the section -- on
      every tablet. It is shown at all widths now and ordered last below lg, so
      the two label columns still pair up side by side above it. */
   .choose-sec2 .choose-map {
      margin-top: 30px;
   }
}

@media (min-width: 992px) {
   /* The stock montage was 514px tall and the 80px label rhythm was set to
      match it. A portrait map runs ~640px, so the same rhythm leaves the
      labels finishing well short of it. Open the gaps up to sit level. */
   .choose-sec2 .single-choose:not(:last-child) {
      margin-bottom: 120px;
   }
}

/* --------------------------------------------------------------------------
   Brands We've Moved (index.html ".brandwall-sec")

   A fixed grid of tiles where individual cells cross-fade to a different brand
   on a stagger -- the whole wall never scrolls, so the eye stays put and the
   roster still cycles through more names than would fit at once. Profile p.12
   lists 25 brands; twelve tiles show at a time on desktop.
   -------------------------------------------------------------------------- */
.brandwall-sec {
   background-color: var(--white);
}

.brandwall-sec .brandwall-lead {
   max-width: 660px;
   margin: 18px auto 0;
}

/* Six across on roughly a 200px pitch, with the marks sitting straight on the
   page -- no tile, no rule, no card. Boxing each logo fights the marks' own
   silhouettes and turns the wall into a table; whitespace does the separating. */
.brandwall-grid {
   display: grid;
   grid-template-columns: repeat(6, 1fr);
   column-gap: 18px;
   row-gap: 30px;
   margin-top: 60px;
}

.brandwall-tile {
   display: flex;
   align-items: center;
   justify-content: center;
   min-height: 130px;
   overflow: hidden;
}

/* Two stacked layers per tile: the incoming mark is painted into the hidden
   one and the opacities are swapped, so the tile cross-fades straight from one
   brand to the next with no empty frame in between. Nothing moves, so a tile
   changing at the edge of vision does not pull attention off whatever the
   visitor is actually reading. */
.brandwall-tile {
   position: relative;
}

.brandwall-layer {
   position: absolute;
   top: 0;
   left: 0;
   right: 0;
   bottom: 0;
   display: flex;
   align-items: center;
   justify-content: center;
   padding: 0 6px;
   opacity: 0;
   /* The mark also eases up from slightly under size rather than only
      brightening. A pure opacity dissolve reads as a blink at this speed; the
      scale gives the eye something continuous to follow, and because the two
      layers are at different scales mid-swap they stay distinguishable instead
      of muddying into each other. Kept small enough not to look like motion. */
   transform: scale(.955);
   transition: opacity .4s cubic-bezier(.4, 0, .2, 1),
               transform .4s cubic-bezier(.4, 0, .2, 1);
}

/* The incoming mark waits out most of the outgoing one before it starts.
   A symmetric cross-fade leaves both at ~50% mid-swap, and two wordmarks
   stacked at half opacity read as a smudge rather than a transition; a plain
   fade-out-then-in avoids that but leaves the tile visibly empty, and with
   twelve tiles running several of those blanks overlap. The offset keeps the
   tile occupied without ever showing two legible names at once. */
.brandwall-layer.is-active {
   opacity: 1;
   transform: scale(1);
   transition: opacity .4s cubic-bezier(.4, 0, .2, 1) .12s,
               transform .4s cubic-bezier(.4, 0, .2, 1) .12s;
}

/* Every mark is capped by height, and the cap moves with its aspect. Sharing
   one cap across all fourteen makes the squarer lockups look half the size of
   the wordmarks: a 4:1 mark spends its height allowance on width and covers
   four times the area a 1:1 mark does at the same height. */
.brandwall-layer img {
   max-width: 100%;
   width: auto;
   object-fit: contain;
}

.brandwall-layer img.is-xwide  { max-height: 52px; }
.brandwall-layer img.is-wide   { max-height: 60px; }
.brandwall-layer img.is-mid    { max-height: 76px; }
.brandwall-layer img.is-square { max-height: 98px; }

/* Brands with no logo file yet fall back to a wordmark, so the wall is usable
   before the client supplies artwork and each tile upgrades to an image the
   moment one is dropped into assets/brands/. */
.brandwall-name {
   font-family: var(--font-family-headings);
   font-size: 19px;
   font-weight: 700;
   line-height: 1.2;
   text-align: center;
   color: #6a7280;
   letter-spacing: -.01em;
}

.brandwall-note {
   margin: 40px auto 0;
   max-width: 900px;
   text-align: center;
   font-size: 13px;
   line-height: 21px;
   color: var(--mute);
}

/* Six, four, three, two -- every step divides the twelve tiles into full rows,
   so the wall never ends on a ragged half-row. */
@media (max-width: 1199.98px) {
   .brandwall-grid {
      grid-template-columns: repeat(4, 1fr);
   }
}

@media (max-width: 767.98px) {
   .brandwall-grid {
      grid-template-columns: repeat(3, 1fr);
      column-gap: 14px;
      row-gap: 10px;
   }

   .brandwall-tile {
      min-height: 100px;
   }

   .brandwall-layer img.is-xwide  { max-height: 38px; }
   .brandwall-layer img.is-wide   { max-height: 44px; }
   .brandwall-layer img.is-mid    { max-height: 56px; }
   .brandwall-layer img.is-square { max-height: 70px; }

   .brandwall-name {
      font-size: 14px;
   }
}

/* Two columns leave each mark almost twice the width it had at three, so the
   caps come back up rather than leaving the logos marooned in whitespace. */
@media (max-width: 479.98px) {
   .brandwall-grid {
      grid-template-columns: repeat(2, 1fr);
   }

   .brandwall-tile {
      min-height: 114px;
   }

   .brandwall-layer img.is-xwide  { max-height: 46px; }
   .brandwall-layer img.is-wide   { max-height: 52px; }
   .brandwall-layer img.is-mid    { max-height: 64px; }
   .brandwall-layer img.is-square { max-height: 82px; }
}

/* --------------------------------------------------------------------------
   Quote form select (index.html FAQ section)

   jquery-nice-select swaps the <select> for a generated div that it styles
   `float: left`. A floated child contributes no height to its parent, so the
   select's .form-group collapses to zero and the generated box overlaps the
   field below it -- which is why the service select and the route input ran
   into each other, with the field icon stranded between them.

   The template shipped with this (its own "Weight" field sat in the same
   overlap); the float earns nothing here because .tv-select is already a
   full-width block, so simply turning it off restores the 27px rhythm without
   a clearfix that would clip the dropdown list when it opens.
   -------------------------------------------------------------------------- */
.faq-sec .contact-form .form-group .nice-select.tv-select {
   float: none;
}

/* The accordion's +/- marker is positioned absolutely at `right: 0`, so it
   sits inside the button's own padding and relies on the question being short
   enough to stop before it. The template's stock questions were four or five
   words; these run longer and wrap to two lines on a phone, where the text ran
   straight under the marker. Reserve the space instead. */
.faq-sec .faq-accordion .accordion-item .accordion-button {
   padding-right: 46px;
}

/* --------------------------------------------------------------------------
   Photo Gallery (index.html ".gallery-sec")

   Replaces the template's blog row, which carried three invented posts
   ("TranHub Named a Finalist...", author "Adam Smith", the same date on each)
   linking to template pages. RD publishes nothing, so the profile's own
   gallery page (p.14) is what belongs here instead.

   Every image is a genuine RD photograph -- no renders, no stock -- because a
   section calling itself a photo gallery should not be showing artwork.
   -------------------------------------------------------------------------- */
.gallery-sec .gallery-lead {
   max-width: 640px;
   margin: 18px auto 0;
}

.gallery-grid {
   margin-top: 50px;
}

.gallery-item {
   margin: 0;
   background-color: var(--white);
   border-radius: 14px;
   overflow: hidden;
   height: 100%;
}

/* The set mixes 4:3 landscape with 3:4 portrait phone shots. A fixed tile with
   object-fit keeps the grid even; the full frame is still there in the
   lightbox, so nothing is lost to the crop. */
.gallery-item a {
   display: block;
   aspect-ratio: 4 / 3;
   overflow: hidden;
}

.gallery-item img {
   width: 100%;
   height: 100%;
   object-fit: cover;
   object-position: center;
   transition: transform .5s cubic-bezier(.4, 0, .2, 1);
}

.gallery-item:hover img {
   transform: scale(1.06);
}

.gallery-item figcaption {
   padding: 14px 16px 16px;
   font-size: 14px;
   line-height: 21px;
   color: var(--body-color);
}

@media (max-width: 575.98px) {
   .gallery-item figcaption {
      padding: 12px 13px 14px;
      font-size: 13px;
   }
}

/* --------------------------------------------------------------------------
   Events & Recognition (index.html ".events-sec")

   Presented with the template's own blog-card markup (.blog-entry2), so the
   cards match the rest of the site's card language and each one opens a
   full event page. Only the section's own intro spacing is needed here.
   -------------------------------------------------------------------------- */
.events-sec .events-lead {
   max-width: 620px;
   margin: 18px auto 0;
}

.events-grid {
   margin-top: 50px;
}

/* The template's opt-card used a 40px PNG icon; these are Font Awesome glyphs,
   which would otherwise inherit the card's body colour at 16px. */
.operation-sec .opt-card .opt-icon {
   display: inline-flex;
   align-items: center;
   justify-content: center;
   width: 46px;
   height: 46px;
   border-radius: 12px;
   background-color: var(--primary);
}

.operation-sec .opt-card .opt-icon i {
   font-size: 20px;
   color: var(--white);
}

/* Call-to-action closing each event page. */
.event-cta {
   background-color: var(--light);
   border: 1px solid var(--theme-border);
}

.event-cta p {
   max-width: 560px;
   margin: 14px 0 22px;
}

/* --------------------------------------------------------------------------
   Event cards use the template's .blog-entry2 markup, whose thumbnail is
   masked by images/blog-mask.png. That mask is painted at its native 348x212
   with `no-repeat`, and the template's own blog art is exactly 348x212 -- so
   any larger image falls outside the mask and simply disappears.

   Scaling the mask to the box instead lets the art be cut at 2x (696x424) for
   HiDPI screens, and pinning the aspect ratio keeps the four cards level
   whatever the source photo's shape.
   -------------------------------------------------------------------------- */
.events-sec .blog-entry2 .blog-thumb {
   -webkit-mask-size: 100% 100%;
   mask-size: 100% 100%;
}

.events-sec .blog-entry2 .blog-thumb img {
   width: 100%;
   aspect-ratio: 348 / 212;
   object-fit: cover;
}

/* The meta line is a category and an organiser rather than the template's
   author-and-date links, so it needs no link colouring -- just the icon. */
.events-sec .blog-entry2 .blog-meta i {
   margin-right: 7px;
   color: var(--primary);
}

/* Titles run to one, two or three lines, so the cards finish at different
   heights unless they are stretched and the footer pushed to the bottom. */
.events-sec .blog-entry2 {
   display: flex;
   flex-direction: column;
   height: 100%;
}

.events-sec .blog-entry2 .blog-inner {
   display: flex;
   flex-direction: column;
   flex: 1 1 auto;
}

.events-sec .blog-entry2 .blog-footer {
   margin-top: auto;
}

/* --------------------------------------------------------------------------
   Event detail pages (event-*.html)

   Built on the template's blog-post shell, minus its invented author, publish
   date and three fake comment threads. The photographs are phone shots in
   portrait, which at the post layout's full width run well over a screen tall
   before the reader reaches a word of copy -- so the lead image is pinned to a
   landscape crop.
   -------------------------------------------------------------------------- */
.event-single .post-thumb img {
   width: 100%;
   height: 520px;
   object-fit: cover;
   object-position: center;
}

@media (max-width: 767.98px) {
   .event-single .post-thumb img {
      height: 300px;
   }
}

.event-single .post-header .pm-info span {
   display: block;
   color: var(--mute);
   font-size: 14px;
}

.event-single .ct-list li {
   margin-bottom: 10px;
}

/* --------------------------------------------------------------------------
   What Sets Us Apart -- section background

   The template used this element as a narrow watermark: half width, 40%
   opacity, a washed-out container ship. It now carries the RD fleet banner
   across the full section instead.

   The image is declared here rather than inline so a gradient can sit on top
   of it in the same stack. That scrim is doing real work: the photograph is a
   bright sunset and the heading and tab copy are white, so it is held dark
   under the text column and allowed to open up on the right, behind the
   picture panel where nothing has to stay legible.

   Opacity stays at 1 and the darkening lives in the gradient -- fading the
   whole layer would mute the vehicles just as much as the sky.

   Cropped from assets/page-02-banner.jpeg, whose left edge carries a baked-in
   logo and headline that would otherwise show through.

   The element no longer carries the `jarallax` class. Jarallax reads the
   computed background-image, strips it and rebuilds its own transformed
   container -- which it cannot do with a layered `gradient, url()` stack, so
   the background silently vanished on the live page while still rendering in
   any preview that did not load custom.js. Losing the parallax drift is worth
   a background that reliably shows up.
   -------------------------------------------------------------------------- */
.operation-sec .operate-bg.bg-cover.position-absolute {
   width: 100%;
   left: 0;
   opacity: 1;
   background-image:
      linear-gradient(90deg,
         rgba(2, 14, 40, .97) 0%,
         rgba(2, 14, 40, .94) 38%,
         rgba(2, 14, 40, .84) 62%,
         rgba(2, 14, 40, .48) 100%),
      url(../assets/operation/section-bg.jpg);
   background-size: cover, cover;
   background-position: center, center right;
   background-repeat: no-repeat, no-repeat;
}

@media (max-width: 991.98px) {
   /* Stacked layout puts copy over the whole width, so keep it dark throughout. */
   .operation-sec .operate-bg.bg-cover.position-absolute {
      background-image:
         linear-gradient(180deg,
            rgba(2, 14, 40, .95) 0%,
            rgba(2, 14, 40, .88) 100%),
         url(../assets/operation/section-bg.jpg);
   }
}

/* --------------------------------------------------------------------------
   What Sets Us Apart -- floating detail card

   The template sat this card inside the image (bottom: 40px). Dropping it
   below the bottom edge lets the photograph read at full height and gives the
   card the look of sitting on top of the section rather than inside the
   picture. The shorter box follows from the copy: a label, a line and a link.
   -------------------------------------------------------------------------- */
.operation-sec .tab-content .operation-content .opt-card {
   height: 211px;
   bottom: -67px;
}

/* The card now overhangs the image, so the section needs room underneath it
   or the next section crowds against it. */
@media (min-width: 992px) {
   .operation-sec {
      padding-bottom: 160px;
   }
}

/* At 211px the card has ~151px of usable height, which the template's icon and
   margins overrun -- the "More Details" link fell outside the box. Tighten the
   stack so all four elements fit. */
.operation-sec .opt-card .opt-icon {
   width: 40px;
   height: 40px;
   border-radius: 10px;
}

.operation-sec .opt-card .opt-icon i {
   font-size: 17px;
}

.operation-sec .tab-content .operation-content .opt-card h3 {
   margin: 12px 0 6px;
   font-size: 18px;
}

.operation-sec .tab-content .operation-content .opt-card p {
   margin-bottom: 0;
   font-size: 14px;
   line-height: 21px;
}

.operation-sec .tab-content .operation-content .opt-card a.link-btn {
   margin-top: 8px;
}

/* --------------------------------------------------------------------------
   Footer

   The template's footer mark was a small SVG, so .f-logo never constrained it.
   assets/dark-bg.png is the RD lockup at 1774x887 and fills the whole footer
   if dropped in unsized -- the same trap the header logo hit. Size by height
   and let the width follow, as with the header.
   -------------------------------------------------------------------------- */
.footer .f-logo img {
   height: 90px;
   width: auto;
   max-width: none;
}

@media (max-width: 575.98px) {
   .footer .f-logo img {
      height: 56px;
   }
}

/* Credential badges under the blurb. All three artworks are opaque on white,
   so each sits on its own white chip rather than being dropped straight onto
   the dark footer. ISO and MSME are circular marks and get circular chips;
   Google's is a wide lockup and keeps a rounded rectangle.

   They are split across two rows on purpose. The Google lockup is 3.3:1, so
   on a shared row it eats the width and the two round marks can only be about
   50px before the set overflows this column. Giving it its own line lets the
   circles run half as big again and line up square with each other. */
.footer .footer-badges {
   margin-top: 26px;
   display: flex;
   flex-direction: column;
   align-items: flex-start;
   gap: 14px;
}

.footer .footer-badges .fb-row {
   display: flex;
   align-items: center;
   gap: 14px;
}

.footer .footer-badges .fb-item {
   background-color: var(--white);
   display: flex;
   align-items: center;
   justify-content: center;
   overflow: hidden;
}

.footer .footer-badges .fb-round {
   width: 78px;
   height: 78px;
   border-radius: 50%;
   padding: 5px;
}

.footer .footer-badges .fb-wide {
   height: 58px;
   padding: 7px 12px;
   border-radius: 10px;
}

.footer .footer-badges .fb-item img {
   display: block;
   max-width: 100%;
   max-height: 100%;
   width: auto;
   object-fit: contain;
}

/* Contact rows: icon in a fixed gutter so the wrapped address lines up. */
.footer .footer-contact {
   list-style: none;
   margin: 0;
   padding: 0;
}

.footer .footer-contact li {
   position: relative;
   padding-left: 28px;
   margin-bottom: 14px;
   font-size: 15px;
   line-height: 23px;
   color: rgba(var(--white-rgb), .75);
}

.footer .footer-contact li i {
   position: absolute;
   left: 0;
   top: 5px;
   font-size: 14px;
   color: var(--primary);
}

.footer .footer-contact li a {
   color: inherit;
}

.footer .footer-contact li a:hover {
   color: var(--primary);
}

/* The newsletter block used to sit above these columns, and the rule plus the
   60px margin existed to divide the two. With it removed the border became a
   line with nothing above it, over a large empty gap. */
.footer-2 .footer-widgets {
   border-top: 0;
   margin-top: 0;
   padding-top: 95px;
}

@media (max-width: 991.98px) {
   .footer-2 .footer-widgets {
      padding-top: 70px;
   }
}

/* --------------------------------------------------------------------------
   Footer background

   The template's .footer .parallax-overly is a 96% dark multiply, tuned to
   bury images/footer-bg.jpg almost completely. Dropping the RD depot photo in
   behind it left the footer looking plain black -- the image was there and
   invisible. Easing the overlay lets the fleet and the depot read while the
   white column text stays well clear of the contrast floor.
   -------------------------------------------------------------------------- */
.footer .parallax-overly {
   background-color: var(--dark);
   opacity: .9;
}

/* --------------------------------------------------------------------------
   Footer text colour

   The template sets the footer links and body copy in --mute-alt (#b0b2b8) at
   80% opacity. That was legible over its own near-black footer, but this one
   carries a photograph of the fleet behind it, and mid-grey text over a busy
   image sits far too close to the background to read. White throughout.
   Links still go orange on hover, so the affordance is unchanged.
   -------------------------------------------------------------------------- */
.footer .footer-widget ul li a {
   color: var(--white);
   opacity: 1;
}

.footer .footer-widget p,
.footer .footer-widget span {
   color: var(--white);
}

.footer .footer-contact li,
.footer .footer-contact li a {
   color: var(--white);
}

/* The two round marks are the same size, but they do not read that way: the
   ISO ring runs to the edge of its artwork while the MSME emblem sits inside a
   wide white margin, so at matching chip sizes MSME looks the smaller of the
   pair. Scaling it up evens the optical weight -- and costs nothing, because
   what gets cropped is white disc against an equally white chip. */
.footer .footer-badges .fb-msme img {
   transform: scale(1.26);
}

/* Registration number, sitting between the blurb and the credential badges --
   the same kind of proof, so they belong together rather than stranded in the
   bottom bar. */
.footer .footer-gst {
   margin: 14px 0 0;
   font-size: 14px;
   letter-spacing: .04em;
   text-transform: uppercase;
   color: rgba(var(--white-rgb), .62);
}

.footer .footer-gst span {
   color: var(--white);
   letter-spacing: .06em;
}

/* The template put the bottom-bar colour and padding on the <a>, so the GST
   line -- which is text, not a link -- inherits the dark body colour and sits
   unaligned against the orange bar without this. */
.footer-2 .footer-nav ul li {
   color: rgba(var(--white-rgb), .88);
   padding: 30px 0 25px;
   font-size: 15px;
   letter-spacing: .02em;
}

@media (max-width: 767.98px) {
   .footer-2 .footer-nav ul li {
      padding: 12px 0 0;
   }
}

/* The bottom bar's <a> rule carries the template's own 30px/20px padding,
   meant for standalone nav links. Inside this credit the <li> already supplies
   that spacing, so the anchor has to sit flush or the line lands off-centre
   against the orange. */
.footer-2 .footer-nav ul li a {
   display: inline;
   padding: 0;
   color: var(--white);
   font-weight: 600;
   text-decoration: underline;
   text-underline-offset: 3px;
   text-decoration-thickness: 1px;
   text-decoration-color: rgba(var(--white-rgb), .45);
}

.footer-2 .footer-nav ul li a:hover {
   text-decoration-color: var(--white);
}

/* --------------------------------------------------------------------------
   Off-canvas menu (the burger panel)

   Replaces the template's stock contents -- a generic agency blurb, an
   air/sea/road freight service list and six social links all pointing at "#".
   The contact block is new: this panel is what phone visitors open, so the
   number and email belong in it.
   -------------------------------------------------------------------------- */
.canvas-menu .canvas-contact h5 {
   margin-bottom: 18px;
}

.canvas-menu .canvas-contact ul {
   list-style: none;
   margin: 0;
   padding: 0;
}

.canvas-menu .canvas-contact li {
   position: relative;
   padding-left: 28px;
   margin-bottom: 12px;
   font-size: 15px;
   line-height: 23px;
   color: var(--body-color);
}

.canvas-menu .canvas-contact li i {
   position: absolute;
   left: 0;
   top: 5px;
   font-size: 14px;
   color: var(--primary);
}

.canvas-menu .canvas-contact li a {
   color: inherit;
   text-decoration: none;
}

.canvas-menu .canvas-contact li a:hover {
   color: var(--primary);
}

/* --------------------------------------------------------------------------
   Equal card heights

   Two sets were ragged because their copy differs in length: the service cards
   (427-453px) and the gallery captions (262-283px). Everything else on the
   page already matched.

   Swiper gives its slides equal height, but the template centres the card
   inside the slide rather than stretching it, so the cards themselves stayed
   at content height. Stretch them, then push the footer down so the "View
   Details" rows line up across the carousel instead of floating at whatever
   height the paragraph above ends.
   -------------------------------------------------------------------------- */
.service-slider2 .swiper-slide {
   /* Swiper puts `height: 100%` on every slide. In a flex wrapper of auto
      height that resolves to content height AND counts as a definite
      cross-size, which opts the slide out of `align-items: stretch` -- so the
      slides sat ragged and no amount of stretching the card inside could fix
      it. Returning the slide to `auto` lets the row equalise them. */
   height: auto;
   align-items: stretch;
}

.service-slider2 .swiper-slide .service-card2 {
   display: flex;
   flex-direction: column;
   width: 100%;
   height: 100%;
}

.service-slider2 .swiper-slide .service-card2 .service-footer {
   margin-top: auto;
}

/* The gallery figures carry height:100%, but their column is a plain block, so
   there is no resolved height for that to be a percentage of. Making the
   column a flex container gives the figure something to stretch against. */
.gallery-grid > [class*="col-"] {
   display: flex;
}

/* That equalises each row-line, but the grid wraps onto two lines and a flex
   line only matches its own tallest item -- so line one still finished taller
   than line two. The whole difference is the caption: one line of text in some
   tiles, two in others. Reserving two lines' worth levels every tile on the
   page, not just within its row. */
.gallery-item figcaption {
   min-height: 72px;   /* 2 x 21px line-height + 30px vertical padding */
}

@media (max-width: 575.98px) {
   .gallery-item figcaption {
      min-height: 0;    /* single column: every caption is on its own line */
   }
}

/* The canvas panel is white, so it takes the navy-on-white lockup rather than
   the white-on-dark one used in the header and footer.

   assets/logo.jpeg is a 1254x1254 square that is 55% empty -- at the panel's
   175px cap the mark itself would render about 70px wide, adrift in white.
   assets/logo-mark.jpg is the same artwork cropped to its ink bounds, so the
   width cap applies to the logo instead of the padding around it. */
.canvas-menu .logo img {
   width: 100%;
   max-width: 190px;
   height: auto;
}

/* --------------------------------------------------------------------------
   Index header

   The homepage now uses .header-default, which is what paints the white pill
   (images/header-bg.png as the nav container's background) seen on the inner
   pages, instead of header-2's translucent dark bar.

   header-2 was also the only thing styling the top bar, and that bar stays --
   so its four topbar rules are carried over here. The white link colour is not
   among them: that comes from the base .header-top rules either way.

   Scoped to .header-default .header-top, which only matches a page that has
   both -- the other header-default pages carry no top bar at all.
   -------------------------------------------------------------------------- */
.header.header-default .header-top {
   margin: 20px 15px 15px;
}

.header.header-default .header-top .social-share a {
   background-color: rgba(var(--white-rgb), .15);
   width: 36px;
   height: 36px;
   font-size: 14px;
}

.header.header-default .header-top .social-share a:hover {
   background-color: var(--primary);
}

@media (max-width: 575.98px) {
   .header.header-default .header-top .social-share a {
      margin-right: 3px;
   }
}

/* ==========================================================================
   Contact page
   The template put four city tabs where the left column now carries RD's two
   real offices, its registration numbers and one map. .contact-sec sits in a
   .container-fluid, so this column runs to the left edge of the viewport and
   is padded from the inside.
   ========================================================================== */

/* The three cards carry different amounts of text, so left alone they end at
   three different heights and the .card-meta watermarks sit on three different
   baselines. The columns are already stretched by the row; this makes the card
   fill its column. */
.contact-widgets .contact-card {
   height: 100%;
}

.contact-widgets .sec-intro .ct-lead {
   max-width: 620px;
   margin: 18px auto 0;
}

/* Side by side, let the photo panel run to the same height as the office panel
   with the form centred in it, instead of ending short and leaving the row
   lopsided. Once the columns stack there is nothing to match, so this is
   confined to lg and up. */
@media (min-width: 992px) {
   .contact-sec .contact-form-wrap > [class*="col-"] {
      display: flex;
   }

   .contact-sec .contact-form-wrap .contact-form {
      flex: 1 1 auto;
      display: flex;
      align-items: center;
   }
}

@media (max-width: 991.98px) {
   .contact-sec .contact-form-wrap .contact-form {
      margin: 0 15px;
   }
}

.contact-sec .office-panel {
   background-color: var(--dark);
   color: rgba(var(--white-rgb), .8);
   padding: 45px 40px;
   height: 100%;
   border-radius: 0 20px 20px 0;
}

@media (min-width: 992px) {
   .contact-sec .office-panel {
      display: flex;
      flex-direction: column;
      /* The column is a flex container, so without this the panel shrinks to
         its text width and leaves a gap between it and the form. */
      flex: 1 1 auto;
   }
}

@media (max-width: 991.98px) {
   .contact-sec .office-panel {
      border-radius: 20px;
      margin: 0 15px 30px;
      padding: 35px 30px;
   }
}

.contact-sec .office-panel .office-block {
   padding-bottom: 25px;
   margin-bottom: 25px;
   border-bottom: 1px solid rgba(var(--white-rgb), .12);
}

.contact-sec .office-panel .office-block h4 {
   color: var(--white);
   font-size: 20px;
   margin-bottom: 12px;
}

.contact-sec .office-panel .office-block h4:before {
   content: "";
   display: inline-block;
   width: 22px;
   height: 2px;
   background-color: var(--primary);
   vertical-align: middle;
   margin-right: 10px;
}

.contact-sec .office-panel .office-block p {
   font-size: 15px;
   line-height: 26px;
   margin: 0;
   color: rgba(var(--white-rgb), .72);
}

/* Credential badges, same three as the footer carries. Each sits on its own
   white chip because all three artworks assume a white ground and would
   otherwise disappear into the panel. */
.contact-sec .office-panel .office-badges {
   display: flex;
   flex-wrap: wrap;
   align-items: center;
   gap: 12px;
   margin-bottom: 30px;
}

.contact-sec .office-panel .office-badges .ob-item {
   background-color: var(--white);
   display: flex;
   align-items: center;
   justify-content: center;
   overflow: hidden;
}

/* All three share one height so they read as a single row rather than three
   chips of different weights. */
.contact-sec .office-panel .office-badges .ob-round {
   width: 64px;
   height: 64px;
   border-radius: 50%;
   padding: 4px;
   flex: 0 0 auto;
}

.contact-sec .office-panel .office-badges .ob-wide {
   height: 64px;
   padding: 8px 12px;
   border-radius: 10px;
   flex: 0 0 auto;
}

.contact-sec .office-panel .office-badges .ob-item img {
   display: block;
   max-width: 100%;
   max-height: 100%;
   width: auto;
   object-fit: contain;
}

/* The MSME emblem carries far more internal padding than the ISO mark it
   pairs with, so it reads smaller at the same chip size. */
.contact-sec .office-panel .office-badges .ob-msme img {
   transform: scale(1.26);
}

/* Between lg and xl the column is too narrow to hold all three chips at full
   size, and the MSME one drops to a second line. Trim the panel padding and
   the chips rather than let the row break. */
@media (min-width: 992px) and (max-width: 1399.98px) {
   .contact-sec .office-panel {
      padding: 40px 26px;
   }

   .contact-sec .office-panel .office-badges {
      gap: 10px;
   }

   .contact-sec .office-panel .office-badges .ob-round {
      width: 50px;
      height: 50px;
   }

   .contact-sec .office-panel .office-badges .ob-wide {
      height: 50px;
      padding: 7px 10px;
   }
}

/* Stacked, the panel is only as wide as the phone, so the chips come down
   again to keep the three on one line. */
@media (max-width: 991.98px) {
   .contact-sec .office-panel .office-badges {
      gap: 8px;
   }

   .contact-sec .office-panel .office-badges .ob-round {
      width: 44px;
      height: 44px;
   }

   .contact-sec .office-panel .office-badges .ob-wide {
      height: 44px;
      padding: 6px 9px;
   }
}

/* Narrower still at lg, where the column is barely 300px wide. */
@media (min-width: 992px) and (max-width: 1199.98px) {
   .contact-sec .office-panel {
      padding: 40px 20px;
   }

   .contact-sec .office-panel .office-badges {
      gap: 8px;
   }

   .contact-sec .office-panel .office-badges .ob-round {
      width: 44px;
      height: 44px;
   }

   .contact-sec .office-panel .office-badges .ob-wide {
      height: 44px;
      padding: 6px 9px;
   }
}

/* The panel is as tall as the form beside it, so let the map take up whatever
   height is left over rather than leaving a dead strip under it. */
.contact-sec .office-panel .office-map {
   border-radius: 14px;
   overflow: hidden;
   line-height: 0;
   flex: 1 1 auto;
   min-height: 300px;
}

.contact-sec .office-panel .office-map iframe {
   display: block;
   width: 100%;
   height: 100%;
   min-height: 300px;
   border: 0;
}

/* ==========================================================================
   Promo banner
   jarallax scales the banner image well past the section and then translates
   it on scroll, so the photo sat off-centre and the trucks were cropped below
   the fold of the band. The jarallax hooks are off the markup now; the image
   stays put and covers the section from its own centre.
   ========================================================================== */

.promo-sec {
   position: relative;
   overflow: hidden;
}

.promo-sec .jarallax-img {
   position: absolute;
   inset: 0;
   width: 100%;
   height: 100%;
   object-fit: cover;
   object-position: center center;
   z-index: 0;
}

/* jarallax used to own the layering; with it gone the overlay and the heading
   need to be lifted back above the photo explicitly. */
.promo-sec .parallax-overly {
   z-index: 1;
}

.promo-sec .container {
   position: relative;
   z-index: 2;
}

/* ==========================================================================
   Gallery page (gallery.html)

   Reuses the homepage gallery's tile, caption and lightbox rules and adds the
   grouping the standalone page needs: fourteen photographs are too many to
   read as one undifferentiated wall, so they run in three labelled sets and
   share a single Fancybox group, which lets the lightbox page through all of
   them in order.
   ========================================================================== */

.gallery-page .gallery-group + .gallery-group {
   margin-top: 70px;
}

.gallery-page .gallery-group-title {
   font-size: 26px;
   margin: 0;
   padding-bottom: 16px;
   border-bottom: 1px solid var(--theme-border);
}

.gallery-page .gallery-group-title:before {
   content: "";
   display: inline-block;
   width: 26px;
   height: 3px;
   background-color: var(--primary);
   vertical-align: middle;
   margin-right: 14px;
}

/* The shared .gallery-grid rule spaces the homepage's single grid off its
   intro; under a group heading that gap is too big. */
.gallery-page .gallery-grid {
   margin-top: 30px;
}

.gallery-page .gallery-cta {
   margin-top: 80px;
   padding: 55px 30px;
   border-radius: 20px;
   background-color: var(--light);
}

.gallery-page .gallery-cta p {
   max-width: 560px;
   margin: 16px auto 30px;
}

@media (max-width: 767.98px) {
   .gallery-page .gallery-group + .gallery-group {
      margin-top: 50px;
   }

   .gallery-page .gallery-group-title {
      font-size: 22px;
   }

   .gallery-page .gallery-cta {
      margin-top: 55px;
      padding: 40px 20px;
   }
}

/* ==========================================================================
   Home page: news & milestones slider (index.html ".blog-slider-sec")

   Built on the template's own .blog-entry card so it matches the rest of the
   site's card language. The template's version carries an invented author and
   publish date; these are real milestones with no byline, so the meta row
   names who presented the recognition instead and a category chip sits on the
   thumbnail.
   ========================================================================== */

/* The template caps .sec-intro at 700px, which is right for a centred heading
   but here it strands the prev/next buttons in the middle of the row. */
.blog-slider-sec .sec-intro {
   max-width: none;
   margin: 0 0 45px;
}

.blog-slider-sec .bs-nav {
   display: flex;
   gap: 12px;
   flex: 0 0 auto;
}

@media (max-width: 991.98px) {
   .blog-slider-sec .bs-nav {
      margin-top: 25px;
   }
}

.blog-slider-sec .bs-nav button {
   width: 52px;
   height: 52px;
   border-radius: 50%;
   border: 1px solid var(--theme-border);
   background-color: transparent;
   color: var(--headings-color);
   font-size: 15px;
   display: flex;
   align-items: center;
   justify-content: center;
   transition: all .3s ease;
}

.blog-slider-sec .bs-nav button:hover {
   background-color: var(--primary);
   border-color: var(--primary);
   color: var(--white);
}

/* Swiper measures slide height from content, so without this the shorter cards
   sit with their thumbnails at different heights across the row. */
.blog-slider-sec .blog-slider .swiper-slide {
   height: auto;
}

.blog-slider-sec .blog-entry {
   margin-bottom: 0;
   height: 100%;
}

/* The four photographs are a mix of landscape and portrait phone shots; a fixed
   ratio with object-fit keeps the row even. */
.blog-slider-sec .blog-entry .entry-thumb {
   position: relative;
   aspect-ratio: 4 / 3;
}

.blog-slider-sec .blog-entry .entry-thumb a {
   display: block;
   height: 100%;
}

.blog-slider-sec .blog-entry .entry-thumb img {
   width: 100%;
   height: 100%;
   object-fit: cover;
   object-position: center;
   transition: transform .5s cubic-bezier(.4, 0, .2, 1);
}

.blog-slider-sec .blog-entry:hover .entry-thumb img {
   transform: scale(1.06);
}

.blog-slider-sec .blog-entry .entry-cat {
   position: absolute;
   left: 16px;
   top: 16px;
   z-index: 1;
   background-color: var(--primary);
   color: var(--white);
   font-size: 12px;
   font-weight: 600;
   letter-spacing: .04em;
   text-transform: uppercase;
   padding: 7px 14px;
   border-radius: 30px;
}

.blog-slider-sec .blog-entry .entry-meta {
   font-size: 14px;
   color: var(--text-mute, #788094);
}

.blog-slider-sec .blog-entry .entry-meta i {
   color: var(--primary);
}

/* Two lines of headline on every card, so the "Read more" links line up across
   the row whether the title wraps once or twice. */
.blog-slider-sec .blog-entry h3.display-4 {
   font-size: 22px;
   line-height: 1.35;
   min-height: 60px;
   margin: 0;
}

.blog-slider-sec .blog-entry h3.display-4 a {
   color: var(--headings-color);
   transition: color .3s ease;
}

.blog-slider-sec .blog-entry h3.display-4 a:hover {
   color: var(--primary);
}

@media (max-width: 767.98px) {
   .blog-slider-sec .blog-entry h3.display-4 {
      font-size: 20px;
      min-height: 0;
   }
}

/* --------------------------------------------------------------------------
   Home hero slider: equal slide heights

   The theme sizes each slide with padding alone, so a slide is exactly as tall
   as its own text. Swiper then sizes the carousel to the TALLEST slide, and a
   shorter slide leaves the difference empty at the bottom - measured at 105px
   between the two seeded slides, showing as a strip of bare section background
   under the photo before the about section starts.

   Stretching every slide to the tallest one closes that gap. The carousel keeps
   the height the theme gave it; only the short slides grow to match, so the
   background photo covers the full section on every slide.
   -------------------------------------------------------------------------- */

.hero-slider2 .swiper-wrapper {
   align-items: stretch;
}

.hero-slider2 .swiper-slide {
   height: auto;
   display: flex;
}

.hero-slider2 .swiper-slide > .slide-item {
   flex: 1 1 auto;
   display: flex;
   flex-direction: column;
   justify-content: center;
}

/* --------------------------------------------------------------------------
   Service detail pages: thumbnail and gallery proportions

   The template cut its art to exact sizes and let the files' own dimensions
   do the layout -- single-service.jpg is 1224x530 and the pair below it,
   serv-g1/g2.jpg, are 597x339. With `img-fluid` (width:100%, height:auto)
   that works only while every page ships art at those ratios.

   The RD pages reuse general photographs instead, and their ratios are all
   over the place: ace-hero.png is square, fleet-reveal.jpeg is portrait
   (768x1024), fleet-hero.png is a 3:1 letterbox. Left to `height:auto` each
   page sizes its own thumbnail -- the TATA ACE page renders a tall square,
   Parcel Delivery an even taller portrait, Intercity a thin strip -- so no
   two service pages match.

   Pinning the template's two ratios and cropping to them with object-fit
   gives every page the same shapes it has on single-service.html, whatever
   the source photograph happens to be.
   -------------------------------------------------------------------------- */
.single-service .service-thumb > img {
   width: 100%;
   aspect-ratio: 1224 / 530;
   object-fit: cover;
   object-position: center;
}

.single-service .gallery-single > img,
.single-service .gallery-single > a > img {
   width: 100%;
   aspect-ratio: 597 / 339;
   object-fit: cover;
   object-position: center;

   /* The template caps these at 50% because the image used to be the flex
      item. On a service page the link is, so the cap would apply again inside
      it and render each photo at a quarter of the row. */
   max-width: none;
}

/* Equal halves rather than the template's `max-width: 50%`, which does not
   account for the flex gap between them and leaves the pair to shrink
   unevenly once a ratio is pinned. */
@media (min-width: 576px) {
   .single-service .gallery-single > img,
   .single-service .gallery-single > a {
      flex: 1 1 0;
      min-width: 0;
      max-width: none;
   }
}

/* --------------------------------------------------------------------------
   Service page body: editor output

   The copy on a service page now comes from the admin's rich-text editor, so
   the markup is whatever the editor emits -- plain <p>, <h3>, <ul> -- not the
   template's hand-written classes. These rules give that output the styling
   the static pages got from `ul.ct-list` and `h3.display-3`, so the editor
   does not have to know about theme class names to produce a page that looks
   right.
   -------------------------------------------------------------------------- */
.service-body h2,
.service-body h3 {
   font-size: 36px;
   margin-top: 40px;
   margin-bottom: 20px;
}

.service-body h4 {
   font-size: 24px;
   margin-top: 32px;
   margin-bottom: 16px;
}

.service-body > :first-child {
   margin-top: 0;
}

.service-body ul {
   padding: 0;
   list-style: none;
   margin: 30px 0;
}

.service-body ul li {
   position: relative;
   z-index: 1;
   margin-bottom: 20px;
   padding-left: 30px;
   break-inside: avoid;
}

.service-body ul li::before {
   position: absolute;
   content: "";
   left: 0;
   top: 4px;
   width: 16px;
   height: 16px;
   background: url(../images/icons/check.png) no-repeat 0;
}

/* The static pages set their ticked lists in two hand-split columns. An editor
   produces one list, so the split is done here instead -- same look, and the
   person writing the page just types a list. */
@media (min-width: 576px) {
   .service-body ul {
      column-count: 2;
      column-gap: 50px;
   }
}

/* --------------------------------------------------------------------------
   Service gallery: lightbox triggers

   The photographs are wrapped in links so Fancybox can open them full size --
   it is already loaded and bound to [data-fancybox] in custom.js. The link has
   to carry the layout the bare <img> used to, or the flex row collapses.
   -------------------------------------------------------------------------- */
.single-service .gallery-single > a {
   display: block;
   line-height: 0;
}

.single-service .gallery-single > a > img {
   transition: opacity .3s ease;
}

.single-service .gallery-single > a:hover > img {
   opacity: .85;
}

/* --------------------------------------------------------------------------
   Service page body: the rest of what the editor can produce

   The editor offers tables, images, alignment, rules and ordered lists, and
   the sanitiser keeps them, so the page has to know how to draw them. Inline
   styles the editor sets -- colour, size, alignment -- apply on their own; what
   is needed here is the structure the theme never had a style for.
   -------------------------------------------------------------------------- */
.service-body img {
   max-width: 100%;
   height: auto;
   border-radius: 12px;
   margin: 10px 0;
}

.service-body ol {
   margin: 24px 0;
   padding-left: 22px;
}

.service-body ol li {
   margin-bottom: 12px;
}

.service-body blockquote {
   border-left: 3px solid var(--primary);
   padding: 4px 0 4px 20px;
   margin: 26px 0;
   font-style: italic;
}

.service-body hr {
   border: 0;
   border-top: 1px solid #e2e6ee;
   margin: 32px 0;
}

/* A table wide enough to overflow scrolls rather than stretching the column
   and pushing the sidebar out of the grid. */
.service-body table {
   width: 100%;
   margin: 26px 0;
   border-collapse: collapse;
   display: block;
   overflow-x: auto;
}

.service-body table th,
.service-body table td {
   border: 1px solid #dfe4ec;
   padding: 10px 14px;
   text-align: left;
   vertical-align: top;
}

.service-body table th {
   background: #f4f6fa;
   font-weight: 600;
}

/* The ticked-list styling is for the unordered list the page is built around;
   a list inside a table cell is a plain list. */
.service-body table ul {
   column-count: 1;
   margin: 0;
}

.service-body table ul li::before {
   display: none;
}

.service-body table ul li {
   padding-left: 0;
}

/* --------------------------------------------------------------------------
   Blog

   The template's byline portrait is a circle only because the artwork it
   shipped with -- images/ava1.png, ava2.png -- is an 80x80 PNG that was
   already cropped round with transparent corners. There is no border-radius
   anywhere in style.css; the only rule on it is a margin.

   Author photographs are uploaded now, so that no longer holds: a phone
   snapshot arrives as an opaque rectangle and drew as one. Round it here, and
   fix the box, so any photograph lands in the same circle the design asks for
   whatever shape it was uploaded at.

   80px matches the artwork the template rendered at its natural size.
   -------------------------------------------------------------------------- */
.post-header .ph-inner img {
   width: 80px;
   height: 80px;
   border-radius: 50%;
   object-fit: cover;
   flex-shrink: 0;
}

/* Same reasoning for the comment portraits. These carry Bootstrap's
   .rounded-circle already, but that alone turns a non-square image into an
   ellipse -- the fixed box and the crop are what keep it a circle. */
.comment-entry .comment-thumb img {
   width: 80px;
   height: 80px;
   object-fit: cover;
   flex-shrink: 0;
}

/* A company answer, badged so a reader can tell it from another visitor's
   comment. */
.rd-staff-badge {
   font-size: .7rem;
   text-transform: uppercase;
   letter-spacing: .06em;
   padding: .15rem .5rem;
   border-radius: 999px;
   vertical-align: middle;
}

/* The "replying to ..." strip above the comment form, revealed by the page's
   own script once Reply is clicked. */
.rd-replying {
   display: none;
}

.rd-replying.is-on {
   display: flex;
}

/* --------------------------------------------------------------------------
   Blog grid thumbnails

   The template's .blog-entry never sizes its own thumbnail -- it relies on the
   artwork being delivered at exactly the column width. Cover photographs are
   uploaded now, so that no longer holds: a 780px file inside a ~380px column
   renders at its natural width and .entry-thumb's overflow:hidden crops the
   right-hand half away, leaving what looks like a tall portrait cut out of the
   left of the photo.

   Same treatment as the home page's news slider above: fix the box at 4:3 and
   let object-fit do the cropping, so every card in the row is the same height
   whatever was uploaded.
   -------------------------------------------------------------------------- */
.blog-page .blog-entry .entry-thumb {
   aspect-ratio: 4 / 3;
}

.blog-page .blog-entry .entry-thumb a {
   display: block;
   height: 100%;
}

.blog-page .blog-entry .entry-thumb img {
   width: 100%;
   height: 100%;
   object-fit: cover;
   object-position: center;
}

/* The template pushes the card title 60px clear of the excerpt to make room
   for a "Learn more" link that sat at the bottom of a fixed-height card. These
   cards size to their content, so that gap just strands the link. */
.blog-page .blog-entry h3 {
   margin-bottom: 20px;
}

/* --------------------------------------------------------------------------
   Article footer: tags and share

   The template's rules were written for a full-width article carrying four
   tags and four share icons. This article sits in a col-lg-8 next to the
   sidebar and carries a fifth icon (copy link), so the icon row wrapped and
   dropped one onto a line of its own.

   Three things in the template fight a tidy row here:

     - .article-footer h4 { margin: 0 0 20px !important } pushes each label
       20px off its own baseline, so against an align-items:center parent the
       "Tags:" and "Share:" labels sat high of the pills beside them. The
       !important is the template's, which is why it is matched here.
     - the pills and icons space themselves with margin-right on inline-block
       elements, which leaves a ragged trailing margin once a row wraps.
     - Bootstrap's ms-3 / d-sm-flex utilities on the wrappers are all
       !important, so the markup drops them and this owns the layout instead.

   Laid out with flex and gap: the label sits on the first line of its group,
   the pills and icons wrap as a grid, and the two groups stack on a narrow
   screen rather than squeezing.
   -------------------------------------------------------------------------- */
.article-footer {
   display: flex;
   flex-wrap: wrap;
   align-items: flex-start;
   justify-content: space-between;
   gap: 24px 40px;
}

/* 40px is the height of a share icon and about that of a tag pill, so the
   label sits centred against the first row of whatever is beside it. */
.article-footer h4 {
   margin: 0 !important;
   line-height: 40px;
   flex: 0 0 auto;
   white-space: nowrap;
}

.article-footer .article-tags,
.article-footer .article-share {
   display: flex;
   align-items: flex-start;
   gap: 16px;
   margin: 0;
   min-width: 0;
}

/* Keeps the share group hard right even when the article carries no tags and
   it is the only child in the row. */
.article-footer .article-share {
   margin-left: auto;
}

.article-footer .tagcloude,
.article-footer .social-share {
   display: flex;
   flex-wrap: wrap;
   gap: 10px;
   margin: 0;
}

/* The template's own spacing, now handled by the gap above. */
.article-footer .tagcloude a,
.article-footer .social-share a {
   margin: 0;
}

/* Below the sm breakpoint the two groups each take the full width, so the
   share row starts at the left edge rather than hanging off the right. */
@media (max-width: 575.98px) {
   .article-footer .article-tags,
   .article-footer .article-share {
      flex: 1 0 100%;
      margin-left: 0;
   }
}

/* A reply from the business is signed with the logo rather than a portrait.
   The mark is a wide lockup (440x263), so it is fitted inside the circle
   rather than cropped to fill it -- object-fit:cover would take a square out
   of the middle and lose both ends of the wordmark. */
.comment-entry .comment-thumb img.rd-staff-avatar {
   object-fit: contain;
   padding: 10px;
   background-color: var(--white, #fff);
   border: 1px solid var(--theme-border, #e6e9f0);
}

/* --------------------------------------------------------------------------
   Enquiry form: the picture code

   The code sits beside its input rather than above it, so the pair reads as
   one question. The refresh button is a real button next to the picture --
   asking for a new image is what draws a new code, so it needs no state.
   -------------------------------------------------------------------------- */
.rd-captcha {
   display: flex;
   align-items: center;
   gap: 10px;
}

/* The picture is given a width to aim for rather than a fixed one: in the home
   page's sidebar column the picture, the refresh button and the gap together
   are wider than the column, and a fixed width would push the button past the
   card's edge. Shrinking is allowed; the code stays legible well below 180px. */
.rd-captcha-img {
   display: block;
   width: 180px;
   min-width: 0;
   max-width: 100%;
   height: 58px;
   border-radius: 8px;
   border: 1px solid var(--theme-border, #e6e9f0);
   background-color: #f7f8fa;
   flex: 0 1 auto;
   object-fit: contain;
}

.rd-captcha-reload {
   width: 40px;
   height: 40px;
   flex: 0 0 auto;
   display: inline-flex;
   align-items: center;
   justify-content: center;
   border-radius: 50%;
   border: 1px solid var(--theme-border, #e6e9f0);
   background-color: transparent;
   color: inherit;
   transition: all .3s ease;
}

.rd-captcha-reload:hover {
   background-color: var(--primary);
   border-color: var(--primary);
   color: var(--white);
}

/* The form sits on a photograph on the contact page, so its own messages need
   a solid ground rather than the translucent default. */
.rd-enquiry-form .alert {
   background-color: var(--white);
}

@media (max-width: 575.98px) {
   .rd-captcha-img {
      width: 150px;
      height: 48px;
   }
}

/* --------------------------------------------------------------------------
   Case study: the figures strip

   A short row of numbers under the summary. The template has nothing like it
   -- its case page goes straight from the title into body copy -- so the rule
   is here rather than an override of something.
   -------------------------------------------------------------------------- */
.single-case .rd-case-stats {
   display: flex;
   flex-wrap: wrap;
   gap: 14px;
   margin: 30px 0 34px;
}

.single-case .rd-case-stat {
   flex: 1 1 150px;
   padding: 18px 20px;
   border: 1px solid var(--theme-border, #e6e9f0);
   border-radius: 14px;
   background-color: var(--bg-shade, #f7f8fa);
}

.single-case .rd-case-stat strong {
   display: block;
   font-family: var(--font-family-headings, "Onest", sans-serif);
   font-size: 32px;
   line-height: 1.1;
   color: var(--primary);
}

.single-case .rd-case-stat span {
   display: block;
   margin-top: 4px;
   font-size: 14px;
   color: var(--text-mute, #788094);
}

/* The pull-out photo and its list share a row; below sm they stack, and the
   photo should not keep a fixed width it no longer has room for. */
.single-case .case-video img {
   width: 100%;
}

/* --------------------------------------------------------------------------
   Case studies: the portfolio grid

   One fault the template leaves behind, which only shows once the grid carries
   real content.

   .portfolio-info is position:absolute with bottom:20px, but nothing in the
   card is positioned -- the template relies on Isotope making .grid-item
   absolute and acting as the context by accident. When Isotope has not laid
   out yet, or cannot, every caption escapes its card and stacks up at the
   bottom of the document, on top of the footer. Giving the card its own
   positioning context makes the caption sit on its photograph either way.

   The photographs then need a fixed box, or a tall one pushes its caption past
   the bottom of the card it belongs to.

   Nothing here touches the caption's own colours: the template already styles
   it as a white panel with dark text that turns orange with white text on
   hover, and the orange accent square behind it is its :after.
   -------------------------------------------------------------------------- */
.project-sec .portfolio-card {
   position: relative;
}

.project-sec .portfolio-card .portfolio-img {
   position: relative;
   display: block;
   aspect-ratio: 4 / 3;
   overflow: hidden;
}

.project-sec .portfolio-card .portfolio-img a {
   display: block;
   height: 100%;
}

.project-sec .portfolio-card .portfolio-img img {
   width: 100%;
   height: 100%;
   object-fit: cover;
}

/* Titles here are real sentences rather than the template's two-word demo
   names, so the panel needs to breathe a little and cap its own height. */
.project-sec .portfolio-card .portfolio-info h3 {
   font-size: 18px;
   line-height: 1.35;
   margin-bottom: 0;
}

/* --------------------------------------------------------------------------
   WhatsApp button

   The floating chat button from the Purbapally site, rebuilt for this one.
   Two things changed on the way over: it stacks above .scroll-top instead of
   fighting it for the same corner, and the mark is the theme's own
   Font Awesome brand glyph rather than a 48 KB PNG -- the brands webfont is
   already loaded for the social icons in the header, so the icon costs
   nothing.

   .scroll-top is 3.125rem across, sitting 1.875rem in from the right -- so
   its centre line is 3.4375rem from the edge. This button is wider, and the
   right offset is set to put its centre on that same line rather than to
   match the edge: two circles of different sizes stacked flush right read as
   a misalignment, stacked on a shared centre they read as a set.
   -------------------------------------------------------------------------- */
.rd-whatsapp {
   position: fixed;
   right: 1.4375rem;   /* 3.4375rem centre line - half of 4rem */
   bottom: 6rem;
   width: 4rem;
   height: 4rem;
   display: flex;
   align-items: center;
   justify-content: center;
   border-radius: 50%;
   background-color: #25d366;
   color: #fff;
   font-size: 2rem;
   line-height: 1;
   text-decoration: none;

   /* Above the page, below the scroll-top widget's 10000 -- neither ever
      covers the other, but the ring should not paint over it either. */
   z-index: 9990;

   box-shadow: 0 .3125rem .9375rem rgba(0, 0, 0, .18);
   transition: transform .25s ease, background-color .25s ease;
}

.rd-whatsapp:hover,
.rd-whatsapp:focus-visible {
   background-color: #1da851;
   color: #fff;
   transform: scale(1.06);
}

.rd-whatsapp:focus-visible {
   outline: 3px solid #fff;
   outline-offset: 2px;
}

/* The ring. Drawn with a pseudo-element rather than on the button itself, so
   the expanding shadow cannot be clipped by the hover transform. */
.rd-whatsapp::before {
   content: "";
   position: absolute;
   inset: 0;
   border-radius: 50%;
   box-shadow: 0 0 0 0 rgba(37, 211, 102, .7);
   animation: rd-whatsapp-pulse 1.8s cubic-bezier(.66, 0, 0, 1) infinite;
}

@keyframes rd-whatsapp-pulse {
   to { box-shadow: 0 0 0 1.375rem rgba(37, 211, 102, 0); }
}

/* Phones: a little smaller so it does not sit on top of the content, but
   still on the scroll-top centre line -- that widget keeps its size at every
   breakpoint, so the sum is the same one. */
@media (max-width: 575.98px) {
   .rd-whatsapp {
      right: 1.6875rem;   /* 3.4375rem centre line - half of 3.5rem */
      bottom: 5.75rem;
      width: 3.5rem;
      height: 3.5rem;
      font-size: 1.75rem;
   }
}

/* A button that beats away in the corner is the first thing to stop for
   someone who has asked for less movement. */
@media (prefers-reduced-motion: reduce) {
   .rd-whatsapp,
   .rd-whatsapp:hover {
      transition: none;
      transform: none;
   }

   .rd-whatsapp::before {
      animation: none;
   }
}
