/* ==========================================================================
   Frontsite extras — project overrides on top of the Boxup template.
   Load order: fonts.css -> font-awesome -> animate.min.css -> bootstrap.min.css
               -> style.css -> frontsite-extras.css  (this file must be LAST)

   Nothing in here is template code. Keep template files unmodified except for
   two documented global brand swaps:
     - the brand-colour swap in bootstrap.min.css (FRONTSITE_BOXUP_MIGRATION_PLAN.md D-1)
     - the typeface swap to Montserrat in style.css, which replaced the
       template's Josefin Sans / Open Sans / Lora trio. Done in place for the
       same reason as the colour: it is a global identity change, not a local
       override, and shadowing every template rule from here would be worse.
       The .font-josefin / .font-lora / .font-open utility classes were kept
       under their original names so template markup and existing views did not
       have to change - they now all resolve to Montserrat, so they are inert
       aliases rather than font switches.
   ========================================================================== */

/* --------------------------------------------------------------------------
   1. Brand tokens
   The compiled bootstrap.min.css was re-branded from Boxup blue to the Pigeon
   brand purple sampled from the logo (uploads/company/pigeon.png). These vars
   exist so our own components can use the palette without hard-coding hexes.
   -------------------------------------------------------------------------- */
:root {
    --fs-accent:        #aa1ad3;  /* Pigeon brand purple, sampled from the logo */
    --fs-accent-strong: #8815a9;  /* 80% shade - hover/active, focus outlines */
    --fs-accent-soft:   #f4e3fa;  /* 12% tint - soft surfaces */
    --fs-accent-tint:   #d58de9;  /* 50% tint, used by BS focus/active states */
    --fs-accent-ink:    #000000;  /* the logo's sparkle mark */

    --fs-dark:          #151a21;  /* was Boxup #091E3E */
    --fs-darker:        #0d1015;  /* was Boxup .bg-black #06152B */

    --fs-ink:           #202327;
    --fs-ink-2:         #4e5760;
    --fs-ink-3:         #7a838d;
    --fs-line:          #d8dde3;
    --fs-canvas:        #f3f5f7;
    --fs-surface:       #ffffff;
    --fs-surface-alt:   #eef2f5;

    /* Typeface. Overriding Bootstrap's own variable rather than only styling
       `body` means every Bootstrap component that reads it - buttons, form
       controls, dropdowns, modals - inherits Montserrat too, instead of
       quietly falling back to the system stack. */
    --fs-font:          "Montserrat", system-ui, -apple-system, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
    --bs-body-font-family: var(--fs-font);
}

/* --------------------------------------------------------------------------
   1b. Base typography

   The template never set a font-family on `body`, so body copy was rendering
   in Bootstrap's default system stack while only headings, buttons and nav
   used a webfont. Setting it here is what actually puts Montserrat on the
   page's running text.

   `optical-sizing` and the explicit `font-synthesis` guard matter for a
   variable font: without the guard a browser that cannot reach a real 600
   weight would synthesise a fake bold by smearing the 400, which looks muddy
   next to the genuine weights.
   -------------------------------------------------------------------------- */
body {
    font-family: var(--fs-font);
    font-optical-sizing: auto;
    font-synthesis-weight: none;
}

/* --------------------------------------------------------------------------
   2. Motion safety  (migration plan risk R1 — THE important one)

   WOW.js sets `visibility:hidden` on every `.wow` element and only reveals it
   from JavaScript. If JS never runs, or the visitor asked for reduced motion,
   the entire page would render blank. These two guards make the content
   unconditionally visible in both cases.

   The `.js` class is set by an inline script in <head> before any paint, so
   `html:not(.js)` is the no-JavaScript state.
   -------------------------------------------------------------------------- */
html:not(.js) .wow,
html:not(.js) .animated {
    visibility: visible !important;
    animation: none !important;
}

@media (prefers-reduced-motion: reduce) {
    .wow,
    .animated {
        visibility: visible !important;
        animation: none !important;
        transition: none !important;
    }

    *,
    *::before,
    *::after {
        animation-duration: 0.001ms !important;
        animation-iteration-count: 1 !important;
        transition-duration: 0.001ms !important;
        scroll-behavior: auto !important;
    }
}

/* --------------------------------------------------------------------------
   3. Spinner failsafe  (migration plan risk R2)

   The template's full-viewport spinner is removed by JS. If JS is absent or
   throws before main.js runs, the overlay would trap the page behind a blank
   white screen. Guard 1: no JS at all. Guard 2: a delayed CSS animation that
   force-dismisses the overlay regardless of what JS did or did not do.

   Preferred fix is to simply not render the spinner at all in the layout —
   it buys nothing on a server-rendered page. This is belt-and-braces.
   -------------------------------------------------------------------------- */
html:not(.js) #spinner {
    display: none !important;
}

@keyframes fs-spinner-failsafe {
    to {
        opacity: 0;
        visibility: hidden;
    }
}

#spinner {
    animation: fs-spinner-failsafe 0.3s ease-out 4s forwards;
}

/* --------------------------------------------------------------------------
   4. Accessibility
   -------------------------------------------------------------------------- */

/* The template's rounded-pill buttons lose Bootstrap's default focus ring in
   several places. Restore a visible, high-contrast one for keyboard users. */
a:focus-visible,
button:focus-visible,
.btn:focus-visible,
.nav-link:focus-visible,
.form-control:focus-visible {
    outline: 3px solid var(--fs-accent-strong);
    outline-offset: 2px;
    box-shadow: none;
}

/* Skip link — rendered as the first element in <body> by the layout. */
.fs-skip-link {
    position: absolute;
    left: -9999px;
    top: 0;
    z-index: 100000;
    padding: 12px 20px;
    background: var(--fs-dark);
    color: #fff;
    border-radius: 0 0 8px 0;
    text-decoration: none;
    font-weight: 600;
}

.fs-skip-link:focus {
    left: 0;
}

/* Bootstrap 5 drops an underline on .btn-link; the footer uses it for nav
   lists, where an underline-on-hover is the clearer affordance. */
.footer .btn-link:hover,
.footer .btn-link:focus-visible {
    text-decoration: underline;
}

/* --------------------------------------------------------------------------
   5. Brand wiring
   The template hard-codes a "Boxup" wordmark. The layout renders the real
   company logo (or the company name as text) instead.
   -------------------------------------------------------------------------- */
.navbar .navbar-brand .fs-logo-text {
    font-family: "Montserrat", sans-serif;
    font-weight: 600;
    font-size: 1.75rem;
    line-height: 1;
    margin: 0;
    color: var(--fs-dark);
}

.navbar .navbar-brand img {
    max-height: 50px;
    width: auto;
}

.footer .fs-logo-text {
    font-family: "Montserrat", sans-serif;
    font-weight: 600;
    font-size: 1.5rem;
    color: #fff;
}

.footer img.fs-footer-logo {
    max-height: 46px;
    width: auto;
}

/* --------------------------------------------------------------------------
   6. Back-to-top
   main.js toggles .is-visible instead of using jQuery fadeIn/fadeOut.
   -------------------------------------------------------------------------- */
.back-to-top {
    display: flex !important;
    opacity: 0;
    visibility: hidden;
    transition: opacity 0.3s ease, visibility 0.3s ease;
}

.back-to-top.is-visible {
    opacity: 1;
    visibility: visible;
}

/* --------------------------------------------------------------------------
   7. Brand pattern  (interlocking circles)

   Source: docs/assets/brand/pattern-circles-source.png — the supplied brand
   pattern, downscaled to 1400px and normalised into an alpha MASK
   (assets/frontsite/img/pattern-circles.png, 59KB vs the 233KB original).

   Storing it as a mask rather than a picture means the pattern is painted in
   whatever colour we choose, at whatever opacity, instead of being locked to
   the source's fixed grey. The artwork's own top-to-bottom fade is preserved
   in the alpha channel, so the pattern emerges toward the bottom of a panel
   exactly as designed.

   Usage:  <div class="footer fs-pattern"> … </div>
   Tune:   --fs-pattern-color / --fs-pattern-opacity on the element.
   -------------------------------------------------------------------------- */
.fs-pattern {
    position: relative;
    isolation: isolate;
}

/* Direct children are lifted above the pattern layer. */
.fs-pattern > * {
    position: relative;
    z-index: 1;
}

/* The pattern layer is only ever created when the browser can actually mask.
   Without this guard a non-supporting browser would paint a solid brand-colour
   block over the whole panel. */
@supports ((-webkit-mask-image: url("../img/pattern-circles.png")) or (mask-image: url("../img/pattern-circles.png"))) {
    .fs-pattern::before {
        content: "";
        position: absolute;
        inset: 0;
        z-index: 0;
        pointer-events: none;
        background-color: var(--fs-pattern-color, var(--fs-accent));
        opacity: var(--fs-pattern-opacity, 0.3);

        -webkit-mask-image: url("../img/pattern-circles.png");
                mask-image: url("../img/pattern-circles.png");
        -webkit-mask-repeat: no-repeat;
                mask-repeat: no-repeat;
        -webkit-mask-position: bottom center;
                mask-position: bottom center;
        -webkit-mask-size: cover;
                mask-size: cover;
    }
}

/* Light surfaces: same geometry, ink-coloured and much quieter. */
.fs-pattern-soft {
    --fs-pattern-color: var(--fs-ink-3);
    --fs-pattern-opacity: 0.16;
}

/* The dark footer carries the strongest expression of the pattern. Kept below
   ~0.3 so the white body copy sitting on top of it stays comfortably legible
   over the lighter lobes of the pattern. */
.footer.fs-pattern {
    --fs-pattern-opacity: 0.26;
}

/* The back-to-top button is fixed at right:45px/bottom:45px and would sit on
   top of the copyright row's right-hand text. Keep the text clear of it. */
@media (min-width: 992px) {
    .copyright .text-md-end {
        padding-right: 70px;
    }
}

/* --------------------------------------------------------------------------
   8. Corridor map  (home hero)

   The inline SVG East-Africa freight map is the one piece of bespoke artwork
   worth carrying over from the old front-site, so its styles are ported here
   from the retired cockpit.css and re-tuned for a DARK hero panel (the
   original sat on a light background) and the Pigeon purple.
   -------------------------------------------------------------------------- */
.ck-map {
    position: relative;
}

.ck-map-svg {
    display: block;
    width: 100%;
    height: auto;
    overflow: visible;
}

.ck-graticule line {
    stroke: rgba(255, 255, 255, 0.07);
    stroke-width: 1;
}

.ck-route-bg {
    fill: none;
    stroke: rgba(255, 255, 255, 0.16);
    stroke-width: 2;
}

.ck-route {
    fill: none;
    stroke: var(--fs-accent);
    stroke-width: 3;
    stroke-linecap: round;
    stroke-linejoin: round;
}

.ck-route-2 {
    fill: none;
    stroke: rgba(255, 255, 255, 0.22);
    stroke-width: 1.6;
    stroke-dasharray: 2 7;
    stroke-linecap: round;
}

.ck-node circle.dot {
    fill: var(--fs-dark);
    stroke: rgba(255, 255, 255, 0.55);
    stroke-width: 1.6;
}

.ck-node.active circle.dot {
    stroke: var(--fs-accent);
    fill: var(--fs-accent);
}

.ck-node.active circle.ping {
    fill: none;
    stroke: var(--fs-accent);
    stroke-width: 1.5;
    transform-origin: center;
    transform-box: fill-box;
    animation: ck-ping 2.8s ease-out infinite;
}

@keyframes ck-ping {
    0%   { r: 5;  opacity: 0.8; }
    100% { r: 22; opacity: 0; }
}

.ck-node text {
    font-family: "Montserrat", sans-serif;
    font-size: 11px;
    letter-spacing: 0.06em;
    fill: rgba(255, 255, 255, 0.85);
    text-transform: uppercase;
}

.ck-node.active text {
    fill: var(--fs-accent-tint);
}

.ck-coord {
    font-size: 9.5px;
    fill: rgba(255, 255, 255, 0.45);
    letter-spacing: 0.02em;
}

/* The route draw-on and the node ping are decorative only. */
@media (prefers-reduced-motion: reduce) {
    .ck-node.active circle.ping {
        animation: none !important;
    }

    .ck-route {
        stroke-dasharray: none !important;
        stroke-dashoffset: 0 !important;
    }
}

/* --------------------------------------------------------------------------
   9. Hero stat counters
   -------------------------------------------------------------------------- */
.fs-stat-n {
    display: block;
    font-family: "Montserrat", sans-serif;
    font-weight: 600;
    font-size: clamp(1.8rem, 3vw, 2.5rem);
    line-height: 1.1;
    color: #fff;
    font-variant-numeric: tabular-nums;
}

.fs-stat-l {
    display: block;
    font-size: 0.875rem;
    color: rgba(255, 255, 255, 0.55);
}

/* --------------------------------------------------------------------------
   10. Section kicker

   The template's own idiom: an italic line in the brand colour above a
   display heading. This REPLACES the pill badge that was previously stamped
   above every section — a tiny tracked eyebrow on every section is AI
   scaffolding, not voice. Use sparingly: not every section needs one.
   -------------------------------------------------------------------------- */
.fs-kicker {
    font-family: "Montserrat", sans-serif;
    font-style: italic;
    font-size: 1.2rem;
    line-height: 1.3;
    color: var(--fs-accent);
    margin: 0 0 0.5rem;
}

.bg-dark .fs-kicker,
.bg-black .fs-kicker {
    color: var(--fs-accent-tint);
}

/* --------------------------------------------------------------------------
   11. Page header  (sub-page hero)

   Boxup gives sub-pages a compact image header with a breadcrumb, NOT the tall
   split hero the home page uses. Keeping that distinction is most of what stops
   every page reading the same.
   -------------------------------------------------------------------------- */
.fs-page-header {
    position: relative;
    overflow: hidden;
    isolation: isolate;
}

.fs-page-header > img {
    position: absolute;
    inset: 0;
    width: 100%;
    height: 100%;
    object-fit: cover;
    z-index: 0;
}

/* Scrim: without it, white type over photography fails contrast wherever the
   image happens to be bright. Angled so the left-hand copy sits on the darkest
   part of the frame. */
.fs-page-header::after {
    content: "";
    position: absolute;
    inset: 0;
    z-index: 1;
    background: linear-gradient(100deg,
        rgba(21, 26, 33, 0.92) 0%,
        rgba(21, 26, 33, 0.78) 45%,
        rgba(21, 26, 33, 0.45) 100%);
}

.fs-page-header > .container {
    position: relative;
    z-index: 2;
}

.fs-page-header .breadcrumb {
    --bs-breadcrumb-divider-color: rgba(255, 255, 255, 0.5);
    --bs-breadcrumb-item-padding-x: 0.6rem;
}

.fs-page-header .breadcrumb a {
    color: rgba(255, 255, 255, 0.72);
    text-decoration: none;
}

.fs-page-header .breadcrumb a:hover {
    color: #fff;
    text-decoration: underline;
}

/* --------------------------------------------------------------------------
   12. Media treatments
   -------------------------------------------------------------------------- */
.fs-media {
    display: block;
    width: 100%;
    height: 100%;
    object-fit: cover;
    border-radius: var(--bs-border-radius, 0.375rem);
}

.fs-media-tall  { aspect-ratio: 3 / 4;  }
.fs-media-wide  { aspect-ratio: 16 / 9; }
.fs-media-square{ aspect-ratio: 1 / 1;  }

/* A duotone-ish wash keeps stock photography on-brand without tinting it into
   mush. Applied only where a photo sits behind or beside brand-coloured UI. */
.fs-media-wash {
    position: relative;
}

.fs-media-wash::after {
    content: "";
    position: absolute;
    inset: 0;
    border-radius: inherit;
    background: linear-gradient(160deg, rgba(170, 26, 211, 0.18), rgba(21, 26, 33, 0.35));
    pointer-events: none;
}

/* Product screenshots, which are NOT photography and must not use .fs-media.
   A screenshot has no subject to crop toward: object-fit:cover against a fixed
   aspect-ratio slices the sidebar or the table off the edge, and a capture with
   its chrome cut away stops reading as a product. So the intrinsic ratio is
   kept and the frame adapts instead. */
.fs-shot {
    display: block;
    width: 100%;
    height: auto;
}

.fs-shot-frame {
    border: 1px solid var(--fs-line);
    border-radius: var(--bs-border-radius, 0.375rem);
    overflow: hidden;
    background: var(--fs-surface);
    /* Lifts the capture off the section background: both are near-white, and
       without the edge the screenshot bleeds into the page. */
    box-shadow: 0 1px 2px rgba(21, 26, 33, 0.04), 0 8px 24px rgba(21, 26, 33, 0.06);
}

/* The frame is a <button>, so the UA's button styling has to be undone. Kept
   as a button rather than a div because enlarging is the only way to read
   these captures - that is functionality, not decoration, and it has to be
   reachable by keyboard. */
.fs-shot-btn {
    display: block;
    width: 100%;
    padding: 0;
    position: relative;
    cursor: zoom-in;
    transition: box-shadow 0.18s ease, transform 0.18s ease;
}

.fs-shot-btn:hover,
.fs-shot-btn:focus-visible {
    box-shadow: 0 2px 6px rgba(21, 26, 33, 0.08), 0 14px 36px rgba(21, 26, 33, 0.12);
    transform: translateY(-2px);
}

.fs-shot-btn:focus-visible {
    outline: 2px solid var(--fs-accent);
    outline-offset: 3px;
}

/* Affordance: without it the captures read as flat images and nobody discovers
   the lightbox. Fades up on hover, but stays permanently visible on touch,
   where there is no hover to reveal it. */
.fs-shot-zoom {
    position: absolute;
    right: 10px;
    bottom: 10px;
    width: 30px;
    height: 30px;
    display: flex;
    align-items: center;
    justify-content: center;
    border-radius: 50%;
    background: rgba(21, 26, 33, 0.72);
    color: #fff;
    font-size: 15px;
    line-height: 1;
    opacity: 0;
    transition: opacity 0.18s ease;
}

.fs-shot-btn:hover .fs-shot-zoom,
.fs-shot-btn:focus-visible .fs-shot-zoom { opacity: 1; }

@media (hover: none) {
    .fs-shot-zoom { opacity: 1; }
}

/* --------------------------------------------------------------------------
   12b. Screenshot lightbox
   -------------------------------------------------------------------------- */
.fs-lightbox {
    position: fixed;
    inset: 0;
    z-index: 1080;              /* above the sticky navbar */
    display: flex;
    align-items: center;
    justify-content: center;
    padding: 4vh 4vw;
    background: rgba(13, 16, 21, 0.86);
    /* The backdrop is the close target, so it must cover the whole viewport
       and sit behind the image without any gap that would swallow a tap. */
    cursor: zoom-out;
    opacity: 0;
    transition: opacity 0.18s ease;
}

.fs-lightbox.is-open { opacity: 1; }

.fs-lightbox img {
    display: block;
    max-width: 100%;
    max-height: 82vh;
    width: auto;
    height: auto;
    border-radius: var(--bs-border-radius, 0.375rem);
    box-shadow: 0 24px 60px rgba(0, 0, 0, 0.5);
    cursor: default;            /* clicking the image itself must NOT close */
}

.fs-lightbox figcaption {
    margin-top: 0.85rem;
    text-align: center;
    color: rgba(255, 255, 255, 0.85);
    font-size: 0.95rem;
    cursor: default;
}

.fs-lightbox-inner {
    max-width: 100%;
    /* Shrink-wraps the image so the backdrop starts exactly at its edge -
       "click outside the frame" has to mean the visible frame, not a padded
       box extending past it. */
    display: inline-block;
}

.fs-lightbox-close {
    position: absolute;
    top: 14px;
    right: 18px;
    width: 42px;
    height: 42px;
    border: 0;
    border-radius: 50%;
    background: rgba(255, 255, 255, 0.12);
    color: #fff;
    font-size: 24px;
    line-height: 1;
    cursor: pointer;
}

.fs-lightbox-close:hover { background: rgba(255, 255, 255, 0.22); }

.fs-lightbox-close:focus-visible {
    outline: 2px solid #fff;
    outline-offset: 2px;
}

/* Set while the lightbox is open, so the page behind cannot scroll under it. */
body.fs-lightbox-open { overflow: hidden; }

@media (prefers-reduced-motion: reduce) {
    .fs-lightbox,
    .fs-shot-btn,
    .fs-shot-zoom { transition: none; }
    .fs-shot-btn:hover { transform: none; }
}

/* --------------------------------------------------------------------------
   13. Flow steps  (ordered sequence, not a card row)
   -------------------------------------------------------------------------- */
.fs-flow { counter-reset: fsflow; }

.fs-flow-step {
    position: relative;
    padding: 2.25rem 1.5rem 0 0;
}

/* The connecting rule, drawn behind the dots. */
.fs-flow-step::before {
    content: "";
    position: absolute;
    top: 11px;
    left: 0;
    right: 0;
    height: 2px;
    background: var(--fs-line);
}

.fs-flow-step:last-child::before { right: 1.5rem; }

.fs-flow-dot {
    position: absolute;
    top: 4px;
    left: 0;
    width: 16px;
    height: 16px;
    border-radius: 50%;
    background: var(--fs-accent);
    box-shadow: 0 0 0 4px var(--fs-surface);
}

.fs-flow-no {
    display: block;
    font-family: "Montserrat", sans-serif;
    font-style: italic;
    color: var(--fs-accent);
    margin-bottom: 0.35rem;
}

.fs-flow-step h5 { margin-bottom: 0.4rem; }
.fs-flow-step p  { font-size: 0.925rem; margin: 0; }

.fs-flow-dark .fs-flow-step::before { background: rgba(255, 255, 255, 0.16); }

/* Brand purple on the dark ground measures 3.16:1 - fine for a graphic, too low
   for the small step number. Use the 50% tint (7.34:1) for the text. */
.fs-flow-dark .fs-flow-no { color: var(--fs-accent-tint); }
.fs-flow-dark .fs-flow-dot { box-shadow: 0 0 0 4px var(--fs-dark); }

@media (max-width: 767.98px) {
    .fs-flow-step { padding: 0 0 1.75rem 2.25rem; }
    .fs-flow-step::before { top: 0; bottom: 0; left: 7px; right: auto; width: 2px; height: auto; }
    .fs-flow-dot { top: 2px; }
}
