/* ======================================= */
/* ---   THEME VARIABLES               --- */
/* ======================================= */

:root {
    /* --- Light Mode (Default) --- */
    --bs-body-bg: #ffffff;

    /* UPDATED: Brand "Midnight Blue" for text instead of Grey/Black */
    --bs-body-color: #0c1844;

    /* Brand Colors */
    --brand-red: #c80036;
    --brand-red-hover: #d62828;

    /* Backgrounds */
    --bg-primary: #ffffff;
    --bg-secondary: #f8f9fa;
    /* Light Gray */
    --bg-tertiary: #e9ecef;
    --bg-card: #ffffff;
    --bg-nav-mobile: rgba(255, 255, 255, 0.98);

    /* Text */
    /* UPDATED: Main text is now your brand blue */
    --text-main: #0c1844;

    /* ACCESSIBILITY FIX: Darkened from #6c757d to #545b62 for 7:1 contrast on light backgrounds */
    --text-muted: #545b62;

    --text-inverse: #ffffff;

    /* UI Elements */
    --border-color: rgba(12, 24, 68, 0.1);
    /* Adapted border to be subtle blue */
    --input-bg: #ffffff;
    --input-border: #ced4da;

    /* Gradients / Overlays */
    --overlay-start: rgba(255, 255, 255, 0.97);
    --overlay-end: rgba(255, 255, 255, 0.88);
    --overlay-middle: rgba(255, 255, 255, 0.85);
    --overlay-bottom: #ffffff;

    /* Shadows */
    --shadow-soft: 0 10px 30px rgba(12, 24, 68, 0.08);
    /* Blue-tinted shadow */

    /* Hiring notice (see .hiring-notice). A red-tinted surface rather than a
       flat brand-red fill: this is a condition to read before applying, not an
       error, and a saturated block next to the form reads as a failure state.
       Measured against #ffffff page / #0c1844 text: 12.9:1 for the body text,
       4.5:1 for the accent, and 1.32:1 for the surface itself, which is what
       makes it read as a distinct block rather than as part of the page. */
    --notice-bg: #fad7e1;
    --notice-border: #c80036;
    --notice-icon: #c80036;
}

[data-bs-theme="dark"] {
    /* --- Dark Mode --- */
    --bs-body-bg: #0d1218;
    --bs-body-color: #ffffff;

    /* ACCESSIBILITY FIX: Adjusted brand-red for dark mode to improve contrast */
    --brand-red: #eb2258;
    /* Previously #c80036 (2.98:1 on #121820), now #e0446b (4.5:1) */
    --brand-red-hover: #f15881;
    /* Slightly lighter for hover */

    /* Backgrounds */
    --bg-primary: #0d1218;
    --bg-secondary: #000000;
    --bg-tertiary: #1a1d21;
    --bg-card: #212529;
    --bg-nav-mobile: rgba(13, 18, 24, 0.98);

    /* Text */
    --text-main: #ffffff;
    --text-muted: rgba(255, 255, 255, 0.6);
    --text-inverse: #212529;

    /* Hiring notice. The light tint would glare here, so this is the dark-mode
       brand red laid over the page background at 26% -- resolved to an opaque
       value rather than left translucent, so the contrast below holds wherever
       the notice is placed instead of depending on what happens to be behind
       it. Measured against #ffffff text: 14.8:1 body, 4.6:1 accent, 1.27:1 for
       the surface against the page. */
    --notice-bg: #471629;
    --notice-border: #eb2258;
    --notice-icon: #f15881;

    /* UI Elements */
    --border-color: rgba(255, 255, 255, 0.1);
    --input-bg: #121820;
    --input-border: rgba(255, 255, 255, 0.1);

    /* Gradients / Overlays */
    --overlay-start: rgba(13, 18, 24, 0.95);
    --overlay-end: rgba(13, 18, 24, 0.8);
    --overlay-middle: rgba(13, 18, 24, 0);
    --overlay-bottom: rgba(13, 18, 24, 0.95);

    /* Shadows */
    --shadow-soft: 0 10px 30px rgba(0, 0, 0, 0.4);
}

/* --- General Styling --- */
body {
    font-family: 'Poppins', sans-serif;
    background-color: var(--bs-body-bg);
    color: var(--bs-body-color);
    transition: background-color 0.3s ease, color 0.3s ease;
}

/* Ensure Headings specifically use the main text color */
h1,
h2,
h3,
h4,
h5,
h6,
.h1,
.h2,
.h3,
.h4,
.h5,
.h6,
.display-1,
.display-2,
.display-3,
.display-4,
.display-5,
.display-6 {
    color: var(--text-main);
}

/* --- Navigation Layout --- */
nav.navbar {
    position: relative;
    width: 100%;
    z-index: 1030;
    background-color: var(--bs-body-bg);
    box-shadow: 0 2px 15px rgba(0, 0, 0, 0.04);
}

.navbar-brand {
    font-weight: 700;
    font-size: 1.5rem;
}

.nav-link {
    font-weight: 600;
    color: var(--text-main);
    opacity: 0.8;
    transition: color 0.3s ease;
}

.nav-link:hover,
.nav-link.active {
    color: var(--brand-red) !important;
    opacity: 1;
}

/* Force nav links to be visible on dark mode hero backgrounds if needed */
[data-bs-theme="dark"] .nav-link {
    color: rgba(255, 255, 255, 0.8);
}

[data-bs-theme="dark"] .nav-link:hover {
    color: #ffffff !important;
}

/* --- Logo Switching Logic --- */
.logo-light {
    display: block;
}

.logo-dark {
    display: none;
}

[data-bs-theme="dark"] .logo-light {
    display: none;
}

[data-bs-theme="dark"] .logo-dark {
    display: block;
}



/* ======================================= */
/* ---   HERO SECTION                  --- */
/* ======================================= */
.hero-section {
    position: relative;
    min-height: 80vh;
    display: flex;
    align-items: center;
    overflow: hidden;
    padding-top: 0;
    padding-bottom: 120px;
    /* Space for the larger curve */
    background-color: var(--bs-body-bg);
}

.hero-bg-layer {
    position: absolute;
    top: 0;
    right: 0;
    width: 60%;
    /* Covers right side */
    height: 100%;
    background-size: cover;
    background-position: center center;
    /* Focus on the subject */
    background-repeat: no-repeat;
    z-index: 0;
}

.hero-gradient-mask {
    position: absolute;
    top: 0;
    left: 0;
    width: 100%;
    height: 100%;
    z-index: 1;
    background: linear-gradient(90deg,
            var(--bs-body-bg) 40%,
            rgba(255, 255, 255, 0.8) 60%,
            rgba(255, 255, 255, 0) 100%);
}

/* Dark Mode adjustment for the gradient */
[data-bs-theme="dark"] .hero-gradient-mask {
    background: linear-gradient(90deg,
            var(--bs-body-bg) 35%,
            rgba(13, 18, 24, 0.95) 55%,
            rgba(13, 18, 24, 0) 100%);
}

/* The Container for Text (Sits on top of everything) */
.hero-content-layer {
    position: relative;
    z-index: 2;
    width: 100%;
}

/*
   The Curved Bottom SVG
   This creates the "Swoosh" at the bottom
*/
.hero-curve-separator {
    position: absolute;
    bottom: -1px;
    left: 0;
    width: 100%;
    line-height: 0;
    direction: ltr;
    z-index: 3;
    /* Decoration only, and it has to sit above the hero content (z-index 3 vs 2)
       to cover the artwork behind the curve. That means its full-width box also
       lies over the bottom of anything the hero content puts there -- on the
       campaign hero, the lower half of the lead form's Send button, where clicks
       were landing on the svg instead. Nothing in here is interactive, so let
       them through. */
    pointer-events: none;
}

.hero-curve-separator svg {
    display: block;
    width: 100%;
    height: 100px;
    /* CHANGED: Increased from 100px for drama */
    fill: var(--bg-secondary);
}


.contact-hero-section,
.about-hero-section,
.jobs-hero-section,
.case-study-hero-section,
.admin-hero-section {
    padding-top: 50px;
}

/*
   Responsive Adjustments
*/
@media (max-width: 991px) {
    .hero-section {
        min-height: auto;
        padding-bottom: 150px;
        text-align: center;
    }

    /* On mobile, make image cover full background but darker */
    .hero-bg-layer {
        width: 100%;
        opacity: 0.2;
    }

    .hero-gradient-mask {
        /* Vertical fade on mobile */
        background: linear-gradient(to bottom,
                var(--bs-body-bg) 0%,
                transparent 100%);
    }

    .hero-text-column {
        margin: 0 auto;
    }
}

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

/* --- Campaign Hero --- */
/* No navbar on campaign pages: pull the hero content toward the top */
.campaign-hero.hero-section {
    align-items: flex-start;
    padding-top: 40px;
}

/* Fixed-width centered content band + fixed height on desktop so the layout stays
   put across a wide zoom range (only the side margins change, nothing reflows). */
@media (min-width: 992px) {
    .campaign-hero .hero-content-layer.container {
        max-width: 1900px;
        margin-left: auto;
        margin-right: auto;
        padding-left: 3rem;
        padding-right: 3rem;
    }

    /* Viewport-independent height (was 80vh) so the hero doesn't grow/shrink
       vertically when zooming. Mobile keeps min-height:auto from the base rules. */
    .campaign-hero.hero-section {
        min-height: 640px;
    }
}

/* The chosen background, or this default when the editor picked none. The
   header carries the choice as --campaign-hero-bg-image (see the block
   template) because the stacked band below needs the same value.

   Keep the fallback a relative url(): collectstatic rewrites relative
   references to their hashed names, and an absolute /img/... would neither
   cache-bust nor resolve in production. */
.campaign-hero .hero-bg-layer {
    background-image: var(--campaign-hero-bg-image, url("../img/circuitboard-background.a14d13ff1620.avif"));
}


/* Thicker curve fill on the campaign hero */
.campaign-hero .hero-curve-separator svg {
    height: 200px;
    fill: var(--campaign-hero-curve, var(--bg-secondary));
}

/* The curve is the top edge of the section below, so it takes that section's
   colour. --bg-secondary suits the light theme, but it is pure black in dark
   and left a black stripe across the page. Matched to the actual next sibling,
   which works because each block's <section> is a sibling of the hero. */
[data-bs-theme="dark"] .campaign-hero {
    /* Default: the page background, which is what a section with no background
       of its own paints -- most of the blocks that can follow a hero. Listing
       surfaces one by one would leave the rest falling back to black. */
    --campaign-hero-curve: var(--bs-body-bg);
}

/* Every section whose dark surface is `rgba(255, 255, 255, 0.03)` -- grep that
   value to check this list is still complete. A custom property cannot be read
   sideways out of the next sibling, so the hero has to name them; the list is
   the cost of the curve belonging to the hero rather than to the section under
   it. `work-with-section` is deliberately absent: its band starts 168px down
   and it paints no surface of its own, so the region under the curve there is
   plain page background. */
[data-bs-theme="dark"] .campaign-hero:has(+ .services-grid-section),
[data-bs-theme="dark"] .campaign-hero:has(+ .why-us-section),
[data-bs-theme="dark"] .campaign-hero:has(+ .tech-stack-section),
[data-bs-theme="dark"] .campaign-hero:has(+ .meet-us-section),
[data-bs-theme="dark"] .campaign-hero:has(+ .process-section),
[data-bs-theme="dark"] .campaign-hero:has(+ .case-studies-section) {
    /* 3% white over the page: the curve lies over the hero photograph, so it
       needs that as an opaque colour rather than a translucent one. */
    --campaign-hero-curve: color-mix(in srgb, #ffffff 3%, var(--bs-body-bg));
}

[data-bs-theme="dark"] .campaign-hero:has(+ .industries-section) {
    --campaign-hero-curve: var(--bg-card);
}

/* The curve's own fill already reaches the bottom of the hero, but the svg is
   scaled to a fractional height, so its last row of pixels can land just shy of
   the edge and open a hairline onto whatever is behind it. This paints the
   section colour across the bottom of the hero, well inside the area the curve
   already covers, so any such seam is the same colour as both sides of it. */
.campaign-hero .hero-curve-separator::after {
    content: "";
    position: absolute;
    left: 0;
    right: 0;
    bottom: 0;
    height: 40px;
    background-color: var(--campaign-hero-curve, var(--bg-secondary));
}

/* ...and the same hairline can open at the hero's own bottom edge, one level
   up. The hero's background is white, the section under it is not, and the
   hero is `overflow: hidden`, so the curve fill above is clipped at the
   section's fractional bottom (742.19px at one common width) while the next
   section is painted from the next whole pixel down. The row between them is
   neither, and the page background shows through it as a white hairline.

   It is scroll- and DPR-dependent, which is what makes it look like clicking
   something caused it: a smooth scroll to #contact-form lands on a different
   subpixel offset and the seam appears or disappears. Overlapping the next
   section by 1px removes the row entirely rather than trying to land the
   rounding correctly. The bottom 40px of the hero is already painted the next
   section's colour by the rule above, so the overlap is invisible. */
.campaign-hero {
    margin-bottom: -1px;
}

/* Align the copy column to the top, level with the form (row is align-items-center) */
.campaign-hero .hero-text-column {
    align-self: flex-start;
}

.campaign-hero-logo img {
    width: 150px;
    height: auto;
}

.campaign-hero-heading {
    width: 24ch;
    /* breaks "Transforming ideas into / digital solutions" into 2 lines */
    max-width: 100%;
}

/* The richtext heading wraps its content in <p>; keep it inline with the h1 */
.campaign-hero-heading p {
    margin: 0;
    font-size: inherit;
    font-weight: inherit;
    line-height: inherit;
    color: inherit;
}

/* Body copy rather than a caption: a heading's 1.2 leading made six stacked
   lines read as one block of grey. 30ch keeps it clear of the artwork, which
   the copy clearance below reserves space for. */
.campaign-hero-subtitle {
    width: 30ch;
    max-width: 100%;
    margin-top: 2rem;
    color: var(--text-muted);
    font-size: 1.0625rem;
    font-weight: 400;
    line-height: 1.55;
}

/* Push the lead form flush to the right edge */
.campaign-hero-form-col {
    margin-left: auto;
    display: flex;
    justify-content: flex-end;
}

.campaign-hero-form-col .campaign-hero-form-card {
    width: 420px;
    max-width: 100%;
    margin-right: 0;
}

/* Centered image sitting BEHIND the hero content (copy + form overlap it).

   The artwork is deliberately taller than the hero: it runs off the bottom
   edge, and the curve separator hides where it stops so the people read as
   standing behind the curve. That only holds while something clips it, so the
   layer clips it here too rather than relying on the section's own overflow —
   an ancestor that stops clipping (or a browser that disagrees about when it
   should) would otherwise let the artwork show through below the curve, in the
   gap above the section that follows. */
.campaign-hero-center-layer {
    position: absolute;
    inset: 0;
    overflow: hidden;
    z-index: 1;
    /* above bg/mask, below content (z-index 2) */
    pointer-events: none;
}

.campaign-hero-center-img {
    /* Same placement the layer used to carry: centred on 45% across and 68%
       down the hero, so the crop and the composition are unchanged. */
    position: absolute;
    top: 68%;
    left: 45%;
    transform: translate(-50%, -50%);
    width: 100%;
    /* Fixed px cap so zoom / ultrawide don't grow it. Sized for a group shot
       cropped tight to its subjects: at 900px a ~1.6:1 crop stands about 570px
       tall, against the 445px a 700px cap gave it. Wider than this and the group
       reaches across the headline on the left and behind the form card on the
       right, so it stops here rather than at the height the old portrait artwork
       happened to have. Artwork with empty space baked into it will still look
       small: the cap scales the frame, not what is inside it. */
    max-width: 900px;
    height: auto;
}

/* --- Keeping the hero artwork clear of the form card ----------------------

   The artwork is centred on 45% of the hero and capped at 900px, which on a
   wide screen leaves it comfortably left of the form. On a laptop it does not:
   the card is a fixed 480px pinned to the right, so as the viewport narrows the
   card advances on artwork that is not narrowing with it. Between roughly
   1280px and 1780px the overlap runs from 274px down to 32px, and what it
   covers is the right-hand figure -- which is why zooming out "fixed" it, by
   buying CSS pixels the layout was short of.

   So the artwork is bounded by the space actually available beside the card
   instead of by a constant. Two clamps, both no-ops once there is room:

     width  -- never wider than the gap left beside the card
     centre -- never so far right that its edge reaches the card

   Above ~1900px both fall away and the 45%/900px composition is exactly what it
   was. Below that they trade a little size for showing all four people. */
@media (min-width: 992px) {
    .campaign-hero {
        /* The artwork gets a lane of its own: reserving the copy side as well
           as the form side means the lane can only be the gap between them,
           so the artwork can never print over either. */
        --campaign-hero-copy-clearance: 400px;
        /* 420px card + 3rem container padding + 32px of daylight. Tied to
           .campaign-hero-form-card's width: change one, change both. */
        --campaign-hero-form-clearance: 500px;
        --campaign-hero-art-lane: calc(
            100% - var(--campaign-hero-copy-clearance)
                - var(--campaign-hero-form-clearance)
        );
        /* A floor, so a narrow desktop shrinks the people rather than losing
           two of them. Below ~1400px the floor wins and the artwork reaches
           back under the copy. */
        --campaign-hero-art-width: clamp(
            480px,
            var(--campaign-hero-art-lane),
            900px
        );
    }

    .campaign-hero-center-img {
        max-width: var(--campaign-hero-art-width);
        /* Centred in the lane, not on the hero. */
        left: calc(
            var(--campaign-hero-copy-clearance)
                + var(--campaign-hero-art-lane) / 2
        );
    }
}

/* Below 1380px the lane hits the artwork's 480px floor, so the artwork is
   wider than the space left for it and prints over the subtitle again. The
   hero drops the cut-out through this band instead. Under 992px the layout
   stacks and .campaign-hero-center-inline shows it in flow, so this is the
   only range with no cut-out at all. */
@media (min-width: 992px) and (max-width: 1379.98px) {
    .campaign-hero-center-layer {
        display: none;
    }
}

@media (max-width: 991px) {

    /* Center the subtitle with the rest of the stacked hero column */
    .campaign-hero-subtitle {
        margin-left: auto;
        margin-right: auto;
    }
}

.lead {
    color: var(--text-muted) !important;
    font-weight: 400;
}

/*
   ACCESSIBILITY NOTE:
   Ideally, text-white-50 should be white text with 50% opacity.
   However, this override maps it to our accessible grey variable to fix
   legacy usage on light backgrounds.
*/
.text-white-50 {
    color: var(--text-muted) !important;
}

/* --- Buttons --- */
.btn {
    padding: 0.8rem 2rem;
    border-radius: 50px;
    font-weight: 600;
    transition: all 0.3s ease;
    border: 2px solid transparent;
}

.btn-custom-red {
    background-color: #c80036;
    color: #fff;
}

.btn-custom-red:focus,
.btn-custom-red:hover {
    background-color: #c80036;
    color: #fff;
    transform: translateY(-2px);
}

.btn-custom-outline {
    background-color: transparent;
    border-color: var(--text-muted);
    color: var(--text-main);
}

.btn-custom-outline:focus,
.btn-custom-outline:hover {
    background-color: var(--text-main);
    border-color: var(--text-main);
    color: var(--bg-primary);
    transform: translateY(-2px);
}

/* --- Project Cards --- */
.featured-projects {
    background-color: var(--bg-secondary);
    padding: 100px 0;
    position: relative;
}

.view-all-link {
    color: var(--brand-red);
    text-decoration: none;
    font-weight: 600;
    transition: color 0.3s ease;
}

.project-card {
    background-color: var(--bg-card);
    border-radius: 15px;
    overflow: hidden;
    transition: transform 0.3s ease, box-shadow 0.3s ease;
    height: 100%;
    display: flex;
    flex-direction: column;
    box-shadow: var(--shadow-soft);
    border: 1px solid var(--border-color);
}

.project-card:hover {
    transform: translateY(-8px);
    box-shadow: 0 15px 30px rgba(0, 0, 0, 0.1);
}

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

.project-card .card-body {
    padding: 25px;
    flex-grow: 1;
    display: flex;
    flex-direction: column;
}

.project-card h5 {
    font-weight: 700;
    margin-bottom: 0.5rem;
    color: var(--text-main);
}

.project-card p {
    color: var(--text-muted);
}

.project-card .details-link {
    display: inline-flex;
    align-items: center;
    color: var(--brand-red);
    text-decoration: none;
    font-weight: 600;
    align-self: flex-start;
    /* Aligns every arrow in a row, whatever the summary length. */
    margin-top: auto;
}

/* At rest the card shows the arrow alone; the label opens out on hover.

   max-width, not width: the label is translated and a fixed width would clip
   one language. Hidden with overflow, not display:none, so it stays in the
   accessibility tree. No colour change on hover, deliberately. */
.project-card .details-link-label {
    display: inline-block;
    max-width: 0;
    overflow: hidden;
    white-space: nowrap;
    opacity: 0;
    transform: translateX(-0.35rem);
    transition:
        max-width 0.35s ease,
        margin-right 0.35s ease,
        opacity 0.25s ease,
        transform 0.35s ease;
}

/* focus-visible too, so a keyboard user does not land on a bare arrow. */
.project-card:hover .details-link-label,
.project-card .details-link:focus-visible .details-link-label {
    max-width: 14rem;
    margin-right: 0.45rem;
    opacity: 1;
    transform: translateX(0);
}

/* Nothing to hover with, so the label just stays out. */
@media (hover: none) {
    .project-card .details-link-label {
        max-width: 14rem;
        margin-right: 0.45rem;
        opacity: 1;
        transform: none;
    }
}

@media (prefers-reduced-motion: reduce) {
    .project-card .details-link-label {
        transition: none;
    }
}

/* --- Services Section --- */
.services-section {
    background-color: var(--bg-secondary);
    padding: 100px 0;
}

.service-card {
    background-color: var(--bg-card);
    padding: 40px;
    border-radius: 15px;
    border: 1px solid var(--border-color);
    height: 100%;
    transition: all 0.3s ease;
    box-shadow: var(--shadow-soft);
}

.service-card:hover {
    transform: translateY(-8px);
    border-color: var(--brand-red);
    box-shadow: 0 15px 30px rgba(0, 0, 0, 0.1);
}

.service-icon {
    font-size: 2.5rem;
    color: var(--brand-red);
    margin-bottom: 20px;
}

.service-card h5 {
    color: var(--text-main);
}

.service-card p {
    color: var(--text-muted);
}

/* --- Industries Section --- */
.industries-section {
    background-color: var(--bg-card);
    padding: 100px 0;
}

.industry-item {
    text-align: center;
}

.industry-logo {
    width: 180px;
    height: 180px;
    border-radius: 50%;
    object-fit: cover;
    border: 3px solid var(--border-color);
    background-color: var(--bg-secondary);
    padding: 5px;
    transition: all 0.3s ease;
}

.industry-item:hover .industry-logo {
    border-color: var(--brand-red);
    transform: scale(1.05);
}

.industry-item h5 {
    color: var(--text-main);
}

/* Campaign landing industries: a full-width heading band filled with the same
   color as the hero curve separator (--bg-secondary), flush against the curve
   above so they read as one. The fill stops before the industry cards. */
.campaign-industries {
    padding-top: 0;
}

.campaign-industries .industries-heading-band {
    background-color: var(--bg-secondary);
    padding: 60px 0 70px;
    margin-bottom: 70px;
}

/* The band matches the curve above it, so it inherits the curve's dark-theme
   problem: --bg-secondary is black while the section around it is --bg-card,
   and the two together read as one stripe. Both take the section's colour
   instead; light theme is already right. */
[data-bs-theme="dark"] .campaign-industries .industries-heading-band {
    background-color: var(--bg-card);
}

.campaign-industries .industries-heading {
    margin-bottom: 0;
}

/* The rich text heading arrives wrapped in its own <p>; keep it looking like
   the h2 it sits in, the way the other section headings do. */
.industries-heading p {
    margin: 0;
    font-size: inherit;
    font-weight: inherit;
    line-height: inherit;
    color: inherit;
}

/* --- Inner Page Hero Sections --- */
.contact-hero-section,
.about-hero-section,
.jobs-hero-section,
.case-study-hero-section,
.admin-hero-section {
    position: relative;
    padding: 100px 0;
    padding-top: 90px;
    background-color: var(--bg-secondary);
    background-size: cover;
    background-position: center;
}

/* Background Images */
.contact-hero-section {
    background-image: url("../img/contact-hero.24e2f99efe94.jpeg");
}

.about-hero-section {
    background-image: url("../img/cool-lighting-laptop.54c2c1036dcc.png");
}

.jobs-hero-section {
    background-image: url("../img/laptop-with-red-keys.023c2bc370d0.jpeg");
}

.case-study-hero-section {
    background-image: url("../img/case-studies.2551aee42c9c.jpeg");
    display: flex;
    align-items: center;
}

.admin-hero-section {
    background-image: url("../img/admin-hero.023c2bc370d0.jpeg");
}

/* Fade Overlay for Inner Heros */
.contact-hero-section.hero-faded::before,
.about-hero-section.hero-faded::before,
.jobs-hero-section.hero-faded::before,
.case-study-hero-section.hero-faded::before,
.admin-hero-section.hero-faded::before {
    content: '';
    position: absolute;
    top: 0;
    left: 0;
    width: 100%;
    height: 100%;
    background: linear-gradient(to bottom,
            var(--overlay-start) 0%,
            var(--overlay-middle) 50%,
            var(--overlay-bottom) 100%);
    z-index: 2;
}

/* Dark Mode Gradient Override */
[data-bs-theme="dark"] .contact-hero-section.hero-faded::before,
[data-bs-theme="dark"] .about-hero-section.hero-faded::before,
[data-bs-theme="dark"] .jobs-hero-section.hero-faded::before,
[data-bs-theme="dark"] .case-study-hero-section.hero-faded::before,
[data-bs-theme="dark"] .admin-hero-section.hero-faded::before {
    background: linear-gradient(to bottom,
            var(--overlay-start) 0%,
            var(--overlay-middle) 50%,
            var(--overlay-bottom) 100%);
}

/* The white fade that keeps dark navbar text readable over a hero photo.

   Two kinds of hero, one editor control (hero_navbar_fade). The inner pages
   already carry a full-height readability wash, so there the field gates that
   wash with .hero-faded, above. Blog, podcast and home have no such wash and
   get this 200px overlay instead.

   The two are labelled differently in the admin -- HeroWashMixin vs
   HeroFadeMixin -- because they are not the same amount of work. Turning this
   overlay off shows more photo; turning the wash off takes away the only thing
   holding a display-3 heading legible over one.

   Named for the hero, not the blog: it sits over six templates across three
   apps, and it lived under the blog banner while nine other page types used
   it -- a blog restyle silently reached all of them. */
.hero-navbar-fade {
    position: absolute;
    top: 0;
    left: 0;
    width: 100%;
    height: 200px;
    /* Solid at the very top and still 0.8 at 35%: the navbar sits in the first
       band, and a gradient that has already given up by then does not carry it. */
    background: linear-gradient(to bottom, rgba(255, 255, 255, 1) 0%, rgba(255, 255, 255, 0.8) 35%, rgba(255, 255, 255, 0) 100%);
    z-index: 2;
    pointer-events: none;
    transition: opacity 0.3s ease;
}

/* Dark mode turns the navbar text white, so the fade would only mute the photo. */
[data-bs-theme="dark"] .hero-navbar-fade {
    opacity: 0;
}

.contact-hero-section .container,
.about-hero-section .container,
.jobs-hero-section .container,
.case-study-hero-section .container,
.admin-hero-section .container {
    position: relative;
    z-index: 3;
}

/* ======================================= */
/* ---   CASE STUDY HERO (SPLIT)       --- */
/* ======================================= */

/*
   The case study detail hero. Its own class rather than a variant of
   .case-study-hero-section above, because that class is also worn by the Case
   Studies index and by every StandardPage -- this is a two-column split and
   they are centred text over a photo.

   Theme-adaptive through section-scoped custom properties, the same shape as
   .services-grid-section and .meet-us-section further down: one set of tokens
   here, redefined once under [data-bs-theme="dark"], and every rule below
   reads the tokens rather than a colour.
*/
.case-study-detail-hero {
    --case-study-hero-ink: #0c1844;
    --case-study-hero-body: var(--text-muted);
    /* The wash fades the photo into the section's own background, so it has
       to be that exact colour: white over a #f8f9fa section left a visible
       seam down the photo's edge. --bg-secondary already flips per theme, so
       there is nothing to override below. */
    --case-study-hero-wash: var(--bg-secondary);

    position: relative;
    display: flex;
    align-items: center;
    min-height: 60vh;
    padding: 120px 0 90px;
    overflow: hidden;
    background-color: var(--bg-secondary);
}

[data-bs-theme="dark"] .case-study-detail-hero {
    --case-study-hero-ink: #ffffff;
    --case-study-hero-body: rgba(255, 255, 255, 0.7);
}

.case-study-hero-photo {
    position: absolute;
    top: 0;
    right: 0;
    width: 58%;
    height: 100%;
    /* The stock photo is the fallback, as it was for .case-study-hero-section
       before this: a case study with no hero image of its own kept a picture
       rather than showing a bare band. Relative url() so the manifest storage
       rewrites and cache-busts it. */
    background-image: var(--case-study-hero-image, url("../img/case-studies.2551aee42c9c.jpeg"));
    background-size: cover;
    background-position: center;
    z-index: 0;
}

/*
   What the "Readability wash over the hero" checkbox does here. The inner
   heroes above gate a full-height wash over the whole photo with the same
   class; a split hero only needs the seam between the photo and the copy
   softened, so this fades the photo's inner edge into the section instead.
   Unchecking shows the photograph at full strength, edge and all.
*/
.case-study-detail-hero.hero-faded .case-study-hero-photo::after {
    content: '';
    position: absolute;
    inset: 0;
    background: linear-gradient(90deg,
            var(--case-study-hero-wash) 0%,
            transparent 48%);
}

.case-study-hero-text-column {
    position: relative;
    z-index: 2;
}

/* A roomier container than the site default, so the copy clears the photo
   filling the right 58%. Still centred, so it narrows with the viewport
   rather than colliding on a laptop. Large screens only: below 992px the
   hero stacks and the photo goes behind the copy. */
@media (min-width: 992px) {
    .case-study-detail-hero > .container {
        max-width: 1500px;
    }
}

/* Constrained by height, so a square badge and a long wordmark sit on the
   same optical line; max-width guards the very wide case. 72px against a 56px
   heading -- a logo is light strokes and needs to out-measure its type. */
.case-study-hero-logo {
    margin-bottom: 1.75rem;
}

.case-study-hero-logo img {
    max-width: 280px;
    max-height: 72px;
    width: auto;
}

.case-study-hero-heading {
    color: var(--case-study-hero-ink);
}

.case-study-hero-intro {
    max-width: 46ch;
    color: var(--case-study-hero-body);
    font-size: 1.15rem;
}

.case-study-hero-byline {
    display: flex;
    align-items: center;
    gap: 1rem;
    margin-top: 2rem;
}

.case-study-hero-byline-photo {
    width: 56px;
    height: 56px;
    border-radius: 50%;
    object-fit: cover;
}

.case-study-hero-byline-name {
    margin: 0;
    color: var(--case-study-hero-ink);
    font-weight: 600;
}

.case-study-hero-byline-role {
    margin: 0;
    color: var(--case-study-hero-body);
    font-size: 0.9rem;
}

@media (max-width: 991px) {
    /* Stacked there is no column to sit beside, so the photo goes behind the
       whole hero at low opacity -- the same trick .hero-bg-layer uses. */
    .case-study-detail-hero {
        min-height: auto;
        padding: 90px 0 60px;
        text-align: center;
    }

    .case-study-hero-photo {
        width: 100%;
        opacity: 0.18;
    }

    .case-study-detail-hero.hero-faded .case-study-hero-photo::after {
        background: none;
    }

    .case-study-hero-intro {
        margin-left: auto;
        margin-right: auto;
    }

    /* The paired logos are display:block, so text-align cannot centre them. */
    .case-study-hero-logo img {
        margin-left: auto;
        margin-right: auto;
    }

    .case-study-hero-byline {
        justify-content: center;
    }
}

/* --- Contact & Forms --- */
.contact-section {
    background-color: var(--bg-primary);
    padding: 100px 0;
}

.contact-textarea {
    height: 120px !important;
}

.contact-info-item .contact-icon {
    flex-shrink: 0;
    width: 50px;
    height: 50px;
    border-radius: 50%;
    background-color: rgba(200, 0, 54, 0.1);
    color: var(--brand-red);
    display: flex;
    justify-content: center;
    align-items: center;
    font-size: 1.5rem;
    margin-right: 20px;
}

.social-icons .social-icon-link {
    display: inline-flex;
    justify-content: center;
    align-items: center;
    width: 45px;
    height: 45px;
    background-color: var(--bg-card);
    color: var(--text-main);
    border: 1px solid var(--border-color);
    border-radius: 50%;
    text-decoration: none;
    margin-right: 10px;
    font-size: 1.1rem;
    transition: all 0.3s ease;
}

.social-icons .social-icon-link:hover {
    background-color: var(--brand-red);
    color: #ffffff;
    border-color: var(--brand-red);
    transform: translateY(-3px);
}

/* Form Wrapper */
.contact-form-wrapper,
.contact-form-wrapper-boxed {
    background-color: var(--bg-card);
    padding: 40px;
    border-radius: 15px;
    box-shadow: var(--shadow-soft);
    border: 1px solid var(--border-color);
}

.contact-form-wrapper .form-label {
    font-weight: 600;
    color: var(--text-main);
    margin-bottom: 0.5rem;
}

.contact-form-wrapper .form-control {
    background-color: var(--input-bg);
    border: 1px solid var(--input-border);
    color: var(--text-main);
    padding: 0.8rem 1rem;
    border-radius: 8px;
    transition: border-color 0.3s ease, box-shadow 0.3s ease;
}

.contact-form-wrapper .form-control:focus {
    background-color: var(--input-bg);
    color: var(--text-main);
    border-color: var(--brand-red);
    box-shadow: 0 0 0 0.25rem rgba(200, 0, 54, 0.25);
}

/* --- Tables --- */
.table {
    --bs-table-bg: var(--bg-card);
    --bs-table-color: var(--text-main);
    --bs-table-border-color: var(--border-color);
    border-radius: 15px;
    overflow: hidden;
}

.table thead {
    background-color: var(--brand-red);
    color: #ffffff;
}

.table thead th {
    background-color: var(--brand-red);
    color: #ffffff;
    border: none;
}

.job-link {
    color: var(--text-main);
    text-decoration: underline;
    text-decoration-color: var(--text-muted);
}

.job-link:hover {
    color: var(--brand-red);
    text-decoration-color: var(--brand-red);
}

.job-listings-section .table-hover tbody tr:hover {
    color: #ffffff;
    /* Make general text white on hover */
}

/* Make the specific link white on hover so it stands out against the dark grey row */
.job-listings-section .table-hover tbody tr:hover .job-link {
    color: #ffffff;
    text-decoration-color: #ffffff;
}

/* --- Footer --- */
.footer-section {
    background-color: #121820;
    /* Keep footer dark by default */
    color: #ffffff;
}

/* Light Mode specific footer override */
[data-bs-theme="light"] .footer-section {
    background-color: #f8f9fa;
    color: #212529;
}

[data-bs-theme="light"] .footer-section p,
[data-bs-theme="light"] .footer-section li,
[data-bs-theme="light"] .footer-section a {
    color: var(--text-muted);
}

[data-bs-theme="light"] .footer-section h5,
[data-bs-theme="light"] .footer-section .h5 {
    color: var(--text-main);
}

.footer-section .nav-link {
    opacity: 1 !important;
}

.social-icons .social-icon-link {
    border-color: var(--text-muted);
    /* Make border visible */
    color: var(--text-main);
    /* Ensure icon is dark enough */
}

.footer-disclaimer {
    font-size: 0.8rem;
    opacity: 0.8;
}

/* --- Animation Class --- */

/* ======================================= */
/* ---   ANIMATION REFINEMENTS         --- */
/* ======================================= */

/* Override Animate.css defaults for a more professional feel */
:root {
    --animate-duration: 0.8s;
    /* Standardize speed */
}

/* Ensure elements hidden via Alpine/JS don't flash */
.opacity-0 {
    opacity: 0 !important;
    transition: opacity 0.3s ease;
}

/* If the user prefers reduced motion (OS setting), disable animations */
@media (prefers-reduced-motion: reduce) {
    .animate__animated {
        /* CSS transitions are disabled */
        animation-duration: 1ms !important;
        transition-duration: 1ms !important;
        animation-iteration-count: 1 !important;
    }

    .opacity-0 {
        opacity: 1 !important;
    }
}

/* ======================================= */
/* ---   CASE STUDY CTA SECTION        --- */
/* ======================================= */

.cta-section-case-study {
    /* Always keep the dark background for this card to make it stand out */
    background-color: #2a313a;
    border: 1px solid rgba(255, 255, 255, 0.1);
    border-radius: 15px;
    padding: 40px;
    overflow: hidden;
    position: relative;
    box-shadow: 0 10px 30px rgba(0, 0, 0, 0.2);
}

/* Force text inside this specific card to be white, even in Light Mode */
.cta-section-case-study h3,
.cta-section-case-study .h3,
.cta-section-case-study p {
    color: #ffffff !important;
}

.cta-section-case-study .text-white-50 {
    color: rgba(255, 255, 255, 0.6) !important;
}

/*
   The full-width band that closes a case study detail page. Dark in both
   themes on purpose -- it is the one thing on the page asking for a reply, and
   it should read as a break from the article above it rather than more of it.
   .cta-section-case-study above is the rounded card this replaced; it and
   .btn-cta-sparkle are still used by other templates, so neither is removed.
*/
.case-study-cta-section {
    --case-study-cta-bg: #0c1844;
    --case-study-cta-ink: #ffffff;
    --case-study-cta-body: rgba(255, 255, 255, 0.7);

    padding: 70px 0;
    background-color: var(--case-study-cta-bg);
    color: var(--case-study-cta-ink);
}

[data-bs-theme="dark"] .case-study-cta-section {
    --case-study-cta-bg: #05070f;
}

.case-study-cta-section h2 {
    color: var(--case-study-cta-ink);
}

.case-study-cta-subtext {
    color: var(--case-study-cta-body);
}

.btn-cta-pill {
    display: inline-flex;
    align-items: center;
    gap: 0.5rem;
    padding: 12px 28px;
    border-radius: 50px;
    background-color: var(--brand-red);
    color: #ffffff;
    font-weight: 700;
    text-decoration: none;
    transition: background-color 0.3s ease, transform 0.3s ease;
}

.btn-cta-pill:hover,
.btn-cta-pill:focus {
    background-color: var(--brand-red-hover);
    color: #ffffff;
    transform: translateY(-2px);
}

/* --- Sparkle Button Styles --- */
.btn-cta-sparkle {
    position: relative;
    background-color: #ffffff;
    color: var(--brand-red);
    border-radius: 12px;
    padding: 12px 28px;
    font-weight: 700;
    border: 3px solid var(--brand-red);
    transition: all 0.3s ease;
    text-transform: capitalize;
    text-decoration: none;
    display: inline-block;
}

.btn-cta-sparkle:focus,
.btn-cta-sparkle:hover {
    transform: scale(1.05);
    box-shadow: 0 5px 20px rgba(200, 0, 54, 0.4);
    background-color: #ffffff;
    color: var(--brand-red-hover);
    border-color: var(--brand-red-hover);
}

/* Sparkles using inline SVG */
.btn-cta-sparkle::before,
.btn-cta-sparkle::after {
    content: '';
    position: absolute;
    width: 24px;
    height: 24px;
    background-repeat: no-repeat;
    /* CHANGED: fill='white' (was %23c80036) */
    background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='white'%3E%3Cpath d='M12 0l-2.5 9.5-9.5 2.5 9.5 2.5 2.5 9.5 2.5-9.5 9.5-2.5-9.5-2.5z'/%3E%3C/svg%3E");
    animation: sparkle-animation 2s ease-in-out infinite;
    pointer-events: none;
}

/* Positioning the left sparkle */
.btn-cta-sparkle::before {
    bottom: -12px;
    left: -8px;
}

/* Positioning the right sparkle */
.btn-cta-sparkle::after {
    top: -12px;
    right: -8px;
    animation-delay: -1s;
    /* Stagger the animation */
}

/* Keyframe animation for sparkle effect */
@keyframes sparkle-animation {
    0% {
        transform: scale(0) rotate(0deg);
        opacity: 0;
    }

    50% {
        opacity: 1;
        transform: scale(1) rotate(45deg);
    }

    100% {
        transform: scale(0) rotate(90deg);
        opacity: 0;
    }
}

/* ======================================= */
/* ---   HIRING NOTICE                 --- */
/* ======================================= */

/* Shown above the application form. Colours come from the --notice-* tokens,
   which are defined in both the :root and [data-bs-theme="dark"] blocks, so it
   stays legible in either theme rather than inheriting one theme's palette. */
.hiring-notice {
    display: flex;
    gap: 0.85rem;
    align-items: flex-start;
    background: var(--notice-bg);
    border: 1px solid var(--notice-border);
    border-left-width: 4px;
    border-radius: 6px;
    padding: 1rem 1.15rem;
    margin-bottom: 1.75rem;
    /* Inherit the theme's body colour rather than setting one: the tinted
       surface is light in light mode and dark in dark mode, so the body colour
       is already the high-contrast choice against it in both. */
    color: var(--text-main);
}

.hiring-notice .bi {
    color: var(--notice-icon);
    font-size: 1.25rem;
    line-height: 1.4;
    flex-shrink: 0;
}

.hiring-notice p {
    margin: 0;
}

/* The heading names the city the applicant typed, so it carries the accent
   colour and the weight. 4.5:1 on the notice surface in both themes. */
.hiring-notice-title {
    font-weight: 700;
    color: var(--notice-icon);
    margin-bottom: 0.3rem !important;
}

/* Disabled because the city given is out of range. Kept obviously inert rather
   than hidden: a button that vanishes reads as a broken page, and the notice
   directly above already says why this one cannot be pressed. */
.btn-custom-red:disabled,
.btn-custom-red[disabled] {
    opacity: 0.45;
    cursor: not-allowed;
}

/* ======================================= */
/* ---   JOB CARDS                     --- */
/* ======================================= */

.job-card-grid {
    display: flex;
    flex-direction: column;
    /* Forces cards to stack vertically (Wide) */
    gap: 20px;
}

.job-listing-card {
    background-color: var(--bg-card);
    border: 1px solid var(--border-color);
    border-radius: 15px;
    padding: 30px;
    transition: transform 0.3s ease, box-shadow 0.3s ease;
    display: flex;
    flex-direction: column;
    justify-content: space-between;
    position: relative;
}

/* Ensure the title link changes color on hover */
.job-listing-card h3 a {
    color: var(--text-main);
    transition: color 0.3s ease;
}

.job-listing-card h3 a:hover {
    color: var(--brand-red);
}

/* Update: Header Alignment */
.job-card-header {
    display: flex;
    align-items: center;
    margin-bottom: 15px;
}

/* New: Ensure the info section takes available space so button pushes right */
.job-card-info {
    flex-grow: 1;
}

/* Helper for text reset */
.text-reset {
    color: inherit !important;
}

.job-listing-card:hover {
    transform: translateY(-3px);
    /* Subtle lift */
    box-shadow: 0 10px 30px rgba(0, 0, 0, 0.15);
    border-color: var(--brand-red);
}

/* Header Area: Icon + Title */
.job-card-header {
    display: flex;
    align-items: center;
    /* Center align icon with text */
    margin-bottom: 15px;
}

/* The colored square box for the icon */
.job-icon-box {
    width: 60px;
    height: 60px;
    border-radius: 12px;
    display: flex;
    align-items: center;
    justify-content: center;
    margin-right: 20px;
    flex-shrink: 0;
    color: #ffffff;
    font-size: 1.75rem;
}

/* Color Classes (Matches Model Choices) */
.bg-brand-red {
    background-color: var(--brand-red);
}

.bg-midnight-blue {
    background-color: #0c1844;
}

.bg-slate {
    background-color: #6c757d;
}

.bg-teal {
    background-color: #007d7e;
}

/* Job Info */
.job-card-info h3 {
    font-size: 1.35rem;
    /* Slightly larger title for wide cards */
    font-weight: 700;
    margin-bottom: 2px;
    color: var(--text-main);
}

.job-location {
    font-size: 0.95rem;
    color: var(--text-muted);
    font-weight: 500;
}

/* Description */
.job-card-desc {
    color: var(--text-muted);
    font-size: 1rem;
    margin-bottom: 20px;
    line-height: 1.6;
    max-width: 90%;
    /* Keeps text from stretching too long on huge screens */
}

/* Pills (Tech Stack) */
.job-card-pills {
    display: flex;
    flex-wrap: wrap;
    gap: 10px;
    margin-top: auto;
}

.tech-pill {
    background-color: var(--bg-tertiary);
    color: var(--text-main);
    padding: 6px 14px;
    border-radius: 20px;
    font-size: 0.85rem;
    font-weight: 600;
}

/* Responsive */
@media (max-width: 768px) {
    .job-listing-card {
        padding: 20px;
    }
}

/* ======================================= */
/* ---   BENEFITS SECTION              --- */
/* ======================================= */

.benefits-section {
    background-color: #121820;
    /* Always keep this dark for visual interest */
    color: #ffffff;
    position: relative;
    overflow: hidden;
}

.benefits-section h2,
.benefits-section .display-4 {
    color: #ffffff !important;
}

/* Background Decorations (Glow effects) */
.benefits-section::before {
    content: '';
    position: absolute;
    top: -150px;
    right: -200px;
    width: 800px;
    height: 400px;
    background-image:
        radial-gradient(circle at top right, #f77f00 0%, transparent 40%),
        radial-gradient(circle at top right, #005f73 0%, transparent 50%);
    opacity: 0.3;
    z-index: 1;
    pointer-events: none;
}

.benefits-section .container {
    position: relative;
    z-index: 2;
}

/* List Styling with Custom Bullets */
.benefits-list {
    list-style: none;
    /* Remove default browser bullets */
    padding-left: 0;
    margin-top: 2rem;
}

.benefits-list li {
    font-size: 1.15rem;
    padding-left: 35px;
    /* Create space for the custom bullet */
    position: relative;
    margin-bottom: 1rem;
    color: rgba(255, 255, 255, 0.9) !important;
}

/* The Red Dot Bullet */
.benefits-list li::before {
    content: '•';
    position: absolute;
    left: 0;
    top: -6px;
    /* Adjust vertical align */
    color: var(--brand-red);
    font-size: 2rem;
    line-height: 1;
}

/* --- Image Collage Styling --- */
.image-collage {
    position: relative;
    height: 450px;
    width: 100%;
}

.image-collage img {
    position: absolute;
    border-radius: 15px;
    box-shadow: 0 10px 40px rgba(0, 0, 0, 0.5);
    border: 4px solid #fff;
    object-fit: cover;
    /* Ensures images fill their box without stretching */
}

/* Position 1: Top Right (Landscape) */
.benefit-img-1 {
    top: 0;
    right: 0;
    z-index: 3;
    width: 55%;
    height: 200px;
}

/* Position 2: Middle Left (Wide) */
.benefit-img-2 {
    top: 35%;
    left: 0;
    z-index: 2;
    width: 50%;
    height: 220px;
}

/* Position 3: Bottom Right (Square-ish) */
.benefit-img-3 {
    bottom: 0;
    right: 10%;
    z-index: 4;
    width: 45%;
    height: 200px;
}

/* Responsive Fallback for Mobile/Tablet */
@media (max-width: 992px) {
    .image-collage {
        height: auto;
        display: flex;
        flex-direction: column;
        gap: 20px;
        margin-top: 40px;
    }

    .image-collage img {
        position: relative;
        width: 100%;
        height: 300px;
        top: auto !important;
        left: auto !important;
        right: auto !important;
        bottom: auto !important;
        border-width: 2px;
    }
}

/* --- Culture Cards --- */
.culture-cards-section .job-listing-card {
    justify-content: flex-start;
    /* Aligns content to the top */
    gap: 1rem;
    /* Adds consistent space between header and text */
}

.values-list ul {
    padding-left: 1.2rem;
    margin-bottom: 0;
}

.values-list li {
    margin-bottom: 0.5rem;
}

/* --- Hiring Process --- */
.hiring-process-section {
    background-color: var(--bg-card);
    /* Alternate background */
}

/* Ensure process content is readable */
.process-content p {
    margin-bottom: 1.5rem;
}

/* Utility: Background Colors that adapt to theme */
.bg-darker {
    background-color: var(--bg-tertiary) !important;
}

/* Ensure text contrast is correct inside this section */
.bg-darker p,
.bg-darker h3 {
    color: var(--text-main);
}

/* ======================================= */
/* ---   PARTNER PROGRAM PAGE          --- */
/* ======================================= */
.partners-hero-section {
    position: relative;
    /* INCREASED padding-top to 180px to uncrowd the header */
    padding-top: 180px;
    padding-bottom: 100px;
    background-size: cover;
    background-position: center;
}

/* Dark overlay covering the whole image to ensure text pops */
.partners-hero-overlay {
    position: absolute;
    inset: 0;
    background: linear-gradient(to bottom, rgba(0, 0, 0, 0.7), rgba(0, 0, 0, 0.85));
    z-index: 1;
}

/*
   White top-down fade for the Navbar.
   This sits BEHIND the navbar but ON TOP of the dark hero image.
   It creates a light backdrop so the dark Light-Mode text is readable.
*/
.partners-navbar-fade {
    position: absolute;
    top: 0;
    left: 0;
    width: 100%;
    /* INCREASED height to 200px so logo is clearer */
    height: 200px;
    background: linear-gradient(to bottom, rgba(255, 255, 255, 0.95) 0%, rgba(255, 255, 255, 0) 100%);
    z-index: 2;
    pointer-events: none;
    transition: opacity 0.3s ease;
}

/* In Dark Mode, text is white, so we hide the white fade to let it sit on the dark image */
[data-bs-theme="dark"] .partners-navbar-fade {
    opacity: 0;
}

/* Content Positioning */
.partner-hero-content {
    position: relative;
    z-index: 3;
}

/* Typography Overrides for the Hero */
.partner-hero-title {
    color: #ffffff;
    text-shadow: 0 2px 4px rgba(0, 0, 0, 0.5);
}

.partner-hero-lead {
    max-width: 800px;
    margin: 1rem auto 0;
    color: #ffffff !important;
    /* Force white over global .lead */
    text-shadow: 0 1px 2px rgba(0, 0, 0, 0.5);
}

.partner-hero-btn {
    box-shadow: 0 1rem 3rem rgba(0, 0, 0, 0.175) !important;
}

.partner-hero-btn.btn-outline-light {
    border: 2px solid #ffffff !important;
    /* Force the border to be visible */
    color: #ffffff;
}

.partner-hero-btn.btn-outline-light:hover {
    background-color: #ffffff;
    color: #000000;
    /* Dark text on hover */
}

/* ======================================= */
/* ---   DARK MODE ADAPTIVE UTILITIES  --- */
/* ======================================= */

/* Use this instead of bg-white */
.bg-adaptive-primary {
    background-color: var(--bg-primary) !important;
}

/* Use this instead of bg-light */
.bg-adaptive-secondary {
    background-color: var(--bg-secondary) !important;
}

/* Ensure text-muted uses the variable that adjusts for dark mode */
.text-muted {
    color: var(--text-muted) !important;
}

/* Specific fix for Partner Benefits Cards */
/* In light mode, we want them slightly grey (secondary).
   In dark mode, we want them slightly lighter than the black background (card color). */
.partner-card-bg {
    background-color: var(--bg-secondary);
}

[data-bs-theme="dark"] .partner-card-bg {
    background-color: var(--bg-card);
    border-color: var(--border-color) !important;
}

/* ======================================= */
/* ---         PARTNER PORTAL          --- */
/* ======================================= */

/* Remove rounded corners from the portal table */
.table-flat {
    border-radius: 0 !important;
}

/* Ensure the header cells themselves don't hold onto the radius */
.table-flat thead th:first-child {
    border-top-left-radius: 0 !important;
}

.table-flat thead th:last-child {
    border-top-right-radius: 0 !important;
}

/* Logo sizing for the portal navbar */
.portal-logo {
    height: 40px;
    width: auto;
    margin-right: 15px;
}

/* ======================================= */
/* ---   PORTAL & AUTH LAYOUT          --- */
/* ======================================= */

/* Replaces: style="min-height: 70vh; display: flex; align-items: center;" */
.portal-auth-container {
    min-height: 70vh;
    display: flex;
    align-items: center;
}

/* Replaces: style="min-height: 80vh;" */
.portal-min-height {
    min-height: 80vh;
}

/* Replaces: style="height: 30px;" */
.portal-brand-divider {
    height: 30px;
}

/* Replaces: style="width: 60px; height: 60px; font-size: 1.5rem;" */
.process-step-circle {
    width: 60px;
    height: 60px;
    font-size: 1.5rem;
}

/* For password reset confirmation cards */
.auth-card-wrapper {
    max-width: 500px;
}

/* For the large checkmark icon */
.icon-lg-success {
    font-size: 4rem;
}

/* For the "Partner Portal" text in navbar */
.portal-nav-text {
    font-size: 1.1rem;
}

/* For dashboard container height */
.dashboard-container {
    min-height: 80vh;
}

/* For the "No Profile" page alignment */
.flex-column-center {
    flex-direction: column;
    justify-content: center;
}

/* ======================================= */
/* ---   BLOG SECTION STYLES           --- */
/* ======================================= */

.blog-hero-section {
    position: relative;
    /* Increase padding to clear the navbar */
    padding-top: 180px;
    padding-bottom: 100px;
    background-size: cover;
    background-position: center;
}

.blog-hero-overlay {
    position: absolute;
    inset: 0;
    /* Dark overlay for text readability */
    background: linear-gradient(to bottom, rgba(0, 0, 0, 0.6), rgba(0, 0, 0, 0.8));
    z-index: 1;
}

.blog-hero-content {
    position: relative;
    z-index: 3;
}

/* Force white text for the lead paragraph in the hero */
.blog-hero-lead {
    color: #ffffff !important;
    text-shadow: 0 1px 2px rgba(0, 0, 0, 0.5);
    max-width: 800px;
    margin: 1rem auto 0;
}

.blog-hero-overlay {
    position: absolute;
    inset: 0;
    /* Increased opacity of the dark overlay for better text contrast */
    background: rgba(0, 0, 0, 0.5);
    z-index: 1;
}

/* Ensure the custom class definitely forces white with a shadow */
.blog-header-text {
    color: #ffffff !important;
    text-shadow: 0 2px 10px rgba(0, 0, 0, 0.8);
    /* Stronger shadow */
}

.blog-meta-text {
    color: rgba(255, 255, 255, 0.9) !important;
    text-shadow: 0 1px 3px rgba(0, 0, 0, 0.9);
    font-weight: 500;
}

/* For Blog Index badges */
.badge-sm {
    font-size: 0.7rem;
}

/* For Blog Post Header Section */
.blog-article-header {
    background-color: #1a1d21;
    padding-top: 140px;
    padding-bottom: 80px;
    position: relative;
}

/* For Blog Post Background Image Overlay */
.blog-article-bg {
    background-size: cover;
    background-position: center;
    opacity: 0.3;
    position: absolute;
    inset: 0;
    width: 100%;
    height: 100%;
}

/* Explicit z-index helper */
.z-index-2 {
    z-index: 2;
    position: relative;
}

/* Bottom fade to smooth transition from Hero to Content */
.blog-header-fade-bottom {
    position: absolute;
    bottom: 0;
    left: 0;
    width: 100%;
    height: 350px;
    /* Adjust height for softness */
    background: linear-gradient(to bottom, rgba(255, 255, 255, 0) 0%, #ffffff 100%);
    z-index: 2;
    pointer-events: none;
}

/* Dark mode variant (fades to dark bg) */
[data-bs-theme="dark"] .blog-header-fade-bottom {
    background: linear-gradient(to bottom, rgba(13, 18, 24, 0) 0%, #0d1218 100%);
}

/* ======================================= */
/* ---   BLOG CARD INTERACTIONS        --- */
/* ======================================= */

.blog-card {
    /* Set an initial subtle border so the layout doesn't jump on hover */
    border: 1px solid rgba(0, 0, 0, 0.05);
    transition: all 0.3s ease;
}

.blog-card:hover {
    /* The "Raise" effect */
    transform: translateY(-5px);
    /* The "Red Outline" effect */
    border-color: var(--brand-red);
    /* Stronger shadow on lift */
    box-shadow: 0 15px 30px rgba(0, 0, 0, 0.1) !important;
}

/* Dark mode adjustment for the initial border */
[data-bs-theme="dark"] .blog-card {
    border-color: var(--border-color);
    background-color: var(--bg-card);
}

/* ======================================= */
/* ---   TEAM SECTION STYLES           --- */
/* ======================================= */

.team-card {
    background-color: var(--bg-card);
    border: 1px solid var(--border-color);
    transition: all 0.3s ease;
}

.team-card:hover {
    transform: translateY(-5px);
    border-color: var(--brand-red);
    box-shadow: 0 10px 30px rgba(0, 0, 0, 0.1);
}

.team-social-link {
    color: var(--text-muted);
    transition: color 0.3s ease;
}

.team-social-link:hover {
    color: #0077b5;
    /* LinkedIn Blue */
}

/* ======================================= */
/* ---   STATS GRID BLOCK              --- */
/* ======================================= */

.stat-card {
    background-color: var(--bg-card);
    border: 1px solid var(--border-color) !important;
}

.stat-number {
    color: var(--brand-red);
}

/* ======================================= */
/* ---   PODCAST CAROUSEL BLOCK        --- */
/* ======================================= */

.podcast-section {
    --podcast-card-bg: #0b1844;
    --podcast-ink: #ffffff;
    --podcast-ink-soft: rgba(255, 255, 255, 0.78);
    --podcast-hairline: rgba(255, 255, 255, 0.22);
    /* Drives the artwork, the grid track and the card height in one place. */
    --podcast-cover-size: 300px;
    --podcast-card-padding: 30px;
    padding: 70px 0;
}

/* The stage stacks every slide in one grid cell, so the card keeps the
   height of the tallest episode and nothing jumps while stepping through.
   min-height pins it to the artwork, so the controls below never move. */
.podcast-section .podcast-stage {
    display: grid;
    min-height: calc(var(--podcast-cover-size) + (var(--podcast-card-padding) * 2));
    background-color: var(--podcast-card-bg);
    border-radius: 30px;
    padding: var(--podcast-card-padding) 44px;
    overflow: hidden;
}

.podcast-section .podcast-slide {
    grid-area: 1 / 1;
    display: grid;
    grid-template-columns: var(--podcast-cover-size) minmax(0, 1.05fr) 3px minmax(0, 1fr);
    gap: 48px;
    align-items: center;
    opacity: 0;
    visibility: hidden;
    transform: translateY(10px);
    transition: opacity 0.45s ease, transform 0.45s ease, visibility 0s linear 0.45s;
}

.podcast-section .podcast-slide.is-active {
    opacity: 1;
    visibility: visible;
    transform: none;
    transition: opacity 0.45s ease, transform 0.45s ease, visibility 0s;
}

/* Both axes are pinned to the same token: the artwork stays square and can
   never be stretched to the height of the card. It is also the positioning
   context and the clip for the facade and the player. */
.podcast-section .podcast-cover {
    position: relative;
    width: var(--podcast-cover-size);
    height: var(--podcast-cover-size);
    align-self: center;
    border-radius: 12px;
    overflow: hidden;
}

/* Only when there is a video: the bands above and below the 16:9 player read
   as part of the player instead of as a hole punched in the card. */
.podcast-section .podcast-cover.has-video {
    background-color: #000000;
}

.podcast-section .podcast-cover-img {
    display: block;
    width: var(--podcast-cover-size);
    height: var(--podcast-cover-size);
    object-fit: cover;
    border-radius: 12px;
}

.podcast-section .podcast-cover-empty {
    display: grid;
    place-items: center;
    background-color: rgba(255, 255, 255, 0.06);
    color: var(--podcast-hairline);
    font-size: 2.5rem;
}

/* The facade: the still frame plus the play affordance, stacked in one cell.
   Clicking it is what loads YouTube -- nothing before that. */
.podcast-section .podcast-play {
    position: absolute;
    inset: 0;
    display: grid;
    place-items: center;
    padding: 0;
    border: 0;
    background: none;
    cursor: pointer;
}

.podcast-section .podcast-play>* {
    grid-area: 1 / 1;
}

/* Matches .podcast-nav, but drawn inside: the square clips its own overflow,
   so an outline offset outwards would be cut off. */
.podcast-section .podcast-play:focus-visible {
    outline: 2px solid var(--brand-red);
    outline-offset: -3px;
}

/* No artwork on the episode: fall back to the YouTube still. That file is 4:3
   with the 16:9 frame centred inside it, so cropping it to 16:9 removes the
   letterbox bands and leaves exactly the video frame -- and it lands where
   the player will, so nothing jumps on click. */
.podcast-section .podcast-poster-yt {
    display: block;
    width: 100%;
    height: auto;
    aspect-ratio: 16 / 9;
    max-height: 100%;
    object-fit: cover;
}

.podcast-section .podcast-play-scrim {
    width: 100%;
    height: 100%;
    background-color: rgba(0, 0, 0, 0.28);
    transition: background-color 0.25s ease;
}

.podcast-section .podcast-play-icon {
    display: grid;
    place-items: center;
    z-index: 1;
    width: 56px;
    height: 56px;
    border-radius: 50%;
    background-color: var(--brand-red);
    color: #ffffff;
    font-size: 1.6rem;
    box-shadow: 0 6px 18px rgba(0, 0, 0, 0.35);
    transition: transform 0.25s ease;
}

/* The triangle is optically left-heavy inside a circle. */
.podcast-section .podcast-play-icon .bi {
    transform: translateX(2px);
}

/* The colour holds and only the scale answers, matching .btn-custom-red in the
   hero's contact form: --brand-red-hover is a warmer, more orange red, and
   swapping to it made this read as a different button on hover. */
.podcast-section .podcast-play:hover .podcast-play-icon {
    transform: scale(1.08);
}

.podcast-section .podcast-play:hover .podcast-play-scrim {
    background-color: rgba(0, 0, 0, 0.12);
}

/* The player letterboxes inside the square: full width, 16:9, centred. */
.podcast-section .podcast-frame {
    position: absolute;
    inset: 0;
    display: grid;
    place-items: center;
}

.podcast-section .podcast-frame iframe {
    display: block;
    width: 100%;
    aspect-ratio: 16 / 9;
    max-height: 100%;
    border: 0;
}

.podcast-section .podcast-cover.is-playing .podcast-play {
    display: none;
}

/* Headline column: the eyebrow starts on the same line as the detail
   heading across the divider, as in the design. */
.podcast-section .podcast-headline {
    align-self: start;
    padding-top: 2px;
}

.podcast-section .podcast-eyebrow {
    display: inline-block;
    margin: 0;
    padding: 0.45em 0.95em;
    border-radius: 4px;
    background-color: var(--brand-red);
    color: #ffffff;
    font-size: 0.72rem;
    font-weight: 700;
    letter-spacing: 0.16em;
    text-transform: uppercase;
    line-height: 1;
}

.podcast-section .podcast-title {
    margin: 18px 0 0;
    max-width: 14ch;
    color: var(--podcast-ink);
    font-size: clamp(1.5rem, 2.1vw, 1.9rem);
    font-weight: 700;
    line-height: 1.2;
    letter-spacing: -0.01em;
}

.podcast-section .podcast-rule {
    align-self: stretch;
    background-color: #ffffff;
}

/* Detail column: reads from the top edge of the card, as in the design. */
.podcast-section .podcast-detail {
    align-self: start;
}

.podcast-section .podcast-detail-heading {
    margin: 0;
    color: var(--podcast-ink);
    font-size: 1.05rem;
    font-weight: 700;
}

.podcast-section .podcast-summary {
    margin: 14px 0 26px;
    color: var(--podcast-ink-soft);
    font-size: 0.97rem;
    line-height: 1.66;
}

.podcast-section .podcast-speaker {
    margin: 0 0 2px;
    color: var(--podcast-ink);
    font-size: 1rem;
    font-weight: 700;
}

.podcast-section .podcast-speaker-role {
    margin: 0;
    color: var(--podcast-ink-soft);
    font-size: 0.95rem;
}

/* Controls: two steppers parked under the left edge of the card. The row has
   a fixed size and sits outside the stage, so the buttons hold the same spot
   on every episode. */
.podcast-section .podcast-controls {
    display: flex;
    align-items: center;
    gap: 14px;
    height: 40px;
    margin-top: 24px;
}

.podcast-section .podcast-nav {
    display: grid;
    place-items: center;
    width: 40px;
    height: 40px;
    flex: 0 0 auto;
    padding: 0;
    border: 0;
    border-radius: 50%;
    background-color: var(--brand-red);
    color: #ffffff;
    font-size: 1.25rem;
    transition: transform 0.25s ease;
}

/* Bootstrap Icons ships a single thin weight for the arrows, so thicken the
   glyph with a stroke in its own colour instead of swapping the icon. */
.podcast-section .podcast-nav .bi {
    -webkit-text-stroke: 0.75px currentColor;
}

.podcast-section .podcast-nav:hover {
    transform: translateY(-2px);
}

.podcast-section .podcast-nav:focus-visible {
    outline: 2px solid var(--brand-red);
    outline-offset: 3px;
}

@media (max-width: 991.98px) {
    .podcast-section {
        --podcast-card-padding: 32px;
    }

    .podcast-section .podcast-stage {
        padding: var(--podcast-card-padding);
    }

    .podcast-section .podcast-slide {
        grid-template-columns: var(--podcast-cover-size) minmax(0, 1fr);
        gap: 32px;
    }

    .podcast-section .podcast-rule {
        display: none;
    }

    .podcast-section .podcast-detail {
        grid-column: 1 / -1;
        padding-top: 28px;
        border-top: 1px solid var(--podcast-hairline);
    }
}

@media (max-width: 575.98px) {
    .podcast-section {
        --podcast-cover-size: min(190px, 55vw);
        --podcast-card-padding: 24px;
        padding: 48px 0;
    }

    .podcast-section .podcast-stage {
        border-radius: 22px;
    }

    .podcast-section .podcast-slide {
        grid-template-columns: minmax(0, 1fr);
        gap: 24px;
    }

    .podcast-section .podcast-detail {
        grid-column: auto;
    }

    /* A 56px circle eats a third of a min(190px, 55vw) square. */
    .podcast-section .podcast-play-icon {
        width: 46px;
        height: 46px;
        font-size: 1.3rem;
    }
}

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

    .podcast-section .podcast-slide,
    .podcast-section .podcast-nav,
    .podcast-section .podcast-play-icon,
    .podcast-section .podcast-play-scrim {
        transition-duration: 0.01ms;
    }

    .podcast-section .podcast-play:hover .podcast-play-icon {
        transform: none;
    }

    .podcast-section .podcast-slide {
        transform: none;
    }

    .podcast-section .podcast-nav:hover {
        transform: none;
    }
}
/* ======================================= */
/* ---   WORK WITH US BLOCK            --- */
/* ======================================= */

/* Every color and measure of this section lives here, so the card, the band
   and the icon column stay in step and the dark theme is a token swap. */
.work-with-section {
    --work-ink: #0c1844;
    --work-accent: var(--brand-red);
    --work-body: var(--text-muted);
    --work-band: var(--bg-secondary);
    --work-rail: rgba(12, 24, 68, 0.14);
    --work-card: #ffffff;
    --work-card-shadow: 0 18px 50px rgba(12, 24, 68, 0.06);
    --work-card-padding: 56px;
}

[data-bs-theme="dark"] .work-with-section {
    --work-ink: #ffffff;
    --work-body: rgba(255, 255, 255, 0.7);
    --work-band: rgba(255, 255, 255, 0.03);
    --work-rail: rgba(255, 255, 255, 0.18);
    --work-card: #121a33;
    --work-card-shadow: none;
}

/* The intro is the card's first row: it spans both columns so the heading sits
   above the CTA and the points instead of beside them. */
.work-with-section .work-with-intro {
    grid-column: 1 / -1;
    text-align: center;
}

.work-with-section .work-with-heading {
    margin: 0;
    color: var(--work-ink);
    font-size: clamp(1.75rem, 1.2rem + 1.7vw, 2.5rem);
    font-weight: 700;
    line-height: 1.15;
}

/* The rich text editor wraps the heading in <p>: keep it part of the heading
   instead of a paragraph sitting inside one. */
.work-with-section .work-with-heading p,
.work-with-section .work-with-cta-heading p {
    margin: 0;
    font-size: inherit;
    font-weight: inherit;
    line-height: inherit;
    color: inherit;
}

.work-with-section .work-with-subtitle {
    max-width: 62ch;
    margin: 14px auto 0;
    color: var(--work-body);
    font-size: 1rem;
    line-height: 1.5;
}

/* The tinted band starts well below the top of the section and the card is
   pulled back up over it, so the card overlaps the band's top edge. The band
   margin, its top padding and the card's negative margin add up to the card's
   original offset, which keeps the card itself where it was. */
.work-with-section .work-with-band {
    background-color: var(--work-band);
    margin-top: 160px;
    padding: 14px 0 90px;
}

.work-with-section .work-with-card {
    margin-top: -104px;
    display: grid;
    grid-template-columns: minmax(0, 0.85fr) minmax(0, 1.15fr);
    gap: 40px 72px;
    align-items: center;
    background-color: var(--work-card);
    border-radius: 12px;
    box-shadow: var(--work-card-shadow);
    padding: var(--work-card-padding);
}

.work-with-section .work-with-cta-heading {
    margin: 0;
    color: var(--work-ink);
    font-size: clamp(1.6rem, 1.2rem + 1.2vw, 2.15rem);
    font-weight: 700;
    line-height: 1.18;
}

.work-with-section .work-with-cta-text {
    margin: 22px 0 0;
    color: var(--work-body);
    font-size: 1rem;
    font-weight: 600;
    line-height: 1.45;
}

.work-with-section .work-with-button {
    display: inline-block;
    margin-top: 32px;
    border: 3px solid var(--work-accent);
    border-radius: 10px;
    padding: 14px 26px;
    color: var(--work-accent);
    font-weight: 700;
    text-decoration: none;
    transition: background-color 0.25s ease, color 0.25s ease;
}

.work-with-section .work-with-button:hover,
.work-with-section .work-with-button:focus-visible {
    background-color: var(--work-accent);
    color: #ffffff;
}

.work-with-section .work-with-button:focus-visible {
    outline: 2px solid var(--work-ink);
    outline-offset: 3px;
}

.work-with-section .work-with-points {
    list-style: none;
    margin: 0;
    padding: 0;
    display: grid;
    gap: 34px;
}

.work-with-section .work-with-point {
    display: grid;
    grid-template-columns: 24px minmax(0, 1fr);
    gap: 22px;
}

/* The icon column doubles as a rail: the icon sits at the top and the divider
   below it runs the height of the point, so the line only ever grows as long
   as the text beside it. */
.work-with-section .work-with-point-rail {
    display: flex;
    flex-direction: column;
    align-items: center;
}

.work-with-section .work-with-point-icon {
    color: var(--work-ink);
    font-size: 1.35rem;
    line-height: 1.25;
    transition: color 0.45s ease;
}

.work-with-section .work-with-point-line {
    position: relative;
    flex: 1;
    width: 2px;
    min-height: 20px;
    margin-top: 14px;
    border-radius: 2px;
    background-color: var(--work-rail);
    overflow: hidden;
}

/* The fill is a separate layer so the red can sweep down the track from the
   icon instead of the whole line switching colour at once. */
.work-with-section .work-with-point-line-fill {
    position: absolute;
    inset: 0;
    background-color: var(--work-accent);
    transform: scaleY(0);
    transform-origin: top;
    transition: transform 0.7s ease;
}

.work-with-section .work-with-point-title {
    margin: 0;
    color: var(--work-ink);
    font-size: 1.3rem;
    font-weight: 700;
    line-height: 1.25;
    transition: color 0.45s ease;
}

/* Set by the block's script as each point scrolls into view. */
.work-with-section .work-with-point.is-lit .work-with-point-icon,
.work-with-section .work-with-point.is-lit .work-with-point-title {
    color: var(--work-accent);
}

.work-with-section .work-with-point.is-lit .work-with-point-line-fill {
    transform: scaleY(1);
}

.work-with-section .work-with-point-text {
    margin: 10px 0 0;
    color: var(--work-body);
    font-size: 0.95rem;
    line-height: 1.6;
}

.work-with-section .work-with-point-empty {
    color: var(--work-body);
}

@media (max-width: 991.98px) {
    .work-with-section {
        --work-card-padding: 40px;
    }

    .work-with-section .work-with-card {
        grid-template-columns: minmax(0, 1fr);
        gap: 44px;
    }
}

@media (max-width: 575.98px) {
    .work-with-section {
        --work-card-padding: 28px;
    }

    .work-with-section .work-with-band {
        margin-top: 115px;
        padding: 10px 0 56px;
    }

    .work-with-section .work-with-card {
        margin-top: -77px;
    }

    .work-with-section .work-with-point {
        gap: 16px;
    }
}

@media (prefers-reduced-motion: reduce) {
    .work-with-section .work-with-button,
    .work-with-section .work-with-point-icon,
    .work-with-section .work-with-point-title,
    .work-with-section .work-with-point-line-fill {
        transition-duration: 0.01ms;
    }
}


/* ======================================= */
/* ---   SERVICES GRID BLOCK           --- */
/* ======================================= */

/* A flat grid, not cards: the icon, the name and the sentence sit directly on
   the section's own background, so the eye reads the column of services rather
   than a row of boxes. Named apart from the card-based .services-section that
   the home page still uses. */
.services-grid-section {
    --services-ink: #0c1844;
    --services-accent: var(--brand-red);
    --services-body: var(--text-muted);
    --services-bg: var(--bg-secondary);

    background-color: var(--services-bg);
    padding: 90px 0;
}

[data-bs-theme="dark"] .services-grid-section {
    --services-ink: #ffffff;
    --services-body: rgba(255, 255, 255, 0.7);
    --services-bg: rgba(255, 255, 255, 0.03);

    /* The button is 0.95rem/700 -- 15.2px, under WCAG's 18.66px bold large-text
       threshold -- so it needs 4.5:1, not 3:1. The section's own accent is the
       brand red, which measures 4.12:1 on this surface, and white on that same
       red for the hover fill is 4.28:1. Both are short, so the button takes its
       own pair here: #f15881 reaches 5.43:1 resting, and the slightly deeper
       #d81f4f carries white at 4.97:1. Same reason --tech-stack-name was
       lifted above. Light mode already clears both (5.68:1 and 5.98:1) and
       falls through to the accent. */
    --services-button-ink: #f15881;
    --services-button-fill: #d81f4f;
}

/* Wide enough that a heading of the usual length falls in two lines, and
   balanced so the break lands mid-sentence instead of leaving a single word
   stranded on the second line. */
.services-grid-heading {
    max-width: 34ch;
    margin: 0 0 56px;
    color: var(--services-ink);
    font-size: clamp(1.75rem, 1.2rem + 1.7vw, 2.5rem);
    font-weight: 700;
    line-height: 1.2;
    text-wrap: balance;
}

/* The rich text editor wraps the heading in <p>: keep it part of the heading
   instead of a paragraph sitting inside one. */
.services-grid-heading p {
    margin: 0;
    font-size: inherit;
    font-weight: inherit;
    line-height: inherit;
    color: inherit;
}

.services-grid {
    list-style: none;
    display: grid;
    grid-template-columns: repeat(3, minmax(0, 1fr));
    gap: 56px 40px;
    margin: 0;
    padding: 0;
}

.services-grid-icon {
    display: block;
    color: var(--services-accent);
    font-size: 1.5rem;
    line-height: 1;
    margin-bottom: 18px;
}

.services-grid-title {
    margin: 0 0 12px;
    color: var(--services-ink);
    font-size: 1.05rem;
    font-weight: 700;
    line-height: 1.3;
}

.services-grid-text {
    margin: 0;
    max-width: 40ch;
    color: var(--services-body);
    font-size: 0.95rem;
    line-height: 1.6;
}

/* Heading and the optional button share a row. The heading is capped at 34ch,
   which leaves the right side of a three-column section empty -- that is where
   the button belongs, level with the heading it belongs to. Under the grid it
   sat beneath the middle column and read as attached to that one card.

   The heading carries the 56px gap to the grid on every other page, so the row
   takes it over and the heading's own bottom margin is dropped here. `wrap`
   means a narrow screen puts the button on its own line under the heading
   rather than crushing both. */
.services-grid-header {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    justify-content: space-between;
    gap: 24px 32px;
    margin-bottom: 56px;
}

.services-grid-header .services-grid-heading {
    margin-bottom: 0;
}

.services-grid-button {
    display: inline-flex;
    align-items: center;
    gap: 8px;
    border: 2px solid var(--services-button-ink, var(--services-accent));
    border-radius: 50px;
    padding: 13px 28px;
    color: var(--services-button-ink, var(--services-accent));
    font-size: 0.95rem;
    font-weight: 700;
    text-decoration: none;
    transition: background-color 0.25s ease, color 0.25s ease;
}

.services-grid-button:hover,
.services-grid-button:focus-visible {
    background-color: var(--services-button-fill, var(--services-accent));
    border-color: var(--services-button-fill, var(--services-accent));
    color: #ffffff;
}

.services-grid-button:focus-visible {
    outline: 3px solid var(--services-accent);
    outline-offset: 3px;
}

.services-grid-empty {
    color: var(--services-body);
}

@media (max-width: 991.98px) {
    .services-grid {
        grid-template-columns: repeat(2, minmax(0, 1fr));
        gap: 44px 32px;
    }
}

@media (max-width: 575.98px) {
    .services-grid-section {
        padding: 64px 0;
    }

    .services-grid-heading {
        margin-bottom: 40px;
    }

    .services-grid {
        grid-template-columns: minmax(0, 1fr);
        gap: 36px;
    }
}


/* ======================================= */
/* ---   WHY US BLOCK                  --- */
/* ======================================= */

.why-us-section {
    --why-us-ink: #0c1844;
    --why-us-accent: var(--brand-red);
    --why-us-body: var(--text-muted);
    --why-us-badge: rgba(200, 0, 54, 0.1);
    --why-us-bg: var(--bg-secondary);

    background-color: var(--why-us-bg);
    padding: 90px 0;
}

[data-bs-theme="dark"] .why-us-section {
    --why-us-ink: #ffffff;
    --why-us-body: rgba(255, 255, 255, 0.7);
    --why-us-badge: rgba(200, 0, 54, 0.22);
    --why-us-bg: rgba(255, 255, 255, 0.03);
}

.why-us-layout {
    display: grid;
    grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
    gap: 40px 64px;
    align-items: center;
}

/* With no image there is no left column to balance, so the copy keeps its own
   measure instead of stretching across the whole container. */
.why-us-layout-single {
    grid-template-columns: minmax(0, 62ch);
    justify-content: center;
}

.why-us-art {
    display: flex;
    justify-content: center;
}

.why-us-image {
    max-width: 100%;
    height: auto;
}

.why-us-heading {
    margin: 0 0 40px;
    color: var(--why-us-ink);
    font-size: clamp(1.6rem, 1.2rem + 1.3vw, 2.25rem);
    font-weight: 700;
    line-height: 1.2;
    text-wrap: balance;
}

/* The rich text editor wraps the heading in <p>: keep it part of the heading
   instead of a paragraph sitting inside one. */
.why-us-heading p {
    margin: 0;
    font-size: inherit;
    font-weight: inherit;
    line-height: inherit;
    color: inherit;
}

.why-us-points {
    list-style: none;
    margin: 0;
    padding: 0;
    display: grid;
    gap: 30px;
}

/* The badge sits in its own column so the title lines up with it and the
   paragraph below stays flush with the title, not with the icon. */
.why-us-point {
    display: grid;
    grid-template-columns: auto minmax(0, 1fr);
    gap: 16px;
    align-items: start;
}

.why-us-point-badge {
    display: flex;
    align-items: center;
    justify-content: center;
    width: 34px;
    height: 34px;
    border-radius: 8px;
    background-color: var(--why-us-badge);
    color: var(--why-us-accent);
    font-size: 1.05rem;
    line-height: 1;
}

.why-us-point-title {
    /* Aligns the title's cap height with the middle of the badge beside it. */
    margin: 5px 0 10px;
    color: var(--why-us-ink);
    font-size: 1.05rem;
    font-weight: 700;
    line-height: 1.3;
}

.why-us-point-text {
    margin: 0;
    max-width: 46ch;
    color: var(--why-us-body);
    font-size: 0.95rem;
    line-height: 1.6;
}

.why-us-point-empty {
    color: var(--why-us-body);
}

.why-us-button {
    display: inline-block;
    margin-top: 40px;
    border: 3px solid var(--why-us-accent);
    border-radius: 10px;
    padding: 14px 26px;
    color: var(--why-us-accent);
    font-weight: 700;
    text-decoration: none;
    transition: background-color 0.25s ease, color 0.25s ease;
}

.why-us-button:hover,
.why-us-button:focus-visible {
    background-color: var(--why-us-accent);
    color: #ffffff;
}

.why-us-button:focus-visible {
    outline: 2px solid var(--why-us-ink);
    outline-offset: 3px;
}

@media (max-width: 991.98px) {
    .why-us-layout {
        grid-template-columns: minmax(0, 1fr);
        gap: 40px;
    }

    /* The logo is decoration, not information: below the copy it would only
       push the call to action further down the page. */
    .why-us-art {
        order: -1;
    }

    .why-us-image {
        max-width: 320px;
    }
}

@media (max-width: 575.98px) {
    .why-us-section {
        padding: 64px 0;
    }

    .why-us-heading {
        margin-bottom: 32px;
    }

    .why-us-button {
        margin-top: 32px;
    }
}

@media (prefers-reduced-motion: reduce) {
    .why-us-button {
        transition-duration: 0.01ms;
    }
}


/* ======================================= */
/* ---   TECH STACK MARQUEE BLOCK      --- */
/* ======================================= */

.tech-stack-section {
    --tech-stack-ink: #0c1844;
    /* Was 0.35, which measured 2.2:1 against the band -- these are the
       integration names, real content answering the loudest silent
       objection on the page, so they clear 4.5:1 now. */
    --tech-stack-name: rgba(12, 24, 68, 0.62);
    --tech-stack-bg: var(--bg-secondary);

    background-color: var(--tech-stack-bg);
    padding: 80px 0;
    overflow: hidden;
}

[data-bs-theme="dark"] .tech-stack-section {
    --tech-stack-ink: #ffffff;
    --tech-stack-name: rgba(255, 255, 255, 0.55);
    --tech-stack-bg: rgba(255, 255, 255, 0.03);
}

.tech-stack-heading {
    max-width: 34ch;
    margin: 0 auto 48px;
    color: var(--tech-stack-ink);
    font-size: clamp(1.4rem, 1.05rem + 1.1vw, 1.9rem);
    font-weight: 700;
    line-height: 1.3;
    text-align: center;
    text-wrap: balance;
}

/* The rich text editor wraps the heading in <p>: keep it part of the heading
   instead of a paragraph sitting inside one. */
.tech-stack-heading p {
    margin: 0;
    font-size: inherit;
    font-weight: inherit;
    line-height: inherit;
    color: inherit;
}

/* The row runs the full width of the viewport, not the container: names are
   meant to be cut off at both edges so the line reads as continuous. */
.tech-stack-row {
    display: flex;
    width: 100%;
    overflow: hidden;
}

.tech-stack-row + .tech-stack-row {
    margin-top: 18px;
}

/* Each copy is at least as wide as the row itself. Over one cycle the pair
   slides left by exactly one copy, so the covered width falls from two copies
   to one -- and one copy still fills the row edge to edge. Anything narrower
   and the row would empty out from the right just before the cycle repeats,
   which reads as the marquee restarting instead of running on.

   The trailing padding matches the gap, which keeps the spacing even where one
   copy meets the next.

   Duration grows with the number of names so a long row does not race past:
   every row moves at the same speed, whatever it holds. */
.tech-stack-track {
    display: flex;
    flex: 0 0 auto;
    justify-content: space-around;
    min-width: 100%;
    gap: 46px;
    margin: 0;
    padding: 0 46px 0 0;
    list-style: none;
    animation: tech-stack-scroll linear infinite;
    animation-duration: calc(var(--tech-stack-count, 6) * 2.6s);
}

/* The second row travels the other way, so the two lines read as a weave
   rather than one block sliding sideways. */
.tech-stack-row-reverse .tech-stack-track {
    animation-direction: reverse;
}

.tech-stack-item {
    color: var(--tech-stack-name);
    font-size: clamp(1.25rem, 1rem + 0.7vw, 1.65rem);
    font-weight: 700;
    white-space: nowrap;
}

.tech-stack-empty {
    margin: 0;
    color: var(--text-muted);
    text-align: center;
}

@keyframes tech-stack-scroll {
    from {
        transform: translateX(0);
    }

    to {
        transform: translateX(-100%);
    }
}

/* Motion is the whole point of the row, so with it turned off there is nothing
   to preserve: hold the names still and drop the duplicate track that only
   existed to make the loop seamless. */
@media (prefers-reduced-motion: reduce) {
    .tech-stack-track {
        animation: none;
    }

    .tech-stack-track[aria-hidden="true"] {
        display: none;
    }
}

@media (max-width: 575.98px) {
    .tech-stack-section {
        padding: 56px 0;
    }

    .tech-stack-heading {
        margin-bottom: 32px;
    }

    .tech-stack-track {
        gap: 32px;
        padding-right: 32px;
    }
}


/* ======================================= */
/* ---   MEET US BLOCK                 --- */
/* ======================================= */

.meet-us-section {
    --meet-us-ink: #0c1844;
    --meet-us-body: var(--text-muted);
    --meet-us-name: #4a5578;
    --meet-us-bg: var(--bg-secondary);
    --meet-us-stagger: 250px;
    --meet-us-stagger-extra: 50px;
    /* Pulls the right-hand portraits back in from the outer edge of their
       column. 0 puts them hard against it; raise it to move them left. */
    --meet-us-outdent: 60px;
    /* Space between a portrait and the caption naming it. */
    --meet-us-caption-gap: 24px;

    background-color: var(--meet-us-bg);
    padding: 90px 0;
}

[data-bs-theme="dark"] .meet-us-section {
    --meet-us-ink: #ffffff;
    --meet-us-body: rgba(255, 255, 255, 0.7);
    --meet-us-name: rgba(255, 255, 255, 0.6);
    --meet-us-bg: rgba(255, 255, 255, 0.03);
}

/* Two equal columns filled by automatic placement, so the source order alone
   produces the zigzag: intro, then portrait 1 beside it, then one portrait per
   cell down the page. Nothing is positioned by hand. */
.meet-us-grid {
    display: grid;
    grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
    gap: 40px 56px;
    align-items: start;
    /* The transform below moves the last right-hand portrait without the grid
       reserving room for it, so the grid buys that room back here and the
       photo cannot spill into the section underneath. */
    padding-bottom: var(--meet-us-stagger-extra);
}

/* The even children are exactly the right-hand column. Dropping them makes the
   portraits read as a diagonal instead of as tidy pairs of rows.

   They also shrink to their artwork and sit at the outer edge of the cell
   instead of stretching across it. The column is wider than any of the
   portraits, so where in it they land is a choice: out, so the pair reads as a
   diagonal rather than as two portraits leaning on the gutter. */
.meet-us-grid > *:nth-child(even) {
    justify-self: end;
    margin-top: var(--meet-us-stagger);
    /* The outdent travels in the transform, not in a margin: a margin would
       reserve its width inside the column and squeeze the portrait narrower at
       the narrowest desktop, where the column has nothing spare. */
    transform: translate(calc(-1 * var(--meet-us-outdent)),
            var(--meet-us-stagger-extra));
}

/* Except the first portrait: it shares the top row with the intro, so it starts
   level with the heading instead of hanging below it. The diagonal picks up
   from the next one down. */
.meet-us-grid > *:nth-child(2) {
    margin-top: 0;
    /* Keeps the outdent, drops only the vertical stagger. */
    transform: translateX(calc(-1 * var(--meet-us-outdent)));
}

/* The copy sits at the middle of the first portrait's height rather than at the
   top of it, so the two read as one row. align-self, not a margin: the portrait
   sets the row height and the copy follows whatever that turns out to be. */
.meet-us-intro {
    align-self: center;
}

.meet-us-heading {
    margin: 0 0 20px;
    color: var(--meet-us-ink);
    font-size: clamp(1.5rem, 1.15rem + 1.2vw, 2rem);
    font-weight: 700;
    line-height: 1.25;
    text-wrap: balance;
}

/* The rich text editor wraps the heading in <p>: keep it part of the heading
   instead of a paragraph sitting inside one. */
.meet-us-heading p {
    margin: 0;
    font-size: inherit;
    font-weight: inherit;
    line-height: inherit;
    color: inherit;
}

.meet-us-text {
    margin: 0;
    max-width: 46ch;
    color: var(--meet-us-body);
    font-size: 0.95rem;
    line-height: 1.7;
}

.meet-us-person {
    margin: 0;
}

.meet-us-photo {
    display: block;
    max-width: 100%;
    height: auto;
}

/* A real gap, measured from the artwork itself.

   This used to be -6px, to climb into the band of transparent pixels the
   artwork carried below the red shape and so appear to sit against it. That
   band is gone -- trim_portrait_margins cropped it away so captions could align
   to the picture instead of to empty air -- and with it the reason for a
   negative margin. Against a tight image box, a negative margin is just the
   caption resting on the photo. */
.meet-us-caption {
    margin-top: var(--meet-us-caption-gap);
}

/* Same rule that decides the column decides the caption: the right-hand
   portraits are the even children, and their caption hangs off the right edge
   of the photo so it reads inwards, toward the middle of the page. */
.meet-us-grid > *:nth-child(even) .meet-us-caption {
    text-align: right;
}

.meet-us-role,
.meet-us-name {
    display: block;
}

.meet-us-role {
    color: var(--meet-us-ink);
    font-size: 0.9rem;
    font-weight: 700;
    line-height: 1.4;
}

.meet-us-name {
    color: var(--meet-us-name);
    font-size: 0.85rem;
    line-height: 1.5;
}

.meet-us-empty {
    margin: 0;
    color: var(--meet-us-body);
}

@media (max-width: 767.98px) {
    /* One column, and the offset that staggered the right-hand column would
       now only open a gap in the middle of the stack. */
    .meet-us-grid {
        grid-template-columns: minmax(0, 1fr);
        gap: 48px;
        padding-bottom: 0;
    }

    /* The outdent belongs to the two-column layout: there is no outer edge to
       sit against in a single column, and left on it would squeeze every second
       portrait narrower than its neighbours. */
    .meet-us-grid > *:nth-child(even) {
        justify-self: stretch;
        margin-top: 0;
        transform: none;
    }

    /* There is no right-hand column left to hang off, so every caption goes
       back to reading from the same edge. */
    .meet-us-grid > *:nth-child(even) .meet-us-caption {
        text-align: left;
    }

    /* The figure shrinks to its portrait and the pair centres together, so the
       caption lines up with the edge of the photo it names. Centring the photo
       alone left the caption against the container instead -- adrift from the
       portrait by however much narrower than the column that portrait was. */
    .meet-us-person {
        width: fit-content;
        margin-inline: auto;
    }

    .meet-us-photo {
        margin: 0 auto;
    }
}

@media (max-width: 575.98px) {
    .meet-us-section {
        padding: 64px 0;
    }
}


/* ======================================= */
/* ---   BUILD TEAM BLOCK              --- */
/* ======================================= */

.build-team-section {
    --build-team-ink: #0c1844;
    --build-team-accent: var(--brand-red);
    --build-team-bg: var(--bg-primary);
    /* Drives the photo and the column under it, so the row keeps its rhythm
       whatever the artwork's own proportions turn out to be. */
    --build-team-photo: 210px;

    background-color: var(--build-team-bg);
    padding: 90px 0;
    text-align: center;
}

[data-bs-theme="dark"] .build-team-section {
    --build-team-ink: #ffffff;
    --build-team-bg: transparent;
}

.build-team-heading,
.build-team-cta-heading {
    margin: 0;
    color: var(--build-team-ink);
    font-weight: 700;
    line-height: 1.3;
    text-wrap: balance;
}

.build-team-heading {
    font-size: clamp(1.4rem, 1.1rem + 1.1vw, 1.9rem);
}

.build-team-cta-heading {
    max-width: 20ch;
    margin: 48px auto 0;
    font-size: clamp(1.3rem, 1.05rem + 0.9vw, 1.7rem);
}

/* The rich text editor wraps each heading in <p>: keep it part of the heading
   instead of a paragraph sitting inside one. */
.build-team-heading p,
.build-team-cta-heading p {
    margin: 0;
    font-size: inherit;
    font-weight: inherit;
    line-height: inherit;
    color: inherit;
}

/* A row that scrolls rather than wraps, so the portraits never shrink to make
   room for each other. `safe` centers the row while it fits and falls back to
   flush-left once it overflows -- plain `center` would push the first member
   off the left edge, where scrolling cannot bring it back. */
/* The one-graphic version: shown at its own proportions, centred, and never
   wider than the content band. Nothing is imposed on it beyond that -- the
   layout inside it is the designer's. */
.build-team-graphic {
    display: block;
    width: 100%;
    max-width: 1140px;
    height: auto;
    margin: 40px auto 0;
}

/* When a page gives both a graphic and the separate portraits, both are in the
   HTML and the viewport picks one -- the graphic here, the portraits under
   768px. A graphic is a fixed row however narrow the screen, so a phone gets
   the portraits, which can reflow. Only these two rules and their pair in the
   media query decide it; the markup inside either branch is untouched. */
.build-team-photo-view {
    display: none;
}

/* The labels that go under a graphic. One equal column per role across the same
   width as the graphic, so each one sits under its own figure however many
   there are -- the graphic spaces its figures evenly, so the columns do too. */
.build-team-labels {
    display: grid;
    grid-auto-flow: column;
    grid-auto-columns: minmax(0, 1fr);
    gap: 16px;
    max-width: 1140px;
    margin: 18px auto 0;
    padding: 0;
    list-style: none;
}

.build-team-label {
    color: var(--build-team-ink);
    font-size: 0.95rem;
    font-weight: 700;
    text-align: center;
    text-wrap: balance;
}

.build-team-row {
    display: flex;
    justify-content: center;
    justify-content: safe center;
    gap: 24px;
    overflow-x: auto;
    list-style: none;
    margin: 40px 0 0;
    padding: 0 0 12px;
    scrollbar-width: thin;
}

.build-team-row:focus-visible {
    outline: 2px solid var(--build-team-accent);
    outline-offset: 6px;
}

.build-team-member {
    flex: 0 0 auto;
    width: var(--build-team-photo);
}

.build-team-photo {
    display: block;
    width: 100%;
    height: auto;
}

.build-team-role {
    display: block;
    margin-top: 14px;
    color: var(--build-team-ink);
    font-size: 0.85rem;
    font-weight: 700;
    line-height: 1.4;
}

.build-team-empty {
    color: var(--text-muted);
}

.build-team-button {
    display: inline-block;
    margin-top: 28px;
    border: 2px solid var(--build-team-accent);
    border-radius: 10px;
    padding: 12px 24px;
    color: var(--build-team-accent);
    font-size: 0.9rem;
    font-weight: 700;
    text-decoration: none;
    transition: background-color 0.25s ease, color 0.25s ease;
}

.build-team-button:hover,
.build-team-button:focus-visible {
    background-color: var(--build-team-accent);
    color: #ffffff;
}

.build-team-button:focus-visible {
    outline: 2px solid var(--build-team-ink);
    outline-offset: 3px;
}

@media (max-width: 767.98px) {
    .build-team-section {
        /* Two across instead of five, so the portraits get bigger here than
           they were when the row had to fit all of them. */
        --build-team-photo: 180px;
        padding: 64px 0;
    }

    .build-team-graphic-view {
        display: none;
    }

    .build-team-photo-view {
        display: block;
    }

    /* No room to scroll sideways on a phone, and nothing to hint that there is
       anything past the right edge -- so the row wraps into two columns and the
       whole team is on the page at once. */
    .build-team-row {
        display: grid;
        grid-template-columns: repeat(2, minmax(0, 1fr));
        justify-items: center;
        /* The cut-outs are each their own height, so bottom-aligning them puts
           the labels of a pair on one line and stands the figures on it. Left
           to stretch, a short portrait pulls its label halfway up the row. */
        align-items: end;
        gap: 28px 16px;
        overflow-x: visible;
        padding-bottom: 0;
    }

    .build-team-member {
        width: 100%;
        max-width: var(--build-team-photo);
    }

    /* An odd count leaves the last one on its own: center it across both
       columns rather than stranding it against the left edge. */
    .build-team-member:last-child:nth-child(odd) {
        grid-column: 1 / -1;
    }

    /* Only reached by a page with a graphic and no portraits behind it, where
       the graphic has to serve this width too. Five columns on a phone leaves a
       word per line, and lining the labels up with the figures is worth less by
       then than being able to read them, so they wrap into a list instead. */
    .build-team-labels {
        grid-auto-flow: row;
        grid-template-columns: repeat(2, minmax(0, 1fr));
        gap: 10px 16px;
    }
}

@media (prefers-reduced-motion: reduce) {
    .build-team-button {
        transition-duration: 0.01ms;
    }
}


/* ======================================= */
/* ---   FOOTER BLOCK                  --- */
/* ======================================= */

.footer-block {
    --footer-block-ink: #0c1844;
    --footer-block-body: var(--text-muted);
    --footer-block-surface: var(--bg-primary);
    --footer-block-accent: var(--brand-red);
    /* The band stays navy in both themes: it is the brand color, not a
       theme surface. */
    --footer-block-band: #0c1844;
    --footer-block-band-ink: #ffffff;
    --footer-block-band-muted: rgba(255, 255, 255, 0.65);
    --footer-block-curve: 48px;

    background-color: var(--footer-block-surface);
}

[data-bs-theme="dark"] .footer-block {
    --footer-block-ink: #ffffff;
    --footer-block-body: rgba(255, 255, 255, 0.7);
    --footer-block-surface: var(--bg-primary);
}

.footer-block .footer-block-cta {
    padding: 72px 0 calc(var(--footer-block-curve) + 24px);
}

.footer-block .footer-block-cta-grid {
    display: grid;
    grid-template-columns: minmax(0, 1.35fr) minmax(0, 0.65fr);
    gap: 40px 72px;
    align-items: start;
}

/* Nothing to sit beside, so the call to action takes the width back. */
.footer-block .footer-block-cta-grid-single {
    grid-template-columns: minmax(0, 1fr);
}

.footer-block .footer-block-heading {
    margin: 0;
    color: var(--footer-block-ink);
    font-size: clamp(1.75rem, 1.25rem + 1.6vw, 2.5rem);
    font-weight: 700;
    line-height: 1.18;
}

/* RichTextBlock wraps the heading in its own paragraph. */
.footer-block .footer-block-heading p {
    margin: 0;
}

.footer-block .footer-block-button {
    display: inline-block;
    margin-top: 32px;
    border: 3px solid var(--footer-block-accent);
    border-radius: 10px;
    padding: 14px 26px;
    color: var(--footer-block-accent);
    font-weight: 700;
    text-decoration: none;
    transition: background-color 0.2s ease, color 0.2s ease;
}

.footer-block .footer-block-button:hover,
.footer-block .footer-block-button:focus-visible {
    background-color: var(--footer-block-accent);
    color: #ffffff;
}

.footer-block .footer-block-list-heading {
    margin: 0 0 18px;
    color: var(--footer-block-ink);
    font-size: 1.05rem;
    font-weight: 700;
}

.footer-block .footer-block-items {
    margin: 0;
    padding: 0;
    list-style: none;
}

.footer-block .footer-block-item {
    display: flex;
    align-items: center;
    gap: 12px;
    margin-top: 14px;
    color: var(--footer-block-body);
    font-weight: 600;
}

.footer-block .footer-block-item:first-child {
    margin-top: 0;
}

.footer-block .footer-block-item-icon {
    flex-shrink: 0;
    color: var(--footer-block-accent);
    font-size: 1.15rem;
}

.footer-block .footer-block-item-link {
    color: inherit;
    text-decoration: none;
}

.footer-block .footer-block-item-link:hover,
.footer-block .footer-block-item-link:focus-visible {
    color: var(--footer-block-accent);
}

.footer-block .footer-block-band {
    position: relative;
    background-color: var(--footer-block-band);
    padding: calc(var(--footer-block-curve) + 24px) 0 40px;
}

/* The curve where the band meets the section above it: a slice of the surface
   color whose bottom edge is a flat ellipse. Drawn here rather than as a
   radius on the band itself, which would bow the wrong way -- up in the
   middle instead of down. */
.footer-block .footer-block-band::before {
    content: "";
    position: absolute;
    top: -1px;
    left: 0;
    right: 0;
    height: var(--footer-block-curve);
    background-color: var(--footer-block-surface);
    border-bottom-left-radius: 50% var(--footer-block-curve);
    border-bottom-right-radius: 50% var(--footer-block-curve);
}

.footer-block .footer-block-bottom {
    position: relative;
    display: flex;
    flex-wrap: wrap;
    justify-content: space-between;
    align-items: center;
    gap: 16px 40px;
}

.footer-block .footer-block-copyright {
    margin: 0;
    color: var(--footer-block-band-ink);
    font-size: 0.95rem;
}

.footer-block .footer-block-note {
    margin: 6px 0 0;
    color: var(--footer-block-band-muted);
    font-size: 0.8rem;
}

.footer-block .footer-block-links {
    display: flex;
    flex-wrap: wrap;
    gap: 12px 28px;
}

.footer-block .footer-block-link {
    color: var(--footer-block-band-ink);
    font-weight: 700;
    text-decoration: none;
}

.footer-block .footer-block-link:hover,
.footer-block .footer-block-link:focus-visible {
    text-decoration: underline;
}

@media (max-width: 767.98px) {
    .footer-block {
        --footer-block-curve: 28px;
    }

    .footer-block .footer-block-cta {
        padding: 56px 0 calc(var(--footer-block-curve) + 16px);
    }

    .footer-block .footer-block-cta-grid {
        grid-template-columns: minmax(0, 1fr);
        gap: 32px;
    }

    .footer-block .footer-block-band {
        padding: calc(var(--footer-block-curve) + 20px) 0 32px;
    }

    .footer-block .footer-block-bottom {
        justify-content: flex-start;
    }
}

@media (prefers-reduced-motion: reduce) {
    .footer-block .footer-block-button {
        transition-duration: 0.01ms;
    }
}


/* ======================================= */
/* ---   CAMPAIGN CONTACT ANCHOR       --- */
/* ======================================= */

/* Every button on a campaign landing page links to #contact-form, which is the
   lead form up in the hero. Scoped to pages that actually carry that hero, so
   anchor jumps everywhere else on the site keep their instant behaviour. */
@media (prefers-reduced-motion: no-preference) {
    html:has(.campaign-hero) {
        scroll-behavior: smooth;
    }
}

/* Stops the form card landing flush against the top edge of the viewport. */
#contact-form {
    scroll-margin-top: 24px;
}

/* --- The same form, two presentations ------------------------------------

   Above 992px the hero's copy and form sit side by side and the form is an
   inline card. Below it they stack, and a 480px card under the subtitle pushes
   the rest of the page off the first screen -- so there the form becomes a
   dialog, and the buttons that used to scroll to it open it instead.

   It is one <dialog> either way: `open` and not modal is the inline card,
   showModal() is the overlay. Native dialog brings the focus trap, Escape and
   the backdrop with it, which is most of why it is a dialog and not a div. */

@media (max-width: 991.98px) {
    /* display:contents, not none. A display:none ancestor stops the dialog
       rendering even in the top layer, and switching the column back on when it
       opens would shift the hero behind the backdrop. This way the column never
       draws and the dialog is free to. */
    .campaign-hero-form-col {
        display: contents;
    }
}

/* In flow: strip the dialog back to nothing, so what shows is the card inside
   it -- exactly as it looked before it was wrapped. The UA gives an open dialog
   position:absolute, a margin, a border and a background; all four have to go. */
.campaign-hero-form-dialog[open]:not(:modal) {
    display: block;
    position: static;
    width: auto;
    max-width: 100%;
    height: auto;
    margin: 0;
    border: 0;
    padding: 0;
    background: none;
    color: inherit;
}

/* Modal: centred in the viewport. The UA gives a modal dialog inset:0, so
   `margin: auto` on all four sides is what centres it on both axes -- an
   explicit top margin pins it to the top instead.

   dvh, not vh, so a phone's collapsing address bar cannot crop the button:
   the max-height leaves 1.5rem of breathing room top and bottom, and the card
   inside scrolls once the form is taller than that. */
.campaign-hero-form-dialog:modal {
    width: min(440px, 92vw);
    max-width: none;
    max-height: calc(100dvh - 3rem);
    margin: auto;
    border: 0;
    padding: 0;
    background: none;
    overflow: visible;
}

.campaign-hero-form-dialog::backdrop {
    background-color: rgba(12, 24, 68, 0.55);
}

/* The card carries its own padding and shadow. Beating
   `.campaign-hero-form-col .campaign-hero-form-card`'s fixed 420px needs the
   pseudo-class to win on specificity, which it does. */
.campaign-hero-form-dialog:modal .campaign-hero-form-card {
    width: auto;
    max-height: calc(100dvh - 3rem);
    overflow-y: auto;
}

@media (max-width: 575.98px) {
    /* 40px of padding either side leaves little room for the fields on a narrow
       phone. */
    .campaign-hero-form-dialog:modal .campaign-hero-form-card {
        padding: 28px 20px;
    }
}

/* A phone has no Escape key, so tapping the backdrop is one way out and this is
   the other. Pointless while the form is an inline card, so it is not shown. */
.campaign-hero-form-close {
    position: absolute;
    top: 0.5rem;
    right: 0.5rem;
    z-index: 1;
    display: none;
    width: 2.25rem;
    height: 2.25rem;
    align-items: center;
    justify-content: center;
    border: 0;
    border-radius: 50%;
    padding: 0;
    background-color: transparent;
    color: var(--text-muted);
    font-size: 1rem;
    line-height: 1;
}

.campaign-hero-form-dialog:modal .campaign-hero-form-close {
    display: flex;
}

.campaign-hero-form-close:hover,
.campaign-hero-form-close:focus-visible {
    color: var(--brand-red);
}

/* The trigger only exists while the form is a dialog. */
.campaign-hero-form-trigger {
    display: none;
}

@media (max-width: 991.98px) {
    .campaign-hero-form-trigger {
        display: inline-flex;
        align-items: center;
        gap: 0.5rem;
        margin-top: 0.5rem;
    }
}

/* --- Hero artwork, stacked ------------------------------------------------

   On a wide screen the artwork is an absolute layer behind the copy and the
   form, which is what lets the group stand behind the curve. Stacked, that
   layer has nothing to sit behind: it lands across the headline and subtitle
   and makes both hard to read. So below 992px the layer is dropped and an
   in-flow copy takes over, between the logo and the headline. */

.campaign-hero-center-inline {
    display: none;
}

@media (max-width: 991.98px) {
    .campaign-hero-center-layer {
        display: none;
    }

    /* The picture ends where the people do.

       A full-height layer behind a stacked hero runs on under the headline, the
       subtitle and the button, which is what this replaces: the band wraps the
       logo and the artwork and nothing else, so its bottom edge *is* the bottom
       of the artwork, wherever that falls for a given image. No height to keep
       in step with the logo or the crop.

       It bleeds sideways to the viewport and upward past the hero's padding, so
       the picture still reaches the edges and the top; .hero-section clips both
       overhangs. Same opacity the layer used here, so the band looks like the
       top of the hero always did. */
    .campaign-hero .hero-bg-layer,
    .campaign-hero .hero-gradient-mask {
        display: none;
    }

    .campaign-hero-band {
        position: relative;
    }

    .campaign-hero-band::before {
        content: "";
        position: absolute;
        /* Enough to clear the hero's own top padding at any zoom; clipped. */
        top: -240px;
        bottom: 0;
        left: calc(50% - 50vw);
        width: 100vw;
        z-index: -1;
        background-image: var(--campaign-hero-bg-image, url("../img/circuitboard-background.a14d13ff1620.avif"));
        background-size: cover;
        background-position: center top;
        background-repeat: no-repeat;
        opacity: 0.2;
    }

    .campaign-hero-center-inline {
        display: block;
        margin: 0 auto 1.25rem;
    }

    .campaign-hero-center-inline-img {
        display: block;
        width: 100%;
        /* Wide enough to read the faces, short enough to leave the headline,
           subtitle and button on the first screen. */
        max-width: 300px;
        height: auto;
        margin: 0 auto;
    }
}

/* ==========================================================================
   Rich text headings
   ========================================================================== */

/* A richtext heading wraps its content in <p>; keep it inline with the heading
   it sits in so the colour feature can be used without changing the type. */
.rich-heading p {
    margin: 0;
    font-size: inherit;
    font-weight: inherit;
    line-height: inherit;
    color: inherit;
}

/* ==========================================================================
   Podcast episodes
   ========================================================================== */

/* The cover art beside the title on an episode page. The artwork is drawn at
   979x1224, so the frame is that shape (1224/979) and nothing is cropped. */
.podcast-cover-art {
    --bs-aspect-ratio: 125%;
    border-radius: 12px;
    overflow: hidden;
    box-shadow: 0 10px 30px rgba(0, 0, 0, 0.35);
}

.podcast-cover-art img {
    width: 100%;
    height: 100%;
    object-fit: cover;
}

/* The same artwork on the cards in the list, in the same shape. */
.podcast-card-art {
    aspect-ratio: 979 / 1224;
    object-fit: cover;
}

.podcast-episode-description p:last-child {
    margin-bottom: 0;
}

/* The chapter rail: jump points under the player. Capped in height so a long
   episode's chapters don't push the transcript off the screen. */
.podcast-chapter-list {
    max-height: 320px;
    overflow-y: auto;
}

.podcast-chapter-list li + li {
    border-top: 1px solid rgba(128, 128, 128, 0.2);
}

.podcast-chapter-jump,
.podcast-chapter-static {
    display: flex;
    align-items: baseline;
    gap: 0.75rem;
    width: 100%;
    padding: 0.5rem 0.25rem;
    background: none;
    border: 0;
    text-align: left;
    color: inherit;
}

.podcast-chapter-jump {
    cursor: pointer;
    border-radius: 6px;
}

.podcast-chapter-jump:hover,
.podcast-chapter-jump:focus-visible {
    background-color: rgba(200, 0, 54, 0.08);
}

.podcast-chapter-time {
    flex: 0 0 auto;
    font-variant-numeric: tabular-nums;
    font-weight: 700;
    color: var(--brand-red);
}

/* The transcript: same visual language as the chapter rail above it, so a row
   that jumps the player looks like the rows that already do. */
.podcast-transcript-row + .podcast-transcript-row {
    border-top: 1px solid rgba(128, 128, 128, 0.2);
}

.podcast-transcript-jump,
.podcast-transcript .podcast-chapter-static {
    align-items: baseline;
    padding: 0.6rem 0.25rem;
}

.podcast-transcript-text {
    line-height: 1.7;
}

/* Headings break the flow rather than sitting in it: an editor scanning for a
   section should find it without reading the paragraphs around it. */
.podcast-transcript-heading {
    font-size: 1.05rem;
    font-weight: 700;
    margin: 1.75rem 0 0.5rem;
    padding-top: 1.25rem;
    border-top: 2px solid var(--brand-red);
    letter-spacing: 0.01em;
}

.podcast-transcript-heading:first-child {
    margin-top: 0;
    padding-top: 0;
    border-top: 0;
}

/* A transcript with no timestamps in it -- typed by hand, or written before
   this field carried them. Reads as plain prose, with no jump point to offer. */
.podcast-transcript-plain {
    margin: 0;
    padding: 0.6rem 0.25rem;
    line-height: 1.7;
}

/* Back to Podcasts, sitting over the episode header art.

   Carries its own dark fill rather than being a bare outline: the top of the
   header can be anything from a dark photo to the white navbar fade, and a
   white-on-white button is one an editor reports as missing. */
.podcast-back-link {
    --bs-btn-color: #fff;
    --bs-btn-bg: rgba(0, 0, 0, 0.55);
    --bs-btn-border-color: rgba(255, 255, 255, 0.6);
    --bs-btn-hover-color: #fff;
    --bs-btn-hover-bg: rgba(0, 0, 0, 0.75);
    --bs-btn-hover-border-color: #fff;
    backdrop-filter: blur(2px);
    text-shadow: 0 1px 2px rgba(0, 0, 0, 0.6);
}

/* Newest/Oldest above the episode grid. Kept on one line: "Newest to Oldest"
   broken across two makes the pair look like four choices. */
.episode-order .btn {
    white-space: nowrap;
}

/* The jump back up from the foot of a long transcript, taken at reading pace
   rather than as a snap.

   Scoped to the episode page rather than left on html: scroll-behavior has to
   sit on the scrolling element, so reaching it from a page needs :has(). The
   header class is the episode page's own. Every other anchor on the site keeps
   the browser's instant jump, which is what it had before podcasts arrived. */
html:has(.podcast-episode-header) {
    scroll-behavior: smooth;
}

@media (prefers-reduced-motion: reduce) {
    html:has(.podcast-episode-header) {
        scroll-behavior: auto;
    }
}

/* The still frame that stands in for the YouTube player until someone presses
   play. Keeps a megabyte of YouTube's script off the page until it is wanted. */
.podcast-player-shell {
    background-color: #000;
}

.podcast-facade {
    /* No position here: Bootstrap's `.ratio > *` lays this out absolutely to
       fill the frame, and it is the same specificity as this rule -- setting
       position: relative wins on order and drops the button out of the box. */
    display: block;
    width: 100%;
    height: 100%;
    padding: 0;
    border: 0;
    background: none;
    cursor: pointer;
}

.podcast-facade-still {
    width: 100%;
    height: 100%;
    object-fit: cover;
}

/* YouTube's own play button, drawn rather than fetched. */
.podcast-facade-play {
    position: absolute;
    top: 50%;
    left: 50%;
    width: 68px;
    height: 48px;
    transform: translate(-50%, -50%);
    background-color: rgba(33, 33, 33, 0.8);
    border-radius: 14px;
    transition: background-color 0.15s ease;
}

.podcast-facade-play::after {
    content: "";
    position: absolute;
    top: 50%;
    left: 50%;
    transform: translate(-40%, -50%);
    border-style: solid;
    border-width: 11px 0 11px 19px;
    border-color: transparent transparent transparent #fff;
}

.podcast-facade:hover .podcast-facade-play,
.podcast-facade:focus-visible .podcast-facade-play {
    background-color: #f00;
}


/* ======================================= */
/* ---   SOLUTIONS BLOCK               --- */
/* ======================================= */

/* Categories with the capabilities listed under each. Services says what we
   do; this says what the buyer ends up holding. Each one is a card with a
   rule across its top that fills with brand red on hover, so the grid has
   some structure to it rather than four columns of loose text. */
.solutions-section {
    --solutions-ink: #0c1844;
    --solutions-accent: var(--brand-red);
    --solutions-body: #454c63;
    --solutions-bg: var(--bg-primary);
    --solutions-card: var(--bg-card);
    --solutions-hairline: rgba(12, 24, 68, 0.1);

    position: relative;
    background-color: var(--solutions-bg);
    padding: 72px 0;
    overflow: hidden;
}

[data-bs-theme="dark"] .solutions-section {
    --solutions-ink: #ffffff;
    --solutions-body: rgba(255, 255, 255, 0.7);
    --solutions-bg: transparent;
    --solutions-hairline: rgba(255, 255, 255, 0.14);
}

.solutions-heading {
    max-width: 24ch;
    margin: 0 0 16px;
    color: var(--solutions-ink);
    font-size: clamp(1.9rem, 1.2rem + 2.1vw, 2.9rem);
    font-weight: 700;
    line-height: 1.12;
    letter-spacing: -0.01em;
    text-wrap: balance;
}

/* The rich text editor wraps the heading in <p>: keep it part of the heading
   instead of a paragraph sitting inside one. */
.solutions-heading p {
    margin: 0;
    font-size: inherit;
    font-weight: inherit;
    line-height: inherit;
    color: inherit;
}

.solutions-subtitle {
    max-width: 58ch;
    margin: 0 0 36px;
    color: var(--solutions-body);
    font-size: 1.14rem;
    line-height: 1.6;
}

.solutions-heading:last-child,
.solutions-subtitle:last-child {
    margin-bottom: 0;
}

/* One row, always, filling the container: the cards divide the width between
   them whatever their number, and never wrap to sit above or below each other.
   `flex: 1 1 0` rather than a grid track is what does it -- every card gets an
   equal share of the row regardless of its content, so three cards fill the
   same width four would, and the section has no ragged gap on its right.
   `min-width: 0` is the part that is easy to miss: a flex item defaults to
   min-width:auto and refuses to shrink below its longest word, which is what
   would force a wrap.

   The height is still governed by the photo, which stays square -- some of this
   artwork carries labels right up to its edges, so cropping it shorter takes
   words with it. A card is as wide as its share of the row and therefore as
   tall as that in the image alone, which is fine from three cards up and the
   reason for the one- and two-card guard below. */
.solutions-grid {
    list-style: none;
    display: flex;
    flex-wrap: nowrap;
    align-items: stretch;
    gap: 28px;
    margin: 0;
    padding: 0;
}

/* One or two cards would each be ~550px wide in a full-width row, and a square
   photo makes that a 550px wall of image before a word is read. Capping the row
   keeps those two cases at a sane size; three or more are unaffected. */
.solutions-grid:has(.solutions-item:only-child),
.solutions-grid:has(.solutions-item:nth-child(2):last-child) {
    max-width: 760px;
}

/* ...and the other end. The list is uncapped -- an editor can add a sixth
   card -- and a row that cannot wrap gives six of them ~160px each on a
   1140px container, each still carrying a square photo, a title, a paragraph
   and four bullets. Past five the row is allowed to wrap instead.

   A wrap threshold rather than `max_num` on the ListBlock: max_num is enforced
   in clean(), so it is retroactive, and an existing page with six cards would
   stop saving from the admin on a section the editor never touched. Layout
   should degrade, not lock the page. */
/* Capped at a quarter row, not left to grow: `flex: 1 1 260px` alone wraps six
   cards as four then two, and the two stretch to 634px each -- double their
   neighbours, which reads as a mistake rather than a second row. The cap keeps
   every card the same width whatever the count, and centring the row means the
   short last row sits under the middle of the one above instead of hanging
   left. */
/* Scoped to desktop. These selectors are specificity (0,4,0) and the phone
   rule below is (0,1,0) -- specificity beats media-query source order, so
   unscoped they would out-rank the 78%-width snap scroller and cap cards at
   ~76px on a 390px screen. The wrap branch is a desktop concern only: on a
   phone the row is already one card wide and scrolling. */
@media (min-width: 768px) {
    .solutions-grid:has(> .solutions-item:nth-child(6)) {
        flex-wrap: wrap;
        justify-content: center;
    }

    .solutions-grid:has(> .solutions-item:nth-child(6)) > .solutions-item {
        flex: 1 1 260px;
        max-width: calc((100% - 3 * 28px) / 4);
    }
}

.solutions-item {
    position: relative;
    flex: 1 1 0;
    min-width: 0;
    display: flex;
    flex-direction: column;
    padding: 28px 26px;
    border: 1px solid var(--solutions-hairline);
    border-radius: 16px;
    background-color: var(--solutions-card);
    transition: transform 0.25s ease, box-shadow 0.25s ease,
        border-color 0.25s ease;
}

/* A rule across the top that draws itself in on hover. Scale from the left
   rather than animating width, so it stays on the compositor. */
.solutions-item::before {
    content: "";
    position: absolute;
    top: -1px;
    left: 24px;
    right: 24px;
    height: 3px;
    border-radius: 3px;
    background-color: var(--solutions-accent);
    transform: scaleX(0);
    transform-origin: left;
    transition: transform 0.3s ease;
}

.solutions-item:hover {
    transform: translateY(-4px);
    border-color: transparent;
    box-shadow: var(--shadow-soft);
}

.solutions-item:hover::before {
    transform: scaleX(1);
}

.solutions-icon {
    display: flex;
    align-items: center;
    justify-content: center;
    width: 52px;
    height: 52px;
    margin-bottom: 22px;
    border-radius: 14px;
    background-color: rgba(200, 0, 54, 0.09);
    color: var(--solutions-accent);
    font-size: 1.4rem;
    line-height: 1;
}

[data-bs-theme="dark"] .solutions-icon {
    background-color: rgba(235, 34, 88, 0.16);
}

.solutions-title {
    margin: 0 0 12px;
    color: var(--solutions-ink);
    font-size: 1.2rem;
    font-weight: 700;
    line-height: 1.3;
}

.solutions-text {
    margin: 0 0 16px;
    max-width: 44ch;
    color: var(--solutions-body);
    font-size: 0.92rem;
    line-height: 1.55;
}

.solutions-capabilities {
    list-style: none;
    margin: 0;
    padding: 16px 0 0;
    border-top: 1px solid var(--solutions-hairline);
    display: grid;
    gap: 10px;
}

.solutions-capability {
    display: grid;
    grid-template-columns: auto minmax(0, 1fr);
    gap: 10px;
    align-items: start;
    color: var(--solutions-body);
    font-size: 0.95rem;
    line-height: 1.5;
}

.solutions-capability-icon {
    color: var(--solutions-accent);
    font-size: 0.95rem;
    line-height: 1.5;
}

/* margin-top:auto takes the slack, so the button sits on the card's floor
   rather than under whatever the copy happened to end at -- and because the
   grid stretches cards to a common height, every button in a row lines up. */
.solutions-action {
    display: flex;
    justify-content: flex-end;
    margin-top: auto;
    padding-top: 20px;
}

.solutions-button {
    display: inline-flex;
    align-items: center;
    gap: 8px;
    border: 2px solid var(--solutions-accent);
    border-radius: 50px;
    padding: 11px 22px;
    color: var(--solutions-accent);
    font-size: 0.92rem;
    font-weight: 700;
    text-decoration: none;
    transition: background-color 0.25s ease, color 0.25s ease;
}

.solutions-button i {
    transition: transform 0.25s ease;
}

.solutions-button:hover,
.solutions-button:focus-visible {
    background-color: var(--solutions-accent);
    color: #ffffff;
}

.solutions-button:hover i {
    transform: translateX(4px);
}

.solutions-empty {
    color: var(--solutions-body);
}

@media (max-width: 767.98px) {
    .solutions-section {
        padding: 68px 0;
    }

    /* Still one row: the cards never stack. But a phone cannot give three of
       them a readable share of 390px -- that is ~115px each -- so below this
       width each card keeps a floor of most of the screen and the row scrolls
       sideways instead of squeezing. Side by side either way, never above or
       below one another. */
    .solutions-grid {
        gap: 18px;
        overflow-x: auto;
        overscroll-behavior-x: contain;
        scroll-snap-type: x mandatory;
        scroll-padding-left: 4px;
        padding-bottom: 8px;
    }

    .solutions-item {
        flex: 1 0 78%;
        scroll-snap-align: start;
        padding: 26px 22px;
    }

    /* The hover lift and the rule that draws in on it are both pointer
       affordances. On a touch screen they only ever fire after the tap has
       already navigated, so the rule is simply shown and the card sits still. */
    .solutions-item::before {
        transform: scaleX(1);
        left: 22px;
        right: 22px;
    }

    .solutions-item:hover {
        transform: none;
        border-color: var(--solutions-hairline);
        box-shadow: none;
    }

    .solutions-icon {
        width: 46px;
        height: 46px;
        margin-bottom: 18px;
        border-radius: 12px;
        font-size: 1.25rem;
    }

    .solutions-title {
        font-size: 1.1rem;
    }

    .solutions-capabilities {
        padding-top: 18px;
    }

    .solutions-action {
        padding-top: 22px;
    }

    .solutions-button {
        flex: 1 1 auto;
        justify-content: center;
    }

    .solutions-subtitle {
        margin-bottom: 40px;
    }
}


/* ======================================= */
/* ---   PROCESS BLOCK                 --- */
/* ======================================= */

/* Numbered steps. The number is a CSS counter off the <ol>, never an editor
   field, so reordering steps in the admin renumbers them and nobody can
   publish a page that counts 1, 2, 2, 4. */
.process-section {
    --process-ink: #0c1844;
    --process-accent: var(--brand-red);
    --process-badge-ink: #ffffff;
    --process-body: #454c63;
    --process-bg: var(--bg-secondary);
    --process-rail: rgba(12, 24, 68, 0.12);
    --process-ghost: rgba(12, 24, 68, 0.05);
    --process-badge-size: 56px;

    position: relative;
    background-color: var(--process-bg);
    padding: 100px 0;
    counter-reset: process-step;
    overflow: hidden;
}

[data-bs-theme="dark"] .process-section {
    --process-ink: #ffffff;
    --process-body: rgba(255, 255, 255, 0.7);
    --process-bg: rgba(255, 255, 255, 0.03);
    --process-rail: rgba(255, 255, 255, 0.18);
    --process-ghost: rgba(255, 255, 255, 0.06);
}

.process-heading {
    max-width: 24ch;
    margin: 0 0 16px;
    color: var(--process-ink);
    font-size: clamp(1.9rem, 1.2rem + 2.1vw, 2.9rem);
    font-weight: 700;
    line-height: 1.12;
    letter-spacing: -0.01em;
    text-wrap: balance;
}

.process-heading p {
    margin: 0;
    font-size: inherit;
    font-weight: inherit;
    line-height: inherit;
    color: inherit;
}

.process-subtitle {
    max-width: 58ch;
    margin: 0 0 64px;
    color: var(--process-body);
    font-size: 1.14rem;
    line-height: 1.6;
}

.process-heading:last-child,
.process-subtitle:last-child {
    margin-bottom: 0;
}

.process-steps {
    list-style: none;
    display: grid;
    /* Overridden per step count by the -c* classes below. Four is the
       fallback for a body saved before those existed. */
    grid-template-columns: repeat(4, minmax(0, 1fr));
    gap: 32px;
    margin: 0;
    padding: 0;
}

/* Balanced by step count in ProcessBlock.get_context: six steps across four
   columns left an orphan row of two, where three columns read as 3 + 3. */
.process-steps-c1 { grid-template-columns: minmax(0, 1fr); }
.process-steps-c2 { grid-template-columns: repeat(2, minmax(0, 1fr)); }
.process-steps-c3 { grid-template-columns: repeat(3, minmax(0, 1fr)); }
.process-steps-c4 { grid-template-columns: repeat(4, minmax(0, 1fr)); }

/* A last row holding a single step centres itself instead of hanging in the
   first column beside two empty ones. process_columns() already avoids this
   for every count from 1 to 20 except 13, which no column count divides -- so
   this is the layout degrading gracefully in the one case arithmetic cannot
   fix, rather than a `max_num` forbidding it. A cap would be retroactive and
   would lock an existing page out of the admin; see ProcessBlock.steps.

   Spanning the full row and capping the width keeps the card the same size as
   its neighbours rather than stretching it across the section. */
.process-steps-c3 > .process-step:last-child:nth-child(3n + 1),
.process-steps-c4 > .process-step:last-child:nth-child(4n + 1) {
    grid-column: 1 / -1;
    justify-self: center;
}

.process-steps-c3 > .process-step:last-child:nth-child(3n + 1) {
    width: calc((100% - 2 * 32px) / 3);
}

.process-steps-c4 > .process-step:last-child:nth-child(4n + 1) {
    width: calc((100% - 3 * 32px) / 4);
}

/* 991.98px, matching where .process-steps itself collapses to one column --
   not 767.98px. Gated at the narrower value, the 768-991px band kept the width
   cap while the grid was already single-column, rendering the lone last card
   at a third the width of the full-width cards above it. */
@media (max-width: 991.98px) {
    .process-steps-c3 > .process-step:last-child:nth-child(3n + 1),
    .process-steps-c4 > .process-step:last-child:nth-child(4n + 1) {
        width: auto;
    }
}

.process-step {
    counter-increment: process-step;
    position: relative;
}

/* An oversized ghost numeral behind each step. Decorative only -- the badge
   below carries the same number at a readable size. */
.process-step::before {
    content: counter(process-step);
    position: absolute;
    top: -34px;
    left: -10px;
    z-index: 0;
    color: var(--process-ghost);
    font-size: 6rem;
    font-weight: 700;
    line-height: 1;
    pointer-events: none;
}

.process-step-number {
    position: relative;
    z-index: 1;
    display: flex;
    align-items: center;
    justify-content: center;
    width: var(--process-badge-size);
    height: var(--process-badge-size);
    margin-bottom: 24px;
    border-radius: 50%;
    background-image: linear-gradient(
        135deg,
        var(--process-accent),
        var(--brand-red-hover)
    );
    color: var(--process-badge-ink);
    font-size: 1.2rem;
    font-weight: 700;
    line-height: 1;
    box-shadow: 0 10px 24px rgba(200, 0, 54, 0.28);
}

.process-step-number::before {
    content: counter(process-step);
}

/* The rail joins one badge to the next. Drawn from the badge rightwards and
   suppressed on the last step, so it never runs off the end of the row. It
   fades out as it travels, which reads as direction. */
.process-step:not(:last-child)::after {
    content: "";
    position: absolute;
    top: calc(var(--process-badge-size) / 2);
    left: var(--process-badge-size);
    width: calc(100% + 32px - var(--process-badge-size));
    height: 2px;
    background-image: linear-gradient(
        to right,
        var(--process-rail),
        transparent
    );
}

.process-step-title {
    position: relative;
    z-index: 1;
    margin: 0 0 10px;
    color: var(--process-ink);
    font-size: 1.15rem;
    font-weight: 700;
    line-height: 1.3;
}

.process-step-text {
    position: relative;
    z-index: 1;
    margin: 0;
    color: var(--process-body);
    font-size: 0.97rem;
    line-height: 1.65;
}

.process-empty {
    color: var(--process-body);
}

/* Below the desktop row the steps become a vertical timeline rather than a
   wrapped grid. A 2x2 grid loses the sequence -- reading order goes across
   then down, which is not what a numbered process means -- and the rail has
   nowhere to run. Turned on its side it keeps both. */
@media (max-width: 991.98px) {
    .process-steps {
        grid-template-columns: minmax(0, 1fr);
        gap: 0;
    }

    .process-step {
        display: grid;
        grid-template-columns: var(--process-badge-size) minmax(0, 1fr);
        column-gap: 22px;
        align-items: start;
        padding-bottom: 40px;
    }

    .process-step:last-child {
        padding-bottom: 0;
    }

    .process-step-number {
        margin-bottom: 0;
    }

    /* The rail now runs down from each badge to the next. Solid rather than
       the desktop fade: vertically the segments meet, so a gradient would
       read as a broken line instead of as direction. */
    .process-step:not(:last-child)::after {
        top: var(--process-badge-size);
        left: calc(var(--process-badge-size) / 2 - 1px);
        width: 2px;
        height: calc(100% - var(--process-badge-size));
        background-image: none;
        background-color: var(--process-rail);
    }

    /* Out from behind the badge, which now sits in its own column. */
    .process-step::before {
        top: -14px;
        left: auto;
        right: 4px;
        font-size: 4.5rem;
    }
}

@media (max-width: 575.98px) {
    .process-section {
        --process-badge-size: 46px;

        padding: 68px 0;
    }

    .process-step {
        column-gap: 18px;
        padding-bottom: 34px;
    }

    .process-step-number {
        font-size: 1.05rem;
    }

    .process-step-title {
        font-size: 1.05rem;
    }

    .process-subtitle {
        margin-bottom: 40px;
    }

    .process-step::before {
        top: -10px;
        font-size: 3.4rem;
    }
}


/* ======================================= */
/* ---   FAQ BLOCK                     --- */
/* ======================================= */

/* An editorial spread: heading and invitation on the left, questions on the
   right. The single-column version left half the row empty beside an 820px
   accordion, which read as something failing to load rather than as space.

   Bootstrap's accordion underneath, so the collapse behaviour and keyboard
   handling come for free and only the chrome is restated here. */
.faq-section {
    --faq-ink: #0c1844;
    --faq-accent: var(--brand-red);
    --faq-body: var(--text-muted);
    --faq-bg: var(--bg-primary);
    --faq-card: var(--bg-card);
    --faq-hairline: rgba(12, 24, 68, 0.1);
    --faq-number: rgba(12, 24, 68, 0.28);

    position: relative;
    background-color: var(--faq-bg);
    padding: 100px 0;
    overflow: hidden;
}

[data-bs-theme="dark"] .faq-section {
    --faq-ink: #ffffff;
    --faq-body: rgba(255, 255, 255, 0.7);
    --faq-bg: transparent;
    --faq-hairline: rgba(255, 255, 255, 0.14);
    --faq-number: rgba(255, 255, 255, 0.32);
}

/* A soft brand wash behind the questions. Decorative, so it is a span with
   aria-hidden rather than a pseudo-element on the section: the section
   already carries overflow and a background it would have to fight. */
.faq-glow {
    position: absolute;
    top: -180px;
    right: -160px;
    width: 620px;
    height: 620px;
    border-radius: 50%;
    background-image: radial-gradient(
        circle,
        rgba(200, 0, 54, 0.1),
        transparent 68%
    );
    pointer-events: none;
}

[data-bs-theme="dark"] .faq-glow {
    background-image: radial-gradient(
        circle,
        rgba(235, 34, 88, 0.14),
        transparent 68%
    );
}

.faq-layout {
    position: relative;
    display: grid;
    grid-template-columns: minmax(0, 0.85fr) minmax(0, 1.15fr);
    gap: 40px 72px;
    align-items: start;
}

/* Follows the questions down on a long list, so the heading is still there
   when someone is reading the last answer. */
.faq-intro {
    position: sticky;
    top: 110px;
}

.faq-eyebrow {
    margin: 0 0 14px;
    color: var(--faq-accent);
    font-size: 0.78rem;
    font-weight: 700;
    letter-spacing: 0.16em;
    text-transform: uppercase;
}

.faq-heading {
    margin: 0;
    color: var(--faq-ink);
    font-size: clamp(1.9rem, 1.2rem + 2.1vw, 2.9rem);
    font-weight: 700;
    line-height: 1.12;
    letter-spacing: -0.01em;
    text-wrap: balance;
}

.faq-heading p {
    margin: 0;
    font-size: inherit;
    font-weight: inherit;
    line-height: inherit;
    color: inherit;
}

.faq-subtitle {
    margin: 20px 0 0;
    max-width: 42ch;
    color: var(--faq-body);
    font-size: 1.02rem;
    line-height: 1.65;
}

.faq-cta {
    display: inline-flex;
    align-items: center;
    gap: 10px;
    margin-top: 28px;
    color: var(--faq-accent);
    font-weight: 700;
    text-decoration: none;
}

.faq-cta i {
    transition: transform 0.25s ease;
}

.faq-cta:hover {
    color: var(--brand-red-hover);
}

.faq-cta:hover i {
    transform: translateX(4px);
}

.faq-accordion {
    counter-reset: faq-item;
    --bs-accordion-bg: transparent;
    --bs-accordion-btn-bg: transparent;
    --bs-accordion-active-bg: transparent;
    --bs-accordion-active-color: var(--faq-accent);
    --bs-accordion-color: var(--faq-body);
    --bs-accordion-border-color: transparent;
    --bs-accordion-btn-focus-box-shadow: 0 0 0 0.2rem rgba(200, 0, 54, 0.18);
}

/* Each question is its own card rather than a row in a ruled list, so the
   open one can lift away from the others. */
.faq-item {
    counter-increment: faq-item;
    position: relative;
    margin-bottom: 14px;
    border: 1px solid var(--faq-hairline);
    border-radius: 14px;
    background-color: var(--faq-card);
    overflow: hidden;
    transition: border-color 0.25s ease, box-shadow 0.25s ease,
        transform 0.25s ease;
}

.faq-item:last-child {
    margin-bottom: 0;
}

/* The accent bar down the open card's left edge. Scaled rather than shown, so
   it grows out of the middle when the answer opens. */
.faq-item::before {
    content: "";
    position: absolute;
    top: 0;
    left: 0;
    width: 3px;
    height: 100%;
    background-color: var(--faq-accent);
    transform: scaleY(0);
    transition: transform 0.3s ease;
}

.faq-item:has(.accordion-button:not(.collapsed)) {
    border-color: transparent;
    box-shadow: var(--shadow-soft);
}

.faq-item:has(.accordion-button:not(.collapsed))::before {
    transform: scaleY(1);
}

.faq-question {
    display: flex;
    align-items: center;
    gap: 18px;
    padding: 24px 26px;
    color: var(--faq-ink);
    font-size: 1.08rem;
    font-weight: 600;
    line-height: 1.4;
}

.faq-question:not(.collapsed) {
    color: var(--faq-accent);
    box-shadow: none;
}

.faq-number {
    flex: none;
    color: var(--faq-number);
    font-size: 0.85rem;
    font-weight: 700;
    font-variant-numeric: tabular-nums;
    letter-spacing: 0.04em;
    transition: color 0.25s ease;
}

/* Zero-padded, so 01 and 10 occupy the same width and the questions line up. */
.faq-number::before {
    content: counter(faq-item, decimal-leading-zero);
}

.faq-question:not(.collapsed) .faq-number {
    color: var(--faq-accent);
}

.faq-question-text {
    flex: 1 1 auto;
}

/* Bootstrap's chevron is a hard-coded dark data: URI, so it disappears
   against a dark background. Recolour it with a mask instead of shipping a
   second image. */
.faq-question::after {
    flex: none;
    margin-left: 0;
    background-image: none;
    background-color: currentColor;
    mask-image: var(--bs-accordion-btn-icon);
    mask-repeat: no-repeat;
    mask-size: 1.15rem;
    -webkit-mask-image: var(--bs-accordion-btn-icon);
    -webkit-mask-repeat: no-repeat;
    -webkit-mask-size: 1.15rem;
}

.faq-answer {
    padding: 0 26px 26px 62px;
    color: var(--faq-body);
    font-size: 0.97rem;
    line-height: 1.75;
}

.faq-answer p:last-child,
.faq-answer ul:last-child,
.faq-answer ol:last-child {
    margin-bottom: 0;
}

.faq-answer a {
    color: var(--faq-accent);
}

.faq-empty {
    color: var(--faq-body);
}

@media (max-width: 991.98px) {
    .faq-section {
        padding: 80px 0;
    }

    .faq-layout {
        grid-template-columns: minmax(0, 1fr);
        gap: 36px;
    }

    /* Nothing left to follow once the heading sits above the questions. */
    .faq-intro {
        position: static;
    }

    /* Sized to the viewport rather than the desktop column it was drawn for.
       At 620px it stopped being a wash in the corner and tinted the whole
       section. */
    .faq-glow {
        top: -150px;
        right: -170px;
        width: 420px;
        height: 420px;
    }

    .faq-subtitle {
        max-width: 56ch;
    }
}

@media (max-width: 575.98px) {
    .faq-section {
        padding: 64px 0;
    }

    .faq-glow {
        top: -120px;
        right: -140px;
        width: 320px;
        height: 320px;
    }

    .faq-item {
        margin-bottom: 12px;
        border-radius: 12px;
    }

    .faq-question {
        gap: 14px;
        padding: 20px 18px;
        font-size: 1rem;
    }

    /* Indented to the question's own text rather than back to the card edge:
       18px padding + the number's column + the gap. The number stays a
       hanging marker instead of the answer sliding out from under it. */
    .faq-answer {
        padding: 0 18px 22px 52px;
    }

    .faq-cta {
        margin-top: 22px;
    }
}


/* ======================================= */
/* ---   OFFER BLOCK                   --- */
/* ======================================= */

/* The lead magnet. Navy in both themes, like the podcast card and the footer
   band: this is the moment the page asks for something, so it stops matching
   the copy around it and becomes the one dark surface on the page. */
.offer-section {
    --offer-bg: var(--bg-primary);
    --offer-card: #0c1844;
    --offer-ink: #ffffff;
    --offer-body: rgba(255, 255, 255, 0.72);
    --offer-accent: #eb2258;
    --offer-hairline: rgba(255, 255, 255, 0.14);

    background-color: var(--offer-bg);
    padding: 100px 0;
}

[data-bs-theme="dark"] .offer-section {
    --offer-bg: transparent;
}

.offer-card {
    position: relative;
    display: grid;
    grid-template-columns: minmax(0, 1.05fr) minmax(0, 0.95fr);
    gap: 40px 64px;
    align-items: center;
    padding: 60px;
    border-radius: 22px;
    background-color: var(--offer-card);
    color: var(--offer-ink);
    overflow: hidden;
}

/* Nothing to sit beside, so the offer takes the width back. */
.offer-card-single {
    grid-template-columns: minmax(0, 1fr);
}

/* A brand-red wash in the corner. Soft rather than the angular wedge used
   behind the portraits: a hard edge crossing the checklist panel read as a
   rendering fault, not as a shape. Decorative, and clipped by the card. */
.offer-shape {
    position: absolute;
    top: -200px;
    right: -160px;
    width: 620px;
    height: 620px;
    border-radius: 50%;
    background-image: radial-gradient(
        circle,
        rgba(235, 34, 88, 0.42),
        rgba(235, 34, 88, 0.12) 45%,
        transparent 70%
    );
    pointer-events: none;
}

.offer-intro {
    position: relative;
    z-index: 1;
}

.offer-heading {
    margin: 0 0 16px;
    color: var(--offer-ink);
    font-size: clamp(1.75rem, 1.2rem + 1.8vw, 2.5rem);
    font-weight: 700;
    line-height: 1.15;
    letter-spacing: -0.01em;
    text-wrap: balance;
}

.offer-heading p {
    margin: 0;
    font-size: inherit;
    font-weight: inherit;
    line-height: inherit;
    color: inherit;
}

.offer-description {
    margin: 0 0 32px;
    max-width: 46ch;
    color: var(--offer-body);
    font-size: 1.02rem;
    line-height: 1.65;
}

.offer-button {
    display: inline-flex;
    align-items: center;
    gap: 10px;
    border-radius: 50px;
    padding: 16px 34px;
    background-color: var(--brand-red);
    color: #ffffff;
    font-weight: 700;
    text-decoration: none;
    box-shadow: 0 14px 30px rgba(200, 0, 54, 0.35);
    transition: transform 0.25s ease;
}

.offer-button i {
    transition: transform 0.25s ease;
}

/* The colour holds; only the lift and the arrow answer. */
.offer-button:hover {
    transform: translateY(-2px);
    color: #ffffff;
}

.offer-button:hover i {
    transform: translateX(4px);
}

.offer-footnote {
    margin: 18px 0 0;
    color: var(--offer-body);
    font-size: 0.85rem;
}

.offer-items {
    position: relative;
    z-index: 1;
    list-style: none;
    margin: 0;
    padding: 32px;
    border: 1px solid var(--offer-hairline);
    border-radius: 16px;
    background-color: rgba(255, 255, 255, 0.05);
    display: grid;
    gap: 18px;
}

.offer-item {
    display: grid;
    grid-template-columns: auto minmax(0, 1fr);
    gap: 14px;
    align-items: center;
    color: var(--offer-ink);
    font-size: 1rem;
    font-weight: 600;
    line-height: 1.45;
}

.offer-item-icon {
    display: flex;
    align-items: center;
    justify-content: center;
    width: 26px;
    height: 26px;
    border-radius: 50%;
    background-color: var(--offer-accent);
    color: #ffffff;
    font-size: 0.8rem;
    line-height: 1;
}

@media (max-width: 991.98px) {
    .offer-section {
        padding: 80px 0;
    }

    .offer-card {
        grid-template-columns: minmax(0, 1fr);
        gap: 34px;
        padding: 44px;
    }

    /* Same reason as the FAQ wash: drawn for a 1140px card, it swamped a
       stacked one. */
    .offer-shape {
        top: -170px;
        right: -150px;
        width: 460px;
        height: 460px;
    }
}

@media (max-width: 575.98px) {
    .offer-section {
        padding: 64px 0;
    }

    .offer-card {
        gap: 28px;
        padding: 32px 24px;
        border-radius: 18px;
    }

    .offer-shape {
        top: -110px;
        right: -100px;
        width: 300px;
        height: 300px;
    }

    /* Full width, because at this size a pill floating in the column reads as
       an afterthought and it is the only thing on the card worth tapping. */
    .offer-button {
        justify-content: center;
        width: 100%;
        padding: 16px 24px;
    }

    .offer-footnote {
        text-align: center;
    }

    .offer-items {
        padding: 22px 20px;
        gap: 16px;
    }

    .offer-item {
        font-size: 0.95rem;
    }
}


/* ======================================= */
/* ---   SECTION MOTIF                 --- */
/* ======================================= */

/* A faint decorative texture a section can opt into: a pulse trace or a field
   of plus signs, for pages whose subject has a visual shorthand.

   Drawn as a mask over a background-color rather than as a coloured SVG, so
   one shape follows whichever accent token the section sets and stays legible
   in both themes. Sections that use it already carry position:relative and
   overflow:hidden. */
.section-motif {
    position: absolute;
    z-index: 0;
    background-color: var(--motif-ink, rgba(200, 0, 54, 0.14));
    pointer-events: none;
}

[data-bs-theme="dark"] .section-motif {
    background-color: var(--motif-ink, rgba(235, 34, 88, 0.16));
}

/* A single ECG trace along the section's bottom edge. Sections pad to 90px or
   more, so the band sits under the content rather than behind the text. */
.section-motif-pulse {
    left: 0;
    right: 0;
    bottom: 0;
    height: 72px;
    mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 240 60'%3E%3Cpath d='M0 30h72l9-19 12 38 11-27 8 8h128' fill='none' stroke='%23000' stroke-width='3' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E");
    mask-repeat: repeat-x;
    mask-size: 240px 60px;
    mask-position: left bottom;
    -webkit-mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 240 60'%3E%3Cpath d='M0 30h72l9-19 12 38 11-27 8 8h128' fill='none' stroke='%23000' stroke-width='3' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E");
    -webkit-mask-repeat: repeat-x;
    -webkit-mask-size: 240px 60px;
    -webkit-mask-position: left bottom;
}

/* The three full-bleed textures differ only in their tile and how big it is
   drawn, so those are custom properties and the plumbing is shared. Pulse is
   not one of them: it anchors to the section's bottom edge instead of filling
   it, and naming the tile in a property is what lets the hero-arch rule below
   re-compose whichever of them a section happens to carry. */
.section-motif-plus,
.section-motif-blueprint,
.section-motif-perspective {
    /* A texture can thin itself out where the copy sits by setting a fade.
       The default is an opaque gradient, which is a no-op through the
       intersect below, so a motif that does not set one is unchanged. */
    --motif-fade: linear-gradient(#000, #000);

    inset: 0;
    mask-image: var(--motif-pattern), var(--motif-fade);
    mask-repeat: repeat, no-repeat;
    mask-size: var(--motif-size), 100% 100%;
    mask-composite: intersect, add;
    -webkit-mask-image: var(--motif-pattern), var(--motif-fade);
    -webkit-mask-repeat: repeat, no-repeat;
    -webkit-mask-size: var(--motif-size), 100% 100%;
    -webkit-mask-composite: source-in, source-over;
}

/* A field of plus signs across the whole section. Fainter than the pulse
   because it covers far more of the page, and it shows in the gutters between
   cards rather than behind them. */
.section-motif-plus {
    --motif-pattern: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 48 48'%3E%3Cpath d='M24 16v16M16 24h16' stroke='%23000' stroke-width='2.5' stroke-linecap='round'/%3E%3C/svg%3E");
    --motif-size: 48px 48px;
    opacity: 0.55;
}

/* A drafting grid: minor lines every 24px, a heavier one every 96px. Fainter
   than the plus field, because a continuous grid covers more ink and has to
   stay under body copy. */
.section-motif-blueprint {
    --motif-pattern: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 96 96'%3E%3Cpath d='M0 24h96M0 48h96M0 72h96M24 0v96M48 0v96M72 0v96' stroke='%23000' stroke-width='1'/%3E%3Cpath d='M0 0h96M0 0v96' stroke='%23000' stroke-width='2.5'/%3E%3C/svg%3E");
    --motif-size: 96px 96px;
    opacity: 0.38;
}

/* Long shallow diagonals crossing the whole section, widely spaced: the
   architectural sweep a corporate photograph has behind it. The tile is
   300x110 rather than square, which is what makes the angle shallow -- a
   square tile gives 45 degrees and reads as hatching instead. Drawn at 420px
   so the lattice stays airy rather than becoming a pattern.

   An exact colour rather than a tint of the section accent, and therefore full
   opacity: the other three take the brand red at low alpha, this one is
   specified as #E5E9EC. */
.section-motif-perspective {
    --motif-pattern: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 300 110'%3E%3Cpath d='M0 110L300 0' stroke='%23000' stroke-width='1.35'/%3E%3Cpath d='M0 0L300 110' stroke='%23000' stroke-width='1'/%3E%3C/svg%3E");
    --motif-size: 420px 154px;
    --motif-ink: #e5e9ec;
    opacity: 1;

    /* The band of the section where the copy sits is thinned out, full
       strength returning at the top and bottom edges. Vertical only, so it
       runs the full width rather than pooling in the centre, and the lines
       stay unbroken where they meet the section above and below. */
    --motif-fade: linear-gradient(
        to bottom,
        #000 0%,
        transparent 30%,
        transparent 70%,
        #000 100%
    );
}



/* On a dark page that grey is a pale wash, far heavier than the other motifs
   read there, so dark takes its own value. */
[data-bs-theme="dark"] .section-motif-perspective {
    --motif-ink: rgba(255, 255, 255, 0.07);
}

/* A motif on the section directly under the hero carries up into the hero's
   arch, so the texture is continuous across the curve instead of stopping at
   the straight edge below it. `overflow: hidden` cannot do that -- it would
   clip the overhang away -- so the clipping moves to a clip-path that is tight
   on three sides and open at the top by the curve's height. */
.campaign-hero + section:has(> .section-motif) {
    overflow: visible;
    clip-path: inset(-200px 0 0 0);
    /* The hero is position:relative with z-index:auto, so it creates no
       stacking context and its curve separator's z-index:3 competes in the
       root one -- painting the curve's opaque fill straight over an overhang
       at z-index:0. Lifting this section above that puts the texture back on
       top of the arch. The z-index also makes the section its own stacking
       context, which is what keeps the motif (0) under its own content (1)
       instead of the two joining the same free-for-all. */
    z-index: 4;
}

/* The overhang is a rectangle but the arch above it is a curve, so its top
   corners would cross the hero photograph and draw lines over it. The fix is
   to mask the overhang to the curve's own shape -- the same path the separator
   svg fills, stretched the same way -- rather than fade it out, which would
   empty the arch of the very texture it is meant to carry.

   Three mask layers, composited bottom-up: everything below the curve, plus
   the curve's fill, intersected with the texture's tile. Anything outside the
   pale arch is masked away; anything inside it gets the tile.

   Pulse is excluded: it anchors to the section's bottom edge, so there is
   nothing at the top for the arch to carry. */
.campaign-hero
    + section
    > :is(.section-motif-plus, .section-motif-blueprint, .section-motif-perspective) {
    /* Matches the separator, which starts 200px above the hero's bottom edge
       -- and the hero's own -1px overlap puts that 1px below this element. */
    top: -199px;

    /* Four layers, composited bottom-up: everything below the curve, plus the
       curve's own fill, intersected with the texture's fade, intersected with
       its tile. Anything outside the pale arch is masked away; anything inside
       it gets the tile at whatever strength the fade allows there. */
    mask-image: var(--motif-pattern), var(--motif-fade), url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 1440 320' preserveAspectRatio='none'%3E%3Cpath d='M0,224 C480,96 960,96 1440,224 L1440,320 L0,320 Z'/%3E%3C/svg%3E"),
        linear-gradient(to bottom, transparent 200px, #000 200px);
    mask-repeat: repeat, no-repeat, no-repeat, no-repeat;
    mask-size: var(--motif-size), 100% 100%, 100% 200px, 100% 100%;
    mask-composite: intersect, intersect, add, add;
    -webkit-mask-image: var(--motif-pattern), var(--motif-fade), url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 1440 320' preserveAspectRatio='none'%3E%3Cpath d='M0,224 C480,96 960,96 1440,224 L1440,320 L0,320 Z'/%3E%3C/svg%3E"),
        linear-gradient(to bottom, transparent 200px, #000 200px);
    -webkit-mask-repeat: repeat, no-repeat, no-repeat, no-repeat;
    -webkit-mask-size: var(--motif-size), 100% 100%, 100% 200px, 100% 100%;
    -webkit-mask-composite: source-in, source-in, source-over, source-over;
}

/* `isolation` is the load-bearing part: it makes the section a stacking
   context so the motif's negative z-index is contained by it, painting after
   the section's own background but before every one of its children. That is
   what lets the texture sit under the content without touching the content at
   all.

   An earlier version lifted the children instead -- `> :not(.section-motif)`
   with position:relative -- which out-specified any direct child that had set
   `position: absolute` for itself and dropped it into normal flow. The FAQ
   block's 620px glow is exactly that. Do not reintroduce a rule that sets
   `position` on children it does not know. */
/* `section` alone, not :is(section, header, footer): every block that offers a
   motif renders a <section>. The hero is the only block rendering a <header>
   and is excluded from the field, and the footer block renders
   <section class="footer-block">, not <footer> -- so those arms matched
   nothing and read as a claim that a real <footer> was handled here.
   `test_every_motif_bearing_block_renders_a_section` fails if a future block
   breaks that, which is the signal to widen this. */
section:has(> .section-motif) {
    position: relative;
    overflow: hidden;
    isolation: isolate;
}

section:has(> .section-motif) > .section-motif {
    z-index: -1;
}

/* The section's own content sits above the texture. Unconditional, and not
   folded into the :has() rule above: these three carry their own absolutely
   positioned decoration, so the guard has to hold whether or not the block is
   currently carrying a motif. Making it conditional regressed FAQ sections
   left at motif "none". */
.solutions-section > .container,
.process-section > .container,
.faq-section > .container {
    position: relative;
    z-index: 1;
}

/* On the navy offer card the texture is white, not brand red, or it
   disappears into the background it is drawn on. */
.offer-card .section-motif {
    --motif-ink: rgba(255, 255, 255, 0.14);
}

@media (max-width: 575.98px) {
    .section-motif-pulse {
        height: 52px;
        mask-size: 170px 42px;
        -webkit-mask-size: 170px 42px;
    }

    /* Set the property, not mask-size: under the hero the mask is a list of
       three layers, and a bare mask-size here would collapse it to one. */
    .section-motif-plus {
        --motif-size: 38px 38px;
    }

    .section-motif-blueprint {
        --motif-size: 64px 64px;
    }

    .section-motif-perspective {
        --motif-size: 300px 110px;
    }
}


/* ======================================= */
/* ---   BLOCK IMAGERY                 --- */
/* ======================================= */

/* A photo across the top of a solution card, bled to the card's edges. The
   icon then straddles the image's lower edge, which needs it opaque -- the
   translucent tint it carries on a plain card would show the photo through. */
.solutions-media {
    margin: -28px -26px 0;
    overflow: hidden;
    border-radius: 16px 16px 0 0;
}

/* Square, matching the artwork these cards are fed: 16/9 cropped the top and
   bottom off a square source, and this illustration work carries labels right
   up to its edges, so a crop takes words with it.

   The slot is now an equal share of the container rather than a capped track,
   so its width follows the card count: ~360px for three across a 1140px
   container, ~265px for four. The template asks for a 700px square, which
   still covers the widest of those on a 2x display. `fill` never upscales, so
   a smaller original is served at its own size and only the request is wasted
   -- but artwork supplied at 1300px or more is then used at full resolution on
   a 2x display. */
.solutions-image {
    display: block;
    width: 100%;
    height: auto;
    aspect-ratio: 1 / 1;
    object-fit: cover;
}

.solutions-item-media .solutions-icon {
    position: relative;
    z-index: 1;
    margin-top: -26px;
    background-color: var(--solutions-card);
    box-shadow: 0 8px 20px rgba(12, 24, 68, 0.14);
}

/* The person the visitor would actually be talking to, in the card's top-left
   corner as a byline over the offer.

   On its own it read as an ornament -- nothing tied it to the copy and the eye
   had to guess who it was -- so it keeps the name and role it was given beside
   the button. As a heading it is compact: a small portrait on the corner, not
   a block the heading has to start below. */
.offer-person {
    display: flex;
    align-items: center;
    gap: 14px;
    margin: 0 0 22px;
}

.offer-person-text {
    min-width: 0;
}

.offer-person-name {
    margin: 0;
    color: var(--offer-ink);
    font-size: 1rem;
    font-weight: 700;
    line-height: 1.3;
}

.offer-person-role {
    margin: 2px 0 0;
    color: var(--offer-body);
    font-size: 0.9rem;
    line-height: 1.3;
}

.offer-avatar {
    flex: none;
    width: 60px;
    height: 60px;
    border-radius: 50%;
    padding: 3px;
    background-image: linear-gradient(
        135deg,
        var(--brand-red),
        var(--offer-accent)
    );
}

.offer-avatar-img {
    display: block;
    width: 100%;
    height: 100%;
    border-radius: 50%;
    object-fit: cover;
}

/* A supporting photo in the FAQ's left column, under the heading. */
.faq-media {
    margin-top: 32px;
    max-width: 420px;
    border-radius: 16px;
    overflow: hidden;
    box-shadow: var(--shadow-soft);
}

/* Square too, so one set of artwork serves every image slot on the page. */
.faq-image {
    display: block;
    width: 100%;
    height: auto;
    aspect-ratio: 1 / 1;
    object-fit: cover;
}

@media (max-width: 991.98px) {
    .faq-media {
        max-width: 100%;
    }
}

@media (max-width: 767.98px) {
    .solutions-media {
        margin: -26px -22px 0;
    }
}


/* ======================================= */
/* ---   CASE STUDIES BLOCK            --- */
/* ======================================= */

/* A centre-stage carousel inside a real container: thumbnails side by side,
   advancing on a timer, with one always highlighted in the middle. The
   container is what makes "only two case studies" look deliberate rather than
   like a card stranded at the left edge of an empty row. */
.case-studies-section {
    --case-studies-ink: #0c1844;
    --case-studies-accent: var(--brand-red);
    --case-studies-body: var(--text-muted);
    --case-studies-bg: var(--bg-secondary);
    --case-studies-card: var(--bg-card);
    --case-studies-hairline: rgba(12, 24, 68, 0.1);
    /* #C70136, as channels so the hover glow can be mixed at two strengths
       without the colour being written out twice. */
    --case-studies-glow: 199, 1, 54;
    /* The glow's reach. Blur plus spread is how far it actually extends past
       a card, and glow-room is the clearance both the container and the
       track's viewport must leave for it -- each of them clips, so anything
       less and the glow ends on a hard edge instead of fading out. */
    --case-studies-glow-blur: 30px;
    --case-studies-glow-spread: 8px;
    --case-studies-glow-room: 48px;
    /* The size a card wants to be. The block's script reads this and may set
       --case-studies-card-width larger, so that with only a few case studies
       the real ones fill the row instead of one being repeated to pad it. */
    --case-studies-card-natural: 300px;
    --case-studies-card-width: var(--case-studies-card-natural);
    --case-studies-gap: 26px;
    --case-studies-arrow-inset: 22px;

    position: relative;
    background-color: var(--case-studies-bg);
    padding: 100px 0;
    overflow: hidden;
}

[data-bs-theme="dark"] .case-studies-section {
    --case-studies-ink: #ffffff;
    --case-studies-body: rgba(255, 255, 255, 0.7);
    --case-studies-bg: rgba(255, 255, 255, 0.03);
    --case-studies-hairline: rgba(255, 255, 255, 0.14);
}

.case-studies-section > .container {
    position: relative;
    z-index: 1;
}

.case-studies-heading {
    max-width: 24ch;
    margin: 0 0 16px;
    color: var(--case-studies-ink);
    font-size: clamp(1.9rem, 1.2rem + 2.1vw, 2.9rem);
    font-weight: 700;
    line-height: 1.12;
    letter-spacing: -0.01em;
    text-wrap: balance;
}

/* The rich text editor wraps the heading in <p>: keep it part of the heading
   instead of a paragraph sitting inside one. */
.case-studies-heading p {
    margin: 0;
    font-size: inherit;
    font-weight: inherit;
    line-height: inherit;
    color: inherit;
}

.case-studies-subtitle {
    max-width: 58ch;
    margin: 0 0 48px;
    color: var(--case-studies-body);
    font-size: 1.05rem;
    line-height: 1.65;
}

.case-studies-heading:last-child,
.case-studies-subtitle:last-child {
    margin-bottom: 0;
}

/* The container. 30px is the radius this codebase gives a carousel stage --
   see .podcast-stage. overflow:hidden is load-bearing: it is what lets the
   track translate past the edges and be clipped by the box instead of
   widening the page. */
.case-studies-stage {
    position: relative;
    margin-top: 44px;
    padding: var(--case-studies-glow-room) 0;
    border: 1px solid var(--case-studies-hairline);
    border-radius: 30px;
    background-color: var(--case-studies-card);
    overflow: hidden;
}

/* One case study: the box holds a panel and nothing else. Its vertical
   padding is no longer the hover glow's clearance -- there is no card to
   glow -- so it is just the panel's margin, and it matches the 60px the
   panel already keeps at its sides. */
.case-studies-stage.is-solo {
    padding: 60px 0;
}

.case-studies-scroller {
    position: relative;
}

/* The strip runs the full width of the container and is clipped by it: with
   copies either side there is always a card at each edge, so insetting the
   viewport would only show the container's background through a gap that no
   longer exists.

   The clip has to be horizontal only, but overflow cannot be hidden on one
   axis and visible on the other -- the visible one is forced to auto, which
   would add a scrollbar. So the box is padded vertically instead and pulled
   back by the same amount: the clip still lands on the sides, but above and
   below it now sits far enough out for the hover glow to fade to nothing
   before it reaches the edge. */
.case-studies-viewport {
    overflow: hidden;
    padding-block: var(--case-studies-glow-room);
    margin-block: calc(var(--case-studies-glow-room) * -1);
}

.case-studies-track {
    list-style: none;
    display: flex;
    gap: var(--case-studies-gap);
    margin: 0;
    padding: 6px 0;
    /* transform is set by the block's script, from the active card's offset */
    transition: transform 0.5s cubic-bezier(0.22, 0.61, 0.36, 1);
    will-change: transform;
}

.case-studies-card {
    flex: 0 0 var(--case-studies-card-width);
    transition: transform 0.45s ease, opacity 0.45s ease;
}

/* Everything that is not the centred card steps back, so the middle of the
   container always reads as the thing being offered. */
.case-studies-card:not(.is-active) {
    transform: scale(0.88);
    opacity: 0.5;
}

.case-studies-open {
    display: flex;
    flex-direction: column;
    width: 100%;
    height: 100%;
    padding: 0;
    border: 1px solid var(--case-studies-hairline);
    border-radius: 16px;
    background-color: var(--case-studies-card);
    text-align: left;
    overflow: hidden;
    cursor: pointer;
    transition: box-shadow 0.25s ease, border-color 0.25s ease;
}

.case-studies-card.is-active .case-studies-open {
    border-color: transparent;
    box-shadow: var(--shadow-soft);
}

/* A red fade all the way around the card on hover. Every card answers the
   pointer, because every card is clickable wherever it sits -- but the one in
   front gets it at full strength and the ones behind only a hint, so the glow
   reinforces which card a click will act on rather than competing with it.
   Held as channels so the two strengths cannot drift apart. */
.case-studies-open:hover,
.case-studies-open:focus-visible {
    border-color: transparent;
    box-shadow: 0 0 var(--case-studies-glow-blur) var(--case-studies-glow-spread)
        rgba(var(--case-studies-glow), 0.22);
}

/* Same shape, more of it: the difference between front and back is opacity
   alone, so tuning the reach is one edit rather than two. */
.case-studies-card.is-active .case-studies-open:hover,
.case-studies-card.is-active .case-studies-open:focus-visible {
    box-shadow: 0 0 var(--case-studies-glow-blur) var(--case-studies-glow-spread)
        rgba(var(--case-studies-glow), 0.6);
}

/* No pointer to answer, so the card a tap would act on carries the glow
   instead of waiting for a hover that will never arrive. */
@media (hover: none) {
    .case-studies-card.is-active .case-studies-open {
        box-shadow: 0 0 var(--case-studies-glow-blur) var(--case-studies-glow-spread)
            rgba(var(--case-studies-glow), 0.45);
    }
}

.case-studies-media {
    display: block;
    overflow: hidden;
}

.case-studies-image {
    display: block;
    width: 100%;
    height: auto;
    aspect-ratio: 4 / 3;
    object-fit: cover;
}

.case-studies-card-title {
    display: block;
    padding: 20px 22px 24px;
    color: var(--case-studies-ink);
    font-size: 1.02rem;
    font-weight: 700;
    line-height: 1.35;
}

/* Pinned to the far left and right inside the container. */
.case-studies-nav {
    position: absolute;
    top: 50%;
    transform: translateY(-50%);
    z-index: 2;
    display: grid;
    place-items: center;
    width: 46px;
    height: 46px;
    border: 0;
    border-radius: 50%;
    background-color: var(--case-studies-accent);
    color: #ffffff;
    font-size: 1.15rem;
    cursor: pointer;
    box-shadow: 0 10px 24px rgba(200, 0, 54, 0.28);
    transition: transform 0.25s ease;
}

/* Bootstrap Icons ships a single thin weight for the arrows, so thicken the
   glyph with a stroke in its own colour instead of swapping the icon. */
.case-studies-nav .bi {
    -webkit-text-stroke: 0.75px currentColor;
}

/* Lifts rather than changing colour, matching .btn-custom-red -- the button
   in the hero's contact form. --brand-red-hover is a warmer, more orange red,
   and swapping to it made these read as a different button on hover.
   translateY carries the -50% that centres the arrow, or hovering would drop
   it half its own height. */
.case-studies-nav:hover {
    transform: translateY(calc(-50% - 2px));
}

.case-studies-nav:focus-visible {
    outline: 2px solid var(--case-studies-accent);
    outline-offset: 3px;
}

/* Beside the cards, not tucked into the container's corners: the strip now
   reaches both edges, so the arrows sit over the outermost cards where the
   hand already is, rather than out in the margin. */
.case-studies-prev {
    left: var(--case-studies-arrow-inset);
}

.case-studies-next {
    right: var(--case-studies-arrow-inset);
}

/* --- the detail panel --- */

/* Sits inside the container, so opening a preview does not change the shape
   of the section -- only what is inside it. */
.case-studies-detail {
    padding: 0 60px;
}

.case-studies-back {
    display: inline-flex;
    align-items: center;
    gap: 8px;
    margin-bottom: 22px;
    padding: 0;
    border: 0;
    background: none;
    color: var(--case-studies-accent);
    font-size: 0.95rem;
    font-weight: 700;
    cursor: pointer;
}

.case-studies-back i {
    transition: transform 0.25s ease;
}

.case-studies-back:hover i {
    transform: translateX(-4px);
}

.case-studies-detail-body {
    display: grid;
    grid-template-columns: minmax(0, 0.9fr) minmax(0, 1.1fr);
    gap: 32px;
    align-items: center;
}

.case-studies-detail-media {
    border-radius: 14px;
    overflow: hidden;
}

.case-studies-detail-image {
    display: block;
    width: 100%;
    height: auto;
    aspect-ratio: 4 / 3;
    object-fit: cover;
}

.case-studies-detail-title {
    margin: 0 0 14px;
    color: var(--case-studies-ink);
    font-size: clamp(1.3rem, 1rem + 1vw, 1.75rem);
    font-weight: 700;
    line-height: 1.25;
}

.case-studies-detail-summary {
    margin: 0 0 26px;
    max-width: 52ch;
    color: var(--case-studies-body);
    font-size: 1rem;
    line-height: 1.7;
}

.case-studies-read {
    display: inline-flex;
    align-items: center;
    gap: 10px;
    border-radius: 50px;
    padding: 13px 26px;
    background-color: var(--case-studies-accent);
    color: #ffffff;
    font-weight: 700;
    text-decoration: none;
    transition: transform 0.25s ease;
}

.case-studies-read i {
    transition: transform 0.25s ease;
}

/* Same again: the colour holds, only the lift and the arrow answer. */
.case-studies-read:hover {
    color: #ffffff;
    transform: translateY(-2px);
}

.case-studies-read:hover i {
    transform: translateX(4px);
}

.case-studies-empty {
    margin: 0;
    color: var(--case-studies-body);
}

/* The copies either side of the real cards. Identical to look at -- the whole
   point is that you cannot tell which is which -- and inert to assistive tech,
   which is handled on the elements themselves. */
.case-studies-card.is-copy {
    pointer-events: auto;
}

/* The house escape hatch: nothing moves, but the centred card is still the
   one that reads as chosen -- the opacity difference carries it without the
   scale or the slide. */
@media (prefers-reduced-motion: reduce) {
    .case-studies-track,
    .case-studies-card {
        transition: none;
    }

    .case-studies-card:not(.is-active) {
        transform: none;
        opacity: 0.55;
    }
}

@media (max-width: 991.98px) {

    .case-studies-detail-body {
        grid-template-columns: minmax(0, 1fr);
        gap: 24px;
    }

    .case-studies-detail-media {
        max-width: 420px;
    }
}

@media (max-width: 575.98px) {
    .case-studies-section {
        --case-studies-card-natural: 232px;
        --case-studies-gap: 16px;
        --case-studies-arrow-inset: 8px;
        /* Tighter here: a phone cannot spare 48px above and below, so the
           glow is drawn smaller to match the clearance it gets. */
        --case-studies-glow-blur: 20px;
        --case-studies-glow-spread: 4px;
        --case-studies-glow-room: 32px;

        padding: 68px 0;
    }

    .case-studies-stage {
        border-radius: 22px;
    }

    .case-studies-nav {
        width: 38px;
        height: 38px;
        font-size: 1rem;
    }

    .case-studies-detail {
        padding: 0 22px;
    }
}


/* ======================================= */
/* ---   OFFER BLOCK: BAND VARIANT     --- */
/* ======================================= */

/* The offer card as a plane instead of a panel, for pages that want the block
   twice without reading as a template. Same markup: .offer-intro becomes
   display:contents so its children become siblings of the item list, which is
   the only way to put the button after the lines without a second template
   branch. */
.offer-section-band {
    position: relative;
    overflow: hidden;
    background-color: var(--offer-card);
    padding: 96px 0;
}

/* The motif and corner wash are positioned against the card, which is only as
   wide as the container -- on a full-width band that leaves the texture with
   two visible edges. The containing block moves up to the section instead. */
/* Deliberately NOT `> .container { position: relative }` as other sections do:
   the motif lives inside the container, so that would re-clip the texture to
   1320px. The content carries its own z-index instead -- DOM order will not do
   it, since in-flow content paints before positioned boxes.

   The card is only a layout box now: the colour moved up to the section so it
   reaches the viewport edges without a 100vw negative-margin trick, and
   `position: static` hands the containing block up with it. */
.offer-section-band .offer-card {
    position: static;
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 0;
    padding: 0;
    border-radius: 0;
    background-color: transparent;
    text-align: center;
}

.offer-section-band .offer-intro {
    display: contents;
}

/* display:contents discards the wrapper's box and its `position: relative;
   z-index: 1` with it -- the guard holding the copy above .section-motif and
   .offer-shape. Without this the red wash and the motif grid paint over the
   copy. .offer-items is absent because it carries the guard in its own base
   rule. */
.offer-section-band .offer-heading,
.offer-section-band .offer-description,
.offer-section-band .offer-person,
.offer-section-band .offer-button,
.offer-section-band .offer-footnote {
    position: relative;
    z-index: 1;
}

.offer-section-band .offer-heading {
    order: 1;
    max-width: 20ch;
    font-size: clamp(2rem, 1.3rem + 2.2vw, 3rem);
}

.offer-section-band .offer-description {
    order: 2;
    max-width: 52ch;
}

/* On the band the lines are the content, so the panel goes and the hairlines
   carry the rhythm. */
.offer-section-band .offer-items {
    order: 3;
    display: flex;
    flex-direction: column;
    gap: 0;
    width: 100%;
    max-width: 56ch;
    margin: 28px 0 36px;
    padding: 0;
    border: 0;
    border-radius: 0;
    background-color: transparent;
    list-style: none;
}

/* Hairlines rather than bullets: the pills are what make the card read as a
   checklist, and this beat is not a list of deliverables. */
.offer-section-band .offer-item {
    display: block;
    justify-content: center;
    gap: 0;
    padding: 16px 8px;
    border-top: 1px solid var(--offer-hairline);
    color: var(--offer-ink);
    font-size: clamp(1.05rem, 0.95rem + 0.4vw, 1.3rem);
    line-height: 1.4;
    text-align: center;
}

.offer-section-band .offer-item:first-child {
    border-top: 0;
}

.offer-section-band .offer-item-icon {
    display: none;
}

.offer-section-band .offer-button {
    order: 4;
}

.offer-section-band .offer-footnote {
    order: 5;
    margin-top: 18px;
}

/* The corner wash is drawn for a card's top-right corner. Across a full-width
   band it would sit off in the margin, so it moves behind the heading. */
.offer-section-band .offer-shape {
    top: -280px;
    right: auto;
    left: 50%;
    transform: translateX(-50%);
}

@media (max-width: 575.98px) {
    .offer-section-band {
        padding: 64px 0;
    }

    .offer-section-band .offer-items {
        margin: 20px 0 28px;
    }
}

/* ======================================= */
/* ---   TECHNOLOGIES USED (block)     --- */
/* ======================================= */

/* The stack a project was built with, inside a case study body. Static tags,
   not the scrolling marquee of .tech-stack-section. */
/* 15px and --bg-card are the site's card language (.project-card,
   .service-card) -- no third radius. */
.technologies-block {
    margin: 2rem 0 2.5rem;
    padding: 1.5rem 1.75rem;
    border: 1px solid var(--border-color);
    border-radius: 15px;
    background-color: var(--bg-card);
    /* The token the cards lift with, so elevation stays consistent. */
    box-shadow: var(--shadow-soft);
}

.technologies-heading {
    margin-bottom: 1rem;
    color: var(--text-main);
    font-size: 1.05rem;
    font-weight: 700;
}

.technologies-list {
    display: flex;
    flex-wrap: wrap;
    gap: 0.6rem;
    margin: 0;
    padding: 0;
    list-style: none;
}

.technologies-item {
    padding: 0.4rem 1rem;
    border: 1px solid var(--border-color);
    border-radius: 999px;
    background-color: var(--bg-secondary);
    color: var(--text-main);
    font-size: 0.9rem;
    font-weight: 600;
    /* "Django REST Framework" wrapping would read as two technologies. */
    white-space: nowrap;
}
