/* ==========================================================================
   Index page — Section 1 (hero/banner sequence), Section 3 (globe embed),
   Section 5 (case-study card), Section 6 (CTA row).
   ========================================================================== */

/* -- Section 1 Part 1: Hero text ---------------------------------------------
   Normal scrolling block, no pinning — the text scrolls away with the page
   like any other content. Nothing here is JS-controlled. */
.hero-part1 {
  position: relative; /* own stacking context, so this reliably paints above the fixed star (z-index:1) regardless of DOM order */
  z-index: 2;
  min-height: 100vh;
  display: flex;
  align-items: center;
  justify-content: center;
  text-align: center;
}
.hero__content { display: flex; flex-direction: column; align-items: center; gap: var(--space-27); }
.hero-subtitle {
  /* SEO plan "Developer Note — Hero H2 Implementation": real <h2>, styled
     as a quiet subtitle, distinct from every other H2 on the page. */
  font-family: var(--font-family-base);
  font-weight: var(--font-weight-regular);
  font-size: var(--font-size-link);
  line-height: var(--line-height-link);
  color: var(--color-text-primary);
}
/* H4 + H2 wrap within 9 of the site's 12 grid columns on desktop (per
   client), unconstrained on mobile. 849px = 9 columns + 8 gutters at the
   same column/gutter math used for the banner text column (73px column,
   24px gutter, BRANDING.md §5): 9*73 + 8*24 = 849. H1 is unconstrained —
   its line breaks are hand-placed in XINSIGHT_COPY.md instead. */
.hero__content .text-h4,
.hero__content .hero-subtitle {
  max-width: 849px;
}
@media (max-width: 767px) {
  .hero__content .text-h4,
  .hero__content .hero-subtitle {
    max-width: none;
  }
}

/* -- Hero star ----------------------------------------------------------------
   `position: fixed`, not part of either Part 1 or Part 2's document flow —
   this is what lets it persist visually across the Part 1 → Part 2
   transition. Anchored by its own CENTER (translate(-50%,-50%)), not its
   top edge, so js/index.js can animate `top` as "where the star's center
   sits" directly — 150px initially, interpolating to the viewport's
   vertical center as Part 1 scrolls past. This is the star's own
   independent animation (driven by scroll progress as a timing input),
   not literally attached to the text's own scroll motion — the text
   scrolls via normal document flow, the star's position/size/opacity are
   all explicit JS-driven values on this separate fixed element. Values
   hold once Part 1 has fully scrolled by (see that file's comments), and
   the element is hidden once the user scrolls past Part 2 into Section 2.
   z-index sits below both Part 1 and Part 2's content. */
.hero-star {
  position: fixed;
  top: 150px;
  left: 50%;
  z-index: 1;
  width: 45px;
  height: 45px;
  transform: translate(-50%, -50%);
  opacity: 0;
  pointer-events: none;
  /* Sized via width/height (set per-frame by js/index.js), not
     transform:scale() — scaling a small element up dramatically via
     transform gets rasterized at its small starting size by the browser
     and stretched, which blurs even vector SVG content. Changing the
     actual box size forces the SVG to re-render natively at each size
     instead. */
}
.hero-star svg { width: 100%; height: 100%; display: block; }
.hero-star path {
  fill: var(--raw-sun-orange-500);
  transition: fill var(--duration-slow) var(--ease-in-out);
}
.hero-star.is-grown path {
  fill: var(--raw-catfish-500);
}

/* -- Section 1 Part 2: pinned banner sequence ---------------------------------
   Scroll budget: 4 units (one per banner) = 4 * 100vh. Matches BANNER_COUNT
   in js/index.js — keep in sync if the banner count ever changes. */
.banner-sequence {
  position: relative;
  z-index: 2; /* above the fixed star, same reasoning as .hero-part1 */
  height: 400vh;
}
.banner-sequence__sticky {
  position: sticky;
  top: 0;
  height: 100vh;
  overflow: hidden;
  background: transparent; /* transparent until .is-backdrop-ready — the fixed star (behind, mid-grow/color-shift) shows through until then */
}
/* Takes over as Part 2's real backdrop once the star has fully grown and
   finished its color crossfade (js/index.js, timed to BRANDING.md §7's
   700ms crossfade) — at that exact moment the star and this background are
   the same catfish-500 color, so the handoff is invisible. From here on
   this background is part of the sticky content itself, not a separate
   fixed element, so it scrolls away in perfect sync with its own content
   when the sticky releases — this is what fixes the dead-zone gap an
   earlier version had (the fixed star and the sticky release weren't
   synchronized, leaving a blank stretch where content had scrolled away
   but the star's backdrop hadn't caught up yet). */
.banner-sequence__sticky.is-backdrop-ready {
  background: var(--color-bg-alt);
}

.banner-sequence__banners {
  position: absolute;
  inset: 0;
}
/* Staged enter/exit (confirmed with client): text fades/slides in first,
   then the Lottie fades in only once the text has settled; on advance, text
   and Lottie fade/slide out together, and the NEXT banner's text only
   starts entering once that exit has fully finished — never overlapping.
   js/index.js drives this as an explicit timed state machine (.is-visible
   toggled on .banner__text and .banner__lottie independently, not a single
   opacity/transform pair on .banner itself like an earlier version had). */
.banner {
  position: absolute;
  inset: 0;
  display: flex;
  /* Top-anchored with a fixed headroom, NOT vertically centered — Figma MCP
     (node 2825:663) confirmed `items-start` + `padding-top: 150px` as the
     starting point; bumped to 175px per client adjustment. Centering was
     the earlier approach, but it anchors on the text block's own height,
     which varies with body-copy length per banner (banner 4's longer
     paragraph vs. banner 1's shorter one), so the heading landed at a
     different Y position each time. A fixed offset keeps every banner's
     content starting at the same spot regardless of how much text it has. */
  align-items: flex-start;
  padding-top: 175px;
  pointer-events: none;
}
.banner.is-active {
  pointer-events: auto;
}
.banner__inner {
  display: grid;
  /* 367px confirmed via Figma MCP (node 2825:664) — ~4 of the site's 12
     grid columns (BRANDING.md §5), not a 50/50 split with the Lottie.
     Signed-off desktop value — kept exactly for ≥992px; see the tablet
     override below for why it doesn't hold at narrower widths. */
  grid-template-columns: 367px 1fr;
  /* Top-aligned to match .banner above — text and Lottie both start at the
     same fixed Y position rather than the Lottie centering itself against
     the text column's (variable) height. */
  align-items: flex-start;
  gap: var(--space-60);
}
/* Tablet (768–991px): the fixed 367px text column doesn't shrink with the
   viewport, so at tablet widths it eats a disproportionate share of a
   much narrower row than the desktop container it was sized for — the
   Lottie column was measured shrinking to 261×196px at 768px (vs. its
   intended 480×360px), well under a legible/proportionate size. This
   only touches the grid split, not the pinned/scroll-jacked sequence
   itself or the star's visibility — those are an intentional mobile-only
   (≤767px) decision made separately (per client) and out of scope here;
   this is purely a column-proportion fix. */
@media (min-width: 768px) and (max-width: 991px) {
  .banner__inner { grid-template-columns: 1fr 1fr; gap: var(--space-40); }
}
.banner__text,
.banner__lottie {
  opacity: 0;
  transform: translateY(var(--reveal-translate-y));
  transition: opacity var(--duration-standard) var(--ease-out),
              transform var(--duration-standard) var(--ease-out);
}
.banner__text.is-visible,
.banner__lottie.is-visible {
  opacity: 1;
  transform: translateY(0);
}
/* Exit target is distinct from the entrance start — slides UP while fading
   out (confirmed with client), not just reversing back down through the
   entrance position. */
.banner__text.is-exiting,
.banner__lottie.is-exiting {
  opacity: 0;
  transform: translateY(calc(-1 * var(--reveal-translate-y)));
}
/* Plain regular-weight white text at Label scale, sentence case as authored
   — confirmed via Figma MCP (node 2825:665): this is NOT the site's bold/
   uppercase/orange "eyebrow" treatment used elsewhere (e.g. Contact's
   [THINK]/[BUILD]/[DELIVER]) — an earlier pass wrongly reused that generic
   .text-eyebrow utility here without checking this instance's actual type
   token, which happens to be Desktop/P_Small_Body, not Label Tag. */
.banner__eyebrow {
  display: block;
  font-family: var(--font-family-base);
  font-weight: var(--font-weight-regular);
  font-size: var(--font-size-link);
  line-height: var(--line-height-link);
  color: var(--color-text-primary);
}
/* Divider line beneath the eyebrow — confirmed via Figma MCP (node
   2825:667, downloaded SVG source): a plain 1px full-width rule,
   text/primary-text color (#F0F4F7), not previously implemented at all. */
.banner__eyebrow-rule {
  display: block;
  width: 100%;
  height: 1px;
  background: var(--color-text-primary);
  margin-top: var(--space-12);
  margin-bottom: var(--space-40);
}
.banner__text h3 { margin-bottom: var(--space-18); }
.banner__text p { margin-bottom: var(--space-27); }
.banner__lottie {
  aspect-ratio: 4 / 3; /* was 1/1 — not a Figma/spec requirement, just an earlier default; changed per client */
  max-width: 480px;
  justify-self: end;
}
.banner__lottie svg,
.banner__lottie canvas { width: 100%; height: 100%; }

@media (max-width: 767px) {
  .hero-star { display: none; }
  .banner-sequence {
    height: auto;
    /* The star (hidden on mobile) is what provides Part 2's backdrop on
       desktop — without it this fell through to the page's default black,
       so it needs its own solid fallback. catfish-500 matches the
       "settled" color the star holds throughout Part 2 on desktop, same
       flat treatment as Section 4's bg-alt (no pattern layered on top —
       the pattern's dot color IS catfish-500, so it's invisible there
       anyway, per PROJECT_STATUS.md's "Design follow-up" note). */
    background: var(--color-bg-alt);
  }
  .banner-sequence__sticky { position: static; height: auto; }
  .banner-sequence__banners { position: static; }
  .banner {
    position: static;
    inset: auto;
    pointer-events: auto;
    padding-block: var(--space-40); /* was space-60 — reduced per client */
  }
  .banner__text,
  .banner__lottie {
    opacity: 1;
    transform: none;
  }
  .banner__inner { grid-template-columns: 1fr; gap: var(--space-40); text-align: left; }
  /* Center-aligned per client, not left-aligned like the text above it.
     max-width:100% (was a fixed 320px cap) so it actually fills the
     available width instead of reading as too small on narrow screens. */
  .banner__lottie { justify-self: center; max-width: 100%; }
  /* Small button size on mobile (matches .btn--small's own values — see
     css/base.css — applied here via override rather than swapping classes,
     since this is purely a responsive style change, not a different
     component instance). */
  .banner__cta {
    padding: var(--space-8) var(--space-18);
    min-width: 120px;
  }
}

/* Same fallback as the mobile block above, applied via JS
   (js/index.js) when `prefers-reduced-motion: reduce` — the pinned
   scroll-scrubbed sequence is exactly the kind of scroll-driven movement
   BRANDING.md §7's reduced-motion policy says to drop, so it's disabled
   entirely rather than just slowed down, same treatment as mobile. Hero
   text needs no such fallback — it was never JS-animated to begin with. */
.banner-sequence.is-static-fallback {
  height: auto;
  background: var(--color-bg-alt); /* star's animation setup is skipped entirely in this mode, so it never gets shown — same gap and same fix as the mobile block above */
}
.banner-sequence.is-static-fallback .banner-sequence__sticky { position: static; height: auto; }
.banner-sequence.is-static-fallback .banner-sequence__banners { position: static; }
.banner-sequence.is-static-fallback .banner {
  position: static;
  inset: auto;
  pointer-events: auto;
  padding-block: var(--space-40); /* was space-60 — reduced per client, matches the mobile block above */
}
.banner-sequence.is-static-fallback .banner__text,
.banner-sequence.is-static-fallback .banner__lottie {
  opacity: 1;
  transform: none;
}

/* -- Section 4: "New Way of Working" ------------------------------------------- */
/* The only real scroll/anchor target on the site — all 4 Part 2 banner CTAs
   link here (`href="#new-way-of-working"`). Native fragment navigation (no
   JS intercepts this click) scrolls the section's own top edge flush to the
   viewport top, which sits directly behind the ~122px fixed header — this
   `scroll-margin-top` adds clearance for that jump specifically, decoupled
   from the section's own (now much smaller, 2026-07-25) padding-top, which
   just governs ordinary scroll-through rhythm now. Reuses the same 150px
   value as `.section--page-header`'s clearance (css/base.css) rather than
   inventing a new number. */
#new-way-of-working {
  scroll-margin-top: 150px;
}

/* -- Section 5: Case-study card ----------------------------------------------- */
.text-heavy-row--case-study .text-heavy-row__col:first-child { padding-top: 0; }
.case-study-card {
  grid-column: span 2;
  background: var(--color-bg-alt);
  padding: var(--space-40);
}
.case-study-card__intro { margin-bottom: var(--space-40); text-align: center; }
.case-study-card__intro .text-eyebrow { display: block; margin-top: var(--space-8); color: var(--color-text-primary); }
.case-study-card__panel {
  display: grid;
  grid-template-columns: 240px 1fr;
  background: var(--color-bg-light);
}
.case-study-card__image { width: 100%; height: 100%; object-fit: cover; }
.case-study-card__text { padding: var(--space-40); color: var(--color-text-secondary); }
.case-study-card__title { color: var(--color-text-secondary); margin-bottom: var(--space-12); }
.case-study-card__cta { margin-top: var(--space-27); }

/* Panel image/text split stacks a full breakpoint earlier than the row
   collapse below (991px vs. 767px) — at tablet the card still sits span-2
   beside the row's text column (data-driven layout, unchanged), but that
   only leaves ~1fr-240px for the text side, squeezing the "Read More"
   button into wrapping onto two lines (confirmed at 768–850px). Stacking
   image-over-text here gives the button its full card width instead. */
@media (max-width: 991px) {
  .case-study-card__panel { grid-template-columns: 1fr; }
}
@media (max-width: 767px) {
  .case-study-card { grid-column: span 1; }
}

/* -- Section 6: CTA buttons ---------------------------------------------------- */
/* Removes the generic .section class's top padding (200px, base.css) —
   per client, no gap wanted above the CTA row. Bottom padding untouched. */
.section--cta-buttons {
  padding-top: 0;
}
.cta-buttons {
  display: flex;
  gap: var(--space-60);
}
@media (max-width: 767px) {
  .cta-buttons { flex-direction: column; gap: var(--space-18); }
}

/* ==========================================================================
   Section 3 — Interactive globe (section3_interactive/earth.html, styles
   copied in as-is per WEBSITE_SPECIFICATION.md's "embedded inline" note —
   see that file's own SPECIFICATION.md for behavior detail, not duplicated
   here). Only path changes: none needed, these are ID/class selectors.
   ========================================================================== */
/* The generic .section class's bottom padding (200px, base.css) left a
   large empty gap — showing the page's own honeycomb pattern, not the
   globe's solid backdrop — between the bottom of the 75vh globe canvas and
   the end of the section. Removed here since the globe itself already
   carries plenty of visual weight; per client, this space should go away
   rather than just shrink. Top padding is untouched (header text above the
   globe still needs its normal spacing). */
.section--globe {
  padding-bottom: 0;
}
.globe-section {
  position: relative;
  width: 100%;
  height: 75vh;
  overflow: hidden;
  /* Solid backdrop, not the section's own honeycomb dot pattern showing
     through — matches section3_interactive/earth.html's own original
     background-color (#191919) before it was embedded into this page;
     the pattern's dots competing visually with the globe's own wireframe
     dots/pins made the whole area read as cluttered. */
  background: var(--color-bg-page);
}
.globe-section canvas {
  display: block;
  cursor: grab;
  touch-action: none;
}
.globe-section canvas:active { cursor: grabbing; }
#info {
  position: absolute;
  top: 20px;
  left: 50%;
  transform: translateX(-50%);
  pointer-events: none;
  background: rgba(58, 32, 0, 0.7);
  padding: 12px 12px;
  border: 1px solid rgba(255, 158, 30, 0.3);
  text-transform: none;
  font-size: 14px;
  line-height: 1.5;
  letter-spacing: 1px;
  max-width: calc(100% - 32px);
  box-sizing: border-box;
  text-align: center;
  color: var(--raw-sun-orange-500);
}
@media (max-width: 480px) {
  #info { font-size: 10px; padding: 12px 12px; letter-spacing: 0.5px; line-height: 1.5; }
}
#tooltip {
  position: absolute;
  width: auto;
  background: rgba(13, 13, 13, 0.9);
  border: 1px solid #ff9e1e;
  color: #ffffff;
  text-align: center;
  padding: 10px;
  border-radius: 4px;
  pointer-events: none;
  display: none;
  font-size: 14px;
  box-shadow: 0 0 15px rgba(255, 158, 30, 0.4);
  transform: translate(-50%, -130%);
}
.panel {
  position: absolute;
  right: 5%;
  top: 20%;
  width: 35%;
  background: rgba(58, 32, 0, 0.7);
  border: 1px solid rgba(255, 158, 30, 0.3);
  padding: 24px;
  color: white;
  transform: translateX(40px);
  opacity: 0;
  transition: all 0.4s ease;
  pointer-events: none; /* inactive panel must not block globe drag */
  z-index: 2;
}
.panel.active {
  transform: translateX(0);
  opacity: 1;
  pointer-events: auto;
}
.panel-header {
  display: grid;
  grid-template-columns: 44px minmax(0, 1fr) 44px;
  align-items: center;
  gap: var(--space-8);
  margin-bottom: 48px;
}
/* CR-14: match site H3 scale (via .text-h3), not the old fixed 18px. */
.panel-title {
  margin: 0;
  text-align: center;
  color: #F0F4F7;
}
.panel-nav {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 44px;
  height: 44px;
  padding: 0;
  border: 1px solid rgba(255, 158, 30, 0.6);
  background: rgba(13, 13, 13, 0.35);
  color: #F0F4F7;
  font-size: 28px;
  line-height: 1;
  cursor: pointer;
  transition: background var(--duration-fast) var(--ease-out),
              border-color var(--duration-fast) var(--ease-out);
}
.panel-nav:hover,
.panel-nav:focus-visible {
  background: rgba(255, 158, 30, 0.2);
  border-color: #ff9e1e;
}
.panel-tree-connectors {
  position: absolute;
  top: 0; left: 0;
  width: 100%; height: 100%;
  pointer-events: none;
  overflow: visible;
}
.panel-tree {
  position: relative;
  display: grid;
  column-gap: 12px;
  row-gap: 32px;
}
.tree-item {
  position: relative;
  min-width: 0;
  font-size: 16px;
  font-weight: 100;
  line-height: 1.35;
  text-align: center;
  color: #F0F4F7;
}
@media (max-width: 768px) {
  .panel {
    left: 0; right: 0; bottom: 0; top: auto;
    width: auto;
    max-height: 60vh;
    overflow-y: auto;
    box-sizing: border-box;
    margin: 0 8px 8px;
    transform: translateY(100%);
  }
  .panel.active { transform: translateY(0); }
  .panel-header { margin-bottom: 24px; }
  .tree-item { font-size: 13px; }
  .panel-tree { row-gap: 24px; }
}
