/* Reachype brand layer over Spike's compiled stylesheet.
 *
 * WHY THIS IS A SECOND FILE AND NOT AN EDIT TO styles.css
 * `/spike/styles.css` is the bought template's own build output, copied
 * verbatim except for two changes it could not survive without (the
 * fonts.googleapis.com @import, which this site's CSP forbids, and the
 * relative asset paths). Every DESIGN deviation lives here instead, so
 * "what did we change" is one 200-line file rather than a diff against
 * 746 KB of minified vendor CSS.
 *
 * Loaded AFTER styles.css. Neither file is inside a cascade layer, so for the
 * `!important` utilities the vendor writes (`.text-primary`, `.bg-primary`,
 * `.text-muted`) equal specificity is broken by SOURCE ORDER and this file
 * wins. That is the whole mechanism — do not add a layer to either file.
 *
 * ══ THE TRAP THIS FILE EXISTS TO AVOID ══════════════════════════════════════
 * Spike's primary is a BLUE (#0085db) and blue does three jobs at once: it
 * fills a button, it writes a link, and it tints a subtle surface. Lime does
 * exactly one of them. #d9ed92 on white measures 1.27:1 — it is a surface you
 * put navy on, and it can never be ink on white.
 *
 * So the primary is split, the same way src/styles/global.css splits it:
 *
 *   --bs-primary        the lime FILL      (btn-primary, bg-primary, badges)
 *   --rc-accent-ink     the accent as INK  (#46660f deep olive, 6.6:1 on white)
 *   --bs-primary-bg-subtle   the lime WASH (#f4fade — the banner band)
 *
 * `.text-primary` and `.link-primary` are re-pointed at the ink below. Leaving
 * them on `--bs-primary-rgb` — which is what the vendor does — would put
 * 1.27:1 text on the page in nine places and none of them would error.
 *
 * --rc-accent-ink FLIPS on dark surfaces, because on near-black the lime IS
 * legible ink (and the olive is not). That is a custom-property channel rather
 * than a `.text-primary-on-dark` class for the same reason global.css opens
 * `--mosl-panel`: CSS cannot ask what ground it is standing on, so the ground
 * answers.
 */

:root,
[data-bs-theme='light'] {
  /* ── Accent ───────────────────────────────────────────────────────────── */
  --bs-primary: #d9ed92;
  --bs-primary-rgb: 217, 237, 146;

  /* ⚠ NOT A LIME WASH. Owner's call, 2026-09-01: the page ground is WHITE and
     nothing on it is tinted yellow. Spike's `-bg-subtle` is a pale version of
     its primary, which for a lime primary is a pale yellow — and this token is
     what the hero band, the tab plate, the icon tiles and the marquee chips all
     reach for. Re-pointed at the site's own neutral page grey (`--mosl-band` in
     styles/global.css), so those plates still read as plates and none of them
     is yellow. The accent survives where it belongs: buttons, the recommended
     plan, the small round glyph tiles. */
  --bs-primary-bg-subtle: #f6f9fb;
  --bs-primary-border-subtle: #e6ecf1;
  --bs-primary-text-emphasis: #46660f;

  /* The accent as foreground. #46660f on white = 6.6:1; see the header. */
  --rc-accent-ink: #46660f;
  --rc-accent-hover: #cbe76a;
  --rc-on-accent: #0d1b2a; /* what goes ON the lime */

  /* ── Ink ──────────────────────────────────────────────────────────────── */
  /* Spike's body colour is #707a82, which measures 4.38:1 on white and fails
     AA. reachype-ui darkened its own equivalent token to #5f7182 (4.9:1) long
     ago for exactly this reason; this says the same thing to Bootstrap's. */
  --bs-body-color: #5f7182;
  --bs-body-color-rgb: 95, 113, 130;
  --bs-heading-color: #0d1b2a;
  --bs-emphasis-color: #0d1b2a;

  /* Restated as literals rather than left as rgba(#707a82, α): the vendor bakes
     the old body colour into both, so overriding --bs-body-color alone leaves
     every `.text-muted` on the page still keyed to the grey it replaced. */
  --bs-secondary-color: rgba(13, 27, 42, 0.62); /* 6.0:1 on white */
  --bs-tertiary-color: rgba(13, 27, 42, 0.45);

  /* ── Near-black ───────────────────────────────────────────────────────── */
  /* Neutral, not Spike's navy #111c2d. With no blue cast the lime is the only
     hue on a dark surface, which is what makes it read as the accent — the same
     call global.css and reachype-ui both made. */
  --bs-dark: #0f1113;
  --bs-dark-rgb: 15, 17, 19;
  --bs-dark-bg-subtle: #16181b;

  /* ── Links ────────────────────────────────────────────────────────────── */
  --bs-link-color: #0d1b2a;
  --bs-link-color-rgb: 13, 27, 42;
  --bs-link-hover-color: #46660f;

  /* ── Structure ────────────────────────────────────────────────────────── */
  --bs-border-color: rgb(13 27 42 / 10%);

  /* Focus must clear 3:1 against whatever it rings (WCAG 2.4.11). Lime does
     that on dark and fails it on white, so the ring is navy on both. */
  --bs-focus-ring-color: rgba(13, 27, 42, 0.28);

  /* ── Type ─────────────────────────────────────────────────────────────── */
  /* Self-hosted from @fontsource-variable — the vendor's fonts.googleapis.com
     @import was removed from styles.css because the CSP has no external origin
     in style-src or font-src. Same family, same weights, one origin. */
  --bs-font-sans-serif: 'Plus Jakarta Sans Variable', 'Plus Jakarta Sans', system-ui, sans-serif;
}

/* ══ THE INK CHANNEL ═════════════════════════════════════════════════════════
   Dark grounds re-declare the accent-as-ink for their own subtree. `.bg-dark`
   covers the two dark sections and the footer; `.btn-dark` is here because a
   dark BUTTON is a dark ground too and its label follows the same rule. */
.bg-dark,
.btn-dark,
.text-bg-dark {
  --rc-accent-ink: #d9ed92;
}

/* ══ THE RE-POINTED UTILITIES ════════════════════════════════════════════════
   Both are `!important` in the vendor and both are `!important` here; same
   specificity, later file wins. */
.text-primary {
  color: var(--rc-accent-ink) !important;
}

.link-primary {
  color: var(--rc-accent-ink) !important;
  text-decoration-color: var(--rc-accent-ink) !important;
}

/* Bootstrap hard-codes `.link-primary:hover` as a literal `RGBA(0,106,175,…)`
   — a shade of Spike's blue that no custom property reaches. Specificity
   (0,2,1) against its (0,2,0). */
a.link-primary:hover,
a.link-primary:focus-visible {
  color: var(--rc-accent-ink) !important;
}

/* ⚠ THE ORDER OF THE NEXT THREE RULES IS THE WHOLE POINT.
   In Spike's own sheet the text utilities come AFTER `.link-primary`, so
   `class="text-dark link-primary"` — which is what the template writes on every
   nav item and every footer link — renders dark at rest and only turns primary
   on hover. Re-declaring `.link-primary` above put it last and inverted that:
   measured, the nav came out olive (#46660f) and every footer link came out
   lime instead of light. Restating the three utilities here restores the
   template's intent, and leaves the override doing what it was actually needed
   for — a BARE `.link-primary`, which would otherwise be #d9ed92 on white. */
.text-dark {
  color: rgba(var(--bs-dark-rgb), var(--bs-text-opacity, 1)) !important;
}

.text-light {
  color: rgba(var(--bs-light-rgb), var(--bs-text-opacity, 1)) !important;
}

.text-white {
  color: rgba(255, 255, 255, var(--bs-text-opacity, 1)) !important;
}

/* ══ THE TAB RAIL ════════════════════════════════════════════════════════════
   `.nav-pills` declares `--bs-nav-pills-link-active-color: #fff` as a literal,
   so re-pointing `--bs-primary` at the lime turned the selected tab into white
   text on #d9ed92. Measured: 1.27:1. It reads as a blank pill.

   The token, not the `color` property, because that is what
   `.nav-pills .nav-link.active` actually resolves — and because a page that
   later uses a second pill rail gets the fix for free. */
.nav-pills {
  --bs-nav-pills-link-active-color: #0d1b2a;
}

/* …and the token is not enough, because `.tabs-pills` — the rail this page
   actually uses — restates both states with LITERAL variables that no theme
   reaches:

     .tabs-pills .nav-item .nav-link.active { color: var(--bs-body-bg) }  → #fff
     .tabs-pills .nav-item .nav-link:hover  { color: var(--bs-primary) }  → lime

   White on lime measured 1.27:1 (a blank-looking pill), and the hover put the
   lime on its own wash, which is 1.1:1 — the label disappears while you point
   at it. Both selectors are (0,3,0), so these have to match rather than rely on
   a variable. */
.tabs-pills .nav-item .nav-link.active {
  color: var(--rc-on-accent);
}

.tabs-pills .nav-item .nav-link:hover {
  color: var(--rc-accent-ink);
}

/* ══ BUTTONS ═════════════════════════════════════════════════════════════════
   The vendor's `.btn-primary` only restates bg/border, inheriting a white label
   from Bootstrap's own compiled block. White on lime is 1.27:1, so every token
   the variant owns is restated here. */
.btn-primary {
  --bs-btn-color: var(--rc-on-accent);
  --bs-btn-bg: #d9ed92;
  --bs-btn-border-color: #d9ed92;
  --bs-btn-hover-color: var(--rc-on-accent);
  --bs-btn-hover-bg: var(--rc-accent-hover);
  --bs-btn-hover-border-color: var(--rc-accent-hover);
  --bs-btn-active-color: var(--rc-on-accent);
  --bs-btn-active-bg: var(--rc-accent-hover);
  --bs-btn-active-border-color: var(--rc-accent-hover);
  --bs-btn-disabled-color: var(--rc-on-accent);
  --bs-btn-disabled-bg: #d9ed92;
  --bs-btn-disabled-border-color: #d9ed92;
  --bs-btn-focus-shadow-rgb: 13, 27, 42;
}

.btn-outline-primary {
  --bs-btn-color: var(--rc-accent-ink);
  --bs-btn-border-color: var(--rc-accent-ink);
  --bs-btn-hover-color: var(--rc-on-accent);
  --bs-btn-hover-bg: #d9ed92;
  --bs-btn-hover-border-color: #d9ed92;
  --bs-btn-active-color: var(--rc-on-accent);
  --bs-btn-active-bg: #d9ed92;
  --bs-btn-active-border-color: #d9ed92;
  --bs-btn-focus-shadow-rgb: 13, 27, 42;
}

.btn-dark {
  --bs-btn-bg: #0f1113;
  --bs-btn-border-color: #0f1113;
  --bs-btn-hover-bg: #212427;
  --bs-btn-hover-border-color: #212427;
  --bs-btn-active-bg: #212427;
  --bs-btn-active-border-color: #212427;
  --bs-btn-disabled-bg: #0f1113;
  --bs-btn-disabled-border-color: #0f1113;
}

/* ══ ANYTHING FILLED WITH THE ACCENT ═════════════════════════════════════════
   `bg-primary` was a saturated blue in the template and everything on it was
   white. It is a pale lime here, so the ink is navy.
   The two full-bleed `bg-primary` BANDS are gone — the page ground is white
   (owner's call, 2026-09-01) — and what is left are small round fills, but the
   rule stays as the safety net for the next `bg-primary` someone writes:
   white-on-lime is invisible rather than merely ugly. */
.bg-primary {
  color: #0d1b2a;
}

.bg-primary .text-white,
.bg-primary .text-light {
  color: #0d1b2a !important;
}

.bg-primary a.text-white {
  color: #0d1b2a !important;
}

/* ══ THE MEASURE ═════════════════════════════════════════════════════════════
   ⚠ THE TEMPLATE'S GUTTERS COME FROM A RUNTIME ATTRIBUTE, and losing them is
     silent. Spike's own page measures 1300px and sits centred, which at 1440
     leaves 82px either side; ours was running edge to edge with 12px of gutter,
     because the rule is

         html[data-boxed-layout=boxed] .container-fluid { max-width: 1300px }

     and nothing in the markup sets that attribute — `assets/js/theme/app.init.js`
     writes it at runtime from `userSettings.BoxedLayout: true`, and this page
     does not ship the theme scripts (they exist to drive a sidebar and a colour
     switcher it has neither of).

   Declared here rather than by adding the attribute: `data-boxed-layout` would
   also pull in the sidebar and page-wrapper rules that go with it, and
   `data-color-theme="Blue_Theme"` — the attribute the template pairs it with —
   re-declares `--bs-primary` at a specificity this file cannot beat, i.e. it
   would put Spike's blue back. One rule, no attributes. */
.container-fluid {
  max-width: 1300px;
}

/* ══ THE DOCUMENT ════════════════════════════════════════════════════════════
   Two rules the rest of the site gets from `styles/global.css`, which this page
   deliberately does not load — so they came off with it.

   `scroll-padding-top` is not cosmetic: MEASURED, following the skip link put
   `<main id="content">` at viewport top 0 with the pinned header covering its
   first 78px. A skip link that lands the reader under the header is the one
   control on the page that exists purely for keyboard users, not working for
   them. 6rem is global.css's own value and clears the 79px bar.

   `scroll-behavior` matches the rest of the site, with global.css's
   reduced-motion guard — a 6,000px smooth scroll is exactly what that media
   query is for. */
html {
  scroll-padding-top: 6rem;
  scroll-behavior: smooth;
}

@media (prefers-reduced-motion: reduce) {
  html {
    scroll-behavior: auto;
  }
}

/* ══ THE STICKY HEADER ═══════════════════════════════════════════════════════
   The template pins the bar by switching it to `position: fixed` at scrollY 60,
   with nothing reserving the space it leaves. MEASURED on this page: the
   document loses 79px the instant you start scrolling and the h1 jumps 78px up
   the page, under the reader's cursor, on the first flick of the wheel.

   `sticky` does the same job and keeps the box in flow, so there is nothing to
   compensate for. It is declared on the resting state as well as on
   `.header-sticky`, which means the position never changes at all — the class
   is left doing only what it should have been doing, i.e. turning the bar white
   and giving it a shadow.

   Verified sticky is not defeated here: nothing in the vendor sheet puts
   `overflow` on `html` or `body`, and the header is a SIBLING of
   `.main-wrapper` (which is `overflow-hidden`), not a child of it. */
.header-fp,
.header-fp.header-sticky {
  position: sticky;
  top: 0;
  z-index: 9;
}

/* ══ BRAND MARKS ═════════════════════════════════════════════════════════════
   Sized by class, not by a `style` attribute: this site's CSP has no
   'unsafe-inline' in style-src, so an inline style is silently dropped and the
   image renders at its intrinsic 176×192. Every dimension on this page that is
   not a Bootstrap utility lives here. */
.rc-mark {
  height: 30px;
  width: auto;
}

.rc-mark-sm {
  height: 20px;
  width: auto;
}

.rc-footer-lockup {
  height: 64px;
  width: auto;
}

/* ══ THE CHANNEL GLYPH ROW ═══════════════════════════════════════════════════
   The template's row of five white tiles carried framework logos as <img>.
   These are inline SVG from `simple-icons` (the same source the rest of the
   site draws them from), so they take a `currentColor`-free brand hex and cost
   no extra request. */
.rc-glyph {
  width: 26px;
  height: 26px;
  display: block;
}

/* ══ THE MARQUEE ═════════════════════════════════════════════════════════════
   The vendor animates `.slide-animation1/2` by translating a fixed -2086px,
   which only loops seamlessly for a row whose repeat period is exactly that
   wide. Its own rows are not (5 unique chips × 298px = 1490px), so the belt
   visibly jumps every 25 seconds.

   These translate -50% instead, over a track whose content is rendered TWICE.
   That is seamless for any number of chips and any chip width, which is what
   lets the rows carry the real capability list rather than a list padded to fit
   a magic number.

   The vendor rules are `.slide-animation1{animation:…}`; these are the same
   specificity in a later file, so they win on source order. */
.slide-animation1,
.slide-animation2 {
  width: max-content;
}

.slide-animation1 {
  animation: rc-marquee 60s linear infinite;
}

.slide-animation2 {
  animation: rc-marquee-rev 60s linear infinite;
}

@keyframes rc-marquee {
  from {
    transform: translate3d(0, 0, 0);
  }
  to {
    transform: translate3d(-50%, 0, 0);
  }
}

@keyframes rc-marquee-rev {
  from {
    transform: translate3d(-50%, 0, 0);
  }
  to {
    transform: translate3d(0, 0, 0);
  }
}

/* An infinite belt is exactly what this media query exists for, and the vendor
   has no guard at all. */
@media (prefers-reduced-motion: reduce) {
  .slide-animation1,
  .slide-animation2 {
    animation: none;
    transform: none;
  }
}

/* ══ THE STUDIO / WORKSPACE CARDS ════════════════════════════════════════════
   `.meet-our-team` is the template's team photo with a caption that slides up
   on hover. The mechanic crosses over; what it holds is a product screenshot
   rather than a person, so the plate needs a fixed shape (a screenshot is a
   different aspect ratio from a portrait) and the caption has to be readable
   before the hover — a caption a touch device never sees is not a caption.

   `object-fit: cover` with a top anchor: an app screenshot's meaning is at the
   top of the frame, so a centre crop throws away the header and keeps the
   whitespace. */
/* ⚠ `height: auto` IS LOAD-BEARING, and its absence is silent.
   `<img width="600" height="800">` — the attributes that reserve the box and
   keep CLS at zero — also set a PRESENTATIONAL HINT of `height: 800px`. A hint
   loses to any author rule that names `height`, and there was none here, so it
   applied and `aspect-ratio` was ignored: the plate measured 336×800 (0.42)
   instead of 336×448, and `object-fit: cover` then threw away a third of every
   screenshot's width. Nothing errors; the pictures just come out cropped. */
.rc-shot {
  aspect-ratio: 4 / 3;
  width: 100%;
  height: auto;
  object-fit: cover;
  object-position: top center;
  display: block;
}

.rc-shot-tall {
  aspect-ratio: 3 / 4;
}

/* The tab panel's screenshot. Same trap: measured 696×521 against a natural
   1280×1042, i.e. stretched 9% wide, because the `height` attribute survived
   `.w-100`. */
.rc-tab-shot {
  width: 100%;
  height: auto;
  display: block;
}

/* The template's card sits at `bottom: -100%` and rises on hover. On a touch
   device there is no hover, so it starts visible here and the hover only lifts
   it clear of the bottom edge. */
.meet-our-team .leadership-card {
  bottom: 0;
}

/* ══ THE HERO PLATE ══════════════════════════════════════════════════════════
   `.banner-image` is `min-width: 1300px` in the vendor — that overflow off the
   right edge IS the composition, and the section's `overflow-hidden` is what
   makes it read as a plate the page is cropping rather than a picture pasted
   on. Kept; only the height is relaxed, because our capture is 3:2 rather than
   the vendor collage's 1158×753. */
.banner-image img {
  width: 1158px;
  max-width: none;
  height: auto;
}

/* ══ THE FLOATING DECOR ON THE CLOSING BAND ══════════════════════════════════
   Two screenshots clipped by the band's edges, exactly as the template does it.
   `aria-hidden` in the markup; everything else is here.

   ⚠ THE TEMPLATE'S OWN VERSION COVERS ITS HEADING, and on this page it made the
     h2 unreadable. Its plates are `position-absolute start-0` / `end-0` inside a
     band whose text is a STATIC `col-lg-9` — so the absolutely positioned
     siblings paint on top of the words. In Spike's screenshot the collision is
     hidden by a saturated blue ground and pale decor; ours is a pale band under
     dark ink and the heading simply disappeared behind two app screenshots.

     Two changes, both minimal: the text column is lifted onto its own stacking
     position, and the plates are pulled outward so only an edge shows — which
     is what "floating past the band" was meant to look like in the first
     place. */
.bg-primary .container-fluid {
  position: relative;
  z-index: 1;
}

.rc-decor-l,
.rc-decor-r {
  z-index: 0;
}

/* Measured, not guessed: at 1440 the text column (col-lg-9 of the 1300px
   measure) spans 232→1207, and a 460px plate flush to the page edge reaches
   284. 15rem pulls its inner edge back to 220, i.e. clear of the words with
   12px to spare — the plates read as something the band is cropping rather
   than as something sitting on the heading. */
.rc-decor-l {
  margin-left: -15rem;
}

.rc-decor-r {
  margin-right: -15rem;
}

.rc-decor {
  width: 460px;
  max-width: 40vw;
  height: auto;
  border-radius: 16px;
  box-shadow: 0 24px 48px -24px rgba(13, 27, 42, 0.35);
}

/* The staleness notice above the plan ladder. Measure-limited so it reads as a
   note rather than as a full-width banner. */
.rc-stale {
  max-width: 44rem;
}

/* ══ SCROLL-TO-TOP ═══════════════════════════════════════════════════════════
   The vendor's `.top-btn` rests at `opacity: 0` and homepage.js animated it by
   writing `topBtn.style.opacity` on every scroll event. A class is what that
   should have been anyway (one paint instead of a write per frame), and it is
   also the only version that survives a CSP with no 'unsafe-inline' if the
   toggle ever moves to setAttribute. `visibility` is what makes the invisible
   button unclickable — opacity alone leaves a 54px hit target over the corner
   of the page. */
.top-btn {
  visibility: hidden;
}

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

/* ══ FOCUS ═══════════════════════════════════════════════════════════════════
   Spike's `.nav-link:focus-visible` draws a blue halo from
   `--bs-focus-ring-color`, which is set above; the accordion and pill buttons
   take the same token, so nothing else is needed here. What IS needed is a
   visible ring on the dark surfaces, where a navy ring on near-black is not a
   ring at all. */
.bg-dark :focus-visible,
footer :focus-visible {
  outline: 2px solid #d9ed92;
  outline-offset: 2px;
}
