/* Generated by JtnContentSeeder - JTN Africa brand overrides.
   theme.css hardcodes --at-theme-primary to the Orisa demo orange and loads after
   the theme options block, so the accent has to be reasserted here. */
:root {
    --at-theme-primary: #2CB34A;
}

/* Service detail hero heading: 10px smaller than the theme's global h1 scale.
   main.css sizes every h1 at 70px base / 84px above 1401px / 58px / 54px / 50px,
   which is built for short marketing headlines. Service names are long - "Systems Development
   & Technology Solutions" - so at those sizes the hero heading swallows the fold.
   Scoped to .sec-1-services-details so the rest of the h1 scale is untouched. */
.sec-1-services-details .section-title {
    font-size: 60px;
}

@media screen and (min-width: 1401px) {
    .sec-1-services-details .section-title {
        font-size: 74px;
    }
}

@media only screen and (min-width: 1200px) and (max-width: 1399px),
       only screen and (min-width: 992px) and (max-width: 1199px) {
    .sec-1-services-details .section-title {
        font-size: 48px;
    }
}

@media only screen and (min-width: 768px) and (max-width: 991px) {
    .sec-1-services-details .section-title {
        font-size: 44px;
    }
}

@media (max-width: 574.98px) {
    .sec-1-services-details .section-title {
        font-size: 40px;
    }
}

/* ---------------------------------------------------------------------------
   Stop long project titles being clipped, at every width.

   main.js splits any .at-title-anim heading into one <span> per character and
   wraps them in .at-title-anim-inner, which main.css sets to display:flex with
   no flex-wrap. A row of character spans therefore cannot break, so a long
   title runs past the card and .fix (overflow:hidden) cuts it off - which also
   drags the whole card wider than the viewport and crops the paragraph and
   tags beside it. Real case-study names are long ("SmartLearn - An Integrated
   Digital Operating Platform for Schools"), so this bites on every card.

   flex-wrap is NOT the fix: wrapping a row of per-character spans breaks words
   mid-letter. Making the spans inline instead lets the browser lay them out as
   ordinary text and break at spaces. That costs the per-letter hover animation,
   which on touch was never usable anyway.

   Was scoped to max-width:991.98px, which left the clipping in place on the
   desktop widths most people review the site at - the 2026-09-02 review picked
   it up there, with every card title losing its end ("Technical University of
   Mombasa - Training & Capacity Building"). The scoping was the bug; the rule
   itself was already right, so it now applies at every width.

   It also makes these headings selectable as ordinary text. A heading split
   into one span per character - 37 of them for "SmartLearn - School Management
   System" - does not select or copy like a sentence, which is part of what the
   same review reported when pasting into Word.
   --------------------------------------------------------------------------- */
.cs-portfolio-title.fix,
.mg-portfolio-content .at-title-anim.fix {
    overflow: visible;
}

.cs-portfolio-title .at-title-anim-inner,
.mg-portfolio-content .at-title-anim-inner {
    display: inline;
    overflow: visible;
}

.cs-portfolio-title .at-title-text span,
.mg-portfolio-content .at-title-text span {
    display: inline;
    translate: none;
    text-shadow: none;
}

/* The actual cause of the clipped card titles, found while checking the above.
   theme.css truncates them to a single line outright:

     .cs-portfolio-title.at-title-anim .at-title-text {
         display: block; white-space: nowrap;
         overflow: hidden; text-overflow: ellipsis;
     }

   That is a deliberate one-line-with-ellipsis treatment, and it is what put the
   "..." on every card in the 2026-09-02 review - a different mechanism from the
   unwrappable character-span row above, which is why unwrapping that alone did
   not fix it. Real names are long enough that most of them lose their end.

   Matched at the same specificity and loaded after theme.css, so this wins.
   Titles now wrap to as many lines as they need; the cards already size to
   their content, so nothing below them moves. */
.cs-portfolio-title.at-title-anim .at-title-text {
    white-space: normal;
    overflow: visible;
    text-overflow: clip;
}

/* Nothing in the work listing may push the page sideways - but words wrap whole.
   This used to say overflow-wrap: anywhere, which lets the browser split a word at
   any letter the moment it does not fit, and that is what printed "School Mana /
   gement" and "Business Con / sulting" (review, 2026-09-16). The titles are now
   sized so their longest word always fits the column instead (see "Project list
   titles" at the end), so the break-anywhere escape hatch is not needed. */
.cs-portfolio-content,
.mg-portfolio-content {
    max-width: 100%;
    overflow-wrap: normal;
    word-break: normal;
    hyphens: manual;
}

/* Footer logo. The markup used data-width="50", rendering the mark at 50px and
   leaving it visually disconnected from the heading next to it. Match the
   header's 37px height and let width follow the aspect ratio. */
.at-footer-logo {
    height: 44px;
    width: auto;
    max-width: 100%;
}

@media (max-width: 767.98px) {
    .at-footer-logo {
        height: 36px;
    }
}

/* ---------------------------------------------------------------------------
   Never invert third-party brand logos.

   Every partners style puts .dark-mode-invert on the logo wrapper, and main.css
   turns that into filter:invert(1) under [data-bs-theme="dark"]. That trick only
   works for single-colour marks. Our partner strip holds real client and vendor
   logos in their own brand colours - inverting them turns M-PESA's green magenta
   and scrambles the Google Workspace palette. Reproducing someone else's mark in
   the wrong colours is a trademark problem, not a styling preference, so these
   are excluded from the filter in both directions.
   --------------------------------------------------------------------------- */
[data-bs-theme="dark"] .at-brand-area .dark-mode-invert,
[data-bs-theme="dark"] .brand-item.dark-mode-invert,
[data-bs-theme="dark"] .brand-item.dark-mode-invert img {
    filter: none !important;
}

/* ---------------------------------------------------------------------------
   Sticky logo.

   Appears opposite the floating MENU button once the page scrolls, linking home.
   It used to sit in a pill mirroring the button; the 2026-09-16 review asked for
   the mark to stand on its own, so there is no border, background or padding.

   Without the pill it would hug the top of the button's box, so it is centred on
   the button instead: the button is 56px tall at top:40px (36px at top:20px on a
   phone), which puts a 26px logo at 40 + (56 - 26) / 2 = 55px, and a 20px one at
   20 + (36 - 20) / 2 = 28px. Horizontal insets live with the button's in "Floating
   controls" at the end, so the pair can never drift apart.

   Visibility rides the button's state through the sibling selector: main.js
   toggles .header-sticky on #header-sticky by ID, so the two appear together.
   --------------------------------------------------------------------------- */
.at-sticky-logo {
    display: inline-flex;
    align-items: center;
    position: absolute;
    top: 55px;
    left: 40px;
    z-index: 10;
    padding: 0;
    border: 0;
    background: none;
    opacity: 0;
    visibility: hidden;
    transform: scale(.5);
    transition: opacity .3s ease, transform .3s ease, visibility .3s ease;
    transition-delay: .4s;
}

.at-sticky-logo img {
    height: 26px;
    width: auto;
    display: block;
}

#header-sticky.header-sticky ~ .at-sticky-logo {
    opacity: 1;
    visibility: visible;
    transform: scale(1);
    position: fixed;
}

/* Matches the compact breakpoint main.css uses for .hamburger-sticky-menu. */
@media only screen and (min-width: 576px) and (max-width: 767px), (max-width: 574.98px) {
    .at-sticky-logo {
        top: 28px;
    }

    .at-sticky-logo img {
        height: 20px;
    }
}

/* The dark-mode logo swap and invert filter that apply to header logos must not
   reach this one. */
[data-bs-theme="dark"] .at-sticky-logo img {
    content: normal;
    filter: none !important;
}

/* ---------------------------------------------------------------------------
   Brand accent on the About feature-card headings.

   These headings ("Understand Before We Build", "Practical Delivery, Lasting
   Value") sat in flat black while the rest of the page carries the green.

   It has to be CSS rather than a <span class="theme-primary"> in the copy:
   about-us-information style 1 renders item titles with {{ }}, so any markup in
   the attribute comes out as visible escaped text - and the heading also carries
   .at-char-animation, which splits it into per-character spans, so inline markup
   would be mangled even if it were allowed through.

   Scoped to .at-about-area: .at-about-title alone appears in seven partials
   (hero, contact, partners, teams among them) and colouring all of them green
   would be far wider than intended.
   --------------------------------------------------------------------------- */
.at-about-area .at-about-title {
    color: var(--at-theme-primary, #2CB34A);
}

/* ---------------------------------------------------------------------------
   Floating contact launcher.

   Sits beside the back-to-top button rather than on top of it: main.css puts
   .back-to-top-wrapper at right:20px / bottom:50px when visible, 44px square, so
   this starts at right:74px (20 + 44 + 10) on the same baseline. It stays put
   when the back-to-top button hides itself below 300px of scroll.

   z-index 98 - one under the back-to-top's 99, so the two never fight, and well
   under the offcanvas panels.
   --------------------------------------------------------------------------- */
.jtn-contact {
    position: fixed;
    right: 74px;
    bottom: 50px;
    z-index: 98;
    display: flex;
    flex-direction: column;
    align-items: flex-end;
    gap: 12px;
}

.jtn-contact__toggle {
    display: inline-flex;
    align-items: center;
    gap: 10px;
    padding: 12px 22px;
    border: 0;
    border-radius: 9999px;
    background: var(--at-common-black, #1E1E1E);
    color: var(--at-common-white, #fff);
    font-weight: 600;
    font-size: 15px;
    line-height: 1;
    box-shadow: 0 8px 20px rgba(3, 4, 28, .28);
    cursor: pointer;
    transition: transform .2s ease, background-color .2s ease;
}

.jtn-contact__toggle:hover {
    transform: translateY(-2px);
    background: var(--at-theme-primary, #2CB34A);
}

.jtn-contact__toggle-icon {
    display: inline-flex;
}

/* One button, two glyphs - swap them on state rather than rewriting the DOM. */
.jtn-contact__close,
.jtn-contact.is-open .jtn-contact__open {
    display: none;
}

.jtn-contact.is-open .jtn-contact__close {
    display: block;
}

.jtn-contact__panel {
    width: 300px;
    max-width: calc(100vw - 94px);
    background: var(--at-common-white, #fff);
    border-radius: 16px;
    box-shadow: 0 12px 32px rgba(3, 4, 28, .22);
    overflow: hidden;
}

.jtn-contact__item {
    display: flex;
    align-items: center;
    gap: 14px;
    padding: 14px 18px;
    text-decoration: none;
    border-bottom: 1px solid var(--at-neutral-100, #EFEFEF);
    transition: background-color .15s ease;
}

.jtn-contact__item:last-child {
    border-bottom: 0;
}

.jtn-contact__item:hover {
    background: var(--at-neutral-50, #F7F7F7);
}

.jtn-contact__icon {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 44px;
    height: 44px;
    flex: 0 0 44px;
    border-radius: 50%;
    color: #fff;
}

/* Channel colours, not brand colours: people recognise WhatsApp green on sight,
   and recolouring it to the JTN palette would cost more than it gains. */
.jtn-contact__icon--whatsapp { background: #25D366; }
.jtn-contact__icon--call     { background: #1A73E8; }
.jtn-contact__icon--email    { background: #1E1E1E; }

.jtn-contact__text {
    display: flex;
    flex-direction: column;
    gap: 2px;
    min-width: 0;
}

.jtn-contact__text strong {
    color: var(--at-common-black, #1E1E1E);
    font-size: 15px;
    line-height: 1.3;
}

.jtn-contact__text span {
    color: var(--at-neutral-500, #777679);
    font-size: 13px;
    line-height: 1.3;
    overflow-wrap: anywhere;
}

/* On a phone the back-to-top button moves to right:20px/bottom:20px, so follow it
   down and shed the label to keep the launcher from crowding the screen. */
@media (max-width: 575.98px) {
    .jtn-contact {
        right: 20px;
        bottom: 76px;
    }

    .jtn-contact__toggle-label {
        display: none;
    }

    .jtn-contact__toggle {
        padding: 14px;
    }

    .jtn-contact__panel {
        width: 280px;
        max-width: calc(100vw - 40px);
    }
}

/* ---------------------------------------------------------------------------
   Contact heading: 10px down from the theme's display scale.

   "Have a Dream Worth Enabling?" is long enough to run to three lines on the
   contact page. Reduced by 5px, then a further 5px on review. .fz-ds-1 sets 124 / 80 / 72 / 60 / 54 across the breakpoints and
   every one of those is !important, so each has to be restated - a single value
   would flatten the responsive scale, and one without !important would simply
   lose.

   Scoped to .sec-4-about .fz-ds-1, which only the contact-form styles use; style
   1 is the one on /contact.
   --------------------------------------------------------------------------- */
.sec-4-about .fz-ds-1 {
    font-size: 70px !important;
}

@media screen and (min-width: 1401px) {
    .sec-4-about .fz-ds-1 {
        font-size: 114px !important;
    }
}

@media only screen and (min-width: 1200px) and (max-width: 1399px),
       only screen and (min-width: 992px) and (max-width: 1199px) {
    .sec-4-about .fz-ds-1 {
        font-size: 62px !important;
    }
}

@media only screen and (min-width: 768px) and (max-width: 991px) {
    .sec-4-about .fz-ds-1 {
        font-size: 50px !important;
    }
}

@media (max-width: 574.98px) {
    .sec-4-about .fz-ds-1 {
        font-size: 44px !important;
    }
}

/* The hexagon quote glyph that opens the Vision & Purpose pull-quote. Its paths are
   fill="currentColor", so colouring the wrapper is enough - no SVG edit needed. The
   markup carries .at-quote-mark for this purpose; targeting its old bare .mb-3 would
   have hit spacing utilities all over the theme. */
.at-quote-mark {
    color: var(--at-theme-primary, #2CB34A);
}

/* ---------------------------------------------------------------------------
   Homepage "What we do" block: green step numbers and green solution icons.

   The [01]..[06] markers carry .neutral-500, and the icons are inline SVGs
   drawn with fill="currentColor" - so both take their colour from CSS and
   neither needs the markup touching.

   Scoped to .sec-4-home-3, the wrapper for services style 3, which only the
   homepage uses. The Solutions page (style 2) and Industries (style 1) keep
   their own treatment.

   The .icon rule deliberately does not force a fill on <img>: a service can
   override its icon with an uploaded image, and recolouring somebody's artwork
   is not something CSS should attempt.
   --------------------------------------------------------------------------- */
/* !important is needed here, not laziness: the markup carries the .neutral-500
   utility, which main.css defines as `color: var(--at-neutral-500) !important`.
   Nothing outranks that without matching it. */
.sec-4-home-3 .navigation-sec4home3 .content > span {
    color: var(--at-theme-primary, #2CB34A) !important;
}

.sec-4-home-3 .icon,
.sec-4-home-3 .icon svg {
    color: var(--at-theme-primary, #2CB34A);
}

/* ---------------------------------------------------------------------------
   Work listing tag pills: stop them being clipped, and hover in brand green.

   The clipping is a flexbox trap, not a width mistake. `.cs-portfolio-content`
   is a flex row set to flex-md-nowrap, and the tag list carries Bootstrap's
   .text-nowrap. A flex item defaults to min-width:auto, so it cannot shrink
   below its own content width - and content that may not wrap has no smaller
   width to shrink to. The row therefore runs past its container and the pills
   get cut off at the edge. (`.w-md-75` on the text side looks like it should
   cap things, but it has no CSS definition anywhere in the theme - it is inert.)

   min-width:0 restores the ability to shrink; letting the list wrap gives it
   somewhere to go. Each pill keeps white-space:nowrap so individual labels like
   "M-PESA PAYMENTS" stay on one line.

   Applies at every width. An earlier version of this fix was scoped to mobile,
   but the base breakpoint sets .cs-portfolio-tag to width:40%, so six pills
   overflow on desktop too.
   --------------------------------------------------------------------------- */
.cs-portfolio-content > *,
.cs-portfolio-tag {
    min-width: 0;
}

.cs-portfolio-tag ul {
    flex-wrap: wrap;
    white-space: normal !important;
}

.cs-portfolio-tag ul li a {
    white-space: nowrap;
}

/* The two columns had nothing separating them and nothing sizing the text side -
   `.w-md-75` in the markup reads like a 75% cap but is not defined anywhere in
   the theme, so the paragraph simply grew until it ran under the pills. Size both
   explicitly and put a real gap between them.

   Note the .mg-portfolio-content prefix throughout this block. The theme's own
   rules are written that way (.mg-portfolio-content .cs-portfolio-tag), which
   out-specifies a bare .cs-portfolio-tag - an earlier version of this fix used
   the short selector and silently lost. */
.mg-portfolio-content.cs-portfolio-content {
    gap: 32px;
}

.mg-portfolio-content .w-md-75 {
    flex: 1 1 auto;
    min-width: 0;
}

.mg-portfolio-content .cs-portfolio-tag {
    flex: 0 0 auto;
    width: auto;
    max-width: 40%;
}

/* Hover filled black below 768px and white above it - inconsistent as well as
   off-brand. Brand green at every width. */
.mg-portfolio-content .cs-portfolio-tag ul li a:hover {
    background: var(--at-theme-primary, #2CB34A);
    border-color: var(--at-theme-primary, #2CB34A);
    color: var(--at-common-white, #fff);
}

/* ---------------------------------------------------------------------------
   Partner logo ticker: smaller marks, tighter spacing.

   carouselTicker.css sets .carouselTicker__item { margin: 0 100px }, putting
   200px between neighbours - reasonable for the demo's handful of wordmarks,
   far too sparse for sixteen client logos. The images also carried no size
   constraint at all and rendered at their natural 413x138.

   Scoped to .at-brand-area, which wraps only the partners block. The
   skills-carousel marquees use the same carouselTicker classes and need to keep
   their wider spacing, so a bare .carouselTicker__item rule would break them.
   --------------------------------------------------------------------------- */
.at-brand-area .carouselTicker__item {
    margin: 0 28px;
    display: flex;
    align-items: center;
}

.at-brand-area .brand-item img {
    height: 60px;
    width: auto;
    max-width: none;
    object-fit: contain;
}

@media (max-width: 767.98px) {
    .at-brand-area .carouselTicker__item {
        margin: 0 18px;
    }

    .at-brand-area .brand-item img {
        height: 52px;
    }
}

/* The wall runs as two rows scrolling opposite ways (see the partners style-2
   partial). Only the gap between them is set here; the plugin lays each row out
   itself. .at-brand-row is on the ticker wrapper, not on the list the plugin
   rewrites, so the spacing survives initialisation. */
.at-brand-area .at-brand-row + .at-brand-row {
    margin-top: 32px;
}

@media (max-width: 767.98px) {
    .at-brand-area .at-brand-row + .at-brand-row {
        margin-top: 22px;
    }
}

/* ---------------------------------------------------------------------------
   Partners heading: full width, left aligned, brand green, 3px smaller.

   .at-about-title-wrap is a flex ROW, so the "WHO WE WORK WITH" pill sat beside
   the heading and pushed it right - leaving it too little width, which is why
   "Organizations that trust JTN Africa" broke across two lines. Stacking the two
   puts the heading back on the left edge with the full column to run in.

   .at-section-title carries no size of its own; it is an h2 inheriting the theme
   scale (72 / 60 / 52 / 50 / 42 across the breakpoints), so each step is
   restated 3px down rather than set once.

   .sec-3-home-2 is used by this partners style and nothing else in the theme.
   --------------------------------------------------------------------------- */
.sec-3-home-2 .at-about-title-wrap {
    flex-direction: column;
    flex-wrap: nowrap;
    align-items: flex-start;
    gap: 12px;
}

.sec-3-home-2 .at-section-title {
    color: var(--at-theme-primary, #2CB34A);
    font-size: 57px;
}

@media screen and (min-width: 1401px) {
    .sec-3-home-2 .at-section-title {
        font-size: 69px;
    }
}

@media only screen and (min-width: 1200px) and (max-width: 1399px),
       only screen and (min-width: 992px) and (max-width: 1199px) {
    .sec-3-home-2 .at-section-title {
        font-size: 47px;
    }
}

@media only screen and (min-width: 768px) and (max-width: 991px) {
    .sec-3-home-2 .at-section-title {
        font-size: 49px;
    }
}

@media (max-width: 574.98px) {
    .sec-3-home-2 .at-section-title {
        font-size: 39px;
    }
}

/* ---------------------------------------------------------------------------
   Content Block style 1, and the eyebrow label it shares with other blocks.

   These were unstyled in the literal sense: the partial hangs .at-section-subtitle
   on the eyebrow and rendered its items as bare <h6>/<p> pairs in a two-column
   grid, and neither .at-section-subtitle nor .at-feature-card appears anywhere in
   main.css or theme.css. They are class names the shortcode partials invented and
   the theme never implemented, so the browser had nothing to apply - which is why
   "Who We Serve" on /industries read as a wall of plain text next to the properly
   designed blocks around it.

   The eyebrow rule is scoped to this block rather than applied to .at-section-subtitle
   everywhere. Four other partials use that class, and the call-to-action one pairs it
   with .text-white for dark banners - Bootstrap sets that colour with !important, so a
   global white pill would render white text on a white pill. The other four keep the
   behaviour they have today; they can follow once someone has looked at each backdrop.

   Rather than invent a look, these borrow the vocabulary main.css already uses:
   .category-tag for the eyebrow (pill, 1px neutral-100 border, uppercase 12px/600,
   leading dot) and .at-service-item for the items (muted number that turns brand
   green on hover). Everything is expressed in the theme's own custom properties so
   the dark palette follows without a second set of rules.
   --------------------------------------------------------------------------- */
.shortcode-content-block-style-1 .at-section-subtitle {
    display: inline-flex;
    align-items: center;
    gap: 8px;
    padding: 10px 14px;
    border-radius: 25px;
    border: 1px solid var(--at-neutral-100, #DFDFDF);
    background: var(--at-neutral-0, #FEFEFE);
    color: var(--at-neutral-900);
    font-size: 12px;
    font-weight: 600;
    line-height: normal;
    letter-spacing: -0.24px;
    text-transform: uppercase;
}

/* Mirrors .category-tag .dot, which main.css renders as a separate element. */
.shortcode-content-block-style-1 .at-section-subtitle::before {
    content: "";
    width: 6px;
    height: 6px;
    border-radius: 50%;
    background: var(--at-theme-primary);
    flex: 0 0 auto;
}

.shortcode-content-block-style-1 .at-content-item {
    height: 100%;
    padding: 28px;
    border: 1px solid var(--at-neutral-100, #DFDFDF);
    border-radius: 16px;
    background: var(--at-neutral-0, #FEFEFE);
    transition: border-color .3s ease, transform .3s ease, box-shadow .3s ease;
}

.shortcode-content-block-style-1 .at-content-item:hover {
    border-color: var(--at-theme-primary);
    transform: translateY(-4px);
    box-shadow: 0 18px 40px rgba(0, 0, 0, .06);
}

/* Same muted-to-primary move .at-service-item makes on its own counter, at the
   smaller size a card this size can carry. */
.shortcode-content-block-style-1 .at-content-item__num {
    display: block;
    margin-bottom: 14px;
    color: var(--at-neutral-500);
    font-size: 14px;
    font-weight: 600;
    letter-spacing: 1.4px;
    opacity: .5;
    transition: color .3s ease, opacity .3s ease;
}

.shortcode-content-block-style-1 .at-content-item:hover .at-content-item__num {
    color: var(--at-theme-primary);
    opacity: 1;
}

/* A pair is not a sequence. The counter helps a reader hold their place in a list
   of six or seven, but numbering exactly two items - "01 Our Vision, 02 Our
   Purpose" - asserts an order that does not exist. Hidden when the row holds
   two, which is the only case where the number carries nothing. */
.shortcode-content-block-style-1 .row > div:first-child:nth-last-child(2) .at-content-item__num,
.shortcode-content-block-style-1 .row > div:first-child:nth-last-child(2) ~ div .at-content-item__num {
    display: none;
}

.shortcode-content-block-style-1 .at-content-item__icon {
    color: var(--at-theme-primary);
}

.shortcode-content-block-style-1 .at-content-item__title {
    color: var(--at-neutral-900);
}

/* Style 2 and style 5 hang their cards on .at-feature-card, which is undefined for
   the same reason. They already carry padding, radius and a background from utility
   classes, so they only need the border and hover to sit in the same system. */
.at-feature-card {
    border: 1px solid var(--at-neutral-100, #DFDFDF);
    transition: border-color .3s ease, transform .3s ease, box-shadow .3s ease;
}

.at-feature-card:hover {
    border-color: var(--at-theme-primary);
    transform: translateY(-4px);
    box-shadow: 0 18px 40px rgba(0, 0, 0, .06);
}

/* The lift is decoration, not information. */
@media (prefers-reduced-motion: reduce) {
    .shortcode-content-block-style-1 .at-content-item,
    .at-feature-card {
        transition: none;
    }

    .shortcode-content-block-style-1 .at-content-item:hover,
    .at-feature-card:hover {
        transform: none;
    }
}
/* ===========================================================================
   C2, typography scale. Approved 2026-09-06 after a before/after review.

   Delete this whole block to revert; nothing else depends on it.

   Measured on the homepage before this: h1 84px, h2 72px, body paragraphs 20px,
   page 16,282px tall. The headings are what make the site feel loud - the body
   size is large but defensible. The review called the whole scale too big.

   Headings need no !important: main.css sets them at plain element specificity
   across a ladder of media queries, and this file loads after it, so one rule
   here beats the lot. That also leaves the service-detail title override above
   intact, because it is a class selector and outranks these.

   The fz-* utilities do need it - main.css marks those !important itself.
   --------------------------------------------------------------------------- */
h1, .h1 { font-size: clamp(36px, 4vw, 62px); }
h2, .h2 { font-size: clamp(28px, 3.2vw, 50px); }
h3, .h3 { font-size: clamp(22px, 2.2vw, 32px); }

:root {
    --at-fz-font-lg: 18px;
    --at-fz-font-2xl: 22px;
    --at-fz-font-3xl: 26px;
    --at-fz-ds-1: 84px;
}

.fz-font-lg { font-size: 18px !important; }
.fz-font-2xl { font-size: 22px !important; }
.fz-font-3xl { font-size: 26px !important; }
.fz-ds-1 { font-size: clamp(40px, 5vw, 84px) !important; }
/* ============================================================= end C2 ==== */

/* ---------------------------------------------------------------------------
   Type and inset corrections from the 2026-09-16 review.
   --------------------------------------------------------------------------- */

/* Project list titles (homepage "Work we are proud of").

   Were the .h5 ladder - 34px on a wide screen, 28 at 1280, 24 at 768, 20 on a
   phone - and read as too loud for card titles. The review asked for at least 5px
   off. One fluid rule replaces the ladder: 28px at 1600 and wider, easing to 18px
   by tablet. At every size the longest word in any title ("Management",
   "Consulting") fits its column whole, which is what lets the break-anywhere rule
   above go. */
.cs-portfolio-title {
    font-size: clamp(18px, 1.75vw, 28px);
    line-height: 1.25;
}

/* Our Work featured card title.

   Took its size from the .h4 ladder, which put it at 48px on a wide screen - louder
   than the list titles above and, at 60% max-width, wrapping to three lines on the
   longer project names. One fluid rule instead, topping out at 40px. The ladder it
   replaces was not monotonic either (48px at 1600 but 30px at 1280 and 36px at 768),
   so this also makes it move in one direction as the screen narrows.

   The 28px floor is not arbitrary: the row-card titles below it sit at 28px from
   1024px down, and the featured card must never end up smaller than the cards it is
   meant to lead. */
.card_case__studies-title {
    font-size: clamp(28px, 2.2vw, 40px);
}

/* Footer menu links. main.css sets 22px; slightly quieter, per the review. */
.alt-footer-link-item ul li a {
    font-size: 18px;
}

@media (max-width: 767.98px) {
    .alt-footer-link-item ul li a {
        font-size: 16px;
    }
}

/* Hamburger panel items. main.css sets 48px, which on a 500px-wide panel is a
   poster, not a menu. Fluid between 28px and 36px. Only size is touched - the
   staggered slide-in main.css runs on the same selector is left exactly as is. */
.at-offcanvas-2-wrapper .at-offcanvas-menu > nav > ul > li > a {
    font-size: clamp(28px, 2.4vw, 36px);
}

/* Floating controls (sticky MENU button and logo).

   These have to line up with the page's own content edge, and that edge moves: the
   site lays its content out in Bootstrap's .container, whose max-width steps at each
   breakpoint (the theme overrides the top step to 1750px in main.css), centred, with
   12px of padding. So the gutter is 12px up to 1400px wide, then grows as the
   viewport outruns 1750px - 65px at 1855, 97px at 1920.

   Fixed insets cannot track that. 20px was outside the content on a wide screen and
   40px was still outside it at 1855 (review, 2026-09-16). The arithmetic below is the
   container's own, so the controls sit exactly on the content edge at every width:

       edge = (viewport - container max-width) / 2 + 12px padding

   Percentages rather than vw deliberately: for a fixed element a percentage resolves
   against the initial containing block, which excludes the scrollbar - exactly what
   the container itself is centred within. 100vw includes the scrollbar and would sit
   the controls a few pixels off.

   If main.css's container steps ever change, these must change with them. */
.at-sticky-logo { left: 12px; }
.hamburger-sticky-menu { right: 12px; }

@media (min-width: 576px) {
    .at-sticky-logo { left: calc((100% - 540px) / 2 + 12px); }
    .hamburger-sticky-menu { right: calc((100% - 540px) / 2 + 12px); }
}

@media (min-width: 768px) {
    .at-sticky-logo { left: calc((100% - 720px) / 2 + 12px); }
    .hamburger-sticky-menu { right: calc((100% - 720px) / 2 + 12px); }
}

@media (min-width: 992px) {
    .at-sticky-logo { left: calc((100% - 960px) / 2 + 12px); }
    .hamburger-sticky-menu { right: calc((100% - 960px) / 2 + 12px); }
}

@media (min-width: 1200px) {
    .at-sticky-logo { left: calc((100% - 1140px) / 2 + 12px); }
    .hamburger-sticky-menu { right: calc((100% - 1140px) / 2 + 12px); }
}

/* Above 1400px the container is capped at 1750px, so the gutter is zero until the
   viewport passes that - max() keeps it from going negative in between. */
@media (min-width: 1400px) {
    .at-sticky-logo { left: calc(max(0px, (100% - 1750px) / 2) + 12px); }
    .hamburger-sticky-menu { right: calc(max(0px, (100% - 1750px) / 2) + 12px); }
}
