/* Synergas theme layer.
 *
 * WHY THIS FILE EXISTS
 * --------------------
 * wagtail-base ships the block LIBRARY (cv + brilliance) but NOT the stylesheet
 * that gives those blocks an appearance. The `b-*` component classes their
 * templates emit - b-cta, b-ctaband, b-card, b-pill and ~120 more - are defined
 * in no CSS file anywhere in the base: mysite.css is purely the built Tailwind
 * output, and input.css is only its source. The Brilliance RGB site keeps its
 * own 881-line brilliance.css for them, in that project rather than in the base.
 *
 * So a downstream site owns its own component layer, and this is Synergas'. The
 * job is to DEFINE those components in the current Litho site's visual language,
 * not to override anything - dropped into Base 8 as it stands, a mapped block
 * renders with correct, semantic, entirely unstyled markup.
 *
 * That is the good news: there is no competing design to unpick, and no
 * specificity fight. The markup hooks are already in place and correct.
 *
 * RULES
 * -----
 * 1. Override base's `b-*` component classes. Do NOT restyle Tailwind utility
 *    classes: base's templates use them directly and a change here would leak
 *    across unrelated blocks.
 * 2. Colours and fonts do NOT belong here. Those come from BrandingSettings and
 *    DefaultFontSettings via the CSS custom properties base already defines
 *    (--theme-color, --alt-theme-color, --text-color, --main-font-family, ...).
 *    Reach for the variable, never a literal hex, or the editor loses control
 *    of the palette.
 * 3. One section per base block, in the same order as
 *    docs/wagtail8-migration/block-mapping.md, with a comment naming the legacy
 *    block whose appearance is being reproduced. That comment is how the next
 *    person knows what "correct" looks like.
 * 4. Litho's own class names (padding-100px-tb, col-xl-7, bg-dark-slate-blue,
 *    interactive-banners-style-08, ...) must NOT appear here. They belong to
 *    Bootstrap + the Litho stylesheet, neither of which Base 8 loads. Reproduce
 *    the LOOK, not the class names.
 *
 * Sections are added as each block is restyled; an empty file is the correct
 * starting state.
 */

/* Comfortaa, for buttons. Litho pulls this in with an @import inside style.css
   rather than a <link> in its base.html, and .btn hardcodes
   `font-family: 'Comfortaa', var(--alt-font-family)`. Buttons across the whole
   site therefore render in Comfortaa, not in any of the three brand families -
   intentional, and easy to miss. @import must precede every rule below. */
@import url('https://fonts.googleapis.com/css2?family=Comfortaa:wght@300;400;500;600;700&display=swap');

/* ---------------------------------------------------------------------------
 * ROOT FONT SIZE - read this before touching any rem value
 *
 * Litho sets `html { font-size: 14px }` and steps it DOWN at breakpoints. That
 * single declaration is the mechanism the whole type scale rides on: every
 * heading, button and spacing value in the theme is expressed in rem, so the
 * entire site shrinks proportionally on smaller screens without a single
 * per-element media query.
 *
 * Base 8 (and Tailwind) assume the browser default of 16px. Without this
 * ladder, every rem value taken from Litho renders ~14% too large on desktop
 * and does not shrink on mobile at all - so headings that measure 56px on the
 * live site come out at 64px here, and the mobile scale is wrong at every
 * breakpoint.
 *
 * Values verified against the live site: 14px desktop, 11px at 390px wide.
 *
 * TRADE-OFF: this also rescales Base 8's own Tailwind utilities, which are
 * rem-based. That is intended - the whole site is being matched to Litho - but
 * it is worth knowing when a base component looks smaller than it does in the
 * base project. Watch the fedit editing chrome in particular.
 * ------------------------------------------------------------------------ */
:root {
    font-size: 14px;
}

@media (max-width: 1199px) { :root { font-size: 13px; } }
@media (max-width: 991px)  { :root { font-size: 12px; } }
@media (max-width: 767px)  { :root { font-size: 11px; } }

/* Litho's body defaults, which the headings inherit their weight from
   (`h1..h6 { font-weight: inherit }` - so a heading is NOT bold unless a class
   makes it so, which is why .font-weight-600 appears all over the markup). */
body {
    font-size: 15px;
    line-height: 30px;
    font-weight: 400;
    font-family: var(--main-font-family), var(--alt-font-family);
    word-break: break-word;
}

/* ---------------------------------------------------------------------------
 * Heading scale (Litho style.css). In rem, so the ladder above scales them.
 * ------------------------------------------------------------------------ */
h1, h2, h3, h4, h5, h6 {
    margin: 0 0 25px;
    padding: 0;
    letter-spacing: -0.5px;
    font-weight: inherit;
}

h1 { font-size: 5rem;    line-height: 5rem; }
h2 { font-size: 3.9rem;  line-height: 4.2rem; }
h3 { font-size: 3.4rem;  line-height: 3.9rem; }
h4 { font-size: 2.85rem; line-height: 3.4rem; }
h5 { font-size: 2.3rem;  line-height: 3rem; }
h6 { font-size: 1.8rem;  line-height: 2.5rem; }

/* ---------------------------------------------------------------------------
 * Container ladder
 *
 * Litho is on BOOTSTRAP 4's container widths, not Bootstrap 5's - measured off
 * the live site: 540 / 720 / 960 / **1170**, with 15px gutters. Bootstrap 5
 * would give 540 / 720 / 960 / 1140 / 1320 at 12px, which is wider on large
 * screens and changes where every heading wraps. Verified at 1440, 1280, 1100,
 * 900, 600 and 390px.
 *
 * --sg-container is the CONTAINER box (1170). --sg-row is the width our GRIDS
 * want, and it is NOT the same number. A Bootstrap `.container` pads 15px a
 * side; the `.row` inside it takes those 30px back with `margin: 0 -15px`; and
 * each column re-adds 15px. So the leftmost column's CONTENT starts at the
 * container's content edge and the rightmost ends at the other one - the span
 * covered by column content is 1140, with a 30px gutter between neighbours.
 * A CSS grid has no negative margins, so `max-width: var(--sg-row)` +
 * `gap: var(--sg-gutter-col)` is what reproduces that geometry: two tracks of
 * a 1140 grid are 555 each, starting at x=150, exactly like `col-lg-6`.
 *
 * --sg-row-nested is one container level deeper (1110, tracks at x=165).
 * Legacy nests a `.container` from the block's own template inside the
 * `.container` its section wrapper opened, and each extra level costs 30px.
 * Which one a block wants is legacy STRUCTURE, invisible to CSS - the
 * transform decides it per block; see CONTAINER_LEVELS there.
 * Exposed as a variable so each component states `max-width: var(--sg-container)`
 * once, instead of every one repeating a five-step media-query ladder.
 * ------------------------------------------------------------------------ */
:root {
    --sg-container: 100%;
    --sg-gutter: 15px;
    /* The gap BETWEEN two column content boxes: 15px of padding from each. */
    --sg-gutter-col: 30px;
    --sg-row: calc(100% - 30px);
    --sg-row-nested: calc(100% - 60px);
}

@media (min-width: 576px)  { :root { --sg-container: 540px;  --sg-row: 510px;  --sg-row-nested: 480px;  } }
@media (min-width: 768px)  { :root { --sg-container: 720px;  --sg-row: 690px;  --sg-row-nested: 660px;  } }
@media (min-width: 992px)  { :root { --sg-container: 960px;  --sg-row: 930px;  --sg-row-nested: 900px;  } }
@media (min-width: 1200px) { :root { --sg-container: 1170px; --sg-row: 1140px; --sg-row-nested: 1110px; } }

/* ---------------------------------------------------------------------------
 * The `.row` negative margin
 *
 * Two ways to space columns, and Litho uses both:
 *
 *   grid-gap model      element spans the content box (--sg-row), `gap: 30px`
 *   gutter-in-cell      element spans the CONTAINER box (1170), every cell
 *                       carries `padding: 0 15px`, no gap at all
 *
 * They land in the same place - a cell's content is 262.5px at x=150 either
 * way - but they need DIFFERENT widths for the element itself, 30px apart.
 * Litho's `.grid` lists and `.row-cols-*` rows are the second kind.
 *
 * Our containers reproduce the content box, so a gutter-in-cell component has
 * to reclaim those 30px, which is exactly what Bootstrap's `.row` does with
 * `margin: 0 -15px`. `width: auto` is required with it: a `width: 100%`
 * element ignores its own negative margins on the right and overflows.
 *
 * The advantage over hardcoding 1170 is that it holds at ANY nesting depth -
 * the component never has to know whether its container came out 1140 or 1110.
 * ------------------------------------------------------------------------ */

/* ---------------------------------------------------------------------------
 * Tokens
 *
 * Litho's spacing scale, expressed once so the per-block sections below stay
 * readable. These mirror the legacy utility classes the old templates leaned
 * on (padding-100px-tb and friends) at the three breakpoints Litho used.
 * ------------------------------------------------------------------------ */
:root {
    --sg-section-pad: 100px;
    --sg-section-pad-md: 75px;
    --sg-section-pad-sm: 50px;
}

@media (max-width: 1199px) {
    :root { --sg-section-pad: var(--sg-section-pad-md); }
}

@media (max-width: 767px) {
    :root { --sg-section-pad: var(--sg-section-pad-sm); }
}

/* ---------------------------------------------------------------------------
 * Fullscreen slider  (legacy: FullscreenSliderBlock)
 *
 * Reproduces Litho's full-viewport hero carousel: edge-to-edge image, dark
 * scrim, centred white heading over a lead line and a CTA, chevrons at the
 * sides and bar indicators along the bottom.
 *
 * A fade carousel keeps all slides stacked, so the track is the positioning
 * context and slides are absolutely positioned over each other. Height comes
 * from 100dvh rather than 100vh: on mobile browsers vh includes the retracting
 * toolbar, which is what makes a "full screen" hero overflow by ~60px and
 * bounce as the bar hides.
 * ------------------------------------------------------------------------ */
.sg-fslider {
    position: relative;
    width: 100%;
    height: 100dvh;
    overflow: hidden;
    margin: 0;
}

.sg-fslider__track {
    position: relative;
    height: 100%;
    margin: 0;
    padding: 0;
    list-style: none;
}

.sg-fslider__slide {
    position: absolute;
    inset: 0;
    opacity: 0;
    /* Kept out of the a11y tree and off the pointer while faded out; opacity
       alone would leave invisible links clickable. */
    visibility: hidden;
    transition: opacity 0.6s ease-in-out, visibility 0.6s;
}

.sg-fslider__slide.is-active {
    opacity: 1;
    visibility: visible;
}

/* Without JS, .sg-fslider--static is still present: show the first slide only,
   so the hero is never a blank viewport-tall hole. */
.sg-fslider--static .sg-fslider__slide {
    opacity: 0;
    visibility: hidden;
}

.sg-fslider--static .sg-fslider__slide:first-child {
    opacity: 1;
    visibility: visible;
}

@media (prefers-reduced-motion: reduce) {
    .sg-fslider__slide { transition: none; }
}

.sg-fslider__img {
    width: 100%;
    height: 100%;
    object-fit: cover;
    display: block;
}

/* No scrim. The legacy FullscreenSliderBlock put its caption straight over the
   image with no overlay div at all - it relied on Bootstrap's .carousel-caption
   and nothing else. An overlay here would be a visible change, so the element
   is kept in the markup (harmless, and the hook exists if the client ever asks
   for one) but paints nothing. */
.sg-fslider__scrim {
    display: none;
}

/* Bootstrap's .carousel-caption is inset 15% left and right, which is what
   constrained the legacy caption's line length. The legacy template added
   d-flex flex-column h-100 justify-content-center, so it centres vertically
   over the full height rather than sitting at the bottom. */
.sg-fslider__caption {
    position: absolute;
    top: 0;
    bottom: 0;
    right: 15%;
    left: 15%;
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    padding: 1.25rem 0;
    text-align: center;
    color: #fff;
}

/* Bootstrap .display-3 .fw-bold .mb-4, verbatim: the fluid size, the 1.2 line
   height and the 4rem desktop cap are Bootstrap's, and .fw-bold overrides
   .display-3's own font-weight: 300. Do not "improve" these numbers - matching
   them is the point. */
.sg-fslider__heading {
    margin: 0 0 1.5rem;
    font-size: calc(1.525rem + 3.3vw);
    font-weight: 700;
    line-height: 1.2;
}

@media (min-width: 1200px) {
    .sg-fslider__heading { font-size: 4rem; }
}

/* Bootstrap .lead .mb-4. No opacity: the legacy markup carried a
   `litho-opacity-09` class that is defined in no stylesheet in the project, so
   it never did anything - reproducing it would make the text lighter than the
   live site. */
.sg-fslider__text {
    margin: 0 0 1.5rem;
    font-size: 1.25rem;
    font-weight: 300;
}

.sg-fslider__cta {
    display: flex;
    justify-content: center;
    flex-wrap: wrap;
    gap: 0.75rem;
}

/* Chevron arrows ------------------------------------------------------- */
.sg-fslider__nav {
    position: absolute;
    top: 50%;
    transform: translateY(-50%);
    z-index: 2;
    display: grid;
    place-items: center;
    width: 3rem;
    height: 3rem;
    border: 0;
    border-radius: 999px;
    background: rgb(255 255 255 / 15%);
    cursor: pointer;
    transition: background 0.2s ease;
}

.sg-fslider__nav:hover,
.sg-fslider__nav:focus-visible {
    background: rgb(255 255 255 / 30%);
}

.sg-fslider__nav--prev { left: clamp(0.5rem, 2vw, 2rem); }
.sg-fslider__nav--next { right: clamp(0.5rem, 2vw, 2rem); }

/* Drawn with a rotated border rather than an icon font, so the arrows do not
   depend on FontAwesome having loaded. */
.sg-fslider__chev {
    width: 0.75rem;
    height: 0.75rem;
    border-right: 2px solid #fff;
    border-bottom: 2px solid #fff;
}

.sg-fslider__nav--prev .sg-fslider__chev {
    transform: rotate(135deg);
    margin-left: -0.2rem;
}

.sg-fslider__nav--next .sg-fslider__chev {
    transform: rotate(-45deg);
    margin-right: -0.2rem;
}

/* Bar indicators ------------------------------------------------------- */
.sg-fslider__dots {
    position: absolute;
    left: 0;
    right: 0;
    bottom: clamp(1rem, 3vh, 2.5rem);
    z-index: 2;
    display: flex;
    justify-content: center;
    gap: 0.5rem;
}

.sg-fslider__dot {
    position: relative;
    width: 2rem;
    height: 3px;
    padding: 0;
    border: 0;
    background: rgb(255 255 255 / 45%);
    cursor: pointer;
    transition: background 0.2s ease;
}

.sg-fslider__dot.is-active {
    background: var(--theme-color, #fff);
}

/* Hit area: the visible bar is 3px tall, far below the ~24px minimum for a
   touch target. This pseudo-element stretches beyond the bar and, being part of
   the button, takes the pointer events with it - so the target grows without
   the indicator looking any different. Needs the dot to be positioned, which
   it is above. */
.sg-fslider__dot::before {
    content: "";
    position: absolute;
    inset: -0.75rem -0.25rem;
}

/* ---------------------------------------------------------------------------
 * Text slider style 04  (synergas/blocks/text_slider)
 *
 * A fixed-height info panel beside a fixed-height, single-slide-at-a-time
 * carousel, full bleed. The carousel reuses .sg-fslider's markup and JS
 * (initSlider() in synergas.js only cares about [data-sg-slider] /
 * .sg-fslider__slide / .sg-fslider__caption, not which block owns them) — the
 * `.sg-tslider__slider` scope below only overrides its CSS: a fixed panel
 * height instead of 100dvh, and a bottom-anchored white caption box instead of
 * a centred full-height one.
 *
 * Breakpoints and numbers are read off the legacy Litho utility classes
 * (col-xl-6/col-lg-5/col-12, h-650px/lg-h-550px/md-h-350px/xs-h-300px,
 * w-55/lg-w-75/md-w-60/sm-w-75/xs-w-80 — see
 * Litho Theme/html/css/{style,responsive}.css), whose OWN breakpoint scale
 * (1199/991/767/575, read off responsive.css's own @media blocks — an
 * unprefixed class's value is simply whatever applies above the highest
 * prefix defined for it, i.e. above 1199 unless a class also has its own
 * "xl-" variant) is not Bootstrap's column one (992/1200): the two
 * columns stack below 992px (Bootstrap lg), go 5/7 from 992-1199px, then
 * 6/6 (50/50) at 1200px+ — independently of when the fixed heights and the
 * caption box's width/padding step down at 1199/991/767/575.
 *
 * `.sg-tslider__info`'s padding-left is the one exception: unlike the height
 * and caption classes, `padding-10-rem-left` DOES have its own "xl-" variant
 * (`xl-padding-15px-left`, ≤1600px) — confirmed by reading responsive.css's
 * actual @media blocks rather than assuming the pattern — so its 10rem base
 * only ever shows above 1600px, while every other number here switches to
 * its "unprefixed" value already at 1200px.
 * ------------------------------------------------------------------------ */
.sg-tslider__row {
    display: flex;
    flex-wrap: wrap;
}

.sg-tslider__info,
.sg-tslider__panel {
    flex: 0 0 100%;
    max-width: 100%;
}

/* 50/50 as soon as the columns go side by side, not the legacy's transient
   5/7 (col-lg-5/col-lg-7) between 992-1199px before col-xl-6 takes over at
   1200px — simpler, and what an even "two cards share the screen" split
   calls for at every side-by-side width. */
@media (min-width: 992px) {
    .sg-tslider__info,
    .sg-tslider__panel { flex: 0 0 50%; max-width: 50%; }
}

.sg-tslider__info {
    display: flex;
    flex-direction: column;
    justify-content: center;
    height: 300px;
    padding-left: 15px;
    background-position: center;
    background-size: cover;
    color: #fff;
    text-align: center;
}

@media (min-width: 576px) {
    .sg-tslider__info { text-align: left; }
}

/* The legacy padding only ever reaches its full 10rem above a 1600px
   viewport — practically every real desktop width (this project's own probes
   run at 1440px) falls under the "xl-padding-15px-left" override above, so
   15px is the default and 10rem is the outlier, not the other way round. */
@media (min-width: 1600px) {
    .sg-tslider__info { padding-left: 10rem; }
}

@media (min-width: 576px)  { .sg-tslider__info { height: 350px; } }
@media (min-width: 992px)  { .sg-tslider__info { height: 550px; } }
@media (min-width: 1200px) { .sg-tslider__info { height: 650px; } }

/* Legacy is `.row.justify-content-center > .col-xl-5.col-sm-9`: the CENTERING
   is the row's, not the column's, and it holds at every width - only the
   TEXT alignment inside changes (text-center below 576px, text-sm-start at
   576px+). An earlier version cleared this margin at 576px+, which reads as
   "text-sm-start" also moving the whole block flush left - it doesn't; the
   block stays centered in the panel and only its own text flips to left. */
.sg-tslider__info-inner {
    max-width: 26rem;
    margin: 0 auto;
}

/* Litho's `.icon-large` is 50px flat - matched exactly, not scaled down for
   this panel. */
.sg-tslider__icon {
    display: block;
    margin-bottom: 1.5rem;
    font-size: 3.125rem;
}

/* Legacy has `.alt-font` on BOTH the eyebrow span and the h4 - same typeface
   for both. Without an explicit rule here the span inherits body's
   var(--main-font-family) and the h4 picks up whatever the global heading
   cascade sets, so the two rendered in two different fonts. */
.sg-tslider__label {
    display: block;
    margin-bottom: 1.25rem;
    font-family: var(--alt-font-family);
    font-size: 1rem;
    font-weight: 600;
    letter-spacing: 1px;
    text-transform: uppercase;
}

/* Litho's `<h4>` here is a bare, un-utility-scaled heading - 2.85rem/3.4rem
   line-height (see the global `h4` rule this panel used to fall through to
   before it got its own font-family). It was previously shrunk to 1.75rem,
   which read small against a 300-650px-tall panel; matched back to the
   legacy size. */
.sg-tslider__heading {
    margin: 0;
    font-family: var(--alt-font-family);
    font-size: 2.85rem;
    line-height: 1.2;
    font-weight: 500;
}

.sg-tslider__panel {
    position: relative;
}

/* No 991px step of its own here - the legacy right panel only defines lg
   (≤1199) and sm (≤767), so it stays at 550px from 1199px down to 768px. */
.sg-tslider__slider {
    height: 450px;
    /* Overrides the base .sg-fslider's `overflow: hidden` (there to crop a
       100dvh hero to the viewport) - the nav buttons sit at a negative
       left offset, hanging over the info panel, and would otherwise be
       clipped at this element's own left edge. */
    overflow: visible;
}

@media (min-width: 768px)  { .sg-tslider__slider { height: 550px; } }
@media (min-width: 1200px) { .sg-tslider__slider { height: 650px; } }

/* Overrides .sg-fslider__caption: bottom-anchored white box over ~55-80% of
   the slide's width, not the base component's full-height centred white
   text - this is the caption-over-image "info card" reading production has,
   not a hero caption. */
.sg-tslider__slider .sg-fslider__caption {
    top: auto;
    left: 0;
    right: auto;
    bottom: 0;
    width: 80%;
    align-items: flex-start;
    justify-content: flex-end;
    /* legacy only defines a padding override at "lg" (≤1199) and the base
       (≥1200) - there is no finer-grained padding step to match the width
       one below, so this stays constant until 1200px. */
    padding: 3.5rem 4rem;
    background: #fff;
    color: #1a1a1a;
    text-align: left;
}

@media (min-width: 576px)  { .sg-tslider__slider .sg-fslider__caption { width: 75%; } }
@media (min-width: 768px)  { .sg-tslider__slider .sg-fslider__caption { width: 60%; } }
@media (min-width: 992px)  { .sg-tslider__slider .sg-fslider__caption { width: 75%; } }
@media (min-width: 1200px) { .sg-tslider__slider .sg-fslider__caption { width: 55%; padding: 4.5rem 6rem; } }

.sg-tslider__text {
    margin: 0;
    font-size: 0.95rem;
    font-weight: 400;
}

/* Legacy's `.slider-arrow-rb`: `position:absolute; bottom:0; right:100%` on
   the wrapper - "right:100%" puts the wrapper's RIGHT edge at the panel's
   LEFT edge (the seam with the info panel), so a 62px-wide wrapper sits 62px
   to the left of it, hanging over the info panel's bottom-right corner, not
   over the slide at all. Inside it, `.swiper-button-next/prev` get
   `transform:none; position:static; margin-top:1px`, which strips their own
   absolute centering and stacks them - prev above next, 1px apart - instead
   of the base component's vertically-centred, screen-edge chevrons.
   `.slider-navigation-style-01` (no `.light`/`.dark`/`.rounded-circle`
   modifier in the legacy markup) is a plain 62px SQUARE, `rgba(0,0,0,.5)`
   background, white icon - not the base circle. */
.sg-tslider__slider .sg-fslider__nav {
    top: auto;
    left: -62px;
    right: auto;
    transform: none;
    width: 62px;
    height: 62px;
    border-radius: 0;
    background: rgb(0 0 0 / 50%);
}

.sg-tslider__slider .sg-fslider__nav:hover,
.sg-tslider__slider .sg-fslider__nav:focus-visible {
    background: #fff;
}

.sg-tslider__slider .sg-fslider__nav:hover .sg-fslider__chev,
.sg-tslider__slider .sg-fslider__nav:focus-visible .sg-fslider__chev {
    border-right-color: #232323;
    border-bottom-color: #232323;
}

.sg-tslider__slider .sg-fslider__nav--prev { bottom: 63px; }
.sg-tslider__slider .sg-fslider__nav--next { bottom: 0; }

.sg-visually-hidden {
    position: absolute;
    width: 1px;
    height: 1px;
    margin: -1px;
    padding: 0;
    overflow: hidden;
    clip-path: inset(50%);
    white-space: nowrap;
    border: 0;
}

/* ---------------------------------------------------------------------------
 * Card slider  (synergas/blocks/card_slider, legacy: Content_Slider_Block)
 *
 * A one-slide-at-a-time carousel of numbered, captioned photo cards. Reuses
 * .sg-fslider's markup and JS (initSlider() only cares about [data-sg-slider]
 * / .sg-fslider__slide / the prev/next data attributes, not which block owns
 * them) but NOT its cross-fade CSS: that positions every slide absolutely
 * over a fixed-height parent so two can overlap mid-transition, which only
 * works when every slide is the same height. This card's height comes from
 * its own content (an image plus a variable-length caption), so slides here
 * swap with a plain display toggle instead - only ever one in flow, sized to
 * its own content, no fixed height to pick.
 * ------------------------------------------------------------------------ */
.sg-cslider {
    position: relative;
    /* No cap: production's own swiper is col-lg-8 of an UNCONSTRAINED,
       edge-to-edge half-column (~720-750px at a 1440px viewport, ~480px
       slide) - narrowing this further on top of that would double the
       shrink, since this column already sits inside a width-capped,
       gutter-separated container (var(--sg-container), 4rem gutter) that
       production's own version never had. Filling the column it already has
       gets closer to production's absolute pixel size than any fraction of
       it would. */
    margin: 0 auto;
}

.sg-cslider__track {
    margin: 0;
    padding: 0;
    list-style: none;
}

.sg-cslider.sg-fslider {
    height: auto;
    overflow: visible;
}

.sg-cslider .sg-fslider__track {
    position: static;
    height: auto;
}

.sg-cslider .sg-fslider__slide {
    position: static;
    display: none;
    opacity: 1;
    visibility: visible;
    transition: none;
}

.sg-cslider .sg-fslider__slide.is-active,
.sg-cslider.sg-fslider--static .sg-fslider__slide:first-child {
    display: block;
}

.sg-cslider__card {
    position: relative;
    background: #fff;
    box-shadow: 0 0 30px rgb(0 0 0 / 8%);
}

.sg-cslider__img {
    display: block;
    width: 100%;
    height: auto;
    object-fit: cover;
}

/* Litho's `padding-4-half-rem-lr padding-3-half-rem-tb`, stepped down a
   breakpoint early: at this card's own (already-halved-then-66%) width, 4.5rem
   a side reads closer to production's own tablet/`lg-padding-2-half-rem-lr`
   step than to its unconstrained desktop value. */
.sg-cslider__caption {
    display: flex;
    align-items: flex-start;
    gap: 1.25rem;
    padding: 2.25rem 2.5rem;   /* Litho's own lg-padding-2-half-rem-lr step */
}

/* Litho's feature-box number is a bare `<h4>` (2.85rem) inside the card;
   sized down here for the same reason as the caption padding above - this
   card is already a fraction of the column, not the full swiper width Litho
   measured that size against. */
.sg-cslider__number {
    flex: 0 0 auto;
    font-family: var(--alt-font-family);
    font-size: 1.875rem;
    font-weight: 600;
    line-height: 1.2;
    color: var(--theme-color, #00aeff);
}

.sg-cslider__body {
    padding-left: 1.25rem;
    border-left: 2px solid rgb(0 0 0 / 10%);
}

/* Litho: `.text-extra-dark-gray.alt-font.font-weight-500.text-extra-medium`
   (16px/28px). */
.sg-cslider__title {
    display: block;
    margin-bottom: 0.35rem;
    font-family: var(--alt-font-family);
    font-size: 1rem;
    line-height: 1.75;
    font-weight: 500;
    color: var(--text-color, #232323);
}

.sg-cslider__text {
    margin: 0;
    font-size: 0.9rem;
    color: #6f6f6f;
}

/* Litho's `.slider-navigation-style-08.dark`: a 50x50 dark circle inset
   15px from the edge, vertically centred over the WHOLE slide (card + its
   caption below the image, not just the photo) - `top: 50%` here is relative
   to `.sg-cslider`, which wraps both. Overrides the base component's larger,
   translucent-white, photo-only nav (sized for a full-viewport hero, not a
   card sitting on its own white background). */
.sg-cslider__nav {
    width: 50px;
    height: 50px;
    background: rgb(35 35 35 / 85%);
    box-shadow: none;
}

.sg-cslider .sg-fslider__nav--prev { left: 15px; }
.sg-cslider .sg-fslider__nav--next { right: 15px; }

.sg-cslider__nav:hover,
.sg-cslider__nav:focus-visible {
    background: #232323;
}

.sg-cslider__chev {
    width: 0.6rem;
    height: 0.6rem;
    border-right-color: #fff;
    border-bottom-color: #fff;
}

/* ---------------------------------------------------------------------------
 * Video hero  (legacy: HeroBannerBackroundVideoBlock)
 *
 * Litho's full-screen video banner: video covering the viewport, dark tint,
 * centred title over a subtitle and CTA.
 *
 * Shares the slider's 100dvh reasoning - vh includes mobile browsers'
 * retracting toolbar, so a 100vh hero overflows and jumps as the bar hides.
 * ------------------------------------------------------------------------ */
.sg-vhero {
    position: relative;
    width: 100%;
    min-height: 100dvh;
    display: grid;
    place-items: center;
    overflow: hidden;
    color: #fff;
}

.sg-vhero__media {
    position: absolute;
    inset: 0;
    z-index: 0;
}

/* The <video> element's class is applied by wagtailvideos' {% video %} tag, so
   these three selectors all describe "the thing covering the viewport". */
.sg-vhero__video,
.sg-vhero__poster,
.sg-vhero__iframe {
    position: absolute;
    inset: 0;
    width: 100%;
    height: 100%;
    object-fit: cover;
    border: 0;
    display: block;
}

.sg-vhero__iframe {
    /* A 16:9 iframe cannot object-fit, so scale it up until it covers and let
       the overflow crop. 177.78vh = 16/9 of the viewport height. */
    width: max(100%, 177.78vh);
    height: max(100%, 56.25vw);
    left: 50%;
    top: 50%;
    transform: translate(-50%, -50%);
    inset: auto;
    /* Never let the iframe take a click intended for the CTA above it. */
    pointer-events: none;
}

.sg-vhero__scrim {
    position: absolute;
    inset: 0;
    background: rgb(0 0 0 / 45%);
}

/* The legacy hero put its copy in a Bootstrap .container, so the line length is
   Bootstrap's breakpoint ladder rather than a chosen max-width. Reproduced here
   because it decides where the heading wraps at every width. */
.sg-vhero__inner {
    position: relative;
    z-index: 1;
    width: 100%;
    max-width: var(--sg-container);
    margin-inline: auto;
    padding-inline: var(--sg-gutter);
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    text-align: center;
}

/* Bootstrap .display-3 .fw-bold .mb-3. .fw-bold overrides .display-3's own
   font-weight: 300, and 1.2 is .display-3's line-height. */
.sg-vhero__title {
    margin: 0 0 1rem;
    font-size: calc(1.525rem + 3.3vw);
    font-weight: 700;
    line-height: 1.2;
}

@media (min-width: 1200px) {
    .sg-vhero__title { font-size: 4rem; }
}

/* Bootstrap .lead .mb-4. */
.sg-vhero__subtitle {
    margin: 0 0 1.5rem;
    font-size: 1.25rem;
    font-weight: 300;
}

.sg-vhero__cta {
    display: flex;
    flex-wrap: wrap;
    justify-content: center;
    gap: 0.75rem;
}

/* ---------------------------------------------------------------------------
 * Buttons — .b-cta  (legacy: .btn and its size/colour variants)
 *
 * Every block's call to action funnels through brilliance's CTABlock, so this
 * is the single highest-leverage component in the layer.
 *
 * Values are Litho's .btn verbatim, including two easy-to-miss decisions:
 * square corners (border-radius: 0), and a 2px TRANSPARENT border on the base
 * class - the border is always there, so a variant only has to recolour it and
 * nothing shifts by 4px when it does.
 * ------------------------------------------------------------------------ */
.b-cta {
    display: inline-block;
    width: auto;
    border: 2px solid transparent;
    border-radius: 0;
    text-transform: uppercase;
    font-family: "Comfortaa", var(--alt-font-family);
    font-weight: 500;
    /* Bootstrap's .btn line-height. Litho's own .btn rule does not set one, so
       without this the button inherits the body's 30px and stands 58px tall
       instead of 48 - which then sets the height of every band it sits in.
       Confirmed against the live site's .btn-large computing to 19.5px. */
    line-height: 1.5;
    text-decoration: none;
    transition: background-color 0.3s ease, color 0.3s ease, border-color 0.3s ease;
}

/* Litho's size ladder. brilliance's sm/md/lg map onto
   .btn-small / .btn-medium / .btn-large.
   `height: auto` on each is load-bearing: brilliance sizes its buttons by a
   FIXED height (`.b-cta` 60px, `.b-cta--lg` 70px) rather than by padding.
   Restating the padding without clearing the height left a 70px-tall button
   whose label sat centred in dead space - the CTA band's button was 62px
   against production's 46px. Litho's .btn has no height at all; padding and
   line-height set it. */
.b-cta { height: auto; }
.b-cta--sm { font-size: 11px; padding: 8px 22px; height: auto; }
.b-cta--md { font-size: 12px; padding: 10px 26px; height: auto; }
.b-cta--lg { font-size: 13px; padding: 12px 32px; height: auto; }

/* Litho .btn-white: solid white, near-black label, inverting to transparent on
   hover. The hover colour is #fff on purpose - these sit on dark imagery. */
.b-cta--light {
    background: #ffffff;
    border-color: #ffffff;
    color: #232323;
}

.b-cta--light:hover,
.b-cta--light:focus-visible {
    background: transparent;
    color: #ffffff;
}

/* Litho .btn-dark-gray. */
.b-cta--dark {
    background: #232323;
    border-color: #232323;
    color: #ffffff;
}

.b-cta--dark:hover,
.b-cta--dark:focus-visible {
    background: transparent;
    color: #232323;
}

/* Litho .btn-transparent-white. */
.b-cta--outline {
    background: transparent;
    border-color: #ffffff;
    color: #ffffff;
}

.b-cta--outline:hover,
.b-cta--outline:focus-visible {
    background: #ffffff;
    border-color: #ffffff;
    color: #232323;
}

/* Litho .btn-link.text-theme: no button chrome, theme-coloured label. */
.b-cta--link {
    padding: 0;
    border-color: transparent;
    background: transparent;
    color: var(--text-theme-color, var(--theme-color));
}

.b-cta--link:hover,
.b-cta--link:focus-visible {
    text-decoration: underline;
}

/* The site's PRIMARY button is Litho's `.btn.bg-theme` - theme background,
   white label - which none of brilliance's four variant names describes. Until
   a `theme` choice is added to CTABlock upstream, content that used bg-theme
   should carry this class alongside .b-cta. See
   docs/wagtail8-migration/block-mapping.md. */
.b-cta--theme {
    background-color: var(--theme-color);
    border-color: var(--theme-color);
    /* #212529 - Bootstrap's default .btn colour, NOT white. The legacy markup
       is `btn btn-large bg-theme` with no text-colour class, so the label keeps
       Bootstrap's body colour over the blue. Measured on the live CTA band as
       rgb(33, 37, 41). Looks like an oversight; reproducing it is the brief. */
    color: #212529;
}

.b-cta--theme:hover,
.b-cta--theme:focus-visible {
    background: transparent;
    color: var(--theme-color);
}

/* ---------------------------------------------------------------------------
 * CTA band — .b-ctaband  (legacy: Background_CTA_Block, 12 pages)
 *
 * Litho markup this reproduces:
 *   section.fix-background.padding-50px-tb.md-padding-40px-tb.sm-padding-30px-tb
 *     div.opacity-medium.bg-dark-slate-blue      <- the tint
 *     div.container > div.row.align-items-center.justify-content-center
 *       div.col-12.col-xl-7.col-md-8   h4.alt-font.font-weight-600.text-white
 *       div.col-12.col-xl-5.col-md-4   a.btn.btn-large.bg-theme
 *
 * Heading left, button right, collapsing to centred and stacked below 768px.
 * The tint is #1f232c at 0.75 - NOT black; guessing that wrong is very visible
 * on a 12-page component.
 * ------------------------------------------------------------------------ */
.b-ctaband {
    position: relative;
    overflow: hidden;
    /* brilliance rounds the band with `border-radius: var(--r-md)`. Litho's is
       a full-bleed square band, and the rounded corners were one of the
       "everything looks rounded" symptoms. */
    border-radius: 0;
    /* `.b-ctaband--dark` also draws a 1px inset white hairline frame
       (`inset 0 1px 0 rgba(255,255,255,.06), inset 0 0 0 1px rgba(255,255,255,
       .05)`), which reads as a faint outline around a full-bleed band.
       Production has no shadow and no border on this section. Found by
       probing box-shadow, which the earlier property checks never looked at. */
    box-shadow: none;
    border: none;
    /* base's `dark-section` (added by tone: dark) paints an OPAQUE slate-900
       background on this element, which sits above the styled wrapper's
       background image and hides it completely - the band renders as a flat
       dark slab. On the Synergas band the darkening is the ::before tint, so
       the band itself has to stay transparent. Any tone: dark block carrying a
       background image has the same problem. */
    background-color: transparent;
    padding-top: 50px;
    padding-bottom: 50px;
    background-position: center center;
    background-repeat: no-repeat;
    background-size: cover;
}

/* `.b-ctaband--dark` (brilliance.css) sets its own `background:` SHORTHAND -
   three RGB brand-glow radial gradients (cyan/green/red) plus a linear-gradient
   fallback - not just a colour. `background-color: transparent` above does not
   touch `background-image`, and `.b-ctaband--dark` (two classes) is EQUAL
   specificity to `.b-ctaband` (one class) plus the two-class selectors below,
   so without an explicit override here brilliance's glow painted over the
   band's real photo, reading as a colour-washed card instead of the plain dark
   tint Litho uses. synergas.css loads after brilliance.css, so this wins on
   load order at equal specificity. `.b-ctaband--light` gets the same shorthand
   reset for the same reason, even though no content currently uses it. */
.b-ctaband--dark,
.b-ctaband--light {
    background: transparent;
    box-shadow: none;
    border: none;
}

@media (max-width: 991px) {
    .b-ctaband { padding-top: 40px; padding-bottom: 40px; }
}

@media (max-width: 767px) {
    .b-ctaband { padding-top: 30px; padding-bottom: 30px; }
}

/* Litho's .opacity-medium over .bg-dark-slate-blue, as a pseudo-element so the
   block needs no extra markup. */
.b-ctaband::before {
    content: "";
    position: absolute;
    inset: 0;
    background-color: #1f232c;
    opacity: 0.75;
    pointer-events: none;
}

/* Bootstrap .container + .row.align-items-center.justify-content-center. */
.b-ctaband__inner {
    position: relative;
    z-index: 1;
    width: 100%;
    /* brilliance gives this `padding: clamp(44px, 6vw, 80px) clamp(24px, 4vw,
       60px)`. Not declaring padding here left that in force, so the band stood
       350px tall against production's 143px: the 50px band padding was being
       added to 80px of inner padding top and bottom. Litho's band has padding
       on the SECTION only, so this element must explicitly have none. */
    padding: 0;
    max-width: var(--sg-container);
    margin-inline: auto;
    /* No padding here, and the gutter moves onto the columns instead.
       Bootstrap's .container pads 15px, then .row cancels it with a -15px
       margin and each .col re-adds it - so a column's width is a percentage of
       the FULL container width, not of its content box. Padding the flex
       parent instead makes every column ~15px narrow: measured 665px against
       the live site's 683px at 1440. */
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    justify-content: center;
    row-gap: 30px;
}

.b-ctaband__heading,
.b-ctaband__ctas {
    padding-inline: var(--sg-gutter);
}

/* h4.alt-font.font-weight-600.text-white.mb-0 — Outfit at 600, white, and the
   heading's default 25px bottom margin removed by .mb-0. Size comes from the
   h4 rule above, so it rides the root-font ladder. */
.b-ctaband__heading {
    flex: 1 1 100%;
    margin: 0;
    /* brilliance caps this at `max-width: 760px`, which wrapped "Ready to
       Elevate Your Gas Supply?" onto two lines. Production keeps it on one:
       the column is 58.33% of a 1140px container, so the width comes from the
       flex basis below and nothing else may constrain it. */
    max-width: none;
    font-family: var(--alt-font-family);
    font-weight: 600;
    color: #ffffff;
    text-align: center;
}

/* The heading block renders its own h-tag inside this wrapper. Only the
   margin and colour are reset - NOT `font`, which would override the h4 scale
   with the wrapper's inherited 15px body size. Family and weight inherit from
   the wrapper, and the size comes from the global heading scale, which is what
   keeps it on the root-font ladder. */
.b-ctaband__heading :is(h1, h2, h3, h4, h5, h6, p) {
    margin: 0;
    color: inherit;
}

.b-ctaband__ctas {
    flex: 1 1 100%;
    display: flex;
    flex-wrap: wrap;
    gap: 10px;
    justify-content: center;
}

/* col-sm-10 on the heading between 576 and 767px - it does NOT go full width
   until below 576. Measured live: 450px of a 540px container. */
@media (min-width: 576px) and (max-width: 767px) {
    .b-ctaband__heading { flex: 0 0 83.333333%; }
}

/* col-md-8 / col-md-4 split: heading left, button right from 768px up. */
@media (min-width: 768px) {
    .b-ctaband__heading { flex: 0 0 66.666667%; text-align: left; }
    .b-ctaband__ctas    { flex: 0 0 33.333333%; justify-content: flex-end; }
}

/* col-xl-7 / col-xl-5 at 1200px and above. */
@media (min-width: 1200px) {
    .b-ctaband__heading { flex: 0 0 58.333333%; }
    .b-ctaband__ctas    { flex: 0 0 41.666667%; }
}

/* Brilliance's RGB accent strip and eyebrow have no counterpart on the Synergas
   site. They only render if an editor turns them on, and they are left unstyled
   rather than hidden so that choice still does something visible if made
   deliberately. */

/* ---------------------------------------------------------------------------
 * FAQ accordion — .sg-faq  (legacy: FAQ_Accordion_Style02, 7 pages)
 *
 * Litho markup this reproduces:
 *   .container > .row.justify-content-center > .col-lg-6.col-md-10
 *     .panel-group.accordion-event.accordion-style-02
 *       .panel > .panel-heading.border-color-white-transparent
 *                  a.accordion-toggle > span.alt-font
 *                  i.icon-feather-plus            (absolute, right)
 *              > .panel-collapse.collapse > .panel-body
 *
 * All values measured off the live accordion, which is the composite of
 * Litho's .accordion-style-02 rules plus the Bootstrap grid:
 *   heading row  padding 15px 25px 15px 0, border-bottom 1px rgba(255,255,255,.1)
 *   question     Outfit 15px/30px w400 #000
 *   body         padding 30px 20px 30px 0, 15px/30px #000
 *
 * Two quirks preserved deliberately:
 *  - The divider is white at 10% opacity on a #f7f7f7 section, so it is
 *    effectively INVISIBLE on the live site. It looks like a mistake (the
 *    class is .border-color-white-transparent, meant for dark sections) but
 *    reproducing it is the brief.
 *  - No panel is expanded on load - every legacy toggle carries `collapsed`.
 * ------------------------------------------------------------------------ */
.sg-faq__container {
    width: 100%;
    max-width: var(--sg-container);
    margin-inline: auto;
    padding-inline: var(--sg-gutter);
}

/* col-lg-6 centred, widening to col-md-10 and then full width. Percentages of
   the container, matching Bootstrap's column maths. */
.sg-faq__col {
    margin-inline: auto;
    padding-inline: var(--sg-gutter);
}

@media (min-width: 768px)  { .sg-faq__col { width: 83.333333%; } }
@media (min-width: 992px)  { .sg-faq__col { width: 50%; } }

.sg-faq__heading {
    font-family: var(--alt-font-family);
    text-align: center;
}

.sg-faq__panel + .sg-faq__panel { margin-top: 0; }

.sg-faq__head {
    /* A button, so the browser's default appearance has to be cleared before
       Litho's heading-row styling applies. */
    appearance: none;
    -webkit-appearance: none;
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 15px;
    width: 100%;
    background: none;
    border: 0;
    border-bottom: 1px solid rgba(255, 255, 255, 0.1);
    padding: 15px 25px 15px 0;
    text-align: left;
    cursor: pointer;
    position: relative;
}

/* .accordion-style-02 .panel:last-child .panel-heading { border: none } - but
   it comes BACK when that last panel is open (.active-accordion). */
.sg-faq__panel:last-child .sg-faq__head { border-bottom: 0; }

.sg-faq__panel:last-child.is-open .sg-faq__head {
    border-bottom: 1px solid rgba(255, 255, 255, 0.1);
}

.sg-faq__q {
    font-family: var(--alt-font-family);
    font-size: 15px;
    line-height: 30px;
    font-weight: 400;
    color: var(--text-color, #000);
    display: inline-block;
}

/* Litho positions the marker absolutely at the right edge, vertically centred. */
.sg-faq__marker {
    position: absolute;
    top: 50%;
    right: 0;
    transform: translateY(-50%);
    width: 15px;
    height: 15px;
    flex: 0 0 auto;
    color: var(--text-color, #000);
    transition: transform 0.25s ease;
}

/* Plus becomes minus: rotating 45deg would make an x, so the vertical stroke is
   hidden instead - which is what the legacy data-active-icon="minus" swap did. */
.sg-faq__panel.is-open .sg-faq__marker path {
    d: path("M5 12h14");
}

@supports not (d: path("M5 12h14")) {
    /* Older engines ignore the `d` property; rotate to a minus-like bar. */
    .sg-faq__panel.is-open .sg-faq__marker { transform: translateY(-50%) rotate(90deg); }
}

.sg-faq__body {
    padding: 30px 20px 30px 0;
    font-size: 15px;
    line-height: 30px;
    color: var(--text-color, #000);
}

.sg-faq__body > :last-child { margin-bottom: 0; }

/* ---------------------------------------------------------------------------
 * Grid + feature card — .b-grid / .b-card
 * (legacy: feature_grid, Service_Banner_Grid_Block,
 *  Interactive_Banner_Grid_Block09, icon_feature_box — 7 pages)
 *
 * Litho markup this reproduces:
 *   .container > .row.row-cols-lg-3.row-cols-sm-2.justify-content-center
 *     .col.md-margin-50px-bottom.wow.animate__fadeIn
 *       .feature-box.padding-3-rem-lr
 *         .feature-box-icon.bg-white.w-130px.h-130px.rounded-circle
 *                          .box-shadow-large.margin-40px-bottom  >  i.icon-large
 *         .feature-box-content > span (title) + p
 *
 * Measured live at 1440px: grid 1140 wide, three 380px cells, each padded
 * 0 15px; card padding 0 42px (padding-3-rem-lr = 3rem at the 14px root);
 * icon 130x130 white circle, shadow 0 0 30px rgba(0,0,0,.08), 40px below it;
 * glyph 50px; title Outfit 15px/30px w500 #232323 with 15px beneath;
 * body 15px/30px.
 * ------------------------------------------------------------------------ */
.b-grid {
    display: grid;
    /* Litho's row-cols-lg-3 row-cols-sm-2: the editor's column count applies
       from 992px, halving to 2 below that and 1 below 576. Gap stays 0 - the
       spacing is the cells' own 15px gutters, as in the Bootstrap grid. */
    grid-template-columns: repeat(var(--grid-cols, 3), minmax(0, 1fr));
    gap: 0;
    /* See "the .row negative margin" near the top: every legacy block that
       lands on this grid renders as a Bootstrap `.row`, so the grid spans its
       container's BOX and the cells' own 15px gutters live inside that. Its
       container now supplies the content span, 30px narrower, so those 30px
       have to be reclaimed here or every cell comes out 7.5-10px short:
       measured 247.5 against the live 255 on a 4-up row, 370 against 380 on a
       3-up one. `width: auto` is required - a `width: 100%` element ignores
       its own negative right margin and overflows. */
    width: auto;
    margin-inline: calc(var(--sg-gutter) * -1);
}

@media (max-width: 991px) {
    .b-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}

@media (max-width: 575px) {
    .b-grid { grid-template-columns: minmax(0, 1fr); }
}

.b-grid__cell {
    padding-inline: var(--sg-gutter);
}

/* Litho's .md-margin-50px-bottom on each column: once the grid narrows, the
   cells need vertical separation that the 3-up desktop layout does not. */
@media (max-width: 991px) {
    .b-grid__cell { margin-bottom: 50px; }
}

/* --- the card ------------------------------------------------------------ */
.b-card {
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    text-align: center;
    position: relative;
    z-index: 1;
    /* .padding-3-rem-lr, tapering the way Litho's lg-/sm- variants do. */
    padding: 0 3rem;
    /* Litho's .feature-box sets no background or border. base's
       b-card--outline would draw one in the Brilliance design; here it must
       not. */
    background: none;
    border: 0;
    text-decoration: none;
    transition-duration: 0.4s;
}

@media (max-width: 1199px) { .b-card { padding: 0 2rem; } }
@media (max-width: 767px)  { .b-card { padding: 0 1rem; } }

/* base's card template hardcodes `animate__fadeInUp` in its class attribute,
   so it cannot be turned off through the styling panel. Litho used plain
   fadeIn - no upward translate - so the animation is renamed rather than the
   template forked. */
.b-card.animate__fadeInUp {
    animation-name: fadeIn;
}

/* The icon disc: a 130px white circle with a soft shadow, 40px above the copy. */
.b-card__glyph {
    display: grid;
    place-items: center;
    width: 130px;
    height: 130px;
    margin-bottom: 40px;
    border-radius: 50%;
    background-color: #ffffff;
    box-shadow: 0 0 30px rgba(0, 0, 0, 0.08);
    color: var(--text-color, #000);
    /* .icon-large */
    font-size: 50px;
    transition: all 0.3s ease-in-out;
}

.b-card__glyph svg {
    width: 50px;
    height: 50px;
}

@media (max-width: 767px) {
    .b-card__glyph { margin-bottom: 30px; }
}

/* The title renders in a real heading tag, so this has to beat the global
   heading scale - it is a class on that element, so it does. Litho's title is
   a 15px span, not a display heading. */
.b-card__title {
    font-family: var(--alt-font-family);
    font-size: 15px;
    line-height: 30px;
    font-weight: 500;
    letter-spacing: normal;
    color: #232323;
    margin: 0 0 15px;
}

.b-card__body {
    font-size: 15px;
    line-height: 30px;
    color: var(--text-color, #000);
}

.b-card__body > :last-child { margin-bottom: 0; }

/* ---------------------------------------------------------------------------
 * Parallax hero section  (legacy: hero_parallax, 14 of 18 pages)
 *
 * Litho gave the hero section a FIXED height via .one-fifth-screen - 900px,
 * stepping to 750px below 1200 and 600px below 768 - not a viewport unit. A
 * section with only a heading inside collapses to the height of its text
 * otherwise, which clips the h1 under the navbar.
 *
 * Applied to a styled wrapper that carries a parallax background and the dark
 * scheme, which is what the transform emits for this hero.
 * ------------------------------------------------------------------------ */
.block-styled.b-bg-parallax.dark-section {
    height: 900px;
    display: flex;
    flex-direction: column;
    justify-content: center;
}

@media (max-width: 1199px) { .block-styled.b-bg-parallax.dark-section { height: 750px; } }
@media (max-width: 767px)  { .block-styled.b-bg-parallax.dark-section { height: 600px; } }

/* dark-section paints an opaque slate background, which would hide the hero's
   own image - the same problem as the CTA band. */
.block-styled.b-bg-parallax.dark-section {
    background-color: transparent;
}

/* ---------------------------------------------------------------------------
 * Media + content split — .b-mediacta
 * (legacy: Image_Content_CTA_Block, 7 pages)
 *
 * Litho markup this reproduces:
 *   section.big-section.cover-background
 *     .container > .row.align-items-center
 *       .col-12.col-xl-4.col-lg-5.col-md-6.order-2.order-md-1   (text)
 *       .col-12.col-lg-7.offset-xl-1.col-md-6.order-1.order-md-2 (image)
 *
 * Measured live: title Outfit 32.2px/42px w400 UPPERCASE (2.3rem, Litho's h5);
 * the highlight paragraph is 16px/28px behind a 4px solid #232323 left border
 * with 25px of padding and 40px/30px margins - a pull-quote accent that is the
 * block's most recognisable feature.
 *
 * Base's own design is a rounded, bordered, clipped card with a dark panel.
 * None of that belongs here, so the card chrome is removed: Litho's version is
 * two plain columns side by side.
 *
 * Note the ORDER SWAP. Litho puts the image first on mobile (order-1) and
 * second from 768px up (order-md-2), so the picture leads on a phone and the
 * copy leads on desktop. Reproducing the desktop layout alone would silently
 * change the mobile reading order.
 * ------------------------------------------------------------------------ */
.b-mediacta {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    /* No card chrome - base rounds, borders and clips this; Litho does not. */
    border: 0;
    border-radius: 0;
    overflow: visible;
    background: none;
    /* .row's negative gutters, so the columns line up with every other grid. */
    margin-inline: calc(var(--sg-gutter) * -1);
}

.b-mediacta__media,
.b-mediacta__panel {
    padding-inline: var(--sg-gutter);
    flex: 0 0 100%;
    max-width: 100%;
}

/* Image first on mobile, copy first from 768px - Litho's order-1/order-md-2. */
.b-mediacta__media { order: 1; }
.b-mediacta__panel { order: 2; margin-bottom: 5rem; }

@media (min-width: 768px) {
    .b-mediacta__media { order: 2; flex: 0 0 50%; max-width: 50%; }
    .b-mediacta__panel { order: 1; flex: 0 0 50%; max-width: 50%; margin-bottom: 0; }
}

@media (min-width: 992px) {
    .b-mediacta__media { flex: 0 0 58.333333%; max-width: 58.333333%; }
    .b-mediacta__panel { flex: 0 0 41.666667%; max-width: 41.666667%; }
}

/* col-xl-4 + offset-xl-1: the text column narrows and a twelfth of a gap opens
   between the two at 1200px and up. */
@media (min-width: 1200px) {
    .b-mediacta__panel { flex: 0 0 33.333333%; max-width: 33.333333%; }
    .b-mediacta__media {
        flex: 0 0 58.333333%;
        max-width: 58.333333%;
        margin-left: 8.333333%;
    }
}

/* The dark panel is a Brilliance motif; Litho's text column has no background. */
.b-mediacta__panel {
    background: none;
    color: inherit;
    /* brilliance pads the panel 70px top and bottom, which Litho's column does
       not have at all - production's computed padding is `0 0 0 15px`. Those
       140px made the panel 378 tall against the live 303.3, and the section's
       own `big-section` rhythm already provides the space. */
    padding-block: 0;
}

/* `pe-lg-0` on the legacy column: no right gutter, so the copy uses the full
   365px rather than 350. Litho drops it below lg, where the columns stack. */
@media (min-width: 992px) {
    .b-mediacta__panel { padding-right: 0; }
}

.b-mediacta__img {
    display: block;
    width: 100%;
    height: auto;
    border-radius: 0;
}

/* .alt-font.text-uppercase at Litho's h5 size. */
.b-mediacta__panel :is(h1, h2, h3, h4, h5, h6) {
    font-family: var(--alt-font-family);
    font-size: 2.3rem;
    line-height: 3rem;
    font-weight: 400;
    text-transform: uppercase;
    /* Litho's h5 keeps its bottom margin here - measured 25px, and without it
       the pull-quote's 40px top margin collapsed against the heading. */
    margin: 0 0 25px;
}

/* The pull-quote accent: .border-width-4px.padding-25px-left.text-extra-medium
   with 40px above and 30px below, held to 85% width on desktop. */
.b-mediacta__panel > p,
.b-mediacta__panel .b-mediacta__text {
    font-size: 16px;
    line-height: 28px;
    /* `text-extra-medium` carries weight 500 and Litho's extra-dark-gray;
       brilliance's panel copy is 300 at 78% black, which read noticeably
       lighter than production's. */
    font-weight: 500;
    color: #232323;
    border-left: 4px solid #232323;
    padding-left: 25px;
    margin: 40px 0 30px;
    width: 85%;
}

@media (max-width: 991px) {
    .b-mediacta__panel > p,
    .b-mediacta__panel .b-mediacta__text { width: 100%; }
}

@media (max-width: 575px) {
    .b-mediacta__panel > p,
    .b-mediacta__panel .b-mediacta__text { margin-top: 30px; }
}

.b-mediacta__actions {
    display: flex;
    flex-wrap: wrap;
    gap: 10px;
}

/* ---------------------------------------------------------------------------
 * Numbered steps — .b-numlist
 * (legacy: Process_Step_Style_02_Block 3 pages, and the checklist 2 pages)
 *
 * Litho's .process-step-style-02, verbatim from style.css:
 *   .process-step-item      flex, row wrap, align-items stretch, text-align left
 *   .process-step-icon      50x50, border-radius 100%, 19px/48px, colour #fff
 *   .process-step-icon-wrap margin-right 40px, and a 1px CONNECTOR line
 *                           absolutely positioned at bottom 20px, left 50%,
 *                           height calc(100% - 90px)
 *
 * That connector is the block's signature - a vertical thread joining the step
 * discs down the list. Easy to miss reading the template, since it is drawn by
 * an empty <span> and exists only in CSS.
 * ------------------------------------------------------------------------ */
.b-numlist {
    display: flex;
    flex-wrap: wrap;
    list-style: none;
    margin: 0;
    padding: 0;
}

.b-numlist__item {
    display: flex;
    position: relative;
    flex-flow: row wrap;
    align-items: stretch;
    justify-content: flex-start;
    text-align: left;
    flex: 0 0 100%;
    transition: box-shadow 0.3s ease, background 0.3s ease;
}

/* The step disc, plus the connector to the next one. The line stops 90px short
   so it runs between the discs rather than through them. */
.b-numlist__n {
    position: relative;
    display: grid;
    place-items: center;
    width: 50px;
    height: 50px;
    flex: 0 0 50px;
    margin-right: 40px;
    border-radius: 100%;
    font-family: var(--alt-font-family);
    font-size: 19px;
    line-height: 48px;
    font-weight: 500;
    color: #fff;
    background-color: var(--theme-color);
}

.b-numlist__item:not(:last-child) .b-numlist__n::after {
    content: "";
    position: absolute;
    top: 100%;
    left: 50%;
    transform: translateX(-50%);
    width: 1px;
    height: calc(100% + 40px);
    background-color: rgba(0, 0, 0, 0.12);
}

.b-numlist__item:hover .b-numlist__n {
    transform: scale(1.1);
    transition: transform 0.3s ease-in-out;
}

/* Badge LEFT, copy stacked beside it — Litho wraps the label and description in
   one `.process-content` div next to the numbered disc.
   `flex: 0 0 calc(100% - 90px)` was not enough on its own: brilliance.css sets
   `.b-numlist__item { display: flex; flex-direction: column }` and this file had
   no `display` rule for that element to beat, so the disc sat ABOVE the label at
   the same x and each step ran 179px tall against production's ~57px. An
   explicit 2-column grid states the intent instead of fighting the flex
   direction, and places the three children by name so their source order does
   not matter. */
.b-numlist__item {
    display: grid;
    grid-template-columns: 50px minmax(0, 1fr);
    column-gap: 40px;
    align-items: start;
}

.b-numlist__n {
    grid-column: 1;
    grid-row: 1 / span 2;
}

.b-numlist__label,
.b-numlist__text {
    grid-column: 2;
    min-width: 0;
}

.b-numlist__label {
    padding-bottom: 0;
}

.b-numlist__text {
    padding-bottom: 40px;
}

/* Measured live: the step label is a 15px/30px span at weight 500, NOT a
   display heading. base renders it in a heading tag, so without an explicit
   size it inherits the global heading scale and comes out at 39.9px - nearly
   three times too large. The `letter-spacing-minus-1px` class in the legacy
   markup has no visible effect at this size and is not reproduced. */
.b-numlist__label {
    font-family: var(--alt-font-family);
    font-size: 15px;
    line-height: 30px;
    font-weight: 500;
    color: #232323;
    letter-spacing: normal;
    margin: 0 0 5px;
}

.b-numlist__text { font-size: 15px; line-height: 30px; }

/* ---------------------------------------------------------------------------
 * Stat strip — .b-statstrip  (legacy: Stats_Counter_Block, 2 pages)
 *
 * Litho renders these as ONE white card floating on a section boundary:
 *   .col-12.box-shadow-medium.bg-white.padding-4-rem-all   (0 0 20px rgba(0,0,0,.08), 4rem)
 *     .row.overlap-section
 *       .col.border-right.border-color-medium-gray.text-center  per stat
 *
 * DEGRADED: the numbers no longer count up. Litho drove that with a `counter`
 * plugin bundled inside theme-vendors.min.js, which Base 8 does not carry.
 * ------------------------------------------------------------------------ */
.b-statstrip {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    justify-content: center;
    background-color: #ffffff;
    box-shadow: 0 0 20px rgba(0, 0, 0, 0.08);
    padding: 4rem;
    position: relative;
    transition: all 0.3s ease-in-out;
}

@media (max-width: 767px) {
    .b-statstrip { padding: 0 15px; box-shadow: none; }
}

.b-statstrip__item {
    flex: 1 1 0;
    min-width: 0;
    text-align: center;
    /* .border-right.border-color-medium-gray, dropped on the last one and on
       small screens (.sm-no-border-right). */
    border-right: 1px solid #e4e4e4;
}

.b-statstrip__item:last-child { border-right: 0; }

@media (max-width: 767px) {
    .b-statstrip__item {
        flex: 0 0 100%;
        border-right: 0;
        margin-bottom: 30px;
    }
}

/* .alt-font.font-weight-500 - the big number. */
/* The legacy counter is an <h4>, so it takes Litho's h4 scale - 2.85rem/3.4rem
   at weight 500 in the alt font, theme-coloured. */
.b-statstrip__num,
.b-statstrip__suffix {
    font-family: var(--alt-font-family);
    font-size: 2.85rem;
    line-height: 3.4rem;
    font-weight: 500;
    color: var(--theme-color);
    margin-bottom: 0.5rem;
}

/* .alt-font.text-extra-dark-gray.font-weight-500.line-height-14px */
.b-statstrip__label {
    display: block;
    font-family: var(--alt-font-family);
    font-weight: 500;
    color: #232323;
    line-height: 14px;
}

/* Brilliance draws an accent bar under the strip; Litho has none. */
.b-statstrip__bar { display: none; }

/* ---------------------------------------------------------------------------
 * Images — .b-figure / .b-img  (legacy: simple_image_block, 2 pages)
 * Litho renders a plain responsive image with square corners.
 * ------------------------------------------------------------------------ */
.b-figure { margin: 0; }

/* The reset above loads after brilliance.css's `.b-figure--{center,left,right}`
 * (equal specificity, one class each) and its `margin: 0` shorthand wins
 * outright, zeroing the left/right auto-margins those alignment modifiers
 * set - a sized figure (Image block → Size) rendered flush left regardless
 * of the editor's Alignment choice. Re-assert them here, after the reset. */
.b-figure--center { margin-left: auto; margin-right: auto; }
.b-figure--left { margin-right: auto; }
.b-figure--right { margin-left: auto; }

.b-img {
    display: block;
    max-width: 100%;
    height: auto;
    border-radius: 0;
}

.b-caption {
    font-size: 15px;
    line-height: 30px;
    color: #757575;
    margin-top: 10px;
}

/* ---------------------------------------------------------------------------
 * Divider — .b-divider  (used by scaffolded title sections)
 * Litho's separator under a Fancy Title is a thin theme-coloured rule.
 * ------------------------------------------------------------------------ */
.b-divider {
    border: 0;
    border-top: 1px solid rgba(0, 0, 0, 0.1);
    margin: 30px 0;
}

/* ---------------------------------------------------------------------------
 * Parallax hero contents  (legacy: hero_parallax, 14 of 18 pages)
 *
 * Litho markup this reproduces:
 *   section.parallax.one-fifth-screen  (background-position 50% 0)
 *     .opacity-extra-medium.bg-extra-dark-gray      <- #232323 at 0.5
 *     .container > .row.one-third-screen
 *       .col-12.page-title-extra-small.text-center.d-flex...flex-column
 *         h1.alt-font.opacity-6.margin-20px-bottom
 *         h2.alt-font.font-weight-500.w-55.letter-spacing-minus-1px
 *            .line-height-50px.no-margin-bottom
 *
 * The type hierarchy here is INVERTED from what the tag names suggest, and
 * getting it wrong is very visible on 14 pages:
 *
 *   h1  ->  .page-title-extra-small h1 { font-size: 16px; line-height: 20px }
 *           plus opacity-6. It is a small, faint EYEBROW, not the headline.
 *   h2  ->  Litho's h2 scale (3.9rem) with line-height forced to 50px and
 *           weight 500. THIS is the focus text.
 *
 * The dark wash is #232323 at 0.5 opacity - not black, and heavier than the
 * CTA band's 0.75-over-#1f232c.
 * ------------------------------------------------------------------------ */
.block-styled.b-bg-parallax.dark-section {
    /* .parallax sets background-position: 50% 0, and the JS ratio scroll
       effect is approximated by the fixed attachment already applied by
       .b-bg-parallax. */
    background-position: 50% 0;
}

/* .opacity-extra-medium over .bg-extra-dark-gray, as a pseudo-element so no
   extra markup is needed. */
.block-styled.b-bg-parallax.dark-section::before {
    content: "";
    position: absolute;
    inset: 0;
    background-color: #232323;
    opacity: 0.5;
    pointer-events: none;
    z-index: 0;
}

/* Keep the copy above the wash. */
.block-styled.b-bg-parallax.dark-section > * {
    position: relative;
    z-index: 1;
}

/* Litho centres the hero copy in the section, both axes. */
.block-styled.b-bg-parallax.dark-section .b-grid,
.block-styled.b-bg-parallax.dark-section [class*="fedit-grid"] {
    text-align: center;
}

/* The faint eyebrow. */
.block-styled.b-bg-parallax.dark-section .b-h1 {
    font-size: 16px;
    line-height: 20px;
    font-family: var(--alt-font-family);
    opacity: 0.6;
    margin: 0 0 20px;
    /* No letter-spacing override. This rule used to force `normal`, on the
       assumption that 16px text should not be tracked in — but the live eyebrow
       measures -0.5px, so the assumption was wrong and the heading scale's own
       default is already correct. */
    color: #ffffff;
}

/* The focus text: Litho's h2 scale at weight 500, held to 55% of the container
   so it wraps the way the live site does.
 *
 * Line-height is DELIBERATELY not overridden. The legacy markup asks for
 * `line-height-50` (plus `sm-line-height-45` / `xs-line-height-30`), but Litho
 * defines `.line-height-50px` - the class names do not match, so none of those
 * rules ever applied and the live h2 keeps its natural 4.2rem (58.8px).
 * Implementing the INTENDED 50px produced something the live site does not do.
 * Measured live: 54.6px/58.8px. */
.block-styled.b-bg-parallax.dark-section .b-subheading {
    display: block;
    font-family: var(--alt-font-family);
    font-size: 3.9rem;
    line-height: 4.2rem;
    font-weight: 500;
    letter-spacing: -1px;
    color: #ffffff;
    margin: 0 auto;
    width: 55%;
    text-align: center;
}

@media (max-width: 991px) {
    .block-styled.b-bg-parallax.dark-section .b-subheading { width: 65%; }
}

@media (max-width: 767px) {
    .block-styled.b-bg-parallax.dark-section .b-subheading { width: 80%; }
}

@media (max-width: 575px) {
    .block-styled.b-bg-parallax.dark-section .b-subheading { width: 100%; }
}

/* ---------------------------------------------------------------------------
 * Header  (legacy: Utilities_headersettings + wagtailmenus)
 *
 * Legacy config, from the dump:
 *   header_style "transparent", logo_position "left",
 *   navigation_style "center", show_language_switcher true,
 *   cta_button_text "JCC Portal", cta_button_style "primary"
 *
 * Measured live at 1440px:
 *   header  72px tall, background TRANSPARENT, position fixed
 *   links   Outfit 14px w500 #fff, no padding, no text-transform
 *   logo    189x36
 *   CTA     Comfortaa 10px on #0d6efd, white, 6px 16px, radius 4px, UPPERCASE
 *
 * The CTA blue is Bootstrap's default primary (#0d6efd), NOT the Synergas theme
 * blue - the legacy setting is cta_button_style: "primary", so it inherits
 * .btn-primary. It reads like an oversight; reproducing it is the brief.
 *
 * base's nav is styled with inline Tailwind (backdrop-blur, border-b, shadow)
 * plus an inline background-color from --nav-background-color. Element+class
 * selectors beat the utilities, but the inline background needs !important -
 * the one place in this layer that does. It is a single site-wide element, and
 * BrandingSettings' colour field cannot express "transparent".
 * ------------------------------------------------------------------------ */
nav.fixed {
    background-color: transparent !important;
    backdrop-filter: none;
    border-bottom: 0;
    box-shadow: none;
}

/* The header row: logo left, nav centred, language + CTA right. */
nav.fixed > div {
    max-width: var(--sg-container);
    min-height: 72px;
    padding-inline: var(--sg-gutter);
    /* base's inner row carries py-2, which at Litho's 14px root adds 14px and
       made the header 86px against the live site's 72. The links' 72px
       line-height sets the height instead. */
    padding-block: 0;
}

/* The logo. `:not(.sg-langs__flag)` is load-bearing: this selector is
   (0,1,2) and beats `.sg-langs__flag` at (0,1,0), so without the exclusion it
   sized the three language flags to 36px tall as well. That overflowed the
   header, base's inner row carries `flex-wrap`, and the nav wrapped to 258px
   against the live site's 72. */
nav.fixed img:not(.sg-langs__flag) {
    height: 36px;
    width: auto;
}

/* --- top-level items --------------------------------------------------- */
.sg-nav__item {
    position: relative;
}

.sg-nav__link {
    display: inline-block;
    padding: 0 16px;
    font-family: var(--alt-font-family);
    font-size: 16px;
    font-weight: 500;
    color: #ffffff;
    text-decoration: none;
    text-transform: none;
    line-height: 72px;
    /* A flex item's box can be squeezed narrower than its own content by its
       siblings (see the 992-1199px note below); without this, that squeeze
       wraps the label onto a second line instead of just running tight. */
    white-space: nowrap;
    transition: color 0.3s ease;
}

.sg-nav__link:hover,
.sg-nav__link.is-current {
    color: var(--theme-color);
}

/* --- dropdowns --------------------------------------------------------- */
/* CSS-only: :focus-within keeps it usable from the keyboard, which the legacy
   hover-only menu was not. */
.sg-nav__submenu {
    position: absolute;
    top: 100%;
    left: 0;
    min-width: 220px;
    padding: 10px 0;
    background-color: #ffffff;
    box-shadow: 0 0 20px rgba(0, 0, 0, 0.08);
    opacity: 0;
    visibility: hidden;
    transform: translateY(6px);
    transition: opacity 0.25s ease, transform 0.25s ease, visibility 0.25s;
    z-index: 10;
}

.sg-nav__item--has-children:hover .sg-nav__submenu,
.sg-nav__item--has-children:focus-within .sg-nav__submenu {
    opacity: 1;
    visibility: visible;
    transform: translateY(0);
}

.sg-nav__sublink {
    display: block;
    padding: 8px 20px;
    font-family: var(--alt-font-family);
    font-size: 16px;
    font-weight: 500;
    color: #232323;
    text-decoration: none;
    white-space: nowrap;
    transition: color 0.3s ease;
}

.sg-nav__sublink:hover,
.sg-nav__sublink.is-current {
    color: var(--theme-color);
}

/* --- the JCC Portal button --------------------------------------------- */
/* base renders NavigationSettings items through nav_menu.html, which is not
   ours to reshape, so the button is targeted by its position in the header. */
nav.fixed a[href*="jccsmart.com"] {
    display: inline-block;
    margin-left: 12px;
    padding: 6px 16px;
    /* Litho's .btn carries `border: 2px solid transparent` so a solid button
       and an outline one are the same size. Without it this measured 113x27
       against the live button's 117x31 — exactly 2px per side. */
    border: 2px solid transparent;
    border-radius: 4px;
    background-color: #0d6efd;
    color: #ffffff;
    font-family: "Comfortaa", var(--alt-font-family);
    font-size: 14px;
    font-weight: 500;
    text-transform: uppercase;
    text-decoration: none;
    line-height: 1.5;
}

/* Production's button is `JCC Portal<i class="fa fa-arrow-right right-icon">`.
   nav_menu.html is base-derived and renders only the label, so the icon is
   attached here as generated content rather than by forking that template. */
nav.fixed a[href*="jccsmart.com"]::after {
    content: "\f061";
    /* FA6, not FA5 as everywhere else - see base.html's note on why this
       project renders with Font Awesome 5. There is no "Font Awesome 6 Free"
       on the page, so the glyph silently failed to paint: no error, no
       fallback box, just an empty 8px margin where the arrow should be. */
    font-family: "Font Awesome 5 Free";
    font-weight: 900;
    margin-left: 8px;
    font-size: 9px;
    vertical-align: middle;
}

nav.fixed a[href*="jccsmart.com"]:hover {
    background-color: #0b5ed7;
    color: #ffffff;
}


/* ---------------------------------------------------------------------------
 * Footer  (legacy: .footer-dark.bg-extra-dark-gray + Utilities_footersettings)
 *
 * Measured live at 1440px: 553px tall, background #232323, body text
 * rgb(136,144,164) at 15px, column headings uppercase, a 210x40 logo and a
 * full-width partner banner.
 *
 * Base 8 has no footer markup at all, so there was nothing to override - the
 * template in synergas/templates/synergas/footer.html is new.
 * ------------------------------------------------------------------------ */
.sg-footer {
    background-color: #232323;
    color: #8890a4;
    font-size: 15px;
    line-height: 30px;
}

.sg-footer__container {
    width: 100%;
    max-width: var(--sg-container);
    margin-inline: auto;
    padding-inline: var(--sg-gutter);
    display: flex;
    flex-wrap: wrap;
}

.sg-footer__top {
    padding: 5rem 0 3rem;
}

@media (max-width: 767px) {
    .sg-footer__top { padding: 3rem 0 2rem; }
}

.sg-footer__col {
    padding-inline: var(--sg-gutter);
    margin-bottom: 30px;
    box-sizing: border-box;
}

/* The nav columns (everything but the brand) share their own flex row, so they
   wrap as a GROUP rather than shedding one column at a time onto a lone row -
   see the comment in footer.html. Desktop: equal-width columns filling the
   remaining space next to the brand, same as Litho's col-lg. Tablet: 2-up,
   same as col-sm-6. Phone: full width, same as col-12. */
.sg-footer__nav {
    display: flex;
    flex-wrap: wrap;
    flex: 1 1 auto;
    min-width: 0;
}

.sg-footer__nav .sg-footer__col {
    flex: 1 1 0;
    min-width: 0;
}

@media (max-width: 991px) {
    .sg-footer__nav .sg-footer__col { flex: 0 0 50%; max-width: 50%; }
}

@media (max-width: 575px) {
    .sg-footer__nav .sg-footer__col { flex: 0 0 100%; max-width: 100%; }
}

/* Litho's footer column headings are uppercase alt-font at body size. */
.sg-footer__heading {
    display: block;
    font-family: var(--alt-font-family);
    font-size: 15px;
    font-weight: 500;
    text-transform: uppercase;
    color: #ffffff;
    margin-bottom: 15px;
}

.sg-footer__list {
    list-style: none;
    margin: 0;
    padding: 0;
}

.sg-footer__list a {
    color: #8890a4;
    text-decoration: none;
    transition: color 0.3s ease;
}

.sg-footer__list a:hover {
    color: var(--theme-color);
}

/* The logo sits as the first column of the top row, as on the live site - not
   in the bottom bar. */
.sg-footer__col--brand {
    flex: 0 0 auto;
    width: 210px;
    min-width: 0;
    margin-right: 40px;
}

@media (max-width: 575px) {
    .sg-footer__col--brand { width: 100%; margin-right: 0; }
}

.sg-footer__bottom {
    border-top: 1px solid rgba(255, 255, 255, 0.1);
    padding: 30px 0;
}

.sg-footer__container--bottom {
    align-items: center;
    justify-content: space-between;
    gap: 20px;
}

.sg-footer__home {
    color: #ffffff;
    text-decoration: none;
    transition: color 0.3s ease;
}

.sg-footer__home:hover { color: var(--theme-color); }

.sg-footer__logo img {
    height: 40px;
    width: auto;
    display: block;
}

.sg-footer__legal {
    margin: 0;
    font-size: 15px;
    color: #8890a4;
}

/* The co-funding banner sits on WHITE below the dark footer, full-bleed, as on
   the live site. */
.sg-footer__banner {
    background-color: #ffffff;
    padding: 10px 0;
}

.sg-footer__banner img {
    display: block;
    /* Constrained to the container, not full-bleed: measured 1039px wide on the
       live site inside a 1440px viewport, i.e. held within the grid. */
    width: 100%;
    max-width: var(--sg-container);
    height: auto;
    margin-inline: auto;
    padding-inline: var(--sg-gutter);
    box-sizing: border-box;
}

/* ---------------------------------------------------------------------------
 * Embedded form — .b-formcard  (legacy: mesage_form_block, 4 pages)
 *
 * Litho's submit is
 *   .btn.btn-extra-large.btn-expand-ltr.btn-rounded.bg-theme
 * which resolves to: 14px, padding 15px 28px, border-radius 50px, theme
 * background. The success message is a Bootstrap .alert.alert-success.
 *
 * base's form card is a bordered, rounded panel with its own glow; Litho's form
 * sits directly on the section, so the panel chrome is removed.
 * ------------------------------------------------------------------------ */
.b-formcard,
.b-formsec {
    background: none;
    border: 0;
    border-radius: 0;
    box-shadow: none;
    padding: 0;
}

.b-formglow { display: none; }

.b-field {
    margin-bottom: 20px;
}

.b-field input,
.b-field textarea,
.b-field select,
.b-inputs input,
.b-inputs textarea,
.b-inputs select {
    width: 100%;
    font-family: var(--main-font-family);
    font-size: 15px;
    line-height: 30px;
    color: var(--text-color, #000);
    background-color: #ffffff;
    border: 1px solid #e4e4e4;
    border-radius: 0;
    padding: 10px 15px;
}

.b-field input:focus,
.b-field textarea:focus,
.b-inputs input:focus,
.b-inputs textarea:focus {
    outline: 0;
    border-color: var(--theme-color);
}

.b-field__err,
.b-field--err input,
.b-field--err textarea {
    border-color: var(--alt-theme-color);
    color: var(--alt-theme-color);
}

.b-field__hint {
    font-size: 13px;
    line-height: 24px;
    color: #757575;
}

/* Litho's submit: rounded pill, theme background, white label. */
.b-formcard .b-cta,
.b-formsec .b-cta,
.b-formcard button[type="submit"],
.b-formsec button[type="submit"] {
    font-size: 14px;
    padding: 15px 28px;
    border-radius: 50px;
    background-color: var(--theme-color);
    border-color: var(--theme-color);
    color: #ffffff;
}

.b-formcard .b-cta:hover,
.b-formsec .b-cta:hover,
.b-formcard button[type="submit"]:hover,
.b-formsec button[type="submit"]:hover {
    background-color: transparent;
    color: var(--theme-color);
}

/* Bootstrap .alert.alert-success, which is what the legacy block rendered. */
.b-formcard__status,
.b-formstatus {
    text-align: center;
    padding: 12px 20px;
    border: 1px solid #badbcc;
    background-color: #d1e7dd;
    color: #0f5132;
    margin-bottom: 20px;
}

.b-formcard__status--err,
.b-formstatus--err {
    border-color: #f5c2c7;
    background-color: #f8d7da;
    color: #842029;
}

/* ---------------------------------------------------------------------------
 * Testimonial / content carousel
 * (legacy: Testimonial_Slider_Block 3 pages, plus the two text sliders)
 *
 * Almost nothing to do here, which is worth recording. brilliance's
 * CarouselBlock exposes only two component hooks - .b-slider__actions and
 * .b-slider__btn - and lays its track out with inline Tailwind. Its slides are
 * .b-card items, already styled by the grid/card section above. So the
 * carousel needs only its control buttons.
 *
 * It also accepts card / media_stat_card / image children ONLY: there is no
 * quote child, so the transform turns each testimonial into a card (author as
 * title, quote as body) rather than emitting a `quote` that would render as
 * nothing.
 *
 * DEGRADED: Litho drove these with Swiper. base's is a CSS snap-scroll track,
 * so the layout holds but the drag and easing feel differ.
 * ------------------------------------------------------------------------ */
.b-slider__btn {
    display: grid;
    place-items: center;
    width: 3rem;
    height: 3rem;
    border: 0;
    border-radius: 999px;
    background: rgb(0 0 0 / 8%);
    color: #232323;
    cursor: pointer;
    transition: background 0.2s ease;
}

.b-slider__btn:hover,
.b-slider__btn:focus-visible {
    background: rgb(0 0 0 / 16%);
}

/* ---------------------------------------------------------------------------
 * Location map  (legacy: Location_Map_Block, 3 pages)
 * Litho renders the map full-width at a fixed 500px (.w-100.h-500px).
 * ------------------------------------------------------------------------ */
.location-map-wrapper { margin: 0; }

.location-map {
    width: 100%;
    height: 500px;
    border-radius: 0;
}

@media (max-width: 767px) {
    .location-map { height: 350px; }
}

/* ---------------------------------------------------------------------------
 * Data table — .b-table  (legacy: Features_table)
 * Litho uses Bootstrap's .table.table-striped.table-hover.table-bordered with
 * a .table-dark header row and .align-middle cells.
 * ------------------------------------------------------------------------ */
.b-table-wrap { overflow-x: auto; }

.b-table {
    width: 100%;
    border-collapse: collapse;
    font-size: 15px;
    line-height: 30px;
}

.b-table th,
.b-table td,
.b-table__cell {
    border: 1px solid #dee2e6;
    padding: 12px 15px;
    vertical-align: middle;
}

/* .table-dark on the header row. */
.b-table thead th {
    background-color: #212529;
    color: #ffffff;
    font-family: var(--alt-font-family);
    font-weight: 500;
}

/* .table-striped */
.b-table tbody tr:nth-child(odd) {
    background-color: rgba(0, 0, 0, 0.03);
}

/* .table-hover */
.b-table tbody tr:hover {
    background-color: rgba(0, 0, 0, 0.06);
}

/* ---------------------------------------------------------------------------
 * Icon point — .b-usp  (used inside grids for icon feature lists)
 * Litho's inline icon+text row: icon left, text beside it, body type.
 * ------------------------------------------------------------------------ */
/* Litho's `.feature-box.feature-box-left-icon`:
     .feature-box-left-icon            flex row wrap, align-items stretch,
                                       justify-content flex-start, text-align left
     .feature-box-icon                 margin-right: 35px
     .feature-box-content              overflow: hidden; flex: 1
   No gap here — the spacing rides on the icon's own Litho margin class, because
   the two variants of this block space differently (35px vs 10px). */
.b-usp {
    display: flex;
    flex-flow: row wrap;
    align-items: flex-start;
    justify-content: flex-start;
    gap: 0;
    text-align: left;
}

/* The size and colour of the glyph come from the Litho utility classes the
   transform now passes through on the `<i>` itself (see the utilities block
   below), NOT from here. brilliance styles this span as a 48x48 dark rounded
   TILE with a 20px glyph — `width`, `height`, `background`, `border-radius`
   and `display` all have to be cleared explicitly or its rule keeps supplying
   them, which is how a bare Litho icon rendered as a dark tile. */
.b-usp__ico {
    flex: 0 0 auto;
    display: inline-block;
    width: auto;
    height: auto;
    margin: 0;
    padding: 0;
    background: none;
    border: 0;
    border-radius: 0;
    color: inherit;
    font-size: inherit;
    line-height: 1;
}

.b-usp__ico svg,
.b-usp__ico .cx-icon {
    width: 1em;
    height: 1em;
}

.b-usp__txt {
    flex: 1;
    overflow: hidden;
    min-width: 0;
}

.b-usp__label {
    margin: 0 0 10px;
    font-family: var(--alt-font-family);
    font-size: 15px;
    line-height: 30px;
    font-weight: 500;
    color: #232323;
}

/* `.last-paragraph-no-margin p:last-of-type { margin-bottom: 0 }` */
.b-usp__body {
    margin: 0;
    font-size: 15px;
    line-height: 30px;
    color: var(--text-color, #000);
}

/* ---------------------------------------------------------------------------
 * Litho icon/type utilities, carried on the element itself
 * ---------------------------------------------------------------------------
 * The legacy templates hardcode an icon's size, colour and spacing as classes
 * on the `<i>` — and they differ per block, not per item, so no single rule on
 * `.b-usp__ico` can be right for all of them. Measured across the live pages,
 * `feature-box-left-icon` icons are:
 *
 *   BusinessFeaturesBlock      icon-medium text-extra-light-gray   40px #b7b7b7
 *   ImageSliderFeaturesBlock   text-large text-theme               18px cyan
 *   FeatureSectionAbout        icon-medium text-theme              40px cyan
 *   NumberedFeatureBoxesBlock  icon-medium text-fast-blue          40px #0038e3
 *   IconFeatureGridBlock       icon-medium + the editor's own text_settings
 *
 * So the transform passes the legacy classes through and these define them —
 * which is also how the legacy site does it. Only the ones actually reachable
 * from migrated content are defined; `.text-theme` already comes from base's
 * input.css.
 * ------------------------------------------------------------------------- */
.icon-medium { font-size: 40px; }
.icon-large  { font-size: 50px; }
.text-large  { font-size: 18px; line-height: 22px; }
.text-extra-light-gray { color: #b7b7b7; }
.text-extra-dark-gray  { color: #232323; }
.text-fast-blue        { color: #0038e3; }
.margin-10px-right { margin-right: 10px; }
.margin-35px-right { margin-right: 35px; }
.margin-35px-bottom { margin-bottom: 35px; }

/* ---------------------------------------------------------------------------
 * Progress bars — .sg-progress  (legacy: Progress_Bars_Block, 1 page: home)
 *
 * Litho markup this reproduces (scripts/litho_rules.py progress progress-bar
 * progress-title progress-step-style-01):
 *   .container > .row
 *     .col-xl-4.col-lg-5.col-md-6                 heading + description
 *     .col-lg-6.offset-xl-2.offset-lg-1.col-md-6   bars
 *       .progress.progress-step-style-01 { overflow: visible; height: 8px;
 *                                           border-radius: 0; margin-top: 30px; }
 *         .progress-title  { position: absolute; top: -28px; text-uppercase }
 *         .progress-bar.bg-theme { border-radius: 6px; width: 0 -> N%;
 *                                   transition: width .6s ease; }
 *
 * Two columns, ~33% / 66% (xl-4 / lg-6 with an lg-1 offset), matching Litho's
 * grid maths; collapses to one stacked column below tablet, same breakpoint as
 * the rest of this file (991px — see .sg-faq__col above). The bar itself is
 * flattened to a simple flex row (label above the track) rather than Litho's
 * absolutely-positioned title/percentage, which is unnecessary complexity once
 * neither has to share a line with anything else.
 * ------------------------------------------------------------------------ */
/* The legacy row is `col-xl-4` + `col-lg-6 offset-xl-2`, and an OFFSET cannot
   be approximated by a gap: it is 2/12 of the row, not a fixed distance. A
   "4 : 7 split with a clamped gap" put the bars at x=605 w=670 against the
   live x=735 w=540 - out by 130px, the single largest per-block error left on
   the site.

   So this reproduces Bootstrap literally: 12 equal tracks across the row (the
   container BOX, hence the negative margin - see "the .row negative margin"),
   each column claiming its own span with its 15px gutters inside. That lands
   the head content at 350@165 and the bars at 540@735, exactly. It also
   degrades the way Litho does, since the lg and md spans are just different
   `grid-column` values rather than another set of guessed proportions. */
.sg-progress {
    display: grid;
    grid-template-columns: minmax(0, 1fr);
    width: 100%;
}

@media (min-width: 768px) {
    .sg-progress {
        grid-template-columns: repeat(12, minmax(0, 1fr));
        align-items: start;
        width: auto;
        margin-inline: calc(var(--sg-gutter) * -1);
    }
    .sg-progress__col { padding-inline: var(--sg-gutter); }
    /* col-md-6 + col-md-6 */
    .sg-progress__col--head { grid-column: 1 / span 6; }
    .sg-progress__col--bars { grid-column: 7 / span 6; }
}

@media (min-width: 992px) {
    /* col-lg-5 + col-lg-6 offset-lg-1 */
    .sg-progress__col--head { grid-column: 1 / span 5; }
    .sg-progress__col--bars { grid-column: 7 / span 6; }
}

@media (min-width: 1200px) {
    /* col-xl-4 + col-lg-6 offset-xl-2 */
    .sg-progress__col--head { grid-column: 1 / span 4; }
    .sg-progress__col--bars { grid-column: 7 / span 6; }
}

.sg-progress__heading {
    font-family: var(--alt-font-family);
    font-weight: 600;
    letter-spacing: -1px;
    color: var(--text-color, #232323);
    margin-bottom: 1.5rem;
}

.sg-progress__desc {
    color: var(--text-color, #000);
    max-width: 90%;
}

@media (max-width: 767px) {
    .sg-progress__desc { max-width: 100%; }
}

.sg-progress__col--bars {
    display: flex;
    flex-direction: column;
    min-width: 0;
}

.sg-progress__item { min-width: 0; }

/* `margin-4-half-rem-bottom`, dropped on the last bar as legacy does with
   `{% if not forloop.last %}`. */
/* 33px, not 63. Litho's 63px sits between two 8px TRACKS and its title is
   absolutely positioned INSIDE the gap (30px above its own track), so the
   track-to-track distance is 8 + 63 = 71. Our flattened item carries that
   title in normal flow, so 63 here would add its 30px on top and space the
   tracks 93 apart. 33 + 30 restores the 71. */
.sg-progress__item + .sg-progress__item { margin-top: 33px; }

@media (max-width: 767px) {
    /* sm-margin-6-rem-bottom: 84px between tracks, less the same 30px. */
    .sg-progress__item + .sg-progress__item { margin-top: 54px; }
}

.sg-progress__title {
    display: block;
    font-family: var(--alt-font-family);
    /* `text-small`: a fixed 13px/22px. The rem values here resolved against
       this project's 14px root and came out 11.4px/15.9px - a fifth smaller
       than production - and `0.02em` of tracking that Litho does not have. */
    font-size: 13px;
    line-height: 22px;
    font-weight: 500;
    text-transform: uppercase;
    letter-spacing: normal;
    color: #232323;
    /* 22 + 8 + the 8px track = the 30px Litho reserves above its own
       absolutely-positioned title, so the tracks land on the same y. */
    margin-bottom: 8px;
}

.sg-progress__track {
    position: relative;
    width: 100%;
    height: 8px;
    /* NO trough. Litho's `.progress` computes to a transparent background with
       square corners - only the filled bar is visible, over whatever the
       section behind it is. We were drawing an 8% black rail production does
       not have, and rounding it. */
    border-radius: 0;
    background-color: transparent;
    overflow: visible;
}

.sg-progress__bar {
    position: relative;
    height: 100%;
    border-radius: 6px;
    background: var(--theme-color);
    /* Set to 0 inline by the template; JS (initProgress) animates it up to
       data-target once the track scrolls into view. */
    transition: width 1s ease;
    /* No min-width. Litho's `.progress-bar` has none: the percentage label is
       absolutely positioned ABOVE the track, not inside it, so a narrow bar has
       nothing to hold open. A floor here made every low percentage read high. */
}

.sg-progress__pct {
    position: absolute;
    /* `-30px`, not `-1.6em`. `em` on an absolutely-positioned offset resolves
       against the element's OWN font-size, so 1.6em of a 0.8125rem (11.4px at
       this project's 14px root) label lifted it a mere 18px - it sat on the
       8px track. Litho pins it at a flat `top: -30px`, and with the 13px/22px
       box below that puts its bottom edge exactly 8px clear of the track,
       sharing the line with `.sg-progress__title` on the left. */
    top: -30px;
    right: 0;
    font-family: var(--alt-font-family);
    /* Matched to the title's fixed 13px/22px rather than left to inherit, so
       both halves of that line sit on one baseline. */
    font-size: 13px;
    line-height: 22px;
    font-weight: 500;
    color: var(--text-color, #232323);
}

@media (prefers-reduced-motion: reduce) {
    .sg-progress__bar { transition: none; }
}

/* ── Language switcher + footer credit ──────────────────────────────────────
   Both close gaps that scripts/compare_with_production.py found against the
   live site: the switcher showed bare locale codes ("el") where production
   shows a flag and the language's own name, and the footer carried no
   copyright/credit line at all. */
.sg-langs {
  position: relative;
  display: flex;
  align-items: center;
  margin-left: 0.5rem;
}

/* Collapsed trigger: flag + code + caret, matching production's dropdown
   toggle. Sized off the header's 72px row so it sits on the single line. */
.sg-langs__toggle {
  display: inline-flex;
  align-items: center;
  gap: 0.375rem;
  padding: 0 12px;
  height: 72px;
  background: none;
  border: 0;
  cursor: pointer;
  font-family: var(--alt-font-family);
  font-size: 16px;
  font-weight: 500;
  /* Matches .sg-nav__link — the nav sits over a dark hero, so the black
     --text-color the inline version used was invisible there. */
  color: #ffffff;
  line-height: 72px;
}

.sg-langs__code {
  letter-spacing: 0.5px;
}

.sg-langs__caret {
  font-size: 11px;
  transition: transform 0.25s ease;
}

.sg-langs__flag {
  width: 20px;
  height: 15px;
  flex: 0 0 auto;
  border-radius: 2px;
  object-fit: cover;
}

/* Same hover/focus-within mechanism as .sg-nav__submenu — no JS. */
.sg-langs__menu {
  position: absolute;
  top: 100%;
  right: 0;
  min-width: 180px;
  padding: 10px 0;
  background-color: #ffffff;
  box-shadow: 0 0 20px rgba(0, 0, 0, 0.08);
  opacity: 0;
  visibility: hidden;
  transform: translateY(6px);
  transition: opacity 0.25s ease, transform 0.25s ease, visibility 0.25s;
  z-index: 10;
}

.sg-langs:hover .sg-langs__menu,
.sg-langs:focus-within .sg-langs__menu {
  opacity: 1;
  visibility: visible;
  transform: translateY(0);
}

.sg-langs:hover .sg-langs__caret,
.sg-langs:focus-within .sg-langs__caret {
  transform: rotate(180deg);
}

.sg-langs__item {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  padding: 8px 20px;
  font-family: var(--alt-font-family);
  font-size: 16px;
  color: var(--text-color, #000);
  text-decoration: none;
  white-space: nowrap;
}

.sg-langs__item:hover,
.sg-langs__item:focus-visible {
  color: var(--theme-color, #00aeff);
}

.sg-langs__item--current {
  color: var(--theme-color, #00aeff);
  font-weight: 600;
}

/* The two-letter code used to be visually hidden here, because the switcher
   sat in the header row and there was no width for it on a phone. It now sits
   inside the collapsing panel as a full-width row, so there is room - and
   production's own mobile switcher shows a language label too. Hiding it left
   a bare flag with a caret and nothing to read. */
@media (max-width: 767px) {
  .sg-langs__toggle {
    padding-inline: 8px;
  }
}

.sg-footer__copyright {
  margin: 0;
  font-size: clamp(0.8rem, 0.78rem + 0.1vw, 0.875rem);
  line-height: 1.6;
  opacity: 0.85;
}

.sg-footer__credit a {
  color: inherit;
  text-decoration: underline;
  text-underline-offset: 2px;
}

.sg-footer__credit a:hover,
.sg-footer__credit a:focus-visible {
  color: var(--theme-color, #00aeff);
}

/* On a phone the credit reads better on its own line, as it does on the live
   site (Bootstrap's d-block d-md-inline pair). */
@media (max-width: 640px) {
  .sg-footer__credit {
    display: block;
  }
}


/* ==========================================================================
   Brilliance component tokens -> the Synergas palette
   --------------------------------------------------------------------------
   mysite/static/css/brilliance.css supplies the geometry for every `b-*`
   component and paints it from its own brand tokens, which its own comments
   describe as "fallback defaults ... overridden from BrillianceBrandSettings".
   This project has no such settings model, so the remap lives here — and it is
   expressed in terms of the SAME custom properties BrandingSettings already
   drives (--theme-color, --alt-theme-color, --theme-color-5), so an editor
   changing the palette in Settings -> Branding restyles these components too,
   rather than being silently overridden by another brand's navy.

   --navy carries 172 of the uses in that file (text, surfaces, borders), so it
   is mapped to the dominant dark of the live site rather than to a blue:
   Litho's `text-extra-dark-gray` is #232323 and production uses it for
   essentially all body copy and headings.

   Loaded after brilliance.css, so these win. Values resolve at use time, so it
   does not matter that base.html's inline :root block sets --theme-color later.
   ========================================================================== */
:root {
  --navy: var(--theme-color-5, #232323);
  --cyan: var(--theme-color, #00aeff);
  --sky: color-mix(in srgb, var(--theme-color, #00aeff) 55%, #ffffff);
  --red: var(--alt-theme-color, #da0f32);
  /* Synergas has no green in its palette; pointing it at the theme blue keeps
     any component that reaches for --green on-brand instead of off-brand. */
  --green: var(--theme-color, #00aeff);
  --deep-navy: #0b0b0b;
}

/* The role-card background is the one asset brilliance.css references by
   relative path (../img/brilliance/images/role-card-bg.jpg). That image was not
   part of the port, so the rule would request a 404 on every page that uses the
   component; neutralised until a Synergas equivalent exists. */
.b-role-card,
.b-rolecard {
  background-image: none;
}

/* --------------------------------------------------------------------------
   Overlap repairs, where brilliance.css out-specifies this file
   --------------------------------------------------------------------------
   57 of the 66 `b-*` classes styled here are also styled by brilliance.css:
   this file is a deliberate re-skin of those components to match Litho. Loading
   brilliance.css underneath fills the 419 classes this file never covered
   (member and news cards, sliders, dots, page wrappers, the card variants), and
   because this file loads LAST it still wins at equal specificity. It only
   loses where brilliance's selector is more specific, which needs repairing
   here rather than by dropping the component sheet.
   -------------------------------------------------------------------------- */

/* brilliance frames the glyph as a 60px BLACK tile and so paints the icon white
   (`.b-card__glyph i { color: var(--white); font-size: 26px }`). This file
   frames the glyph as a 130px WHITE circle to match Litho's
   `feature-box-icon`, so that rule left the icon white-on-white and invisible.

   The font-size half matters just as much and was missed: brilliance's
   `.b-card__glyph i` is (0,2,0), so it beat the 50px INHERITED from
   `.b-card__glyph`, and the icons drew at 26px against production's 50px
   (`icon-large`). Restating it here at the same specificity, later in the
   cascade, is what fixes it.

   An earlier version of this comment said production puts `text-fast-blue`
   (#0038e3) on these icons. It does not — that class appears nowhere in any
   rendered page. The icons in the circular feature boxes carry
   `text-theme--not-important`, which is not a Litho class at all, so they
   inherit and render BLACK; see `.b-card--outline` below. Other icons on the
   site do use a real `text-theme`, which is why the default here stays the
   theme colour. */
.b-card__glyph i,
.b-card__glyph svg,
.b-card__glyph .cx-icon {
  color: var(--theme-color, #00aeff);
  fill: currentColor;
  /* .icon-large */
  font-size: 50px;
  line-height: 1;
}

/* The circular feature boxes (legacy `icon_feature_box`) are the instances
   whose icons carry the typo'd `text-theme--not-important`, so production
   renders them black rather than cyan — measured rgb(0, 0, 0) on the live home
   page. Reproduced because the brief is 1:1 with production; it looks like an
   unintended regression there, and reverting it is a one-line change if the
   branded colour is preferred. */
.b-card--outline .b-card__glyph i,
.b-card--outline .b-card__glyph svg,
.b-card--outline .b-card__glyph .cx-icon {
  color: inherit;
}

/* Litho's `.feature-box` has no surface at all, so no corner to round.

   The single-class `.b-card--outline` rule below is NOT enough on its own:
   brilliance also ships `.b-card--outline.b-card--h-auto:not(.b-card--rgb)
   :not(.b-card--bar) { border-radius: 20px }` for the "auto height" outline
   card variant (used by the two-up media+text bands, e.g. the "Pioneer in
   Cyprus Energy" card), and that selector's four classes outweigh this
   file's one - it kept its 20px rounded corners regardless of load order.
   Restated here at matching specificity so the cascade's tie-break (later
   sheet wins) actually applies. */
.b-card--outline {
  border-radius: 0;
}
.b-card--outline.b-card--h-auto:not(.b-card--rgb):not(.b-card--bar) {
  border-radius: 0;
}

/* brilliance sets small labels — card kickers, member roles, CTA-band eyebrows —
   in Roboto Mono via `--mono`, 33 uses. Synergas has no monospace in its type
   system; the live site sets these in the alt font. Remapping the TOKEN fixes
   every one of those uses at once, rather than chasing each component. */
:root {
  --mono: var(--alt-font-family), "Helvetica Neue", sans-serif;
}

/* --------------------------------------------------------------------------
   Section rhythm — production's `section { padding: 40px 0 }`
   --------------------------------------------------------------------------
   The legacy sections got their vertical padding from a bare `section` rule.
   Nothing reproduced it here, so the migrated pages had no vertical rhythm at
   all: computed padding 0 on 51 of 51 sections, and the pages read as one dense
   wall of text.

   40px, NOT the 130px this first shipped as. That number came from the vendored
   "Litho Theme/html/css/style.css" in this repo, which is the stock theme —
   production's own served stylesheet overrides it:

       section                    { padding: 40px 0; overflow: hidden; }
       section.half-section       { padding: 80px 0; }
       section.big-section        { padding: 160px 0; }
       section.extra-big-section  { padding: 200px 0; }

   (verified by fetching https://synergas.com.cy/static/css/style.55e0a0c57433.css)
   At 130px every page ran ~60% taller than production, with ~260px voids
   between sections. The lesson is to measure the DEPLOYED stylesheet, not the
   theme the site was built from.

   The three -section modifiers are not reproduced: the transform carries no
   per-section legacy class, and the pages audited use the 40px base throughout.
   A section needing 160px will have to carry that intent through block data.

   This has to be CSS, not block data. `_style_open.html` renders
   `pt-{{ pt|default:'0' }}`, and Django's `default` filter fires on the empty
   string, so the spacing field's "Default" choice still emits `pt-0` — no value
   means "no padding utility".

   `section > .block-styled` targets ONLY the section-level wrapper: the same
   class is on every container, column, row and card, and those must stay at 0
   or they would double up. At (0,2,1) it also out-specifies the `pt-0`/`pb-0`
   utilities on the element itself.
   -------------------------------------------------------------------------- */
section > .block-styled {
  padding-block: 40px;
}

/* A section painting a full-bleed background image is a hero: it sizes itself
   from the image and the copy inside it, and production's heroes are `p-0`.
   NOTE the selector shape. The parallax/background wrapper is `main`'s DIRECT
   CHILD DIV, ABOVE the <section> — not the section's own child — so the obvious
   `section > .block-styled.b-bg-parallax` never matched anything and heroes kept
   the full section padding. An audit pass caught that ("hero exception FAILED")
   and it took reading the real nesting to see why. */
main > .b-bg-parallax section > .block-styled,
main > .b-bg-fit-width section > .block-styled {
  padding-block: 0;
}

/* --------------------------------------------------------------------------
   Media CTA on a light section
   --------------------------------------------------------------------------
   brilliance.css hard-codes `.b-mediacta__title { color: #fff }` and
   `.b-mediacta__text { color: rgba(255,255,255,.78) }` because in the reference
   app that block always sits on a dark panel. This project emits it on white
   sections too, and the result was a completely INVISIBLE section — the audit
   caught "Our Purpose" on /about-us/ as white-on-white with only a stray rule
   visible. Default to the body colours here and let the dark-section wrapper
   opt back into white, which is the same shape as the glyph repair above.
   -------------------------------------------------------------------------- */
.b-mediacta__title {
  color: var(--text-color, #232323);
}

.b-mediacta__text {
  color: color-mix(in srgb, var(--text-color, #232323) 78%, transparent);
}

.dark-section .b-mediacta__title,
.b-bg-parallax .b-mediacta__title,
.b-ctaband--dark .b-mediacta__title {
  color: #ffffff;
}

.dark-section .b-mediacta__text,
.b-bg-parallax .b-mediacta__text,
.b-ctaband--dark .b-mediacta__text {
  color: rgba(255, 255, 255, 0.78);
}

/* --------------------------------------------------------------------------
   A column carrying a background colour reads as a contained panel
   --------------------------------------------------------------------------
   `cv/blocks/layout/templates/column.html` puts `.b-col-card` on the inner
   wrapper to "paint the column's background on the inner card (padding +
   rounded + shadow)", but the class is defined NOWHERE in the project — the
   template is the only occurrence. So a coloured column got its colour with
   zero padding and only content height, which is wrong for
   `three_column_hero`'s middle panel: Litho's `.bg-theme` columns are padded
   (`padding-5-half-rem-lr` / `lg-padding-3-rem-lr`) and fill the row height.

   Lives here rather than in mysite/static/css/input.css — the audit's spec
   suggested input.css, but that file is a Tailwind source compiled into
   mysite.css and this checkout has no package.json and no tailwindcss binary,
   so there is no build step to run. synergas.css loads last and needs none.
   -------------------------------------------------------------------------- */
.b-col-card {
  padding: 3rem;
  height: 100%;
}

@media (max-width: 991px) {
  .b-col-card {
    padding: 2rem;
  }
}

/* --------------------------------------------------------------------------
   Nav link colour must follow what is BEHIND the nav
   --------------------------------------------------------------------------
   `.sg-nav__link` was hardcoded white, which is right on the pages whose first
   section is a full-bleed dark hero — the nav is fixed and transparent, so it
   sits over the photo. It is wrong on every page without one: on
   /about-us/business-profile/ the links were white on white at a contrast ratio
   of 1.00, so four of the five nav items were invisible. Production shows dark
   text on its white header there.

   Default to the branding's own `--nav-text-color`, then go white only when the
   page's FIRST section is a dark/parallax hero. `:has()` is doing real work
   here: the hero is a DESCENDANT OF A SIBLING of the nav, so there is no
   ancestor to hang this on and no way to express it without looking forward.
   -------------------------------------------------------------------------- */
.sg-nav__link,
.sg-langs__toggle,
.sg-langs__code {
  color: var(--nav-text-color, #0f172a);
}

/* The hero classes land on MAIN'S FIRST CHILD DIV, above the <section> — not
   on `section > .block-styled`. The earlier selector asked for them on the
   inner element, so it matched nothing and every hero page kept the dark
   default: on /our-products/ four of five nav links were navy on a near-black
   photo. Same mistake the hero padding rule made; measured against the real
   DOM this time (`main > div.block-styled.dark-section.b-bg-parallax`). */
/* DESKTOP ONLY. Below 992px the header is an opaque white bar whatever is
   behind it, so white link text there is invisible - it is what made every
   label in the open mobile panel disappear. These selectors are (0,3,2), so
   no mobile rule on `.sg-nav__link` could outrank them; scoping the behaviour
   to the band it belongs to is the fix, not more !important.

   The whole NINE-selector list has to be inside the media query. Wrapping
   only its last three left the first six dangling on a trailing comma, which
   swallowed the `@media` and destroyed the rule outright - every hero page's
   nav links fell back to `--nav-text-color` and went navy on a dark photo. */
@media (min-width: 992px) {
  body:has(main > div:first-child.b-bg-parallax) .sg-nav__link,
  body:has(main > div:first-child.b-bg-parallax) .sg-langs__toggle,
  body:has(main > div:first-child.b-bg-parallax) .sg-langs__code,
  body:has(main > div:first-child.dark-section) .sg-nav__link,
  body:has(main > div:first-child.dark-section) .sg-langs__toggle,
  body:has(main > div:first-child.dark-section) .sg-langs__code,
  body:has(.sg-vhero) .sg-nav__link,
  body:has(.sg-vhero) .sg-langs__toggle,
  body:has(.sg-vhero) .sg-langs__code {
    color: #ffffff;
  }

  /* `.sg-nav__link:hover { color: var(--theme-color) }` further up this file
     never wins here: adding `:hover` to a class selector only adds one to the
     specificity's B column, so its (0,2,0) is still below this block's
     (0,3,2). The extra `:hover`/`:focus-visible` pseudo-class below pushes
     these selectors to (0,4,2), which is enough to win without `!important`. */
  body:has(main > div:first-child.b-bg-parallax) .sg-nav__link:hover,
  body:has(main > div:first-child.b-bg-parallax) .sg-nav__link:focus-visible,
  body:has(main > div:first-child.dark-section) .sg-nav__link:hover,
  body:has(main > div:first-child.dark-section) .sg-nav__link:focus-visible,
  body:has(.sg-vhero) .sg-nav__link:hover,
  body:has(.sg-vhero) .sg-nav__link:focus-visible {
    color: var(--theme-color);
  }
}

/* --------------------------------------------------------------------------
   Neutralise brilliance's opinionated card GEOMETRY
   --------------------------------------------------------------------------
   Loading brilliance.css filled 419 previously-unstyled classes, but its card
   is a fixed-size portrait tile — `max-width: 380px` with `aspect-ratio: 38/50`
   — because in the reference app that is the design. Here the cards sit in grid
   cells up to 585px wide, so every one rendered as a 380x500 box with 205px of
   dead space beside it, and the aspect ratio forced `.b-card__title` down by
   ~177px of margin. Production's equivalent cards are content-height and fill
   their cell.

   Geometry only — brilliance keeps supplying the surface, border and shadow.
   -------------------------------------------------------------------------- */
.b-card {
  max-width: none;
  aspect-ratio: auto;
  height: auto;
}

/* The title drop is driven by a custom property, so the fix is to neutralise
   THAT rather than fight the margin: brilliance's
   `.b-card:not(.b-card--h-auto):not(.b-card--inline) .b-card__title` is (0,3,0)
   and a plain `.b-card__title { margin-top: 0 }` at (0,1,0) never won. The 64%
   drop exists to push the title down inside the fixed 38/50 portrait tile; with
   the aspect ratio gone it was just ~177px of dead space between the icon and
   its title. Same selector shape, loaded later, so it wins on order. */
.b-card:not(.b-card--h-auto):not(.b-card--inline) {
  --card-stack-drop: 0%;
}

/* --------------------------------------------------------------------------
   Image cards: the title has to be light like the rest of the card
   --------------------------------------------------------------------------
   brilliance sets `.b-card--img { color: var(--white) }` and whitens
   `.b-card--img .b-card__body` explicitly, but has no rule for the TITLE, so
   this file's `.b-card__title { color: #232323 }` won and the titles rendered
   dark-on-dark over the photo — measured at a 1.52:1 contrast ratio, i.e.
   invisible, on 13 cards across the product pages while the kicker beside them
   was correctly white. Third instance of the same cascade clash as the
   white-on-white media CTA, so it is worth stating the rule once: whenever
   brilliance colours a CONTAINER, this file must not colour its children
   unconditionally.
   -------------------------------------------------------------------------- */
.b-card--img .b-card__title,
.b-card--img .b-card__title--h3 {
  color: #ffffff;
}

/* --------------------------------------------------------------------------
   Sliders show ONE slide, as every legacy slider did
   --------------------------------------------------------------------------
   brilliance's carousel is a multi-card rail: its slides are
   `w-[min(380px,80vw)]` in an `overflow-x-auto snap-x` track, so 1.5 of them
   showed at 1440px and the rest sat clipped off-canvas. Every slider this site
   migrates from was Swiper at `slidesPerView: 1` — verified in the legacy
   templates: ImageSliderFeaturesBlock, TextSliderBlockStyle04,
   VideoBackgroundHeroBlock all hardcode 1, and ContentSliderBlock defaults to
   1 — and production's testimonial slider likewise shows a single centred quote.

   The track already carries `snap-x snap-mandatory`, so widening each slide to
   the full track is all that is needed: one slide per view, the arrows page
   through them, and it degrades to a swipe on touch.

   Targeted by DATA ATTRIBUTE, not by class: the slide's own classes are
   Tailwind arbitrary values (`w-[min(380px,80vw)]`) which are not a stable
   hook, whereas `data-b-slider-track` is what the block template and its JS
   both key on. (An audit pass read that attribute as a `.b-slider-track` class
   and concluded the CSS was missing — it is not; the track is styled by inline
   utilities.)
   -------------------------------------------------------------------------- */
[data-b-slider-track] > * {
  width: 100%;
  max-width: 100%;
}

/* The coverflow/"selectable" variant is a deliberate centre-focus rail — it
   centres the active card with its neighbours peeking — so it keeps its own
   card width. */
[data-b-coverflow] [data-b-slider-track] > * {
  width: auto;
  max-width: none;
}

/* --------------------------------------------------------------------------
   The heading scale — Litho's, from production's own stylesheet
   --------------------------------------------------------------------------
   `.b-h1` .. `.b-h6` had NO rule in either stylesheet, so every heading tier
   rendered at the same ~30px and the two tiers production clearly distinguishes
   (a 39.9px section title vs a 48px page title) looked identical. The audit
   measured production's h4 at 39.9px, which is 2.85rem at this project's 14px
   root — exactly the value below, so the scale is production's rather than
   approximated.

   From https://synergas.com.cy/static/css/style.55e0a0c57433.css:
     h1 5rem/5rem   h2 3.9rem/4.2rem   h3 3.4rem/3.9rem
     h4 2.85rem/3.4rem   h5 2.30rem/3rem   h6 1.80rem/2.5rem
     h1..h6 { margin: 0 0 25px; letter-spacing: -0.5px; font-weight: inherit }

   `font-weight: inherit` is deliberate and load-bearing in Litho: a heading is
   NOT bold unless a class makes it so, which is why the legacy markup carries
   `font-weight-500`/`-600` everywhere. Reproducing it here keeps migrated
   headings at the same weight as the live ones.

   Sizes are in rem and the root already steps down at Litho's breakpoints
   (14/13/12/11px), so the whole scale is responsive with no extra queries —
   which is the mechanism Litho relies on.
   -------------------------------------------------------------------------- */
.b-h1, .b-h2, .b-h3, .b-h4, .b-h5, .b-h6 {
  margin: 0 0 25px;
  padding: 0;
  /* Litho's defaults, but as var() FALLBACKS so a per-block Advanced styling
     choice can win. `--fw` / `--ls` are set inline on the .block-styled wrapper
     from the block's Text settings; with no choice made, the fallback is what
     Litho ships and the heading is indistinguishable from before.

     `inherit` as a var() fallback is legal and keeps the original meaning: a
     heading has no weight of its own, so it takes the surrounding weight unless
     the editor says otherwise. That is the whole reason the live site's markup
     carries `font-weight-600` on individual headings. */
  letter-spacing: var(--ls, -0.5px);
  font-weight: var(--fw, inherit);
  font-family: var(--alt-font-family), sans-serif;
}

.b-h1 { font-size: 5rem;    line-height: 5rem; }
.b-h2 { font-size: 3.9rem;  line-height: 4.2rem; }
.b-h3 { font-size: 3.4rem;  line-height: 3.9rem; }
.b-h4 { font-size: 2.85rem; line-height: 3.4rem; }
.b-h5 { font-size: 2.30rem; line-height: 3rem; }
.b-h6 { font-size: 1.80rem; line-height: 2.5rem; }

/* Card and member titles are component text, not page headings — they keep
   whatever their component sets, or the scale above would blow them up. */
.b-card__title,
.b-card__title--h3,
.b-member__name,
.b-ncard__title,
.b-numlist__label {
  font-size: inherit;
  line-height: inherit;
  letter-spacing: normal;
  margin: 0;
}

/* --------------------------------------------------------------------------
   Section eyebrows — the small uppercase label above a section title
   --------------------------------------------------------------------------
   Every legacy block that pairs a subtitle with a heading renders that subtitle
   as an uppercase, letter-spaced, half-opacity label. From the templates:

     CtaTitleBlock.html      <span class="alt-font font-weight-500
                               text-extra-dark-gray text-uppercase
                               letter-spacing-2px opacity-5 d-block
                               margin-30px-bottom">
     ProcessStepStyle02, BusinessFeaturesBlock: same treatment.

   and from production's stylesheet: `.letter-spacing-2px { letter-spacing: 2px }`,
   `.opacity-5 { opacity: .5 }`, `.font-weight-500 { font-weight: 500 }`. The
   span carries no size class, so it inherits the 15px body size.

   Ours rendered at 30px in sentence case, which the audit flagged on page after
   page ("local 30px sentence case vs prod small uppercase"). `text-transform`
   and `letter-spacing` are not expressible through the block's style fields —
   `TextSettings` has only align/size/colour/family — so this has to be CSS.

   Scoped to subheadings that are NOT in a parallax hero: in that hero Litho
   inverts the roles, making the SUBHEADING the big 3.9rem focus text (see the
   rule above), so this must not touch it.
   -------------------------------------------------------------------------- */
/* Also excludes the `--sm` variant. That is Litho's `text-medium
   text-uppercase` label (14px/20px, weight 400, no tracking, full opacity),
   not a section eyebrow, and this rule's specificity — (0,4,0), because
   `:not()` contributes its argument's — silently outranked the variant's own
   rule further down the file. */
.b-page .b-subheading:not(.b-bg-parallax .b-subheading):not(.b-subheading--sm) {
  font-family: var(--alt-font-family), sans-serif;
  font-size: 15px;
  line-height: 1.6;
  font-weight: 500;
  text-transform: uppercase;
  letter-spacing: 2px;
  opacity: 0.5;
  margin: 0 0 30px;
}

.block-styled.b-bg-parallax .b-subheading {
  text-transform: none;
  letter-spacing: -1px;
  opacity: 1;
}

/* --------------------------------------------------------------------------
   Standalone images fill their column, as `img-fluid w-100` does
   --------------------------------------------------------------------------
   `.b-img` was `max-width: 100%` only, so an image narrower than its column
   rendered at its intrinsic pixel size, flush left. The bulk-tanks dimension
   drawings came out 400x176 and 230x243 against production's 1110x488 and
   540x571, and that whole section measured 606px tall locally against 1615px
   on production.

   Production's own markup for those images is
   `class="img-fluid w-100 h-auto"` — width 100%, height auto — so filling the
   column is the legacy behaviour, not a liberty. `height: auto` keeps the
   aspect ratio, and the block's own `aspect`/`size` fields still win where an
   editor sets them because those emit inline styles.
   -------------------------------------------------------------------------- */
.b-figure .b-img,
.b-figure > img {
  width: 100%;
  height: auto;
}

/* --------------------------------------------------------------------------
   The page-title hero band
   --------------------------------------------------------------------------
   Litho's page-title hero (tilt_effect_hero) carries its own generous vertical
   spacing in the markup — `mt-5 pt-5` above and `margin-7-rem-bottom` below,
   i.e. 6rem and 7rem — on top of whatever the section rule gives. Nothing
   carried that, so a hero with no background image collapsed to text height:
   /about-us/business-profile/ was 153px tall in total with its title colliding
   with the fixed 72px header, against production's ~900px band.

   Applies to the FIRST section of a page only, and only when it is not a
   full-bleed hero: a parallax hero sizes itself from its image and already
   reserves the space (production's own are `p-0`/`full-screen` for that
   reason). Production's business-profile background is a RELATIVE path
   (`images/our-team-bg2.jpg`) that 404s on production too, so there is no image
   to import for that page — the band is the spacing alone, which is exactly
   what this reproduces.
   -------------------------------------------------------------------------- */
main > div:first-child:not(.b-bg-parallax):not(.b-bg-fit-width) section > .block-styled:has(.b-h1) {
  padding-block: 6rem 7rem;
}

/* --------------------------------------------------------------------------
   Buttons, tables and forms — production's own measurements
   --------------------------------------------------------------------------
   All three sets of values come from production's served stylesheet, so they
   are the live site's rather than an approximation:

     .btn.btn-medium      { font-size: 12px; padding: 10px 26px }
     .btn.btn-round-edge  { border-radius: 4px }
     table th, table td   { border: none; border-bottom: 1px solid #e4e4e4;
                            padding-top: 20px; padding-bottom: 20px }
     .medium-input, .medium-textarea { padding: 15px 20px; font-size: 14px }
   -------------------------------------------------------------------------- */

/* brilliance sizes its buttons from its own design tokens, on a different
   scale entirely, and its variant classes are written `.b-cta.b-cta--dark`
   upstream — (0,2,0) — so anything meant to beat them has to be doubled up.
   Only the properties that are the SAME at every size belong here; the size
   itself stays on the `--sm/--md/--lg` ladder above.

   This rule used to also set `font-size: 12px; padding: 10px 26px;
   border-radius: 4px; letter-spacing: 0.5px` on every variant, on the stated
   grounds that "production's is a compact 12px pill-ish button with a 4px
   radius". Measured on the live site, that describes only the JCC Portal
   button in the header (117x31, 10px, 6px 16px, radius 4px), which has its
   own rule further up. The CTA band's button — Litho `btn btn-large bg-theme`
   — computes 169x48, `padding: 12px 32px`, `font-size: 13px`,
   `letter-spacing: normal` and `border-radius: 0`. Flattening the ladder onto
   the header pill's metrics made every button on the site 12px too small with
   rounded corners, which is a good part of "the buttons look broken,
   everything for some reason seems rounded". */
.b-cta,
.b-cta.b-cta--lg,
.b-cta.b-cta--sm,
.b-cta.b-cta--md,
.b-cta.b-cta--dark,
.b-cta.b-cta--light,
.b-cta.b-cta--theme,
.b-cta.b-cta--outline {
  line-height: 1.5;
  border-radius: 0;
  letter-spacing: normal;
  height: auto;
}

/* The size ladder, doubled up for the same specificity reason. */
.b-cta.b-cta--sm { font-size: 11px; padding: 8px 22px; }
.b-cta.b-cta--md { font-size: 12px; padding: 10px 26px; }
.b-cta.b-cta--lg { font-size: 13px; padding: 12px 32px; }

/* Legacy tables centre every cell and separate rows with a hairline; the
   caption sits BELOW the table. Ours was left-aligned with a transparent
   header and the caption above. */
.b-table th,
.b-table td,
.b-table__cell {
  text-align: center;
  border: none;
  border-bottom: 1px solid #e4e4e4;
  padding-top: 20px;
  padding-bottom: 20px;
}

.b-table thead th {
  background-color: #808080;
  color: #ffffff;
  font-family: var(--alt-font-family), sans-serif;
}

.b-table caption {
  caption-side: bottom;
  text-align: center;
  padding-top: 20px;
}

/* Form fields: production's `medium-input` metrics, and two columns as its
   layout has — name|address, email|phone, message paired like any other
   field (production pairs it with the next field in sort_order, e.g. its own
   consent checkbox - it is NOT forced full-width just for being a textarea).
   Consent itself stays a dedicated full-width row before the submit here
   (see .b-consent below) - the block renders it last regardless of the
   backing form's field order, by design. The section title is hidden because
   production's form carries no heading at all. */
/* The whole head, not just the title: production's form section carries no
   heading AND no eyebrow above the fields. Hiding only `__title` left the
   "SEND US A MESSAGE" eyebrow behind. */
.b-formsec__head,
.b-formsec__title {
  display: none;
}

.b-field input,
.b-field textarea,
.b-field select {
  padding: 15px 20px;
  font-size: 14px;
  border-radius: 4px;
  /* Production's inputs carry `border-color-theme`, not a fixed grey - the
     border above (`.b-field input` earlier in this file) was measured before
     that class was noticed and left #e4e4e4. */
  border-color: var(--theme-color);
}

/* brilliance.css only floors this at `min-height: 140px` (resize: vertical) -
   fine for its own placeholder textarea, but Django's default Textarea widget
   attrs (`rows="10"`) drive the actual intrinsic size, which a min-height
   cannot cap, and it renders over 300px tall. An explicit height overrides
   that regardless of the `rows` attribute; `resize: vertical` (inherited
   above) still lets a visitor drag it taller. */
.b-field textarea {
  height: 140px;
}

/* There is no `.b-fields` wrapper — the `.b-field` blocks are children of
   `.b-formcard` itself, alongside the status line, the consent row and the
   submit, so the card is the grid and everything that is not a field spans it. */
.b-formcard {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 0 25px;
}

.b-formcard > *:not(.b-field),
.b-field--lg,
.b-consent,
.b-formstatus {
  grid-column: 1 / -1;
}

/* An empty status bar was rendering as a green strip above every form. */
.b-formstatus:empty,
.b-formcard__status:empty {
  display: none;
}

@media (max-width: 767px) {
  .b-formcard {
    grid-template-columns: 1fr;
  }
}

/* Production's forms are placeholder-only, so the field labels are visually
   hidden — but KEPT in the DOM and still associated by `for`/`id`, because a
   placeholder is not an accessible label. synergas.js copies each label's text
   into its input's placeholder (initFormPlaceholders). Excludes `.b-check`:
   a checkbox's label IS its visible text - production shows it beside the
   box, same as the dedicated consent row below. */
.b-field > label:not(.b-check) {
  position: absolute;
  width: 1px;
  height: 1px;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
}

/* brilliance.css's `.b-field label:not(.b-cvdrop) { display: block; ... }`
   (a descendant selector, not just `>`) also matches a checkbox's own
   `.b-check` label once it's wrapped in a `.b-field` (see the template
   comment above the checkbox branch) - equal-or-higher specificity than
   `.b-check`'s own `display: flex; gap: 10px`, and it wins there because it
   loads later. That collapses the flex layout, so the checkbox and its text
   render on top of each other instead of side by side. Re-asserted here,
   later still, at matching specificity. */
.b-field label.b-check {
  display: flex;
  align-items: flex-start;
  gap: 10px;
}

/* The consent checkbox keeps its visible text — production shows it beside the
   box, as a checkbox has nowhere to put a placeholder. */
.b-consent .b-check,
.b-consent .b-check__text,
.b-field .b-check__text {
  position: static;
  width: auto;
  height: auto;
  clip-path: none;
  white-space: normal;
}

/* --------------------------------------------------------------------------
   The form submit — measured, after two wrong descriptions of it
   --------------------------------------------------------------------------
   Production's submit is `btn btn-extra-large btn-expand-ltr`, and its own
   stylesheet gives:

     .btn                  { display: inline-block; width: auto;
                             border-radius: 0; text-transform: uppercase;
                             border: 2px solid transparent }
     .btn.btn-extra-large  { font-size: 14px; padding: 16px 40px }

   So it is a SQUARE, AUTO-WIDTH, uppercase button. It is not the rounded pill
   an earlier pass implemented here "from Litho" (Litho's `.btn` radius is 0,
   and the submit carries no `btn-round-edge`), and it is not the "circular
   cyan SUBMIT" the audit described. Both were wrong, which is why this waited
   for a measurement instead of a third guess.

   Overrides the earlier `.b-formcard .b-cta` pill, so it needs to match that
   selector's specificity and come later — it does both.
   -------------------------------------------------------------------------- */
.b-formcard .b-cta,
.b-formsec .b-cta,
.b-formcard button[type="submit"],
.b-formsec button[type="submit"] {
  display: inline-block;
  width: auto;
  /* Production's submit sits in a `.col.text-center.text-md-end` - centred
     under the fields on mobile, pushed to the right of the grid from md up. */
  justify-self: center;
  font-size: 14px;
  padding: 16px 40px;
  border-radius: 0;
  text-transform: uppercase;
}

@media (min-width: 768px) {
  .b-formcard .b-cta,
  .b-formsec .b-cta,
  .b-formcard button[type="submit"],
  .b-formsec button[type="submit"] {
    justify-self: end;
  }
}

/* --------------------------------------------------------------------------
   Masked heading — image-filled letterforms
   --------------------------------------------------------------------------
   Production's /about-us/our-team/ hero renders "together" as text whose
   letterforms are filled with a staff photo, from `tilt_effect_hero`'s
   `text_mask_image`. Its own values:

     .text-extra-big { font-size: 230px; line-height: 230px }
     .image-mask     { -webkit-text-fill-color: transparent;
                       -webkit-background-clip: text }
     plus `cover-background` (background-size: cover, centred) and
     `letter-spacing-minus-5px`, dropping to -1px below the sm breakpoint.

   The clip is unprefixed FIRST and prefixed second so it works in browsers
   that support either. Without the mask class the text stays a normal heading,
   so a missing image degrades to readable rather than invisible.
   -------------------------------------------------------------------------- */
.sg-masktext {
  font-family: var(--alt-font-family), sans-serif;
  /* 230px is production's value, but its hero sits in a `container-fluid` and
     ours in the 1170px container, so at 230px the word wrapped to two lines.
     `min()` keeps production's size wherever it fits and scales down instead of
     wrapping — 16vw is 230px at the 1440px viewport this was compared at, so
     the two agree there. */
  font-size: min(230px, 16vw);
  line-height: 1;
  white-space: nowrap;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: -5px;
  margin: 0;
}

.sg-masktext--masked {
  background-position: center;
  background-size: cover;
  background-repeat: no-repeat;
  background-clip: text;
  -webkit-background-clip: text;
  color: transparent;
  -webkit-text-fill-color: transparent;
}

@media (max-width: 767px) {
  .sg-masktext { letter-spacing: -1px; }
}

/* --------------------------------------------------------------------------
   Corner radii — Litho's scale, not brilliance's
   --------------------------------------------------------------------------
   brilliance is built on a 20px+ radius language: `--r-md: 20px`, and a
   `--notch: 20px 20px 100px 20px` that rounds one corner enormously. Its
   ImageBlock even DEFAULTS `rounded` to "md", so every migrated image picked up
   20px corners the moment brilliance.css started loading.

   Litho — and so production — is essentially square. Its whole radius
   vocabulary is `.border-radius-6px`, `.border-radius-8px`, `.btn-round-edge`
   at 4px, and `.btn` itself at 0. Nothing on the live site is rounded by 20px,
   and "everything looks rounded" was the single most visible symptom of the
   mismatch.

   Remapped at the TOKEN level, which is where brilliance reads them, so this
   corrects every component at once — cards, images, carousel slides, panels,
   inputs — instead of chasing each class. `--r-md` at 8px also happens to be
   exactly the `border-radius-8px` the legacy Swiper slide and feature-box carry,
   so `rounded: "md"` now means the right thing rather than the wrong one.
   -------------------------------------------------------------------------- */
:root {
  --r-xs: 2px;
  --r-sm: 6px;
  --r-md: 8px;
  --r-pill: 8px;
  --r-pill-sm: 6px;
  --r-input: 4px;
  --r-cta: 4px;
  --r-learn: 4px;
  /* The notched corners are a brilliance signature with no Litho equivalent. */
  --notch: 8px;
  --header-notch: 8px;
  --footer-notch: 8px;
}

/* A standalone image is square on the live site — Litho renders
   `img-fluid w-100 h-auto` with no radius at all. */
.b-figure .b-img,
.b-figure > img {
  border-radius: 0;
}

/* --------------------------------------------------------------------------
   Inline cards — Litho's `feature-box feature-box-left-icon`
   --------------------------------------------------------------------------
   The legacy `icon_feature_box` variant: a bare 40px glyph with 35px of space
   to its right, then a LEFT-ALIGNED title and a description capped at 80%
   width. No disc, no padding, no card chrome.

   This file styles `.b-card__glyph` as a 130px white shadowed circle, which is
   what `feature_grid` renders on production and what `.b-card` is otherwise
   tuned for (`padding: 0 42px`, centred). Every one of those has to be undone
   here rather than weakened there, because the two variants are both `card`
   blocks and only this modifier tells them apart.

   Measured on production's /en/our-services/ (cell content 360px):
     box      flex row, align-items stretch, text-align left, no padding
     icon     wrapper margin 0 35px 10px 0; <i> 40px/40px, margin-bottom 35px
     title    Outfit 15px/30px w500 #232323, margin-bottom 10px, block
     body     15px/30px w400, width 80% (`w-80`), NO bottom margin
              (`last-paragraph-no-margin`)
   -------------------------------------------------------------------------- */
.b-card--inline {
    /* A GRID, not a flex row, because the DOM nests the glyph one level down:
       brilliance's inline branch is `.b-card__head > (glyph + title)` with the
       body as a SIBLING of head. Litho's shape is icon | (title above body),
       so the icon has to span both rows - which it cannot do from inside head.
       `display: contents` on head drops its box and promotes glyph and title
       to grid items of the card, so the icon can take `grid-row: span 2`.
       Left as a flex row, all three sat side by side and the title wrapped to
       77.9px wide against the live 293.9. */
    display: grid;
    grid-template-columns: auto minmax(0, 1fr);
    align-items: start;
    /* `.b-card` carries the disc variant's `padding: 0 42px`; the left-icon
       box has none, and its content starts at the cell's content edge. */
    padding: 0;
    background: none;
    border: 0;
    border-radius: 0;
    box-shadow: none;
    text-align: left;
}

.b-card--inline .b-card__head {
    display: contents;
}

.b-card--inline .b-card__glyph {
    grid-column: 1;
    grid-row: 1 / span 2;
    /* Litho's `align-items: stretch` makes the icon column as tall as the
       content; the glyph itself stays at the top via its own bottom margin. */
    align-self: stretch;
    width: auto;
    height: auto;
    min-width: 0;
    /* 35px right is Litho's `.feature-box-left-icon .feature-box-icon`; the
       10px bottom is the wrapper's own `margin-10px-bottom`. */
    margin: 0 35px 10px 0;
    padding: 0;
    background: none;
    box-shadow: none;
    border: 0;
    border-radius: 0;
    place-items: start;
}

.b-card--inline .b-card__glyph i,
.b-card--inline .b-card__glyph svg,
.b-card--inline .b-card__glyph .cx-icon {
    width: auto;
    height: auto;
    font-size: 40px;              /* .icon-medium */
    line-height: 40px;
    /* `margin-35px-bottom` sits on the <i>, not the wrapper. It is what lifts
       the glyph to the top of the row rather than centring it against a
       multi-line description. */
    margin: 0 0 35px;
}

.b-card--inline .b-card__body,
.b-card--inline .b-card__title {
    grid-column: 2;
    min-width: 0;
    text-align: left;
}

/* The selector has to be THIS long. brilliance ships
   `.b-card--inline .b-card__head .b-card__title { margin-bottom: 0 }` at
   (0,3,0) - it zeroes the gap because in ITS inline layout the title shares a
   row with the icon, so there is nothing below it. Litho's has the description
   underneath and wants 10px. A `.b-card--inline .b-card__title` at (0,2,0)
   silently lost, which showed up as the card measuring 150px against the live
   160px. Same selector shape, loaded later, so it wins on order. */
.b-card--inline .b-card__head .b-card__title,
.b-card--inline .b-card__title {
    display: block;
    margin: 0 0 10px;
    font-family: var(--alt-font-family);
    font-size: 15px;
    line-height: 30px;
    font-weight: 500;
    color: #232323;
}

.b-card--inline .b-card__body {
    /* `w-80`, and it drops to 100% below Litho's lg breakpoint. */
    width: 80%;
    margin: 0;
    font-size: 15px;
    line-height: 30px;
    font-weight: 400;
}

@media (max-width: 1199px) { .b-card--inline .b-card__body { width: 100%; } }
@media (max-width: 991px)  { .b-card--inline .b-card__body { width: 90%; } }
@media (max-width: 767px)  { .b-card--inline .b-card__body { width: 100%; } }

/* --------------------------------------------------------------------------
   Product card — Litho's `interactive-banners-box`
   --------------------------------------------------------------------------
   Photo on TOP, white text panel BELOW: `padding-35px-all text-center`, an
   uppercase `text-medium alt-font font-weight-500` title, the description, and
   a `btn btn-link thin btn-large text-theme` text link. The photo carries the
   circular white arrow (`btn btn-white btn-rounded`) Litho centres on it.

   Replaces mapping these onto brilliance's card, which treats an image as a
   BACKGROUND and printed the copy over the photo in a ~90px tall box.
   -------------------------------------------------------------------------- */
.sg-pcard {
  display: flex;
  flex-direction: column;
  height: 100%;
  background-color: #ffffff;
  overflow: hidden;
  /* Litho: `border-radius-6px box-shadow-large bg-white h-100 overflow-hidden`.
     6px is the card's own radius and is NOT the global --r-* remap, so it is
     written literally — the radius tokens are 8px everywhere else. */
  border-radius: 6px;
  box-shadow: 0 0 30px rgba(0, 0, 0, 0.08);
  transition: all 0.3s ease-in-out;
}

.sg-pcard__media {
  position: relative;
  display: block;
  overflow: hidden;
}

.sg-pcard__media img {
  display: block;
  width: 100%;
  height: auto;
  transform: scale(1);
  transition-duration: 0.3s;
}

.sg-pcard:hover .sg-pcard__media img {
  transform: scale(1.1);
}

/* `opacity-full-dark bg-transparent-theme` — a 0.9-opacity theme wash that
   only appears on hover. */
.sg-pcard__overlay {
  position: absolute;
  inset: 0;
  background-color: var(--theme-color, #00aeff);
  opacity: 0;
  transition: opacity 0.3s ease-in-out;
}

.sg-pcard:hover .sg-pcard__overlay {
  opacity: 0.9;
}

/* `btn btn-white btn-rounded` — 44px, centred, hover-only. */
.sg-pcard__arrow {
  position: absolute;
  top: 50%;
  left: 50%;
  z-index: 1;
  transform: translate(-50%, -50%);
  display: grid;
  place-items: center;
  width: 44px;
  height: 44px;
  border-radius: 50%;
  background-color: #ffffff;
  color: #232323;
  font-size: 14px;
  opacity: 0;
  transition: opacity 0.3s ease-in-out;
}

.sg-pcard:hover .sg-pcard__arrow {
  opacity: 1;
}

.sg-pcard__body {
  padding: 35px;
  text-align: center;
}

.sg-pcard__title {
  display: block;
  margin-bottom: 10px;
  font-family: var(--alt-font-family), sans-serif;
  /* `text-medium` is 14px, not 15 - measured against production at both
     1440 and 390. Ours read 15px at every width. */
  font-size: 14px;
  line-height: 26px;
  font-weight: 500;
  text-transform: uppercase;
  color: #232323;
}

/* Litho's `text-extra-dark-gray-hover`: the title does NOT change colour on
   hover. */
.sg-pcard__title:hover {
  color: #232323;
}

/* The description and the "Learn more" link share one box. The description is
   in flow and sets the height; the link is absolutely positioned over it and
   slides up on hover as the description fades out. */
.sg-pcard__swap {
  position: relative;
  display: block;
}

.sg-pcard__text {
  display: block;
  font-size: 15px;
  line-height: 26px;
  color: #232323;
  opacity: 1;
  transition: all 0.3s ease-in-out;
}

/* Fade the description out only when there is a "Learn more" link to slide up
   into its place. The link renders solely when the card HAS a destination
   (`{% if href and self.link_label %}`), so on a link-less card this rule used to
   fade the description to nothing and leave the body blank on hover — reading as
   the text turning white and disappearing. */
.sg-pcard:hover .sg-pcard__swap:has(.sg-pcard__more) .sg-pcard__text {
  opacity: 0;
}

.sg-pcard__more {
  position: absolute;
  bottom: 0;
  left: 0;
  width: 100%;
  font-family: var(--alt-font-family), sans-serif;
  font-size: 15px;
  line-height: 26px;
  text-transform: uppercase;
  color: var(--theme-color, #00aeff);
  opacity: 0;
  transform: translateY(20px);
  transition: all 0.3s ease-in-out;
}

.sg-pcard:hover .sg-pcard__more,
.sg-pcard__more:focus-visible {
  opacity: 1;
  transform: translateY(0);
}

@media (max-width: 575px) {
  .sg-pcard__body { padding: 25px; }
}

/* --------------------------------------------------------------------------
   Product card, cover mode — Litho's `interactive-banners-style-09`
   --------------------------------------------------------------------------
   The OTHER banner-grid shape, used by the cylinder-size grid. No white panel
   below the photo: the photo IS the card, at its own natural height, with the
   title sitting over its lower-left corner beside a fire icon. On hover the
   title and icon fade out and a theme-tinted panel rises from the bottom
   holding the description and, if linked, a "Learn more" link — production's
   `.interactive-banners-hover-action`, `bottom: -25px -> 0`.

   Mapping the cylinder grid onto the panel-below card (`.sg-pcard` above)
   cropped every tank photo into a fixed 4:3 box and put the spec list in a
   separate strip underneath, a third of production's height: the tall
   portrait tank photos are the whole point of this shape, not incidental. */
.sg-pcard--cover {
  position: relative;
  overflow: hidden;
  border-radius: 0;
  box-shadow: none;
  background: none;
}

.sg-pcard--cover .sg-pcard__photo {
  display: block;
  width: 100%;
  height: auto;
  transition: transform 2s ease-in-out;
}

.sg-pcard--cover:hover .sg-pcard__photo {
  transform: scale(1.4);
  transition-duration: 4s;
}

/* Litho's `opacity-light bg-extra-dark-gray` — a constant, low-opacity dark
   wash so the white title reads against a light photo even before hover. */
.sg-pcard--cover .sg-pcard__scrim {
  position: absolute;
  inset: 0;
  background-color: rgba(35, 35, 35, 0.3);
  pointer-events: none;
}

.sg-pcard--cover .sg-pcard__overlay-body {
  position: absolute;
  inset: 0;
  padding: 40px;
}

.sg-pcard--cover .sg-pcard__cover-title {
  position: relative;
  z-index: 1;
  display: block;
  font-family: var(--alt-font-family), sans-serif;
  font-size: 20px;
  font-weight: 600;
  color: #ffffff;
  transition: opacity 0.3s ease-in-out;
}

/* Production's per-instance override on this page: the title fades rather
   than staying put once the description panel covers it. */
.sg-pcard--cover:hover .sg-pcard__cover-title {
  opacity: 0;
  visibility: hidden;
}

.sg-pcard--cover .sg-pcard__cover-icon {
  position: absolute;
  left: 40px;
  bottom: 40px;
  color: #ffffff;
  font-size: 28px;
  transition: all 0.5s ease-in-out;
}

.sg-pcard--cover:hover .sg-pcard__cover-icon {
  opacity: 0;
  transform: scale(0);
}

.sg-pcard--cover .sg-pcard__cover-reveal {
  position: absolute;
  inset: 0;
  display: flex;
  flex-direction: column;
  justify-content: flex-end;
  padding: 40px;
  background-color: var(--theme-color, #00aeff);
  opacity: 0;
  transform: translateY(25px);
  transition: all 0.5s ease-in-out;
}

.sg-pcard--cover:hover .sg-pcard__cover-reveal {
  opacity: 0.92;
  transform: translateY(0);
}

.sg-pcard--cover .sg-pcard__cover-text {
  display: block;
  margin-bottom: 10px;
  font-size: 15px;
  line-height: 26px;
  color: #ffffff;
  white-space: pre-line;
}

/* Static, not the panel-below card's absolutely-positioned swap link: the
   reveal panel already only exists on hover, so the link doesn't need its
   own opacity/position choreography on top of that. */
.sg-pcard--cover .sg-pcard__more {
  position: static;
  display: inline-block;
  width: auto;
  opacity: 1;
  transform: none;
  color: #ffffff;
}

@media (max-width: 575px) {
  .sg-pcard--cover .sg-pcard__overlay-body,
  .sg-pcard--cover .sg-pcard__cover-reveal { padding: 25px; }
  .sg-pcard--cover .sg-pcard__cover-icon { left: 25px; bottom: 25px; }
}

/* ---------------------------------------------------------------------------
   Portfolio filter grid — .b-portfolio (synergas/blocks/portfolio_grid)
   ---------------------------------------------------------------------------
   Litho's masonry_block: an "All / category" tab bar above an Isotope masonry
   grid of PLAIN product photos — the gradient only appears on hover
   (production; see M37 in visual-fidelity-audit.md, the bug this fixes was a
   permanent dark overlay + object-fit:cover crop borrowed from b-card--img,
   a different, always-photo-hero component).

   .b-portfolio__item starts as a floated 25%-wide column (a reasonable no-JS
   grid) and Isotope switches it to `position: absolute` once it initialises —
   that's what makes the masonry packing and the filter's re-flow animation
   possible, and is also why the item's OWN width/gutter live here as CSS
   rather than being computed in JS. .b-portfolio__sizer is a zero-content
   reference element Isotope measures for `columnWidth`; it is not rendered
   visually, only present for its computed width. ------------------------ */
.b-portfolio__filter {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: 10px 35px;
  margin: 0 0 45px;
  padding: 0;
  list-style: none;
}

.b-portfolio__filter-link {
  display: inline-block;
  padding-bottom: 6px;
  border-bottom: 2px solid transparent;
  font-family: var(--alt-font-family), sans-serif;
  font-size: 14px;
  font-weight: 500;
  letter-spacing: 0.5px;
  text-transform: uppercase;
  color: var(--text-color, #232323);
  text-decoration: none;
  transition: color 0.25s ease, border-color 0.25s ease;
}

.b-portfolio__filter-link:hover,
.b-portfolio__filter-link.is-active {
  color: var(--theme-color, #00aeff);
  border-bottom-color: var(--theme-color, #00aeff);
}

.b-portfolio__grid {
  position: relative;
}

.b-portfolio__sizer,
.b-portfolio__item { width: 25%; }

.b-portfolio__item {
  float: left;
  box-sizing: border-box;
  padding: 0 15px 30px;
}

.b-portfolio__media {
  position: relative;
  overflow: hidden;
  border-radius: 4px;
  box-shadow: 0 0 20px rgb(0 0 0 / 8%);
}

.b-portfolio__img {
  display: block;
  width: 100%;
  height: auto;
  transition: transform 0.4s ease;
}

.b-portfolio__item:hover .b-portfolio__img { transform: scale(1.05); }

/* Gradient reveal, off by default — the M37 fix: production shows a plain
   photo on white and only darkens on hover, not a permanent scrim. */
.b-portfolio__hover {
  position: absolute;
  inset: 0;
  display: flex;
  align-items: flex-end;
  background: linear-gradient(transparent, rgba(0, 0, 0, 0.8));
  opacity: 0;
  transition: opacity 0.3s ease;
}

.b-portfolio__item:hover .b-portfolio__hover { opacity: 1; }

.b-portfolio__hover-body { width: 100%; padding: 18px; }

.b-portfolio__kicker {
  display: block;
  margin-bottom: 4px;
  font-size: 12px;
  text-transform: uppercase;
  letter-spacing: 0.5px;
  color: #ffffff;
  opacity: 0.75;
}

.b-portfolio__title {
  margin: 0;
  font-family: var(--alt-font-family), sans-serif;
  font-size: 17px;
  font-weight: 700;
  line-height: 1.3;
  color: #ffffff;
}

@media (max-width: 991px) {
  .b-portfolio__sizer,
  .b-portfolio__item { width: 33.3333%; }
}

@media (max-width: 767px) {
  .b-portfolio__sizer,
  .b-portfolio__item { width: 50%; }
}

@media (max-width: 480px) {
  .b-portfolio__sizer,
  .b-portfolio__item { width: 100%; }
}

/* ---------------------------------------------------------------------------
   Header geometry and surface
   ---------------------------------------------------------------------------
   base's header row is `max-w-6xl mx-auto` — a 1152px centred column, so at
   1440px the logo started 148px in. Production's is `container-fluid
   nav-header-container` — full width with a fixed side inset, putting the logo
   at x=50 and the first menu item at x=385 rather than x=495.

   The surface: over a dark hero production's navbar is `bg-transparent` with
   `border-bottom border-color-white-transparent` and no shadow. base paints a
   80%-opacity `--nav-background-color` INLINE on the element, so overriding it
   needs `!important` — an inline style outranks any stylesheet selector. That
   is the one place in this file where !important is load-bearing rather than
   lazy.
   --------------------------------------------------------------------------- */
nav.fixed > div {
    max-width: none;
    padding-inline: 50px;
}

/* `:not(.sg-nav--appear)` is load-bearing. This selector is (0,3,3) and its
   `background-color` is `!important`, so it outranks the sticky rule below at
   (0,3,1) — the scroll-up state kept the transparent hero surface and the
   white box never appeared. Scoping it to "not appeared" is also what it
   means: transparent over the hero, opaque once the sticky bar returns. */
/* DESKTOP ONLY, for the same reason. This is (0,4,3) WITH !important, so it
   beat the mobile rule's `background-color: #ffffff !important` at (0,1,1) and
   the phone header stayed transparent over the hero - the state the mobile
   nav must never be in, because its panel and its links are opaque-white
   furniture. */
@media (min-width: 992px) {
body:has(main > div:first-child.b-bg-parallax) nav.fixed:not(.sg-nav--appear),
body:has(main > div:first-child.dark-section) nav.fixed:not(.sg-nav--appear),
body:has(.sg-vhero) nav.fixed:not(.sg-nav--appear) {
    background-color: transparent !important;
    border-bottom-color: rgba(255, 255, 255, 0.1);
    box-shadow: none;
    backdrop-filter: none;
}
}

/* ---------------------------------------------------------------------------
   Sticky header — Litho's reverse-scroll behaviour
   ---------------------------------------------------------------------------
   The classes are toggled by initStickyNav() in synergas.js; these are the
   Litho rules they stand in for:

     .sticky .header-reverse-scroll            transform: translateY(-100%)
     .sticky.header-appear .header-reverse-scroll
                                               transform: inherit
     .sticky.header-appear .header-light       background-color: #fff !important
                                               box-shadow: 0 0 35px rgba(0,0,0,.1)
                                               border-bottom: none !important
     .sticky .navbar.header-light .nav-link    color: #232323 !important

   Note where each one applies: the WHITE box only appears with `header-appear`
   (i.e. once you scroll back up), but the BLACK link colour applies whenever
   the header is sticky. That asymmetry is Litho's, and it is invisible in
   practice because a translated-off bar cannot be seen — reproduced anyway so
   the two states are separable.

   `!important` on the colours is not laziness, and not merely copied from
   Litho: `body:has(main > div:first-child.b-bg-parallax) .sg-nav__link` is
   (0,3,2), which outranks anything reasonable a nav-scoped selector can be.
   --------------------------------------------------------------------------- */
nav.fixed {
    transition: transform 0.3s ease, background-color 0.3s ease,
                box-shadow 0.3s ease, border-color 0.3s ease;
}

nav.fixed.sg-nav--sticky {
    transform: translateY(-100%);
}

nav.fixed.sg-nav--sticky.sg-nav--appear {
    transform: none;
    background-color: #ffffff !important;
    box-shadow: 0 0 35px rgba(0, 0, 0, 0.1);
    border-bottom-color: transparent;
    backdrop-filter: none;
}

/* Black links, language code and caret while stuck, over the white box. */
nav.fixed.sg-nav--sticky :is(.sg-nav__link, .sg-langs__toggle, .sg-langs__code, .sg-langs__caret) {
    color: #232323 !important;
}

/* The rule above is `!important`, so the plain `.sg-nav__link:hover` further
   up this file (not `!important`) never applies while the header is stuck —
   hovering a link over the white box did nothing. `:hover` here is also
   `!important` and more specific ((0,4,1) vs. (0,3,1)), so it wins. */
nav.fixed.sg-nav--sticky :is(.sg-nav__link:hover, .sg-nav__link:focus-visible) {
    color: var(--theme-color) !important;
}

/* The active page keeps its theme colour rather than going black with the
   rest — matching `.sticky .navbar .nav-link.active` on the live site. */
nav.fixed.sg-nav--sticky .sg-nav__link.is-current {
    color: var(--theme-color) !important;
}

/* A reduced-motion visitor still gets the hide/show and the white box; only
   the tweening is dropped. Animating nothing here would leave the bar
   permanently off-screen. */
@media (prefers-reduced-motion: reduce) {
    nav.fixed { transition: none; }
}

/* ---------------------------------------------------------------------------
 * Partner logo strip — .sg-logos  (legacy: logo_carousel)
 *
 * Litho markup this reproduces:
 *   .row.row-cols-lg-4.row-cols-sm-2.client-logo-style-02
 *     .col.text-center > a.client-logo > img        (original file, no crop)
 *
 * `.client-logo-style-02 .client-logo { opacity: .6 }`, rising to 1 on hover,
 * is the whole of its styling — there is no card, border, shadow or caption.
 * The transform had these on brilliance's FeatureCardBlock, which treats an
 * image as a card BACKGROUND, so each logo became a 760x1000 portrait crop
 * under a dark scrim with an empty <h3> beneath it.
 * ------------------------------------------------------------------------- */
.sg-logos {
    display: grid;
    grid-template-columns: repeat(var(--sg-logos-cols, 4), minmax(0, 1fr));
    align-items: center;
    /* Bootstrap's gutter model, not a grid gap: `.row` has `margin: 0 -15px`
       and each `.col` re-adds `padding: 0 15px`, so a column is a full 1/4 of
       the container (292.5px of 1170) with the 15px living INSIDE it. A 30px
       grid gap instead subtracts from the track, which measured 270px against
       the live 292.5px. */
    gap: 0;
    /* See "the .row negative margin" below: this element IS a Bootstrap row. */
    width: auto;
    margin-inline: calc(var(--sg-gutter) * -1);
}

.sg-logos__cell {
    display: flex;
    align-items: center;
    justify-content: center;
    padding-inline: 15px;
}

.sg-logos__link {
    display: inline-block;
    opacity: 0.6;
    transition: opacity 0.2s ease;
}

.sg-logos__link:hover,
.sg-logos__link:focus-visible {
    opacity: 1;
}

/* Width-capped only, which is Litho's `img { max-width: 100% }` and nothing
   else. A `max-height` here looked tidier — it stops the near-square badges
   towering over the wide wordmarks — but production genuinely lets them tower:
   the strip is 265.7px tall there because the Melissi logo fills its column,
   and the row's `align-items-center` centres the short ones against it. Ours
   was 90px. Matching the target means keeping production's proportions, not
   improving on them. */
.sg-logos__img {
    display: block;
    width: auto;
    height: auto;
    max-width: 100%;
}

/* `row-cols-1 row-cols-sm-2 row-cols-lg-4`, read the way Bootstrap 5 means
   it: the numbered `row-cols-N` is a MIN-width rule, so it is 4 across from
   992px, 2 from 576px, and ONE below that - not "two across below 768".
   Measured on production: the cell is 292.5 wide at 1440, 270 at 700 and the
   full 390 at 390. Ours was 2-up at 390, so each logo had half the width it
   has on the live site. */
@media (max-width: 991px) {
    .sg-logos { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}

@media (max-width: 575px) {
    .sg-logos { grid-template-columns: minmax(0, 1fr); }
}

/* ---------------------------------------------------------------------------
   Feature-card title and body metrics
   ---------------------------------------------------------------------------
   Two defects the parity probe found on the home page's circular feature
   boxes, both of which made the card 40px shorter than production's (290px
   against 330px):

   1. The grouped rule that strips the heading scale off card titles
      (`.b-card__title, .b-card__title--h3, .b-member__name, … { margin: 0 }`)
      also wiped Litho's `margin-bottom: 15px` on the title and left the body
      with none against production's 25px. It has to keep `margin: 0` for the
      member/news/numlist labels it also covers, so the card metrics are
      restored here instead, after it.

   2. `.b-card` is `flex-direction: column; align-items: center`, which makes
      every child SHRINK TO ITS TEXT. The title measured 168px wide inside a
      360px card, so a two-word title wrapped where production's 266px-wide
      block did not. Litho centres with `text-align`, not with flex alignment,
      so the text elements have to stretch back to the full column width.
   --------------------------------------------------------------------------- */
.b-card__title {
    margin: 0 0 15px;
}

.b-card__body {
    margin: 0 0 25px;
}

.b-card__title,
.b-card__body {
    align-self: stretch;
}

/* ---------------------------------------------------------------------------
 * Image carousel — .sg-carousel  (legacy: Content_Slider_Block)
 *
 * Litho markup this reproduces:
 *   .swiper-container.white-move.border-all.border-width-12px
 *                    .border-color-white.box-shadow-large.border-radius-8px
 *     .swiper-wrapper > .swiper-slide > img
 *     .swiper-button-prev / -next  .slider-navigation-style-01.light
 *
 * Every value measured on production's /en/our-services/gas-for-home/:
 *   container  540x412  border 12px solid #fff  radius 8px
 *              box-shadow 0 0 30px rgba(0,0,0,.08)
 *   slide      516x388  (the container's content box) — ONE per view
 *   nav        40x40 white squares at the frame's inside edges, centred
 *
 * The 8px radius here is deliberate and is NOT the global radius remap: this
 * component genuinely has `border-radius-8px` in the live markup, unlike the
 * cards and buttons whose rounding had to be removed.
 * ------------------------------------------------------------------------- */
.sg-carousel {
    width: 100%;
}

.sg-carousel__frame {
    position: relative;
    border: 12px solid #ffffff;
    border-radius: 8px;
    box-shadow: 0 0 30px rgba(0, 0, 0, 0.08);
    overflow: hidden;
}

.sg-carousel__track {
    display: flex;
    transition: transform 0.5s ease-in-out;
}

@media (prefers-reduced-motion: reduce) {
    .sg-carousel__track { transition: none; }
}

/* One slide per view: the flex basis is the whole viewport and the track is
   moved by whole multiples of it. */
.sg-carousel__slide {
    flex: 0 0 100%;
    min-width: 0;
}

/* The slide holds a rendered ImageBlock (`figure.b-figure > img.b-img`), which
   carries `b-rounded-md`. The frame owns the rounding here, so the image must
   not also round — production's slide images are square-cornered inside the
   white mount. */
.sg-carousel__slide .b-figure,
.sg-carousel__slide figure {
    margin: 0;
}

.sg-carousel__slide img {
    display: block;
    width: 100%;
    height: auto;
    border-radius: 0;
}

/* `slider-navigation-style-01 light`: a 40px white square, not a circle. */
.sg-carousel__nav {
    position: absolute;
    top: 50%;
    transform: translateY(-50%);
    z-index: 2;
    display: grid;
    place-items: center;
    width: 40px;
    height: 40px;
    padding: 0;
    border: 0;
    border-radius: 0;
    background-color: #ffffff;
    color: #232323;
    /* Measured on the live arrows, which are 15px inside a 40px box — not the
       18px that looked right by eye. */
    font-size: 15px;
    line-height: 1;
    cursor: pointer;
    transition: background-color 0.3s ease, color 0.3s ease;
}

.sg-carousel__nav:hover,
.sg-carousel__nav:focus-visible {
    background-color: var(--theme-color);
    color: #ffffff;
}

/* Positioned against the frame's PADDING box, so `left: 0` lands just inside
   the 12px border — production's arrows sit at x=177 for a frame at x=165. */
.sg-carousel__nav--prev { left: 0; }
.sg-carousel__nav--next { right: 0; }

/* ---------------------------------------------------------------------------
 * Testimonial slider — .sg-quotes  (legacy: Testimonial_Slider_Block)
 *
 * Litho markup this reproduces (TestimonialSliderBlock.html):
 *   .col-12.col-lg-10.padding-10-rem-lr.sm-padding-7-rem-lr.xs-padding-6-rem-lr
 *     .row.align-items-center
 *       .col-auto  .swiper-button-prev.font-weight-600.alt-font.text-small
 *                  .text-dark-purple                       -> the word PREV
 *       .col       .swiper-container > .swiper-wrapper > .swiper-slide
 *                    i.fas.fa-quote-left.text-theme  (70px, 50px below)
 *                    p.text-large.line-height-36px.margin-40px-bottom.w-90
 *                    span.alt-font.font-weight-500.text-uppercase.d-block
 *                    span.alt-font.text-small.text-uppercase.d-block
 *       .col-auto  ... -> the word NEXT
 *
 * Litho values used: .text-large 18px, .text-small 13px/22px,
 * .text-dark-purple #333045, .w-90 90%, .padding-10-rem-lr 10rem,
 * .margin-seven-bottom 7%.
 * ------------------------------------------------------------------------- */
/* --sg-row, not --sg-container: in Litho this block's own outer box is the
   `.row` inside the container (1140 at 1200+), and `.sg-quotes__inner` is the
   `.col-lg-10` of that row. Measured in place, the block already inherits 1140
   from the section's grid, so this cap only bites when the block is placed
   full-bleed - and 1170 there would make it 30px wider than the same block
   inside a container. */
.sg-quotes {
    width: 100%;
    max-width: var(--sg-row);
    margin-inline: auto;
}

/* `.col-12.col-lg-10.position-relative.padding-10-rem-lr`.
 *
 * `position: relative` is load-bearing, not decoration: the nav controls are
 * ABSOLUTELY positioned against this box (see .sg-quotes__nav), so this is
 * their containing block and the 10rem of side padding is the gutter they sit
 * in. Nothing here is a flex row.
 *
 * An earlier pass read the target markup's `col-auto` / `col` / `col-auto`
 * wrappers as a live Bootstrap row and laid the nav out IN FLOW either side of
 * the slide. Measured on production, Litho's `.swiper-button-prev` rule makes
 * those buttons `position: absolute`, so the `col-auto` columns collapse to
 * zero width and the slide keeps the full column. In flow they cost the slide
 * 126px of text plus 30px of gutter - at 390 that left 101px of slide against
 * production's 330. */
.sg-quotes__inner {
    position: relative;
    width: 83.333333%;
    margin-inline: auto;
    padding-inline: 10rem;
}

.sg-quotes__title {
    margin: 0 0 7%;
    font-family: var(--alt-font-family);
    font-weight: 500;
    color: #232323;
    text-align: center;
}

/* The `.col` holding the swiper. Full width of the padded box, since the nav
   is out of flow. */
.sg-quotes__viewport {
    min-width: 0;
    overflow: hidden;
}

/* Reuses initCarousel(): same data attributes, same one-slide-per-view track
   moved by multiples of 100%. */
.sg-quotes__track {
    display: flex;
    transition: transform 0.5s ease-in-out;
}

@media (prefers-reduced-motion: reduce) {
    .sg-quotes__track { transition: none; }
}

.sg-quotes__slide {
    flex: 0 0 100%;
    min-width: 0;
    text-align: center;
}

.sg-quotes__markwrap {
    display: flex;
    justify-content: center;
}

/* The 50px sits on the glyph itself, as it does on the live site
   (`margin-50px-bottom` is a class on the <i>, not on its wrapper). */
.sg-quotes__mark {
    display: block;
    margin-bottom: 50px;
    color: var(--theme-color);
    font-size: 70px;
    line-height: 1;
}

/* A plain <p>, per Litho. It was a <blockquote>, which picks up UA margins and
   brilliance's own `blockquote` rule - that added `3.6px 0 3.6px 19.8px` of
   padding and made the quote 43px tall against production's 36px. */
.sg-quotes__text {
    display: inline-block;
    width: 90%;
    margin: 0 0 40px;
    padding: 0;
    border: 0;
    font-size: 18px;
    line-height: 36px;
    color: var(--text-color, #000);
}

.sg-quotes__author {
    display: block;
    font-family: var(--alt-font-family);
    font-weight: 500;
    text-transform: uppercase;
    color: #232323;
}

.sg-quotes__role {
    display: block;
    font-family: var(--alt-font-family);
    font-size: 13px;
    line-height: 22px;
    text-transform: uppercase;
    color: var(--text-color, #000);
}

/* The controls are the words PREV / NEXT, each with a vertical rule on its
   INNER edge, pinned to the padded box's two edges and vertically centred.
 *
 * An earlier pass measured this against Litho's PLAIN `.swiper-button-prev`
 * (the image carousel's arrow glyph, which computes a 40x40 white square) and
 * reproduced that instead. Wrong component: this markup's class is
 * `.slider-custom-text-prev.swiper-button-prev` - the "custom text" variant,
 * which style.css overrides completely:
 *
 *   .slider-custom-text-prev.swiper-button-prev,
 *   .slider-custom-text-next.swiper-button-next {
 *       width: auto; height: auto; padding: 25px 0;
 *   }
 *   .slider-custom-text-prev.swiper-button-prev { border-right: 1px solid #E4E4E4; padding-right: 45px; }
 *   .slider-custom-text-next.swiper-button-next { border-left:  1px solid #E4E4E4; padding-left:  45px; }
 *   …:hover { opacity: .7 }
 *
 * No `!important` and nothing more specific competes with it here (unlike the
 * plain variant, which loses to `.swiper-button-prev`'s own base rule), so
 * this time the markup's own utility classes DO paint the text:
 * `alt-font font-weight-600 text-small text-dark-purple` = 13px/22px, weight
 * 600, #333045 (`.text-small` 13px/22px, `.text-dark-purple` #333045).
 *
 * `top: 50%` + translate rather than swiper's `margin-top: -22px`, so it stays
 * centred whatever the slide's height (which varies with the quote's length).
 */
.sg-quotes__nav {
    position: absolute;
    top: 50%;
    transform: translateY(-50%);
    padding: 25px 0;
    border: 0;
    background: none;
    cursor: pointer;
    font-family: var(--alt-font-family);
    font-size: 13px;
    line-height: 22px;
    font-weight: 600;
    color: #333045;
    transition: opacity 0.3s ease;
}

.sg-quotes__nav[data-dir="prev"] {
    left: 0;
    padding-right: 45px;
    border-right: 1px solid #e4e4e4;
}

.sg-quotes__nav[data-dir="next"] {
    right: 0;
    padding-left: 45px;
    border-left: 1px solid #e4e4e4;
}

.sg-quotes__nav:hover,
.sg-quotes__nav:focus-visible {
    opacity: 0.7;
}

@media (max-width: 767px) {
    .sg-quotes__inner { width: 100%; padding-inline: 7rem; }
    .sg-quotes__mark { margin-bottom: 25px; font-size: 50px; }
    .sg-quotes__text { margin-bottom: 25px; line-height: 30px; }
    /* `.slider-custom-text-*` drops the divider's outer padding to 30px below
       767px. */
    .sg-quotes__nav[data-dir="prev"] { padding-right: 30px; }
    .sg-quotes__nav[data-dir="next"] { padding-left: 30px; }
}

@media (max-width: 575px) {
    /* `xs-padding-6-rem-lr`. 6rem is 66px at the 11px root, so the control
       still clears the slide comfortably. Below 575px the divider's outer
       padding drops again, to 8px. */
    .sg-quotes__inner { padding-inline: 6rem; }
    .sg-quotes__nav[data-dir="prev"] { padding-right: 8px; }
    .sg-quotes__nav[data-dir="next"] { padding-left: 8px; }
}

/* ---------------------------------------------------------------------------
   Icon point, variant B — Litho's `ul.list-style-02` checklist row
   ---------------------------------------------------------------------------
   `Image_Slider_Features_Block` does not render the left-icon feature box at
   all. Its items are list rows:

     ul.p-0.list-style-02.margin-2-rem-top.margin-3-rem-bottom
       li.margin-15px-bottom
         i.fa.fa-check-square.text-large.text-theme.margin-10px-right
         span.text-extra-dark-gray.alt-font

   So the label is weight 400 with NO bottom margin and the 15px of rhythm
   sits on the row — measured 30px tall against 40px for the feature-box
   variant, whose label carries `margin-10px-bottom` and weight 500.

   Both variants are the same block, so they are told apart by whether the
   item has a description: only the feature-box variant ever has one. `:has()`
   is doing real work — there is no class to key on, and adding one would mean
   a new block field for a distinction the content already expresses.
   --------------------------------------------------------------------------- */
.b-usp:not(:has(.b-usp__body)) {
    align-items: center;
    margin-bottom: 15px;
}

.b-usp__txt:not(:has(.b-usp__body)) .b-usp__label {
    margin-bottom: 0;
    font-weight: 400;
}

/* ---------------------------------------------------------------------------
   Body copy — .b-body / .b-lead
   ---------------------------------------------------------------------------
   Every ParagraphBlock renders as `.b-body`, and that class is styled ONLY by
   brilliance — nothing here overrode it, so all 27 paragraph instances on the
   site were using brilliance's body type:

     .b-body { font-weight: 300;
               font-size: calc(var(--fs, clamp(15px, 1.7vw, 18px)) * var(--tscale, 1));
               line-height: 1.6; }

   which computes 18px / 28.8px / 300 at 1440. Litho's body is `15px / 30px /
   400` (its `body` rule), and a measured target paragraph agrees exactly. So
   every paragraph on the site has been a size too large, a weight too light
   and 1.2px tight on leading.

   Colour is deliberately not set here: the section tone already governs it,
   and brilliance's `.dark-section .b-body` rule gives the right value on dark
   bands.

   `--fs` is left working: a block that sets an explicit font size through the
   styling panel still wins, because that is what the variable is for.
   --------------------------------------------------------------------------- */
.b-body {
    font-size: var(--fs, 15px);
    line-height: 30px;
    font-weight: 400;
}

.b-lead {
    font-size: var(--fs, 18px);
    line-height: 30px;
    font-weight: 400;
}

/* brilliance shrinks body copy at three breakpoints (14px, 12px, 11px), which
   is its own scale; Litho keeps 15px/30px down to phones. */
@media (max-width: 1199px) {
    .b-body { font-size: var(--fs, 15px); }
}

@media (max-width: 767px) {
    .b-body { font-size: var(--fs, 15px); }
}

/* ---------------------------------------------------------------------------
 * Team member card — .b-member  (legacy: Team_Grid_Block, `team-style-02`)
 *
 * The live card, read from the AJAX endpoint that renders it
 * (`/team-members/api/load-more-team-members/`, since the page injects these
 * rather than serving them):
 *
 *   .col.team-style-02.text-center > a > figure
 *     .team-member-image.border-radius-5px.overflow-hidden  aspect-ratio: 3/4
 *       img.w-100.object-fit-cover                          300x400
 *       .team-member-details.bg-theme                       (hover overlay)
 *     figcaption.team-member-position.text-center.padding-35px-tb
 *       .text-extra-dark-gray.alt-font.line-height-18px.text-medium
 *        .text-uppercase.font-weight-500                    -> name
 *       span.text-small.text-uppercase                      -> role
 *
 * Litho values used: .text-medium 14px/20px, .text-small 13px/22px,
 * .line-height-18px 18px, .padding-35px-tb 35px.
 *
 * brilliance's version is a different card: a 3/3.2 photo with a 19px
 * weight-300 name and a 12px monospace role. Every one of those is restated
 * below, because anything left undeclared it keeps supplying.
 * ------------------------------------------------------------------------- */
.b-member__photo {
    aspect-ratio: 3 / 4;
    border-radius: 5px;
    object-fit: cover;
    /* brilliance adds a 14px gap under the photo; Litho has none — the
       figcaption's own 35px of padding is the whole gap. */
    margin-bottom: 0;
}

/* `figcaption.padding-35px-tb` wraps both lines; there is no caption element
   in base's markup, so the 35px is split onto the two text rows. */
.b-member__name {
    margin-top: 35px;
    margin-bottom: 0;
    font-family: var(--alt-font-family);
    font-size: 14px;
    line-height: 18px;
    font-weight: 500;
    text-transform: uppercase;
    color: #232323;
}

.b-member__role {
    margin-bottom: 35px;
    font-family: var(--alt-font-family);
    font-size: 13px;
    line-height: 22px;
    font-weight: 400;
    text-transform: uppercase;
    color: var(--text-color, #000);
}

@media (max-width: 767px) {
    .b-member__name { margin-top: 25px; }
    .b-member__role { margin-bottom: 25px; }
}

/* ---------------------------------------------------------------------------
 * Bracketed statistic — .sg-fancybox  (legacy: company_stats, home)
 *
 * Litho's `fancy-text-box-style-01`, measured from its own CSS:
 *   .fancy-text-box                       display: inline-block; position: relative
 *   .padding-3-half-rem-all               padding: 3.5rem
 *   .fancy-text-content                   font-size: 80px; line-height: 72px;
 *                                         margin-bottom: 0; margin-right: 15px
 *   .letter-spacing-minus-5px             letter-spacing: -5px
 *   label: .text-extra-medium 16px/28px, .line-height-20px, .w-90px,
 *          .top-minus-4px, font-weight-500, d-inline-block, align-middle
 *   brackets: border 10px solid; height 100%; width 67px; absolute;
 *             top 50%; translateY(-50%); left one drops border-right,
 *             right one drops border-left and sits at right: 0
 *
 * The border colour is `border-color-theme`, which is not in Litho's own
 * stylesheet — it is the site's, and resolves to the theme colour.
 * ------------------------------------------------------------------------- */
.sg-fancybox {
    display: inline-block;
    position: relative;
    padding: 3.5rem;
}

/* Each bracket is a 67px-wide, full-height box with a 10px border on three
   sides — the fourth side is dropped so the pair reads as [ and ]. */
.sg-fancybox__bracket {
    position: absolute;
    top: 50%;
    transform: translateY(-50%);
    display: block;
    width: 67px;
    height: 100%;
    border: 10px solid var(--theme-color);
    pointer-events: none;
}

.sg-fancybox__bracket--l {
    left: 0;
    border-right: 0;
}

.sg-fancybox__bracket--r {
    right: 0;
    border-left: 0;
}

.sg-fancybox__num {
    display: inline-block;
    vertical-align: middle;
    margin-right: 15px;
    margin-bottom: 0;
    font-family: var(--alt-font-family);
    font-size: 80px;
    line-height: 72px;
    font-weight: 600;
    letter-spacing: -5px;
    color: #232323;
}

.sg-fancybox__label {
    display: inline-block;
    vertical-align: middle;
    position: relative;
    top: -4px;
    width: 90px;
    font-family: var(--alt-font-family);
    font-size: 16px;
    line-height: 20px;
    font-weight: 500;
    text-align: left;
    color: #232323;
}

@media (max-width: 575px) {
    .sg-fancybox { padding: 30px; }
}

/* ---------------------------------------------------------------------------
 * News grid — .sg-newsgrid / .sg-news  (legacy: Blog_Grid_Block)
 *
 * Litho's `blog-grid grid grid-3col md-grid-2col xs-grid-1col
 * gutter-extra-large` of `blog-post` cards. Values from its own stylesheet:
 *   .gutter-extra-large li        padding: 15px          (so a 30px gutter)
 *   .blog-post                    position: relative; overflow: hidden
 *   .border-radius-5px            5px
 *   .box-shadow-medium            0 0 20px rgba(0,0,0,.08)
 *   .blog-category                absolute; top/right 23px; 11px/14px;
 *                                 background rgba(23,23,23,.75); #fff;
 *                                 uppercase; radius 2px; padding 6px 13px
 *   .post-details                 padding: 2.5rem 3rem
 *   date                          .text-small 13px, theme colour, 10px below
 *   title                         .text-extra-medium 16px/28px, weight 500,
 *                                 #232323, 15px below
 *   .avtar-image                  30px, round, 10px right  (no field for it)
 *
 * Production lays this out with Isotope masonry. A CSS grid is used instead:
 * the cards are equal-width and start at the same y, so masonry and a grid
 * are indistinguishable here, and it removes a jQuery dependency.
 * ------------------------------------------------------------------------- */
.sg-newsgrid {
    display: grid;
    grid-template-columns: repeat(var(--sg-news-cols, 3), minmax(0, 1fr));
    gap: 0;
    /* NO negative margin, unlike `.sg-logos`. Litho's `.grid` lists are not
       `.row`s: production's `ul.blog-grid` sits at the col's content width
       (1140 at x=150) and pads each `li` by 15px inside that, so the outer
       cards are inset 15px from the container edge and their content starts at
       x=165. Reclaiming the gutter here put them at x=150 with 360px of
       content against the live 350px. Measured, not assumed - the logo strip
       one screen up does the opposite. */
    width: 100%;
}

/* `.grid.gutter-extra-large li { padding: 15px }` — the gutter lives inside
   the cell, as it does in Bootstrap, not as a grid gap. */
.sg-newsgrid__cell {
    padding: 15px;
    min-width: 0;
}

/* Below `sm` the list DOES take the gutter back, unlike at desktop. Measured
   on production at 390: `ul.blog-grid` is 390 wide at x=0 - the full viewport -
   and its `li` is 390 with the usual 15px inside, so a card's content is 360.
   Ours sat at 360 with 330 of content, i.e. a card 30px narrower than the
   original on every phone. The desktop behaviour (spanning the column's
   CONTENT, no reclaim) is unchanged - see the note above it. */
@media (max-width: 575px) {
    .sg-newsgrid {
        width: auto;
        margin-inline: calc(var(--sg-gutter) * -1);
    }
}

.sg-news {
    position: relative;
    overflow: hidden;
    height: 100%;
    border-radius: 5px;
    background-color: #ffffff;
    box-shadow: 0 0 20px rgba(0, 0, 0, 0.08);
    transition: box-shadow 0.3s ease-in-out;
}

/* `aspect-ratio` pins the media box to the 800x560 thumbnail rendition's
   proportions, full stop — it never changes on hover. That's deliberate: the
   "grow" effect below moves `.sg-news__body` with a `transform`, which never
   feeds into layout sizing, so the card's own height (and the grid row it
   shares with its siblings) can't be disturbed by a hover no matter how many
   cards are in that row. */
.sg-news__media {
    position: relative;
    display: block;
    aspect-ratio: 800 / 560;
    overflow: hidden;
}

.sg-news__media img {
    display: block;
    width: 100%;
    height: 100%;
    object-fit: cover;
    transition: all 1s cubic-bezier(0, 0, 0.2, 1);
}

/* Card hover — Litho's `.blog-post:hover .blog-post-image img`: the photo
   zooms and dims, and the category chip inverts. Timings are Litho's: 1s
   cubic-bezier(0,0,.2,1) on the image, .5s on the chip. */
.sg-news:hover .sg-news__media img,
.sg-news:focus-within .sg-news__media img {
    opacity: 0.6;
    transform: scale3d(1.1, 1.1, 1.1);
}

.sg-news:hover .sg-news__cat,
.sg-news:focus-within .sg-news__cat {
    background: #ffffff;
    color: #232323;
}

@media (prefers-reduced-motion: reduce) {
    .sg-news__media img { transition: none; }
    .sg-news:hover .sg-news__media img,
    .sg-news:focus-within .sg-news__media img { transform: none; }
}

.sg-news__cat {
    position: absolute;
    top: 23px;
    right: 23px;
    padding: 6px 13px;
    border-radius: 2px;
    background: rgba(23, 23, 23, 0.75);
    color: #ffffff;
    font-family: var(--alt-font-family);
    font-size: 11px;
    line-height: 14px;
    text-transform: uppercase;
    /* Litho declares this only inside its :hover rule, so the chip snapped
       back instantly on mouse-out; on the base rule it eases both ways. */
    transition: all 0.5s ease-in-out;
}

/* Sits in normal flow right below `.sg-news__media` — both are fully visible
   at rest. `transform` (not a height/flex change) is what slides it up over
   the image on hover: transforms are compositor-only, so they never trigger
   layout and always interpolate smoothly, unlike animating a flex-basis away
   from `auto` (which most browsers can't tween at all — it just snaps). The
   card's own background is white too, so the strip this vacates at the
   bottom on hover reads as part of the card, not a gap. */
.sg-news__body {
    position: relative;
    z-index: 2;
    background-color: #ffffff;
    padding: 2.5rem 3rem;
    transition: transform 0.45s cubic-bezier(0.4, 0, 0.2, 1);
}

.sg-news:hover .sg-news__body,
.sg-news:focus-within .sg-news__body {
    transform: translateY(-55%);
}

@media (prefers-reduced-motion: reduce) {
    .sg-news__body { transition: none; }
}

.sg-news__date {
    display: inline-block;
    margin-bottom: 10px;
    font-family: var(--alt-font-family);
    font-size: 13px;
    line-height: 22px;
    color: var(--theme-color);
}

.sg-news__title {
    display: block;
    margin-bottom: 15px;
    font-family: var(--alt-font-family);
    font-size: 16px;
    line-height: 28px;
    font-weight: 500;
    color: #232323;
    text-decoration: none;
    transition: color 0.3s ease;
}

.sg-news__title:hover,
.sg-news__title:focus-visible {
    color: var(--theme-color);
}

/* Litho leaves the paragraph its default 25px bottom margin here, not the
   15px the title uses — measured on the live card. */
.sg-news__text {
    margin: 0 0 25px;
    font-size: 15px;
    line-height: 30px;
}

.sg-news__by {
    display: flex;
    align-items: center;
}

.sg-news__byline {
    font-family: var(--alt-font-family);
    font-size: 13px;
    line-height: 22px;
    margin-right: auto;
}

.sg-news__author {
    transition: color 0.3s ease;
}

.sg-news__author:hover {
    color: var(--theme-color);
}

@media (max-width: 991px) {
    .sg-newsgrid { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}

@media (max-width: 575px) {
    .sg-newsgrid { grid-template-columns: 1fr; }
    .sg-news__body { padding: 2rem; }
}

/* ---------------------------------------------------------------------------
 * Separator tick — .sg-sep  (legacy: Fancy_Title_Block)
 *
 * Litho: `w-40px h-2px bg-theme separator-line-vertical margin-30px-tb
 * d-inline-block`, where `.separator-line-vertical` is
 * `transform: rotate(-90deg)`. So a 40x2 bar paints as a 2px-wide, 40px-tall
 * tick. Measured live: box 2.0x40.0, rgb(0, 174, 255), margin 30px 0,
 * display inline-block.
 *
 * The rotation is load-bearing, not decorative: a rotated element keeps its
 * 40x2 LAYOUT box, so this reserves 2px of height plus 30px either side (62px)
 * and the tick overflows it. Declaring `width: 2px; height: 40px` upright
 * would look identical but reserve 100px and push every fancy-title heading
 * 38px down the page.
 *
 * Centring comes from the wrapper: the transform emits this inside a centred
 * block, and an inline-block centres on `text-align`.
 * ------------------------------------------------------------------------- */
.sg-sep {
    /* BLOCK with auto side margins, not `inline-block` centred by the parent's
       `text-align`. The transform emits this with `text=CENTRED`, and the
       value IS stored - `"text": {"text_align": "text-center"}` is in the
       StreamField - but `StyledBlock`'s style struct has no `text` group, so
       the key is dropped on LOAD and never reaches the styled wrapper's class
       list. Same silent-drop shape as the prefixed CTA link keys: valid data
       in, nothing out, no error anywhere.

       The tick is centred on every page of the live site (Litho's is
       `d-inline-block` inside a `text-center` column), so centring it here is
       unconditional and does not depend on that plumbing. It was sitting at
       x=19 at EVERY width - a stray vertical bar at the left margin above
       each section heading, not just a mobile problem. */
    display: block;
    width: 40px;
    height: 2px;
    margin-block: 30px;
    margin-inline: auto;
    background-color: var(--theme-color);
    transform: rotate(-90deg);
}

/* ---------------------------------------------------------------------------
   company_stats' three columns — the remaining text metrics
   ---------------------------------------------------------------------------
   All three of these classes have exactly ONE instance on the site (home's
   "61 years" section), so they are styled to Litho's values for that section
   rather than being generalised from it:

     "Since 1965"   .alt-font.text-medium.text-uppercase.margin-20px-bottom
                    -> 14px/20px, weight 400, no tracking, 20px below
     statement      .alt-font.text-extra-large.text-extra-dark-gray
                    .font-weight-500.line-height-34px.w-85
                    -> 20px/34px, weight 500, #232323, 85% wide
     "About company" .btn.btn-link.thin.btn-extra-large.text-extra-dark-gray
                    -> padding 7px 0 2px, border-bottom 1px, transparent,
                       #232323, 14px; `.btn.btn-link:hover { opacity: .7 }`

   Measured before and after; each was wrong in a way that reads as "close but
   not the same": the eyebrow was 15px with 2px of tracking against 14px with
   none, the statement 18px/30px/400 against 20px/34px/500, and the link was a
   32px-padded cyan pill against a 14px underlined dark text link.
   --------------------------------------------------------------------------- */
.b-subheading--sm {
    margin: 0 0 20px;
    font-family: var(--alt-font-family);
    font-size: 14px;
    line-height: 20px;
    font-weight: 400;
    letter-spacing: normal;
    text-transform: uppercase;
    opacity: 1;
}

.b-lead {
    width: 85%;
    font-family: var(--alt-font-family);
    font-size: var(--fs, 20px);
    line-height: 34px;
    font-weight: 500;
    color: #232323;
}

@media (max-width: 1199px) {
    .b-lead { width: 100%; }
}

/* Doubled up to beat `.b-cta.b-cta--lg`, which was supplying `padding: 12px
   32px` — the reason a text link rendered as a padded pill. */
.b-cta.b-cta--link {
    padding: 7px 0 2px;
    border: 0;
    border-bottom: 1px solid;
    background: transparent;
    color: #232323;
    font-size: 14px;
    line-height: 1.5;
}

.b-cta.b-cta--link:hover,
.b-cta.b-cta--link:focus-visible {
    background: transparent;
    color: #232323;
    opacity: 0.7;
}

/* ---------------------------------------------------------------------------
   `.big-section` — 160px of vertical rhythm, per legacy block
   ---------------------------------------------------------------------------
   Litho's `.big-section { padding: 160px 0 }` is hardcoded in ten legacy block
   templates, and five of those are LIVE (the other five sit inside
   `{% comment %}` blocks and so contribute nothing):

     LIVE       SimpleCTABlock, CompanyStats, ImageContentCTABlock,
                FeatureGridBlock, CtaTitleBlock
     commented  TestimonialSliderBlock, BusinessFeaturesBlock,
                ProcessStepStyle02Blocks, FancyTitleBlock, LocationMapBlock

   It is not in the content data — `big-section` appears 0 times in the dump,
   and the legacy Section template's own comment says the section pads nothing
   and lets "the inner block supply its own vertical rhythm (e.g.
   .big-section)". So it is a property of the BLOCK TYPE, and the transform's
   spacing field cannot express it anyway: its scale stops at `pt-20` (80px).

   Only the two whose rendered output has a marker unique to one legacy block
   are done here. `FeatureGridBlock` and `CtaTitleBlock` share their markers
   with other leaves (`.b-card--outline`, plain headings), so keying on them
   would pad sections that production does not — those belong with the
   section-level pass in pixel-parity-tracker.md, which has to read the legacy
   block type rather than guess from the output.
   --------------------------------------------------------------------------- */
main > div > section > .block-styled:has(> .b-sec-anchor .sg-fancybox),
main > div > section > .block-styled:has(> .b-sec-anchor .b-mediacta),
main > div > section > .block-styled:has(.sg-fancybox),
main > div > section > .block-styled:has(.b-mediacta) {
    padding-block: 160px;
}

@media (max-width: 991px) {
    main > div > section > .block-styled:has(.sg-fancybox),
    main > div > section > .block-styled:has(.b-mediacta) {
        padding-block: 100px;
    }
}

/* ---------------------------------------------------------------------------
 * Numbered feature card — .sg-nfeat  (legacy: numbered_feature_boxes)
 *
 * Litho markup this reproduces:
 *   .feature-box.h-100.feature-box-left-icon-middle.padding-4-rem-all.bg-white
 *               .box-shadow-small.box-shadow-large-hover.border-radius-8px
 *               .overflow-hidden.last-paragraph-no-margin
 *     .feature-box-icon.margin-20px-right > i.icon-medium.text-fast-blue
 *     .feature-box-content.line-height-22px
 *        > div.text-extra-dark-gray.alt-font.font-weight-500.line-height-20px
 *        > span.text-small.alt-font
 *     .bg-medium-light-gray.margin-25px-top.w-100.h-1px
 *     p.margin-25px-top
 *     h3.alt-font.opacity-2.font-weight-500.letter-spacing-minus-2px
 *       .position-absolute.bottom-minus-20px.right-50px.m-0
 *
 * Every value measured on production's /en/our-services/gas-for-business/:
 *   card      255x397  padding 56px  radius 8px  #fff
 *             box-shadow 0 0 15px rgba(0,0,0,.08)
 *   icon      <i> 40px/40px #0038e3, wrapper margin-right 20px
 *   title     15px/20px  w500  #232323
 *   subtitle  13px/22px  w400  #000
 *   rule      1px #ededed, 25px above, full width
 *   body      15px/30px, 25px above, NO bottom margin
 *   numeral   47.6px/54.6px  w500  ls -2px  opacity .2, bottom -20px right 50px
 *
 * The card is a flex WRAP container, not a two-element row: only the icon and
 * the title/subtitle pair share a line (they total exactly the 143px content
 * width), and the rule, the body and the numeral are full-width rows below.
 * That is the shape `layout="inline"` on a FeatureCardBlock could not express.
 *
 * The 8px radius is genuine here — `border-radius-8px` is in the live markup,
 * unlike the cards and buttons whose rounding had to be removed.
 * ------------------------------------------------------------------------- */
.sg-nfeat {
    position: relative;
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    /* `h-100`: cards in a row match the tallest. */
    height: 100%;
    padding: 4rem;
    background-color: #ffffff;
    border-radius: 8px;
    box-shadow: 0 0 15px rgba(0, 0, 0, 0.08);
    /* Clips the lower half of the numeral, which hangs 20px past the edge. */
    overflow: hidden;
    text-align: left;
    /* Litho fades box-shadow-large-hover in rather than snapping to it. */
    transition: box-shadow 0.3s ease;
}

@media (max-width: 1199px) { .sg-nfeat { padding: 2.5rem; } }  /* lg-padding-2-half-rem-all */
@media (max-width: 991px)  { .sg-nfeat { padding: 4rem; } }    /* md-padding-4-rem-all */

.sg-nfeat:hover {
    /* box-shadow-large-hover */
    box-shadow: 0 0 30px rgba(0, 0, 0, 0.08);
}

@media (prefers-reduced-motion: reduce) {
    .sg-nfeat { transition: none; }
}

.sg-nfeat__head {
    display: flex;
    align-items: center;
    width: 100%;
}

.sg-nfeat__icon {
    margin-right: 20px;
    /* The glyph is 40px tall; the wrapper must not stretch the flex line. */
    line-height: 0;
}

.sg-nfeat__icon i {
    font-size: 40px;
    line-height: 40px;
    color: #0038e3;
}

.sg-nfeat__text {
    display: block;
    /* min-width:0 so a long word wraps instead of widening the flex line and
       pushing the icon off the card. */
    min-width: 0;
    /* `line-height-22px` on `.feature-box-content`. Load-bearing, not
       inherited decoration: the subtitle is an INLINE span, so its line boxes
       are sized by max(this block's strut, its own line-height). Left at the
       body's 30px the strut won, and each wrapped line of subtitle came out
       8px too tall - 46px against the live 38, which is the whole 16px the
       card was too tall. */
    line-height: 22px;
}

.sg-nfeat__title {
    display: block;
    font-family: var(--alt-font-family);
    font-size: 15px;
    line-height: 20px;
    font-weight: 500;
    color: #232323;
}

.sg-nfeat__sub {
    /* inline, as production has it: as a block it took the full content width
       and wrapped a line earlier than the original. */
    display: inline;
    font-family: var(--alt-font-family);
    font-size: 13px;
    line-height: 22px;
    font-weight: 400;
    color: #000000;
}

.sg-nfeat__rule {
    display: block;
    width: 100%;
    height: 1px;
    margin-top: 25px;
    /* bg-medium-light-gray */
    background-color: #ededed;
}

.sg-nfeat__body {
    width: 100%;
    margin: 25px 0 0;      /* last-paragraph-no-margin */
    font-size: 15px;
    line-height: 30px;
    font-weight: 400;
    color: #000000;
}

.sg-nfeat__num {
    position: absolute;
    right: 50px;
    bottom: -20px;
    margin: 0;
    font-family: var(--alt-font-family);
    font-size: 47.6px;
    line-height: 54.6px;
    font-weight: 500;
    letter-spacing: -2px;
    opacity: 0.2;
    color: #000000;
}

@media (max-width: 767px) { .sg-nfeat__num { bottom: -15px; } }

/* `h-100` needs a height to inherit, same problem as `.sg-ccard` above: the
 * grid cell (`.b-grid__cell`) stretches to the row, but the `.block-styled`
 * wrapper each block renders inside it is a plain block of content height,
 * so the card's `height: 100%` resolves against `auto` and does nothing -
 * every card sits at its own height instead of matching the row's tallest.
 * Unlike `.sg-ccard`, this card wants FULL width, so the chain is left at
 * the flex default `stretch` on the cross axis (no `align-items: flex-start`
 * override) while still passing the row's height down column-wise. The
 * wrapper's default `pb-6` is zeroed for the same reason as `.sg-ccard`'s -
 * it sat inside the flexed height and left a gap under the card that
 * production doesn't have. */
.b-grid:has(.sg-nfeat) {
    align-items: stretch;
}

.b-grid__cell:has(.sg-nfeat) {
    display: flex;
}

.b-grid__cell:has(.sg-nfeat) > .block-styled {
    display: flex;
    flex-direction: column;
    flex: 1 1 auto;
    padding-bottom: 0;
}

/* This card's row is `row-cols-lg-4` (via col-lg-3) with a single centred
 * `col-md-9` below that, not the generic `.b-grid` ladder's 2-up stop at
 * <992 (right for the icon-circle cards' row-cols-sm-2, wrong here) — the
 * row goes straight from 4-up to ONE column, just narrower, until col-12
 * takes over below md. Scoped to :has(.sg-nfeat) so the other grids sharing
 * `.b-grid` keep their own ladder. */
@media (max-width: 991px) {
    .b-grid:has(.sg-nfeat) { grid-template-columns: minmax(0, 1fr); }
    .b-grid__cell:has(.sg-nfeat) { margin-bottom: 30px; }  /* md-margin-30px-bottom */
}

@media (min-width: 768px) and (max-width: 991px) {
    /* col-md-9: the single column is 75% of the row, centred. */
    .b-grid__cell:has(.sg-nfeat) { max-width: 75%; margin-inline: auto; }
}

/* ---------------------------------------------------------------------------
 * Contact card — .sg-ccard  (legacy: Contact_Feature)
 *
 * Litho markup this reproduces:
 *   .feature-box.feature-box-hide-show-hover.bg-white.border-radius-6px
 *               .overflow-hidden.box-shadow-large.box-shadow-extra-large-hover
 *               .h-100.d-flex.flex-column
 *     .feature-box-move-bottom-top.padding-5-rem-lr.padding-15px-tb
 *       .feature-box-icon > i.icon-medium.text-theme.margin-35px-bottom
 *       .feature-box-content.last-paragraph-no-margin
 *          > span.text-extra-dark-gray.text-extra-medium.d-block.alt-font
 *                .font-weight-500
 *          > a.alt-font.text-decoration-underline
 *       .move-bottom-top.margin-10px-top > p
 *
 * Every value measured on production's /en/contact-page/:
 *   card    292.1x233  #fff  radius 6px  box-shadow 0 0 30px rgba(0,0,0,.08)
 *           flex column, centred, overflow hidden
 *   panel   padding 15px 70px, translateY(30px) AT REST
 *   icon    40px/40px #00aeff, margin-bottom 35px
 *   title   Outfit 16px/28px w500 #232323   (`text-extra-medium`)
 *   link    Outfit 15px/30px #828282, underlined
 *   reveal  margin-top 10px, opacity 0, translateY(20px) AT REST
 *
 * The width is INTRINSIC: the card is a flex item in a `d-flex` column and is
 * never stretched, so 292.1 is the title's 152.1px plus the 140px of side
 * padding. Giving it `width: 100%` would make it 360 and centre the text in
 * the wrong place.
 *
 * The hover moves the panel and reveals the description together, which is
 * what keeps the card's height fixed - the description occupies its space at
 * rest, invisibly, and the 30px offset is what makes the visible block look
 * vertically centred until you hover.
 * ------------------------------------------------------------------------ */
/* `h-100` needs a height to inherit, and the chain does not supply one: the
   grid cell stretches to the row (CSS grid's default) but the `.block-styled`
   wrapper inside it is a plain block of content height, so `height: 100%` on
   the card resolved against `auto` and did nothing. Making both a flex column
   passes the row height down. `align-items: flex-start` is what keeps
   `fit-content` below meaningful - a stretching flex item would be forced back
   to the full 360px. */
/* `.b-grid` is `align-items: start`, which is right for the disc cards but
   leaves these cells at their own content height, so the short card could not
   reach the row height however the flex chain was set up. Scoped to this card
   so the other grids keep their alignment. */
.b-grid:has(.sg-ccard) {
    align-items: stretch;
}

.b-grid__cell:has(.sg-ccard) {
    display: flex;
}

.b-grid__cell:has(.sg-ccard) > .block-styled {
    display: flex;
    flex-direction: column;
    align-items: flex-start;
    flex: 1 1 auto;
    /* base's styled wrapper defaults to `pb-6` (21px at this root), which sat
       INSIDE the cell and so came off the card's height - 203 against the live
       233 - and added a gap under the row that production has only below its
       md breakpoint, where the columns stack. */
    padding-bottom: 0;
}

@media (max-width: 991px) {
    .b-grid__cell:has(.sg-ccard) > .block-styled {
        margin-bottom: 30px;       /* md-margin-30px-bottom */
    }
}

@media (max-width: 575px) {
    .b-grid__cell:has(.sg-ccard) > .block-styled {
        margin-bottom: 15px;       /* xs-margin-15px-bottom */
    }
}

.sg-ccard {
    display: flex;
    flex-direction: column;
    /* INTRINSIC width, as on production: the card is a flex item in a `d-flex`
       column and is never stretched, so a short card is only as wide as its
       widest line plus the 140px of side padding - measured 292.1 against the
       360px cell. Stretched, the text centred in the wrong place. The two
       cards whose copy already overflows 360 are unaffected, which is why only
       one of the three looked wrong. */
    width: fit-content;
    max-width: 100%;
    /* Fills the row height rather than `height: 100%`, since the parent is now
       a flex column and this is its main axis. */
    flex: 1 1 auto;
    background-color: #ffffff;
    border-radius: 6px;
    box-shadow: 0 0 30px rgba(0, 0, 0, 0.08);
    overflow: hidden;
    text-align: center;
}

/* `box-shadow-extra-large-hover` */
.sg-ccard:hover {
    box-shadow: 0 0 50px rgba(0, 0, 0, 0.12);
}

.sg-ccard__panel {
    padding: 15px 5rem;
    transform: translateY(30px);
    transition: transform 0.3s ease;
}

.sg-ccard:hover .sg-ccard__panel {
    transform: translateY(0);
}

@media (max-width: 1199px) { .sg-ccard__panel { padding: 15px 2.5rem; } }
@media (max-width: 991px)  { .sg-ccard__panel { padding: 15px 4.5rem; } }

.sg-ccard__icon i {
    font-size: 40px;               /* .icon-medium */
    line-height: 40px;
    margin-bottom: 35px;
    display: inline-block;
    color: var(--theme-color, #00aeff);
}

.sg-ccard__title {
    display: block;
    font-family: var(--alt-font-family);
    font-size: 16px;               /* .text-extra-medium */
    line-height: 28px;
    font-weight: 500;
    color: #232323;
}

.sg-ccard__link {
    font-family: var(--alt-font-family);
    font-size: 15px;
    line-height: 30px;
    font-weight: 400;
    color: #828282;
    text-decoration: underline;
}

.sg-ccard__reveal {
    margin-top: 10px;
    opacity: 0;
    transform: translateY(20px);
    transition: opacity 0.3s ease, transform 0.3s ease;
}

.sg-ccard:hover .sg-ccard__reveal {
    opacity: 1;
    transform: translateY(0);
}

.sg-ccard__body {
    margin: 0;                     /* last-paragraph-no-margin */
    font-size: 15px;
    line-height: 30px;
    font-weight: 400;
    color: #000000;
}

@media (prefers-reduced-motion: reduce) {
    .sg-ccard__panel,
    .sg-ccard__reveal { transition: none; }
}

/* ---------------------------------------------------------------------------
 * Stat panel — .sg-statpanel  (legacy: Stats_Counter_Block)
 *
 * Litho markup this reproduces:
 *   .row.overlap-section  (margin-top set in JS)
 *     .col-12.box-shadow-medium.bg-white.padding-4-rem-all
 *       .row.row-cols-1.row-cols-md-3
 *         .col.border-right.border-color-medium-gray.text-center
 *           h4.alt-font.font-weight-500.mb-2.counter.text-theme[data-to]
 *           div.text-center
 *             > span.alt-font.text-extra-dark-gray.font-weight-500
 *                   .line-height-14px.d-block
 *             > span.alt-font
 *
 * Every value measured on production's /en/our-services/:
 *   panel    1110x240.6 at x=165, padding 56px, #fff,
 *            box-shadow 0 0 20px rgba(0,0,0,.08)
 *   cell     311.7 wide, centred, 1px right border #e4e4e4 (last: none)
 *   number   Outfit 39.9px/47.6px w500 #00aeff, ls -0.5px, 7px below
 *   label    Outfit 15px/14px w500 #232323, block
 *   caption  Outfit 15px/30px w400 #000, inline
 *
 * The number is a BLOCK filling the cell, not a shrink-to-fit flex item -
 * `text-align: center` is what centres it, so the element must span the cell
 * for the text to land where production's does.
 * ------------------------------------------------------------------------ */
.sg-statpanel {
    /* The negative top margin is applied by initStatPanel() in synergas.js.
       Litho computes it as -(panelHeight / 2) - sectionPaddingTop, so it
       cannot live here. */
    width: 100%;
}

.sg-statpanel__inner {
    display: grid;
    grid-template-columns: repeat(var(--sg-stat-cols, 3), minmax(0, 1fr));
    align-items: center;
    /* 41px, not 56, because the cells carry the other 15. Production nests a
       Bootstrap `.row` inside the padded panel, so the row reclaims 15px a
       side and each cell re-adds it: the cells come out 342.7 wide, not the
       332.7 they measure if the panel keeps all 56 and the grid has none.
       41 + 15 puts the text back at the same 56px from the panel edge and the
       dividers at the same x, without a third element to hold the row. */
    padding: 4rem calc(4rem - 15px);                /* padding-4-rem-all */
    background-color: #ffffff;
    box-shadow: 0 0 20px rgba(0, 0, 0, 0.08);       /* box-shadow-medium */
}

.sg-statpanel__cell {
    text-align: center;
    padding-inline: 15px;

    /* `border-right border-color-medium-gray`, dropped on the last cell. */
    border-right: 1px solid #e4e4e4;
}

.sg-statpanel__cell:last-child { border-right: 0; }

.sg-statpanel__num {
    display: block;
    margin: 0 0 7px;                                /* mb-2 */
    font-family: var(--alt-font-family);
    font-size: 39.9px;
    line-height: 47.6px;
    font-weight: 500;
    letter-spacing: -0.5px;
    color: var(--theme-color, #00aeff);             /* text-theme */
}

.sg-statpanel__label {
    display: block;
    font-family: var(--alt-font-family);
    font-size: 15px;
    line-height: 14px;                              /* line-height-14px */
    font-weight: 500;
    color: #232323;
}

.sg-statpanel__caption {
    font-family: var(--alt-font-family);
    font-size: 15px;
    line-height: 30px;
    font-weight: 400;
    color: #000000;
}

/* Litho drops the dividers and stacks the cells below its sm breakpoint, and
   removes the panel's own padding and shadow so it reads as plain text. */
@media (max-width: 767px) {
    .sg-statpanel__inner {
        grid-template-columns: minmax(0, 1fr);
        padding: 0;                                 /* sm-no-padding-tb, and
                                                       the cells keep 15px */
        box-shadow: none;                           /* sm-box-shadow-none */
    }
    .sg-statpanel__cell {
        border-right: 0;
        margin-bottom: 30px;                        /* sm-margin-30px-bottom */
    }
    .sg-statpanel__cell:last-child { margin-bottom: 0; }
}

/* ---------------------------------------------------------------------------
 * Checklist — .sg-check  (legacy: AnimatedHeadline_Checklist_Block)
 *
 * Litho markup this reproduces:
 *   h6.alt-font.font-weight-600.margin-20px-bottom
 *   ul.w-90.list-style-01.font-weight-500.text-extra-dark-gray
 *     li.margin-10px-bottom
 *       > i.{icon}.text-extra-medium-gray.margin-10px-right.text-theme
 *       > span.list-hover.bg-white.box-shadow-small.border-radius-5px
 *
 * Every value measured on production's /en/our-services/maintenance-services/:
 *   list   width 90%, 15px/30px, weight 500, #232323, no markers
 *   row    padding 12px 25px 14px, margin-bottom 10px, height 56
 *   icon   14px/14px weight 900 #00aeff, margin-right 10px
 *
 * The row's padding exists to shape `.list-hover`, the white panel revealed
 * behind the text on hover - so it has to be here even though nothing is
 * visible in the resting state.
 * ------------------------------------------------------------------------- */
.sg-check {
    width: 90%;                          /* w-90 */
    /* `.sg-check` is a block-level `<ul>` with a fixed width, so `text-align`
       on an ancestor (the editor's generic "Text alignment" setting, or
       anything else) can never center it - that only centers inline content,
       never a block box's own width. Scoping this to `.text-center` and
       requiring the editor to opt in left it uncentered by default, which
       is wrong for how this block is actually used: as a single full-width
       column (paragraph above, checklist below), never a left-half of a
       two-column row. Center it unconditionally, the same way
       `.sg-tslider__info-inner` centers itself regardless of text-align. */
    margin: 0 auto;
    padding: 0;
    list-style: none;
    font-size: 15px;
    line-height: 30px;
    font-weight: 500;
    color: #232323;
}

@media (max-width: 1199px) { .sg-check { width: 100%; } }   /* lg-w-100 */
@media (max-width: 767px)  { .sg-check { width: 85%; } }    /* sm-w-85 */
@media (max-width: 575px)  { .sg-check { width: 100%; } }   /* xs-w-100 */

.sg-check__item {
    position: relative;
    margin: 0 0 10px;
    padding: 12px 25px 14px;
    /* The row itself shifts on hover, not just the panel behind it - a plain
       opacity fade on `.sg-check__hover` reads as "nothing happened" on this
       project's plain white section background, where the white panel it
       fades in has nothing to contrast against. */
    transition: transform 0.25s ease;
}

.sg-check__item:hover {
    transform: translateX(6px);
}

.sg-check__icon {
    /* `text-theme`, and `text-extra-medium-gray` before it in the class list
       loses - production's check marks are cyan, not grey. */
    color: var(--theme-color, #00aeff);
    font-size: 14px;
    line-height: 14px;
    margin-right: 10px;
    transition: color 0.25s ease;
}

.sg-check__item:hover .sg-check__icon {
    color: var(--alt-theme-color, #232323);
}

.sg-check__text,
.sg-check__icon {
    position: relative;
    z-index: 1;
}

.sg-check__hover {
    position: absolute;
    inset: 0;
    z-index: 0;
    background-color: #ffffff;
    border-radius: 5px;
    box-shadow: 0 4px 20px rgba(0, 0, 0, 0.12);   /* box-shadow-small, upsized
                                                       so it reads over a
                                                       same-colour page bg */
    opacity: 0;
    transition: opacity 0.25s ease;
}

.sg-check__item:hover .sg-check__hover { opacity: 1; }

@media (prefers-reduced-motion: reduce) {
    .sg-check__item,
    .sg-check__icon,
    .sg-check__hover { transition: none; }
    .sg-check__item:hover { transform: none; }
}

.sg-check__title {
    font-family: var(--alt-font-family);
    font-weight: 600;
    margin: 0 0 20px;                    /* margin-20px-bottom */
}

/* ---------------------------------------------------------------------------
 * Location map — .location-map  (legacy: Location_Map_Block)
 *
 * The container already matches production exactly: 1440x500 at x=0,
 * transparent, no filter. One thing did not - cv's `location_map.html` sets an
 * inline `border-radius: 12px`, and Litho's map div is square-cornered
 * (`w-100 h-500px`, no radius). Inline, so it needs !important; overriding the
 * base template would mean forking it for one declaration.
 * ------------------------------------------------------------------------ */
.location-map {
    border-radius: 0 !important;
}

/* ---------------------------------------------------------------------------
 * Mobile navigation  (< 992px)
 *
 * The legacy nav is `navbar navbar-expand-lg`, so it collapses below **992px**
 * - Bootstrap's lg, not a Tailwind breakpoint. base's nav had no mobile state
 * at all: the links stayed in a flex row with `flex-wrap`, so at 390px the
 * header measured **623px tall** against production's 75.4 and the menu
 * covered three quarters of the screen.
 *
 * Everything here is measured on production at a true 390px viewport. Getting
 * that measurement needed a harness change of its own - Chrome's headless
 * window will not go below 500px wide, so `probe_parity.py` renders the page
 * in a 390px iframe below that floor. Before it did, every "390px" reading was
 * silently a 500px one.
 *
 *   header      75.4 tall (74.4 + a 1px bottom border), background #fff
 *   container   padding 0; logo 36px tall at x=15
 *   toggle      22x14, margin 30px 0 28px, four 14x2 lines in #232323
 *   panel       full width, #fff, box-shadow 0 20px 15px rgba(23,23,23,.05)
 *   list        padding 15px 0 25px
 *   link        block, padding 9px 15px, 15px/30px, weight 500, #232323
 *   caret       a 48x48 tap target at the right edge
 *   submenu     padding 0 15px 20px
 *
 * The header is OPAQUE WHITE on mobile. On desktop it is transparent over the
 * hero and only turns white once scrolled; below lg it is white from the
 * start, which is why `nav.fixed`'s `background-color: transparent
 * !important` has to be undone here rather than merely overridden.
 * ------------------------------------------------------------------------ */
@media (max-width: 991px) {
    nav.fixed {
        background-color: #ffffff !important;
        border-bottom: 1px solid rgba(0, 0, 0, 0.06);
    }

    nav.fixed > div {
        /* Litho's `.nav-header-container` has no padding of its own - the 15px
           to the logo comes from the logo column. `flex-wrap` is what lets the
           panel take a second row below the header. */
        padding-inline: 0;
        min-height: 74px;
        flex-wrap: wrap;
        align-items: center;
    }

    /* The logo column's 15px gutter, and the 36px `mobile-logo`. */
    nav.fixed > div > a {
        padding-left: 15px;
    }

    .sg-nav__toggle {
        /* Four 2px lines spread across 14px by `space-between`, giving three
           2px gaps - which is how production's four `.navbar-toggler-line`
           spans fit a 14px button with `margin-top: 0` on each. Spacing them
           with a 3px margin instead came to 17px and overflowed the button. */
        display: flex;
        flex-direction: column;
        justify-content: space-between;
        width: 22px;
        height: 14px;
        margin: 30px 15px 28px auto;
        padding: 0;
        border: 0;
        background: none;
        cursor: pointer;
    }

    .sg-nav__toggle-line {
        display: block;
        width: 100%;
        height: 2px;
        margin: 0;
        background-color: #232323;
    }

    .sg-nav__collapse {
        /* Hidden until the toggle opens it. `flex-basis: 100%` puts it on the
           row below the logo and the hamburger rather than beside them. */
        display: none;
        flex: 0 0 100%;
        max-width: 100%;
        background-color: #ffffff;
        box-shadow: 0 20px 15px rgba(23, 23, 23, 0.05);
    }

    .sg-nav__collapse.is-open { display: block; }

    .sg-nav__links {
        display: flex;
        flex-direction: column;
        align-items: stretch;
        gap: 0;
        width: 100%;
        padding: 15px 0 25px;
        font-size: 15px;
    }

    .sg-nav__item {
        position: relative;
        width: 100%;
    }

    .sg-nav__link {
        display: block;
        width: 100%;
        padding: 9px 15px;
        font-size: 15px;
        line-height: 30px;
        font-weight: 500;
        color: #232323;
    }

    /* Litho's caret is a 48x48 tap target pinned to the right edge, NOT a
       glyph beside the label - at 390px a caret inline with the text left the
       link's tap area ending mid-row. */
    .sg-nav__item--has-children > .sg-nav__link {
        padding-right: 48px;
    }

    .sg-nav__caret {
        position: absolute;
        top: 0;
        right: 0;
        display: block;
        width: 48px;
        height: 48px;
        padding: 0;
        border: 0;
        background: none;
        font-size: 15px;
        line-height: 48px;
        text-align: center;
        color: #232323;
        cursor: pointer;
    }

    /* Accordion, not hover: there is no hover on a touch screen, so the
       desktop `:hover`/`:focus-within` reveal has to be switched off here or
       a submenu would open the moment a link took focus and never close. */
    .sg-nav__submenu,
    .sg-nav__item--has-children:hover .sg-nav__submenu,
    .sg-nav__item--has-children:focus-within .sg-nav__submenu {
        display: none;
    }

    .sg-nav__item.is-expanded > .sg-nav__submenu {
        display: block;
        position: static;
        width: 100%;
        min-width: 0;
        padding: 0 15px 20px;
        border: 0;
        border-radius: 0;
        box-shadow: none;
        background: none;
        transform: none;
        opacity: 1;
        visibility: visible;
    }

    .sg-nav__sublink {
        display: block;
        padding: 6px 0;
        font-size: 13px;
        line-height: 30px;
        color: #232323;
    }

    /* The language switcher stays. Production has TWO of them: a
       `language-switcher-column` that is `d-none d-lg-block` (desktop only)
       AND an `li.nav-item.dropdown.language-switcher` inside the menu list
       itself, which is what a phone gets. Reading only the first one, this
       hid the switcher on mobile entirely - there is no other way to change
       language on a phone, so that was a functional loss, not a cosmetic one.
       We have a single `.sg-langs`, so on mobile it becomes a menu row. */
    .sg-nav__actions {
        display: flex;
        flex-direction: column;
        align-items: stretch;
        width: 100%;
        padding: 0;
    }

    .sg-langs {
        width: 100%;
        /* The desktop rule gives it side margin to sit in the header row; in
           the panel that pushed the row 5.5px off the left edge. */
        margin: 0;
    }

    .sg-langs__toggle {
        /* Production's mobile switcher row is 33px tall, not the 48 of a menu
           link: `padding: 9px 15px` around a 15px line. The desktop rule sets
           a 72px line-height to fill the header bar, which made this row 72
           tall inside the panel. */
        width: auto;
        height: auto;
        min-height: 0;
        justify-content: flex-start;
        padding: 9px 15px;
        font-size: 15px;
        line-height: 15px;
        font-weight: 500;
        color: #232323;
    }

    .sg-langs__menu {
        /* Static so it pushes the panel open like the submenu accordions
           rather than floating over them, but CLOSED at rest: production's
           mobile switcher row is 34px tall, and left open this made it 118 and
           pushed the panel 27px past the target's height.

           `:focus-within` is the reveal, not `:hover` - a tap on the <button>
           focuses it, so this works on a touch screen with no JS, the same
           mechanism the desktop switcher already uses. */
        display: none;
        position: static;
        width: 100%;
        min-width: 0;
        padding: 0 15px 10px;
        border: 0;
        border-radius: 0;
        box-shadow: none;
        transform: none;
    }

    .sg-langs:focus-within .sg-langs__menu { display: block; }

    /* `cta-button-column .header-button` is `d-none d-md-inline-block`, so the
       JCC button appears from 768px up: present in the tablet band of the
       collapsed nav, absent on a phone. Targeted by href because
       `nav_menu.html` renders it with base's Tailwind classes and gives it no
       hook of its own - the same selector the desktop rule above uses. */
    nav.fixed a[href*="jccsmart.com"] {
        margin: 0 15px 15px;
    }
}

@media (max-width: 767px) {
    nav.fixed a[href*="jccsmart.com"] { display: none; }
}

/* Desktop is unchanged. `display: contents` dissolves the collapse wrapper so
   the menu and the actions stay DIRECT flex children of the header row - the
   verified three-group `justify-between` layout depends on that, and a plain
   `display: block` wrapper would have collapsed them into one group and
   pushed the first menu item from x=385 to x=592, which is exactly the
   regression the comment in base.html warns about.

   `flex-wrap: nowrap` is added here rather than left to base.html's own
   `flex-wrap` utility class: that class is unconditional (it also drives the
   *mobile* two-row layout, where the collapse panel needs to drop to a second
   row), so above 992px nothing had ever put the row back to one line. Once
   the three groups' combined width passed the `max-w-6xl` container - which
   the six-item Our Services dropdown's parent link plus the language switcher
   and the JCC button did well before 1200px - the header wrapped onto a
   second row instead of staying the single fixed-height bar production has
   at every desktop width. Litho's own navbar-expand-lg never wraps above its
   breakpoint; only the toggler/collapse can move content to another row, and
   only below it. */
@media (min-width: 992px) {
    nav.fixed > div { flex-wrap: nowrap; }
    .sg-nav__collapse { display: contents; }
    .sg-nav__toggle { display: none; }
    .sg-nav__caret { display: none; }
}

/* 992-1199px squeeze: the header's natural content (logo + 5 links + language
   switcher + CTA) needs ~985px, but the max-w-6xl container only offers
   ~944px at a 992px viewport (less its px-6 padding). `flex-wrap: nowrap`
   above stops the whole ROW from wrapping, but with no flex-shrink override
   the individual `.sg-nav__link` boxes - `display: inline-block`, sized by
   flexbox rather than by their own text - get squeezed narrower than their
   label, and the label text wraps onto a second line instead. Every dropdown
   item does this AT ONCE, so the header shows a "first word" row (About, Our,
   Our, Distribution, Contact) and a "second word" row (Us, Products,
   Services, Network, Us) - which is what a DevTools panel docked to the side
   puts you at constantly, since it narrows the viewport into exactly this
   band.

   Trimming link padding and closing the inter-item gap reclaims ~76px, which
   closes the shortfall. Scoped to 992-1199px only - the wider desktop
   measurements (padding 0 16px, gap-1) are already verified against
   production and untouched above 1199px. */
@media (min-width: 992px) and (max-width: 1199px) {
    .sg-nav__link { padding: 0 10px; }
    .sg-nav__links { gap: 0; }
}

/* Belt and braces for the same squeeze: `white-space: nowrap` above stops the
   label text itself from wrapping, but the flex item around it could still
   shrink below that nowrap text's width and clip it. `flex-shrink: 0` on
   every top-level nav item, plus the language switcher and the CTA, keeps
   each at its natural width - if the viewport is narrower than all of them
   combined, the row now overflows instead of mangling any one label, which
   is far less jarring than text silently wrapping mid-word. */
@media (min-width: 992px) {
    .sg-nav__item,
    .sg-langs,
    nav.fixed a[href*="jccsmart.com"] {
        flex-shrink: 0;
    }
}

/* ---------------------------------------------------------------------------
 * Reveal-on-scroll safeguard
 *
 * WOW.js sets `visibility: hidden` INLINE on every `.wow` box that is not yet
 * in view, and it does that regardless of the reader's motion preference -
 * animate.css's own `prefers-reduced-motion` block disables the ANIMATION but
 * cannot un-hide the element. Measured: 13 of the 16 `.wow` boxes on
 * /en/our-services/ are hidden at load with reduced motion forced, exactly as
 * without it.
 *
 * A reduced-motion reader would therefore still meet blank sections and have
 * to scroll to fill them in, with no fade to explain why. So opt them out of
 * the mechanism entirely: nothing is ever hidden, everything is simply there.
 * `!important` because WOW.js's hiding is an inline style.
 *
 * This also protects the parity harness, which forces reduced motion - though
 * note the measurements were never wrong: `visibility: hidden` suppresses
 * PAINT, not layout, so geometry and computed styles read correctly through
 * it. That is why every probe in this file's history agreed with production
 * while the screenshots came out washed out.
 * ------------------------------------------------------------------------ */
@media (prefers-reduced-motion: reduce) {
    .wow {
        visibility: visible !important;
        animation: none !important;
    }
}

/* ---------------------------------------------------------------------------
 * Team card — .sg-team  (legacy: team_cards_block / Team_Grid_Block)
 *
 * Litho's `team-style-02`. Markup this reproduces:
 *   div.col.team-style-02.text-center > a[href=member page]
 *     figure
 *       .team-member-image.border-radius-5px.overflow-hidden (aspect 3/4)
 *         img.w-100.object-fit-cover
 *         .team-member-details.bg-theme.d-flex.flex-column
 *                             .padding-2-half-rem-lr
 *           > div.text-white.opacity-7.w-85   (the bio)
 *           > .social-icon.w-100.position-absolute.bottom-40px.left-0px
 *       figcaption.team-member-position.padding-35px-tb
 *         > div.text-extra-dark-gray.alt-font.line-height-18px.text-medium
 *               .text-uppercase.font-weight-500
 *         > span.text-small.text-uppercase
 *
 * Measured on production's own card markup (which arrives by AJAX), 4-up at
 * 1440:
 *   cell     360x558, padding 0 15px
 *   media    330x440 (3/4), radius 5px
 *   photo    330x440, object-fit cover
 *   details  330x440, padding 0 35px, flex column centred
 *   bio      white at opacity .7, width 85%
 *   social   absolute, bottom 40px, full width; icon 13px white
 *   caption  padding 35px 0
 *   name     14px/18px w500 #232323 uppercase
 *   role     13px/22px w400 uppercase
 *
 * THE HOVER IS A SLIDE, NOT A FADE. Litho parks the panel at `top: 100%` -
 * directly below the photo, clipped by `overflow: hidden` - and moves it to
 * `top: 0` on hover over 0.3s ease-in-out. Fading it in would be visibly the
 * wrong motion, and the panel is the whole point of the component: it is what
 * carries the bio and the LinkedIn link.
 * ------------------------------------------------------------------------- */
.sg-team { text-align: center; }

.sg-team__link {
    display: block;
    color: inherit;
    text-decoration: none;
}

.sg-team__figure { margin: 0; }

.sg-team__media {
    position: relative;
    overflow: hidden;              /* clips the parked panel */
    border-radius: 5px;
    aspect-ratio: 3 / 4;
}

.sg-team__photo {
    display: block;
    width: 100%;
    height: 100%;
    object-fit: cover;
    border-radius: 5px;
}

.sg-team__photo--empty { background-color: #f7f7f7; }

.sg-team__details {
    position: absolute;
    left: 0;
    top: 100%;                     /* parked below the photo */
    width: 100%;
    height: 100%;
    padding: 0 2.5rem;             /* padding-2-half-rem-lr */
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    background-color: var(--theme-color, #00aeff);
    transition: top 0.3s ease-in-out;
}

.sg-team__figure:hover .sg-team__details,
.sg-team__link:focus-visible .sg-team__details { top: 0; }

@media (prefers-reduced-motion: reduce) {
    .sg-team__details { transition: none; }
}

.sg-team__bio {
    width: 85%;                    /* w-85 */
    color: #ffffff;
    opacity: 0.7;                  /* opacity-7 */
}

.sg-team__bio p { margin: 0; }

.sg-team__social {
    position: absolute;
    left: 0;
    bottom: 40px;                  /* bottom-40px */
    width: 100%;
}

.sg-team__social-link {
    color: #ffffff;
    font-size: 13px;
    line-height: 13px;
    display: inline-block;
}

/* `.social-icon a:hover i { transform: scale3d(1.3, 1.3, 1.3) }` */
.sg-team__social-link i { transition: transform 0.2s cubic-bezier(0.2, 0, 0.3, 1); }
.sg-team__social-link:hover i { transform: scale3d(1.3, 1.3, 1.3); }

.sg-team__caption {
    padding: 35px 0;               /* padding-35px-tb */
    text-align: center;
}

@media (max-width: 767px) {
    .sg-team__caption { padding: 25px 0; }   /* sm-padding-25px-tb */
}

.sg-team__name {
    font-family: var(--alt-font-family);
    font-size: 14px;               /* text-medium */
    line-height: 18px;             /* line-height-18px */
    font-weight: 500;
    text-transform: uppercase;
    color: #232323;
}

.sg-team__role {
    font-size: 13px;               /* text-small */
    line-height: 22px;
    font-weight: 400;
    text-transform: uppercase;
    color: #000000;
}

/* The team grid is the one grid that is NOT flush with the page container.
   `TeamGridBlock.html` opens
   `container-fluid padding-seven-lr xl-padding-three-lr md-padding-2-half-rem-lr
   xs-padding-15px-lr` INSIDE the section's container, so it insets itself.
   Measured on production at 1440: the fluid box is 1140 wide (our `--sg-row`)
   with 34.19px of padding a side - 3% - giving a 275.4 cell and a 245.4 photo.
   Ours was 262.5, i.e. 17px too wide, because the inset was missing entirely.

   The percentage sits here alongside `.b-grid`'s -15px reclaim, so it resolves
   against the reclaimed 1170 rather than the 1140: 3% of 1170 is 35.1, which
   lands the cell at 274.95 against production's 275.4. */
.b-grid--members {
    padding-inline: 3%;
}

/* The team grid's own column ladder. Litho's row is
   `row-cols-1 row-cols-sm-2 row-cols-lg-3 row-cols-xl-4`, i.e. 4 from 1200,
   3 from 992, 2 from 576 and ONE below that.

   brilliance ships its own ladder for this grid at `.b-grid.b-grid--members`
   (0,2,0) - `repeat(3)` below 1024 and `repeat(2)` below 600 - which
   outranked our `.b-grid` rules at (0,1,0) and left the cards 2-up at 390
   where production shows one, and 3-up at 700 where it shows two. Same
   selector shape, loaded later, so these win on order.

   Verified 1-up at 390 on the target (cell 360 at x=15, filling the
   container) and 4-up at 1440 (cell 275.4). */
@media (max-width: 1199px) {
    .b-grid.b-grid--members { grid-template-columns: repeat(3, minmax(0, 1fr)); }
}

@media (max-width: 991px) {
    .b-grid.b-grid--members { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}

@media (max-width: 575px) {
    .b-grid.b-grid--members { grid-template-columns: minmax(0, 1fr); }
}

@media (max-width: 991px) {
    .b-grid--members { padding-inline: 2.5rem; }  /* md-padding-2-half-rem-lr */
}

@media (max-width: 575px) {
    .b-grid--members { padding-inline: 15px; }   /* xs-padding-15px-lr */
}

/* ---------------------------------------------------------------------------
 * The container's own gutter is `--sg-row`, at every width
 *
 * brilliance gives `.fedit-grid:not(.is-fluid)` a mobile gutter of its own -
 * `width: calc(100% - 12px)` and `padding-inline: clamp(20px, 5vw, 48px)` -
 * described in its source as "a small, even side gutter so cards/borders
 * don't touch the screen edge". That is a Brilliance design decision, and it
 * is not Litho's: Litho's container below `sm` is `width: 100%` with 15px of
 * padding, which `--sg-row` already encodes (`calc(100% - 30px)`).
 *
 * Doubling them cost 40px of content at 390: the container grid came out
 * 360 wide with 20px of padding, so 320 of content, and the logo cell 350
 * against production's 390. It only showed on mobile because brilliance scopes
 * the gutter to a max-width band, which is why every desktop measurement in
 * this file agreed while the phone was consistently narrow.
 *
 * With this, `max-width: var(--sg-row)` plus `margin-inline: auto` governs at
 * EVERY width - the same mechanism desktop already used - and the mobile
 * ladder falls out correctly: at 390 a 1-level block gets 360 of content at
 * x=15 (reclaimed to the full 390 by a `.row`-model component) and a 2-level
 * block 330 at x=30, matching Litho's nested containers exactly.
 *
 * NOTE this supersedes an earlier diagnosis of "percentage compounding" in the
 * mobile ladder. There is none: `calc(100% - 30px)` resolves against the
 * section, which is full-width, so it gives 360 correctly. The lost width was
 * always this gutter.
 * ------------------------------------------------------------------------ */
.grid.fedit-grid:not(.is-fluid) {
    width: auto;
    padding-inline: 0;
}

/* ---------------------------------------------------------------------------
 * Litho's `*-rem-*` utilities must stay in REM
 *
 * The root font size is not constant on this site - measured on production
 * AND on ours, identically: 14px at 1440, 12px at 991, 11px at 700 and 390.
 * So `padding-4-rem-all` is 56px on a desktop and 44px on a phone, and every
 * one of these paddings that this file had translated into a fixed pixel
 * value was right only at the breakpoint it was measured at. The numbered
 * feature card was 56px against production's 44px at 390 - 24px of extra
 * height per card - purely from that.
 *
 * Rule for this file: a Litho class whose NAME says rem keeps rem here. Fixed
 * pixels are for classes whose names say pixels (`padding-15px-lr`,
 * `margin-30px-bottom`), which do not scale.
 * ------------------------------------------------------------------------ */

/* The numbered-feature row's columns are `xs-margin-15px-bottom` below sm,
   narrower than the `md-margin-30px-bottom` set above for the rest of the
   <992 range - measured 15px at 390 against the 30px that rule would
   otherwise leave. */
@media (max-width: 575px) {
    .b-grid__cell:has(.sg-nfeat) { margin-bottom: 15px; }
}

/* ---------------------------------------------------------------------------
 * Circular feature card — icon hover fill
 *
 * Production nests an extra element inside the disc that base does not emit:
 *
 *   <div class="feature-box-icon … rounded-circle">
 *     <i class="fa fa-truck icon-large"></i>
 *     <div class="feature-box-icon-hover bg-alt-theme rounded-circle"></div>
 *   </div>
 *
 * Litho's rules for it are only two:
 *   .feature-box-icon-rounded .feature-box-icon-hover { position:absolute;
 *       top:0; left:0; width:100%; height:100%; opacity:0 }
 *   .feature-box:hover .feature-box-icon-hover { opacity:1 }
 * with the 0.4s coming from `.feature-box * { transition-duration: 0.4s }`.
 *
 * Reproduced as a pseudo-element so no template override is needed. Measured on
 * the live home page, hovered:
 *   fill  rgb(218,15,50) = --alt-theme-color, opacity 0 -> 1
 *   icon  rgb(0,0,0) -> rgb(255,255,255), and it stays ABOVE the fill because
 *         production gives it `position: relative; z-index: 1`
 * The disc itself does NOT change colour; only the layer over it does.
 *
 * Scoped with `:not(.b-card--inline)`: `.b-card--inline` is the OTHER
 * `.b-card__glyph` variant (Litho's `feature-box-left-icon`, see ~3170) - a
 * bare 40px icon with no disc at all. Production's plain feature box has no
 * `.feature-box-icon-hover` layer and no hover effect on the icon; unscoped,
 * this fill rule still fired there because `.b-card--inline` only resets the
 * glyph's box (width/background/border-radius), not this `::after`, so an
 * inset:0 circle was appearing (0-sized, since the glyph auto-sizes to the
 * icon) and the icon was flashing white on hover on cards that should not
 * react to hover at all.
 * ------------------------------------------------------------------------ */
.b-card:not(.b-card--inline) .b-card__glyph {
    /* The fill is positioned against the disc, which was static until now. */
    position: relative;
}

.b-card:not(.b-card--inline) .b-card__glyph::after {
    content: "";
    position: absolute;
    inset: 0;
    border-radius: 50%;
    background-color: var(--alt-theme-color, #da0f32);
    opacity: 0;
    transition: opacity 0.4s ease-in-out;
    pointer-events: none;
}

.b-card:not(.b-card--inline):hover .b-card__glyph::after {
    opacity: 1;
}

/* Keeps the glyph over the fill, exactly as production's z-index: 1 does. */
.b-card:not(.b-card--inline) .b-card__glyph i,
.b-card:not(.b-card--inline) .b-card__glyph svg,
.b-card:not(.b-card--inline) .b-card__glyph .cx-icon {
    position: relative;
    z-index: 1;
    transition: color 0.4s ease-in-out;
}

/* (0,3,0), the same specificity as `.b-card--outline .b-card__glyph i` above,
   so this has to stay LATER in the file to win. */
.b-card:not(.b-card--inline):hover .b-card__glyph i,
.b-card:not(.b-card--inline):hover .b-card__glyph svg,
.b-card:not(.b-card--inline):hover .b-card__glyph .cx-icon {
    color: #ffffff;
}

@media (prefers-reduced-motion: reduce) {
    .b-card:not(.b-card--inline) .b-card__glyph::after,
    .b-card:not(.b-card--inline) .b-card__glyph i,
    .b-card:not(.b-card--inline) .b-card__glyph svg,
    .b-card:not(.b-card--inline) .b-card__glyph .cx-icon { transition: none; }
}
