/* =========================================================
   GlobalTradeTech.co — Base Stylesheet
   ========================================================= */

:root {
  --color-navy: #0b2545;
  --color-seagreen: #2f6f5e;
  --color-text: #1a1a1a;
  --color-text-secondary: #5a5f66;
  --color-bg: #ffffff;
  --color-bg-alt: #f7f8f9;
  --color-border: #e5e7eb;
  --color-accent-soft: #4f9b93;
  --color-heading-dark: #16243c;
  --color-grey-light: #f7f8f9;
  --color-grey-medium: #d4d8dc;
  --color-dot: #6bc4b3;

  --gradient-blue: linear-gradient(100deg, var(--color-navy) 0%, #3062c6 100%);

  --gradient-brand: linear-gradient(100deg, var(--color-navy) 0%, var(--color-seagreen) 100%);

  --gradient-seagreen-royal: linear-gradient(100deg, var(--color-seagreen) 0%, #4169e1 100%);
  --gradient-navy-charcoal: linear-gradient(100deg, var(--color-navy) 0%, #202b3a 100%);
  --gradient-seagreen-deepblue: linear-gradient(100deg, var(--color-seagreen) 0%, #1e3a8a 100%);

  /* The single sitewide standard for gradient-clipped TEXT (headings,
     highlighted words/phrases, stat numbers, etc.) — identical to the
     RFQ button's own background (see .btn-primary), the reference/
     target every gradient-text instance sitewide was standardized to
     2026-08-17. Not used for buttons, backgrounds, borders, or any
     other non-text gradient — those keep using --gradient-brand/
     --gradient-blue/etc. above, which are shared with genuinely
     different (non-text) uses elsewhere and were deliberately left
     alone. Any new gradient text should reference this variable (or
     the .grad-text utility class below) rather than introducing a new
     one-off gradient value. */
  --gradient-logo: linear-gradient(100deg, #0097b1 0%, #7ed957 100%);

  --container-width: 1200px;
  --header-height: 100px;

  /* Sitewide font system (see CLAUDE.md's Typography section for the
     full assignment table and history — this replaced an earlier
     Michroma system, which itself had replaced an even earlier
     Orbitron/Lato/Inter system). Each keeps the same system-font
     fallback chain the site already used, so a failed/slow webfont
     load still degrades gracefully instead of falling back to the
     browser's generic default. Unlike Michroma (single-weight only,
     forcing the browser to fake/synthesize bold), League Spartan ships
     real 700 and 800 cuts — both are loaded (see every page's own
     <head>) and headings render in genuine, not synthesized, bold. */
  --font-body: "Inter", -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
  --font-heading: "League Spartan", -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
  --font-nav: "Inter", -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
}

* {
  box-sizing: border-box;
  margin: 0;
  padding: 0;
}

html {
  scroll-behavior: smooth;
  overflow-x: clip;
}

/* overflow-x: clip on both html and body (2026-08-21, changed from
   overflow-x: hidden the same day — see below) — the root-level safety
   net for the "Our Offerings" dropdown/submenu bug: .dropdown-menu/
   .submenu (further down) are position: absolute and only ever toggle
   opacity/visibility, never display, so even while fully invisible
   their boxes still count toward the page's scrollable width at
   ~900–1100px widths, where the nav sits close enough to the viewport
   edge for this to matter. This clips any such invisible overflow at
   the document edge instead of producing a scrollbar. Safe here
   specifically because nothing on this site relies on genuine
   page-level horizontal scroll or a full-bleed 100vw element (checked
   before adding this) — an .is-locked-fold/.scroll-locked section
   further down already uses a stricter overflow: hidden of its own for
   an unrelated feature and is unaffected by this.

   MUST be `clip`, not `hidden`, on this specific pair of elements —
   this was a real regression, not a style choice. `overflow-x: hidden`
   forces the browser to compute the *other* axis (`overflow-y`) as
   `auto` per spec (any non-`visible` value on one axis pairs the other
   axis away from `visible`), and once html/body's overflow-y is
   anything but the default `visible`, `.site-header`'s `position:
   sticky` (further down) stops sticking at all sitewide — confirmed by
   temporarily reverting this to `hidden` and watching the header
   scroll fully off-screen at every width tested, including mobile.
   `overflow-x: clip` was designed for exactly this: it clips the x-axis
   without forcing y into a scroll/auto state (confirmed via
   getComputedStyle: overflow-y stays `visible`), so sticky positioning
   keeps working while horizontal overflow is still fully contained —
   confirmed by re-testing the original nav-dropdown fix (900/1000/1100px,
   including the Electronics & Electrical nested flyout) after this
   change: still no scrollbar, no cutoff. */
body {
  font-family: var(--font-body);
  color: var(--color-text);
  background: var(--color-bg);
  line-height: 1.6;
  -webkit-font-smoothing: antialiased;
  overflow-x: clip;
}

/* Sitewide font system — applies automatically to every page, current
   and future, since it targets real heading tags and the shared nav/
   button classes rather than per-component selectors. Font-family
   ONLY: no size/weight/spacing/color changes here — every element's
   existing font-weight, font-size, color etc. is left exactly as it
   was. Body copy (paragraphs, labels, descriptions, etc.) doesn't need
   its own rule here — it already inherits Inter from `body` above once
   `--font-body` itself was changed to lead with Inter. The 4 offering
   pages' hero "subheading" classes (`.av-hero-subheading`,
   `.imro-hero-subheading`, `.auto-hero-subheading`,
   `.elec-hero-subheading`) are `<p>` tags, not real heading tags, but
   are visually/semantically subheadings, so each gets its own explicit
   `font-family: var(--font-heading)` where it's defined further down —
   they'd otherwise silently inherit the body font instead. */
h1, h2, h3, h4, h5, h6 {
  font-family: var(--font-heading);
  /* Added 2026-08-24 (modernization pass, applied for review — see
     PROGRESS.md for the before/after comparison this was checked
     against). No heading anywhere previously set its own letter-spacing
     (confirmed via grep before adding this), so this is a genuine,
     sitewide-visible change, not a no-op. A small negative value on a
     geometric sans at real display-heading sizes reads as a tighter,
     more "designed" modern-SaaS feel rather than the font's own default
     spacing — kept deliberately subtle (-0.01em is roughly 0.14px per
     character at a 14px heading, more at larger sizes) specifically so
     it doesn't affect line-wrapping at any of the fluid clamp() sizes
     used sitewide. */
  letter-spacing: -0.01em;
}

.main-nav,
.footer-links,
.btn,
.whatsapp-btn,
.tooltip-icon,
.file-upload-btn {
  font-family: var(--font-nav);
}

img {
  max-width: 100%;
  display: block;
}

a {
  text-decoration: none;
  color: inherit;
}

ul {
  list-style: none;
}

.container {
  width: 100%;
  max-width: var(--container-width);
  margin: 0 auto;
  padding: 0 24px;
}

/* =========================================================
   Header
   ========================================================= */

.site-header {
  position: sticky;
  top: 0;
  z-index: 100;
  background: var(--color-bg);
  border-bottom: 1px solid var(--color-border);
  transition: background-color 0.3s ease, border-color 0.3s ease, box-shadow 0.3s ease, backdrop-filter 0.3s ease;
}

/* Glass effect once scrolled past the hero (added 2026-08-24,
   modernization pass) — toggled by initHeaderScrollState() in
   script.js based on window.scrollY, not tied to any particular
   section, so it works identically on every page regardless of hero
   height. rgba(255, 255, 255, 0.75) is --color-bg's own white made
   translucent (can't apply alpha to a CSS custom property directly
   without color-mix(), so it's spelled out literally here) — the
   backdrop-filter blur needs *something* translucent behind it to
   blur, a fully opaque background would hide the effect entirely.
   Doesn't affect or depend on the nav dropdown/scroll-lock logic at
   all — purely a background/shadow swap on the header itself. */
.site-header.header-scrolled {
  background: rgba(255, 255, 255, 0.75);
  backdrop-filter: blur(14px);
  -webkit-backdrop-filter: blur(14px);
  border-bottom-color: transparent;
  box-shadow: 0 8px 30px rgba(11, 37, 69, 0.1);
}

.header-inner {
  display: flex;
  align-items: center;
  justify-content: space-between;
  height: var(--header-height);
}

.logo-link {
  display: flex;
  align-items: center;
  height: 100%;
}

.logo-img {
  height: 72px;
  width: auto;
}

/* Groups the nav, the header CTA button, and the mobile toggle together
   on the right side, so .header-inner's space-between only has to split
   space between the logo and this whole group (not fight over 3+ items). */
.header-right {
  display: flex;
  align-items: center;
  gap: 32px;
}

.header-cta-btn {
  padding: 11px 22px;
  font-size: 13px;
  white-space: nowrap;
}

.header-cta-short {
  display: none;
}

.main-nav .nav-list {
  display: flex;
  align-items: center;
  gap: 40px;
}

.main-nav a,
.nav-dropdown-trigger {
  font-size: 15px;
  font-weight: 600;
  letter-spacing: 0.02em;
  color: var(--color-text);
  cursor: pointer;
  padding: 8px 0;
  position: relative;
}

.main-nav a::after,
.nav-dropdown-trigger::after {
  content: "";
  position: absolute;
  left: 0;
  bottom: 0;
  width: 0;
  height: 2px;
  background: var(--gradient-brand);
  transition: width 0.25s ease;
}

.main-nav a:hover::after,
.nav-dropdown:hover .nav-dropdown-trigger::after {
  width: 100%;
}

/* "Our Offerings" dropdown */
.nav-dropdown {
  position: relative;
}

.nav-dropdown-trigger {
  display: flex;
  align-items: center;
  gap: 6px;
}

.nav-dropdown-trigger .caret {
  display: inline-block;
  width: 7px;
  height: 7px;
  border-right: 2px solid var(--color-text);
  border-bottom: 2px solid var(--color-text);
  transform: rotate(45deg) translateY(-2px);
  transition: transform 0.2s ease;
}

.nav-dropdown:hover .nav-dropdown-trigger .caret {
  transform: rotate(225deg) translateY(2px);
}

.dropdown-menu {
  position: absolute;
  top: 100%;
  left: 50%;
  transform: translateX(-50%) translateY(8px);
  min-width: 240px;
  /* Added 2026-08-21, same fix as .submenu's own max-width below —
     keeps this top-level menu from ever claiming more width than the
     viewport has at narrow desktop widths (~900–1100px), so it stays
     fully visible/centered under its trigger instead of relying on the
     new html/body overflow-x: clip above to clip it. */
  max-width: calc(100vw - 32px);
  background: var(--color-bg);
  border: 1px solid var(--color-border);
  border-radius: 10px;
  box-shadow: 0 12px 28px rgba(11, 37, 69, 0.12);
  padding: 8px;
  opacity: 0;
  visibility: hidden;
  transition: opacity 0.2s ease, transform 0.2s ease, visibility 0.2s ease;
}

.nav-dropdown:hover .dropdown-menu,
.nav-dropdown:focus-within .dropdown-menu {
  opacity: 1;
  visibility: visible;
  transform: translateX(-50%) translateY(4px);
}

.dropdown-menu li a,
.dropdown-menu li span {
  display: block;
  padding: 10px 14px;
  font-size: 14px;
  font-weight: 500;
  border-radius: 6px;
  color: var(--color-text);
}

.dropdown-menu li a:hover,
.dropdown-menu li span:hover {
  background: var(--color-bg-alt);
  color: var(--color-seagreen);
}

.dropdown-menu li a::after {
  display: none;
}

/* Second-level "Our Offerings" flyout */
.dropdown-category {
  position: relative;
}

.dropdown-menu .category-trigger {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 10px;
  transform-origin: left center;
  transition: opacity 0.2s ease, transform 0.2s ease, color 0.2s ease;
}

.category-trigger .sub-caret {
  display: inline-block;
  width: 7px;
  height: 7px;
  padding: 0;
  border-right: 2px solid var(--color-text);
  border-bottom: 2px solid var(--color-text);
  transform: rotate(-45deg) translateX(-2px);
  transition: transform 0.2s ease;
  flex-shrink: 0;
}

.dropdown-menu:hover .category-trigger {
  opacity: 0.7;
}

.dropdown-category:hover .category-trigger {
  opacity: 1;
  transform: scale(1.06);
  color: var(--color-seagreen);
}

.submenu {
  position: absolute;
  top: -8px;
  left: 100%;
  margin-left: 10px;
  width: 250px;
  max-width: calc(100vw - 32px);
  background: var(--color-bg);
  border: 1px solid var(--color-border);
  border-radius: 10px;
  box-shadow: 0 12px 28px rgba(11, 37, 69, 0.14);
  padding: 8px;
  opacity: 0;
  visibility: hidden;
  transform: translateX(-6px);
  transition: opacity 0.2s ease, transform 0.2s ease, visibility 0.2s ease, width 0.15s ease;
  z-index: 5;
}

.dropdown-category:hover .submenu,
.dropdown-category:focus-within .submenu {
  opacity: 1;
  visibility: visible;
  transform: translateX(0);
}

.submenu li a,
.submenu li .submenu-placeholder {
  display: block;
  padding: 10px 14px;
  font-size: 13.5px;
  font-weight: 500;
  border-radius: 6px;
  color: var(--color-text);
}

.submenu li a:hover {
  background: var(--color-bg-alt);
  color: var(--color-seagreen);
}

.submenu li a::after {
  display: none;
}

.submenu-placeholder {
  color: var(--color-text-secondary);
  font-style: italic;
  cursor: default;
}

/* Force-closes the "Our Offerings" dropdown/submenu the instant a menu
   link is actually clicked (see the dropdownParent/closeDropdownMenu
   logic in script.js) — without this, the menu stays visually open
   afterward: clicking a link doesn't itself change :hover/:focus-within
   (the desktop open-state above) or clear a lingering .open class (the
   mobile open-state below), so it would otherwise linger open through
   the same-document hash navigation the submenu's own subcategory links
   trigger. !important is required to beat both of those rule sets
   regardless of source order. Opacity/visibility (not display) so one
   rule works at every viewport width without a separate mobile
   override. Applied to the outer .nav-dropdown so a single class closes
   both the top-level menu and any open submenu at once. script.js lifts
   this class again as soon as the mouse actually leaves the menu (or
   after a short fallback timeout), so this is a one-shot reset at the
   moment of navigation, not a permanent change to how the menu opens on
   hover. */
.nav-dropdown.menu-force-closed .dropdown-menu,
.nav-dropdown.menu-force-closed .submenu {
  opacity: 0 !important;
  visibility: hidden !important;
  pointer-events: none !important;
}

/* Mobile nav toggle */
.nav-toggle {
  display: none;
  flex-direction: column;
  justify-content: center;
  align-items: center;
  gap: 5px;
  width: 40px;
  height: 40px;
  background: none;
  border: none;
  cursor: pointer;
}

.nav-toggle span {
  display: block;
  width: 24px;
  height: 2px;
  background: var(--color-navy);
  transition: transform 0.25s ease, opacity 0.25s ease;
}

@media (max-width: 860px) {
  /* Fixes a real bug (reported in production): without this, the logo
     is a flex item (.logo-link, itself wrapping the <img> in its own
     nested flex context) with the browser's default flex-shrink:1 — so
     whenever this row's total content doesn't fit the viewport, the
     browser can render the logo non-uniformly squashed (narrower than
     its real 4:1 aspect ratio, while .logo-img's explicit height stays
     fixed) instead of just leaving it at natural size. flex-shrink:0
     makes the logo un-shrinkable, so it always renders at its correct
     aspect ratio — anything else in this row (the CTA button, the
     toggle, the gaps below) absorbs a width squeeze instead. Scoped to
     this mobile media query only; desktop has ample room and was never
     affected, and this rule doesn't apply there. */
  .logo-link {
    flex-shrink: 0;
  }

  .nav-toggle {
    display: flex;
  }

  /* Below this width .main-nav becomes an absolutely-positioned overlay
     (see below), so it no longer takes up space in this row — .header-
     right's visible content becomes just the CTA button + toggle. */
  .header-right {
    gap: 16px;
  }

  .header-cta-btn {
    padding: 9px 14px;
    font-size: 12px;
  }

  /* Below ~580px, "REQUEST A QUOTE" at any readable size no longer
     leaves room for the full-size logo without the row actually
     overflowing — measured directly: at 430px, with flex-shrink:0
     above preventing the logo from silently absorbing the shortfall by
     distorting, the row's natural content needs ~522px, well past the
     viewport. Switching to a short "QUOTE" label plus a smaller logo
     (below) closes that gap with real width, not a squashed image. */
  @media (max-width: 580px) {
    .header-cta-full {
      display: none;
    }

    .header-cta-short {
      display: inline;
    }

    .header-cta-btn {
      padding: 9px 16px;
    }

    /* Kept in the same ~71% ratio to the desktop .logo-img size when
       that was increased for a sitewide logo-size bump (62px→72px
       desktop, 44px→51px here). */
    .logo-img {
      height: 51px;
    }

    .header-inner {
      gap: 12px;
    }
  }

  /* Below ~420px, even the short "QUOTE" label plus the 51px-tall logo
     from the tier above doesn't leave room (measured directly: at
     362px the row needed ~374px) — shrink further here specifically,
     rather than letting the row overflow the viewport (which is what
     would happen now that flex-shrink:0 above stops the old silent
     "shrink by distorting the logo" fallback from masking it). Also
     trims the toggle and both gaps, per the same budget — verified
     directly down to 320px with real margin to spare on the header's
     own content once .globe-video's own separate, unrelated overflow
     (a different section of this page, not part of this fix) is
     excluded. */
  @media (max-width: 420px) {
    .logo-img {
      height: 40px;
    }

    .header-cta-btn {
      padding: 8px 12px;
    }

    .nav-toggle {
      width: 34px;
      height: 34px;
    }

    .header-inner {
      gap: 8px;
    }

    .header-right {
      gap: 10px;
    }
  }

  .main-nav {
    position: absolute;
    top: var(--header-height);
    left: 0;
    right: 0;
    background: var(--color-bg);
    border-bottom: 1px solid var(--color-border);
    max-height: 0;
    overflow: hidden;
    transition: max-height 0.3s ease;
  }

  .main-nav.open {
    max-height: 720px;
    overflow-y: auto;
  }

  .main-nav .nav-list {
    flex-direction: column;
    align-items: flex-start;
    gap: 0;
    padding: 8px 24px 20px;
  }

  .main-nav .nav-list > li {
    width: 100%;
    padding: 12px 0;
    border-bottom: 1px solid var(--color-border);
  }

  .dropdown-menu {
    position: static;
    transform: none;
    opacity: 1;
    visibility: visible;
    box-shadow: none;
    border: none;
    padding: 4px 0 0 12px;
    display: none;
  }

  .nav-dropdown.open .dropdown-menu {
    display: block;
  }

  .nav-dropdown:hover .dropdown-menu {
    transform: none;
  }

  .category-trigger {
    padding: 8px 0;
  }

  .dropdown-menu:hover .category-trigger,
  .dropdown-category:hover .category-trigger {
    opacity: 1;
    transform: none;
    color: var(--color-text);
  }

  .dropdown-category.open .category-trigger {
    color: var(--color-seagreen);
  }

  .dropdown-category.open .category-trigger .sub-caret {
    transform: rotate(135deg);
  }

  .submenu {
    position: static;
    transform: none;
    opacity: 1;
    visibility: visible;
    box-shadow: none;
    border: none;
    margin-left: 0;
    width: auto;
    max-width: none;
    padding: 2px 0 2px 14px;
    display: none;
  }

  .dropdown-category.open .submenu {
    display: block;
  }

  .dropdown-category:hover .submenu {
    transform: none;
  }
}

/* =========================================================
   Buttons
   ========================================================= */

.btn {
  display: inline-block;
  padding: 14px 32px;
  font-size: 14px;
  font-weight: 700;
  letter-spacing: 0.06em;
  border-radius: 6px;
  border: none;
  cursor: pointer;
  transition: transform 0.2s ease, box-shadow 0.2s ease;
}

/* Background is the exact logo gradient (same literal values as
   .grad-logo/.about-cta-grad/.contact-hero-grad/the 4 offering-page
   hero subheadings & buttons) — deliberately NOT var(--gradient-brand)
   even though that's what this rule used before: this is now the
   sitewide standing button-color rule, see CLAUDE.md's design-system
   section. --gradient-brand itself is untouched and still used
   elsewhere (nav underline, stat-number/heading gradient text, image
   frames, connector dots) — only this one declaration changed. */
.btn-primary {
  background: linear-gradient(100deg, #0097b1 0%, #7ed957 100%);
  color: #ffffff;
}

.btn-primary:hover {
  transform: translateY(-2px);
  box-shadow: 0 10px 24px rgba(11, 37, 69, 0.25);
}

.btn-hover-grow {
  transition: transform 0.25s ease, opacity 0.25s ease, box-shadow 0.25s ease;
}

.btn-hover-grow:hover {
  /* Tuned 2026-08-24 (modernization pass) — scale(1.06) → 1.03 (the
     brief's requested "1.02-1.03x" range; 1.06 read as more of a jump
     than a micro-interaction) and a second, brand-teal glow shadow layer
     added alongside the existing dark drop-shadow (kept, not replaced —
     it's still what gives the lift its depth) so hovering reads as a
     soft glow, not just a flat color swap. #0097b1 is the exact teal
     already used as --gradient-logo's own first stop (the same gradient
     every .btn-primary background already uses), not a new color.
     Every button already using this class (header/footer "REQUEST A
     QUOTE", the RFQ and Contact form submit buttons) gets this
     automatically — see CLAUDE.md for the full list. */
  transform: scale(1.03);
  opacity: 0.95;
  box-shadow: 0 10px 24px rgba(0, 0, 0, 0.3), 0 0 22px rgba(0, 151, 177, 0.35);
}

/* =========================================================
   Hero
   ========================================================= */

.hero {
  position: relative;
  height: calc(100vh - var(--header-height));
  min-height: 460px;
  max-height: 760px;
  overflow: hidden;
  background: var(--color-navy);
}

/* 120%/-10% oversize for scroll parallax slack — same pattern as every
   .*-hero-bg layer sitewide (see .page-hero-bg's own comment, Hero
   section further down, for the full explanation); object-fit: cover
   crops a video the same way background-size: cover crops a div. */
.hero-video {
  position: absolute;
  top: -20%;
  left: 0;
  right: 0;
  width: 100%;
  height: 140%;
  object-fit: cover;
  object-position: center;
  will-change: transform;
}

.hero-overlay {
  position: absolute;
  inset: 0;
  background: linear-gradient(180deg, rgba(11, 37, 69, 0.55) 0%, rgba(11, 37, 69, 0.35) 45%, rgba(11, 37, 69, 0.6) 100%);
}

.hero-content {
  position: relative;
  height: 100%;
  display: flex;
  align-items: center;
  justify-content: center;
  text-align: center;
}

.hero-tagline {
  position: absolute;
  left: 50%;
  top: 50%;
  transform: translate(-50%, -50%);
  width: 100%;
  max-width: 820px;
  padding: 0 24px;
  color: #ffffff;
  font-size: clamp(28px, 5vw, 48px);
  font-weight: 700;
  line-height: 1.25;
  text-shadow: 0 2px 18px rgba(0, 0, 0, 0.35);
  opacity: 0;
  transition: opacity 1s ease;
}

.hero-tagline.active {
  opacity: 1;
}

/* =========================================================
   Home — About section
   ========================================================= */

.about-home {
  padding: 96px 0;
  background: var(--color-bg);
}

.about-home-inner {
  display: flex;
  align-items: center;
  gap: 64px;
}

.about-home-image,
.about-home-text {
  flex: 1 1 50%;
}

.about-home-image img {
  width: 100%;
  border-radius: 16px;
}

/* Animated globe (replaces the static About-section image) */

.animated-globe {
  position: relative;
  width: 100%;
  max-width: 420px;
  aspect-ratio: 1 / 1;
  margin: 0 auto;
}

/* Circular video "sphere" — replaces the old SVG globe-sphere/globe-
   continents illustration. A single <video> (see initGlobeVideo() in
   script.js) with the native `loop` attribute — the earlier two-video
   crossfade-swap approach (removed 2026-08-17) was built for a
   previous, differently-timed source file; the file itself was
   replaced 2026-08-14 with a much shorter clip, and the crossfade code
   wasn't updated at the same time, so it was producing a premature/
   broken-looking loop plus a white-border/shadow flash by the time
   this was diagnosed. See PROGRESS.md's 2026-08-17 session log.
   Kept the same box-shadow the old .globe-sphere used for a subtle 3D
   rim/depth so it still reads as a sphere rather than a flat circle —
   this is the sphere's permanent, always-on frame, not a loop-restart
   effect, and is only ever visible on its own (i.e. looking like a
   "flash") if the video briefly fails to cover the wrap's white
   background, which is exactly the failure mode the crossfade removal
   fixes.

   NOT using mix-blend-mode: screen here, despite the brief asking for
   it — tested it directly and it does not do what was intended.
   Screen blend's formula is result = 1-(1-top)(1-bottom); against a
   pure-white backdrop (bottom = 1 in every channel), that reduces to
   1-(1-top)*0 = 1 for *any* top value, so it paints the entire video
   solid white, not just the black background — confirmed empirically
   (the whole sphere disappeared, not just its black margin) as well
   as by the formula. Screen only hides black correctly against a
   *dark/black* backdrop, which this white section is not. The
   video's actual black background is a fairly wide, uniform margin
   around the sphere. So instead .globe-video is scaled up (see below)
   to push that margin outside the circular clip entirely — no
   blend-mode trick needed, and the Earth's actual colors render
   unmodified.

   Scale factor recalibrated 2026-08-17 (1.16 → 1.32) for the replacement
   source file (see the video-fix note above) — the old 1.16 value was
   tuned for the original, since-replaced file and was leaving a visible
   uniform white ring (the wrap's own white `background`, see below)
   between the video's own visible globe content and this box-shadow,
   confirmed against a user-supplied screenshot: the globe content was
   rendering at ~89% of the wrap's diameter, i.e. roughly a 1.12×
   additional scale-up needed on top of the already-applied 1.16
   (1.16 × 1.125 ≈ 1.3, rounded up slightly to 1.32 for a small safety
   margin against under-covering). Since this is a relative `scale()`
   multiplier (not a fixed pixel value) on an element already sized as
   a percentage of its container, it scales proportionally at every
   viewport/breakpoint automatically — no separate responsive handling
   needed. If a future video file is swapped in again, re-check this
   value the same way: screenshot the rendered globe, measure the
   visible-sphere-diameter to full-frame-diameter ratio, and scale
   accordingly. */
.globe-video-wrap {
  position: relative;
  width: 100%;
  height: 100%;
  border-radius: 50%;
  overflow: hidden;
  /* Brand-colored placeholder (2026-08-24) — .globe-video itself stays
     opacity: 0 until its lazy-loaded data arrives (initGlobeVideo() in
     script.js), so whatever this wrapper's own background is shows
     through in the meantime. Was var(--color-bg) (plain white), which
     read as a blank/broken white circle on a cold-cache load; reusing
     --gradient-brand here (same navy-to-seagreen already used for the
     nav underline and the About intro-image-frame border — a non-text
     UI accent, not a new color) gives it a deliberate, on-brand look
     instead. */
  background: var(--gradient-brand);
  box-shadow:
    inset -20px -20px 55px rgba(0, 0, 0, 0.45),
    inset 12px 12px 30px rgba(255, 255, 255, 0.18),
    0 25px 50px rgba(11, 37, 69, 0.3);
}

.globe-video {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  transform: scale(1.32);
  opacity: 0;
  transition: opacity 0.9s ease;
}

.globe-video.is-visible {
  opacity: 1;
}

.about-home-text h2 {
  font-size: clamp(24px, 3vw, 34px);
  font-weight: 700;
  color: var(--color-navy);
  line-height: 1.3;
  margin-bottom: 20px;
}

.about-home-text p {
  color: var(--color-text-secondary);
  /* font-family no longer set explicitly here — redundant now that
     --font-body itself leads with Lato (see the sitewide font-system
     block near the top of this file); this rule already inherited
     Lato either way, this just removes the duplicate declaration. */
  font-weight: 400;
  font-size: 16px;
  margin-bottom: 28px;
}

@media (max-width: 860px) {
  .about-home-inner {
    flex-direction: column;
  }

  .about-home-image,
  .about-home-text {
    width: 100%;
  }

  .about-home {
    padding: 64px 0;
  }
}

/* =========================================================
   Home — Industries section
   ========================================================= */

.industries {
  padding: 96px 0;
  background: linear-gradient(180deg, var(--color-grey-light) 0%, var(--color-grey-medium) 100%);
}

.industries-heading {
  font-size: clamp(24px, 3vw, 34px);
  font-weight: 700;
  color: var(--color-navy);
  line-height: 1.3;
  text-align: center;
  max-width: 760px;
  margin: 0 auto 56px;
}

.industries-grid {
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  gap: 20px;
}

/* Scroll-linked toggle entrance/exit (reworked 2026-08-25, follow-up
   fix — was originally a one-shot entrance) — cards 1-2 slide from the
   left, 3-4 from the right, both with a fade, every time the grid
   scrolls into view, and slide back out + fade whenever it's ~90%+
   scrolled out of view (initIndustryCardsEntrance() in script.js toggles
   `.is-entered` on/off, indefinitely, based on IntersectionObserver's
   `isIntersecting` — no `{once:true}`, nothing is ever stripped).
   Uses the standalone `translate` property (not `transform`) for the
   slide so it composes cleanly alongside `.industry-card:hover`'s own
   `transform: translateY(-4px)` lift — `translate` and `transform` are
   independent CSS properties that both apply simultaneously, so a
   settled `.is-entered` card (translate: 0 0) can still be hovered
   normally with no specificity conflict, even though `.is-entered` now
   persists indefinitely instead of being removed after one animation. */
.industries-grid .industry-card.card-enter-left,
.industries-grid .industry-card.card-enter-right {
  opacity: 0;
  transition: opacity 0.55s ease-out, translate 0.55s ease-out;
}

.industries-grid .industry-card.card-enter-left {
  translate: -60px 0;
}

.industries-grid .industry-card.card-enter-right {
  translate: 60px 0;
}

.industries-grid .industry-card.card-enter-left.is-entered,
.industries-grid .industry-card.card-enter-right.is-entered {
  opacity: 1;
  translate: 0 0;
}

@media (prefers-reduced-motion: reduce) {
  .industries-grid .industry-card.card-enter-left,
  .industries-grid .industry-card.card-enter-right {
    opacity: 1;
    translate: 0 0;
    transition: none;
  }
}

.industry-card {
  background: #ffffff;
  border-radius: 14px;
  padding: 36px 28px;
  box-shadow: 0 10px 30px rgba(11, 37, 69, 0.08);
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  transition: transform 0.25s ease, box-shadow 0.25s ease;
}

.industry-card:hover {
  transform: translateY(-4px);
  box-shadow: 0 16px 36px rgba(11, 37, 69, 0.14);
}

.industry-icon {
  width: 48px;
  height: 48px;
  color: var(--color-accent-soft);
  margin-bottom: 20px;
}

.industry-icon svg {
  width: 100%;
  height: 100%;
}

.industry-card h3 {
  font-size: 18px;
  font-weight: 700;
  color: var(--color-heading-dark);
  margin-bottom: 14px;
}

.industry-card p {
  font-size: 14.5px;
  color: var(--color-text-secondary);
  margin-bottom: 20px;
  flex-grow: 1;
}

.card-link {
  font-size: 14px;
  font-weight: 700;
  color: var(--color-accent-soft);
  letter-spacing: 0.02em;
}

.card-link:hover {
  text-decoration: underline;
}

/* Tighter proportions for just this section's 4 cards — .industry-card
   (and its h3/p) is a shared base also reused by About's trust/
   capability/perf cards and this same page's .process-card further
   down, so every value below is overridden through .industries-grid
   rather than edited on the shared selectors above, to avoid changing
   anything about those other cards. Reduces padding, icon size, and
   inter-element margins/line-height to cut the excess vertical space
   that was making these 4 cards read as too tall/narrow, while
   .industry-card's existing flex-column + p{flex-grow:1} (unchanged)
   keeps "Read More" aligned at a consistent bottom position across all
   4 — CSS grid's default align-items:stretch already equalizes card
   height across the row, this just trims what each equalized card is
   full of. The grid's own gap above was also reduced (28px → 20px) to
   give each of the 4 columns a little more width within the section's
   existing container, per the same goal, without touching the shared
   .container class other sections/pages rely on. */
.industries-grid .industry-card {
  padding: 26px 22px;
}

.industries-grid .industry-icon {
  width: 38px;
  height: 38px;
  margin-bottom: 12px;
}

.industries-grid .industry-card h3 {
  margin-bottom: 8px;
}

.industries-grid .industry-card p {
  margin-bottom: 14px;
  line-height: 1.5;
}

@media (max-width: 1024px) {
  .industries-grid {
    grid-template-columns: repeat(2, 1fr);
  }
}

@media (max-width: 560px) {
  .industries-grid {
    grid-template-columns: 1fr;
  }

  .industries {
    padding: 64px 0;
  }
}

/* =========================================================
   Home — Supply Intelligence process section
   ========================================================= */

.supply-intel {
  padding: 96px 0;
  background: linear-gradient(180deg, var(--color-grey-medium) 0%, var(--color-grey-light) 100%);
}

.supply-heading {
  font-size: clamp(26px, 3.4vw, 38px);
  font-weight: 800;
  text-align: center;
  max-width: 820px;
  line-height: 1.35;
  margin: 0 auto 64px;
}

.supply-heading .text-plain {
  color: #111111;
}

.supply-heading .grad-blue {
  background: var(--gradient-logo);
  background-clip: text;
  -webkit-background-clip: text;
  color: transparent;
}

.supply-heading .grad-brand-text {
  background: var(--gradient-logo);
  background-clip: text;
  -webkit-background-clip: text;
  color: transparent;
}

.process-grid-wrap {
  position: relative;
}

.process-connector {
  display: none;
}

.process-grid {
  display: grid;
  grid-template-columns: 1fr;
  gap: 24px;
}

.process-card h3 {
  background: var(--gradient-logo);
  background-clip: text;
  -webkit-background-clip: text;
  color: transparent;
  font-weight: 800;
}

.process-card p {
  margin-bottom: 0;
}

/* Scroll-linked pop-in/out toggle (added 2026-08-25, follow-up fix,
   item 10; extended to About's own "How Supply Intelligence..." section
   the same day, item 13, since it's the same 6-card content with
   different card markup/classes — see .perf-process-grid below) — the
   6 "Supply Intelligence" cards scale+fade in one at a time, in order,
   every time this section scrolls into view, and reverse (shrink +
   fade out, together, not staggered — see initCardPopGrid() in
   script.js) whenever it's ~90%+ scrolled out. Same toggle machinery as
   .industries-grid's .card-enter-left/-right above (single-threshold
   IntersectionObserver, persistent class, never stripped). Uses the
   standalone `scale` property rather than `transform` for the same
   reason that block uses `translate` instead of `transform` — it
   composes independently alongside `.industry-card:hover`'s own
   `transform: translateY(-4px)` lift (`.process-card`/`.perf-card` are
   both `.industry-card` variants, see the "Cards" note in CLAUDE.md),
   so the hover-lift keeps working normally while `.is-popped` is
   present. The per-card stagger delay is set in JS via inline
   `transition-delay` (different on the way in vs. cleared to 0 on the
   way out) rather than in this CSS, since the two directions need
   different delay behavior on the exact same property. Both grids
   share this one declaration block (identical values) rather than two
   separate copies, since the brief specifically asked for About's
   version to look and behave identically to Home's. */
.process-grid .process-card.card-pop,
.perf-process-grid .perf-card.card-pop {
  opacity: 0;
  scale: 0.85;
  transition: opacity 0.45s ease-out, scale 0.45s ease-out;
}

.process-grid .process-card.card-pop.is-popped,
.perf-process-grid .perf-card.card-pop.is-popped {
  opacity: 1;
  scale: 1;
}

@media (prefers-reduced-motion: reduce) {
  .process-grid .process-card.card-pop,
  .perf-process-grid .perf-card.card-pop {
    opacity: 1;
    scale: 1;
    transition: none;
  }
}

@media (min-width: 900px) {
  .process-grid-wrap {
    aspect-ratio: 1100 / 550;
    max-width: 1100px;
    margin: 0 auto;
  }

  .process-connector {
    display: block;
    position: absolute;
    inset: 0;
    width: 100%;
    height: 100%;
    pointer-events: none;
  }

  .connector-line {
    fill: none;
    stroke: var(--color-navy);
    stroke-opacity: 0.45;
    stroke-width: 2.5;
    stroke-linecap: round;
    stroke-linejoin: round;
    vector-effect: non-scaling-stroke;
  }

  .connector-dot {
    fill: var(--color-dot);
  }

  .process-grid {
    position: absolute;
    inset: 0;
    grid-template-columns: repeat(3, 1fr);
    grid-template-rows: repeat(2, 1fr);
    column-gap: 3.6364%;
    row-gap: 12.7273%;
  }

  .process-card {
    position: relative;
    margin: 0;
  }

  .card-1 { grid-column: 1; grid-row: 1; }
  .card-2 { grid-column: 2; grid-row: 1; }
  .card-3 { grid-column: 3; grid-row: 1; }
  .card-4 { grid-column: 3; grid-row: 2; }
  .card-5 { grid-column: 2; grid-row: 2; }
  .card-6 { grid-column: 1; grid-row: 2; }
}

/* =========================================================
   Home — Call to Action section
   ========================================================= */

.cta {
  position: relative;
  background: var(--color-navy);
  overflow: hidden;
  padding: 120px 0;
}

.cta-bg {
  position: absolute;
  inset: 0;
  background-size: cover;
  background-position: center;
  opacity: 0.65;
}

.cta-content {
  position: relative;
  z-index: 1;
  max-width: 760px;
  margin: 0 auto;
  text-align: center;
}

.cta-heading {
  font-size: clamp(28px, 4vw, 42px);
  font-weight: 800;
  line-height: 1.3;
  margin-bottom: 24px;
}

.cta-heading .text-white {
  color: #ffffff;
}

.cta-heading .grad-logo {
  background: var(--gradient-logo);
  background-clip: text;
  -webkit-background-clip: text;
  color: transparent;
}

.cta-text {
  color: #ffffff;
  font-size: 16px;
  line-height: 1.7;
  opacity: 0.92;
  margin-bottom: 36px;
}

@media (max-width: 640px) {
  .cta {
    padding: 80px 0;
  }
}

/* =========================================================
   Home — Footer
   ========================================================= */

.main-footer {
  position: relative;
  background: var(--color-grey-light);
  padding: 72px 0 100px;
}

/* Fixed-corner utility button, not part of the column grid — extra
   bottom padding above reserves clear space for it so it never overlaps
   the "Useful Links" column's content, on desktop or on the stacked
   mobile layout. */
.back-to-top-btn {
  position: absolute;
  right: max(24px, calc((100% - var(--container-width)) / 2 + 24px));
  bottom: 28px;
  width: 44px;
  height: 44px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  border: none;
  border-radius: 50%;
  background: var(--color-navy);
  color: #ffffff;
  cursor: pointer;
  transition: transform 0.25s ease, background 0.25s ease, box-shadow 0.25s ease;
}

.back-to-top-btn:hover,
.back-to-top-btn:focus-visible {
  background: var(--color-seagreen);
  transform: scale(1.08);
  box-shadow: 0 8px 18px rgba(11, 37, 69, 0.25);
  outline: none;
}

.back-to-top-icon {
  width: 20px;
  height: 20px;
}

.main-footer-grid {
  display: grid;
  grid-template-columns: 1.2fr 1fr 1fr;
  gap: 48px;
}

.footer-logo {
  width: 100%;
  max-width: 230px;
  height: auto;
  margin-bottom: 20px;
}

.footer-taglines p {
  color: var(--color-text-secondary);
  font-size: 14.5px;
  line-height: 1.7;
}

.footer-heading {
  font-size: 14px;
  font-weight: 700;
  letter-spacing: 0.08em;
  color: var(--color-navy);
  margin-bottom: 20px;
}

.footer-company-name {
  font-weight: 700;
  font-size: 17px;
  color: var(--color-text);
  margin-bottom: 12px;
}

.footer-address {
  color: var(--color-text-secondary);
  font-size: 14.5px;
  line-height: 1.6;
  margin-bottom: 12px;
}

.footer-strong {
  font-weight: 700;
  color: var(--color-text);
  font-size: 14.5px;
  margin-bottom: 8px;
}

.footer-whatsapp-btn {
  margin-top: 4px;
}

.footer-quote-btn {
  margin-top: 20px;
}

.footer-links li {
  margin-bottom: 12px;
}

.footer-links a {
  color: var(--color-text-secondary);
  font-size: 14.5px;
  transition: color 0.2s ease;
}

.footer-links a:hover {
  color: var(--color-seagreen);
}

@media (max-width: 860px) {
  .main-footer-grid {
    grid-template-columns: 1fr;
    gap: 40px;
  }

  .main-footer {
    padding: 56px 0 92px;
  }

  .back-to-top-btn {
    bottom: 24px;
  }
}

/* =========================================================
   Scroll-in section reveal
   ========================================================= */

/* Tuned 2026-08-24 (modernization pass) — was translateY(48px)/0.7s, a
   more pronounced slide than the "subtle, premium, not flashy" brief
   asked for. 20px/0.5s ease-out reads as a gentle fade-up instead of a
   noticeable slide. Also extended sitewide this same pass — see
   revealEls in script.js for the current full section list (still
   deliberately excludes .subcat-section on the 4 offering pages, to
   avoid any interaction with the scroll-lock fix). */
.reveal {
  opacity: 0;
  transform: translateY(20px);
  transition: opacity 0.5s ease-out, transform 0.5s ease-out;
}

.reveal.is-visible {
  opacity: 1;
  transform: translateY(0);
}

@media (prefers-reduced-motion: reduce) {
  .reveal {
    opacity: 1;
    transform: none;
    transition: none;
  }
}

/* =========================================================
   About Us page — Hero
   ========================================================= */

.page-hero {
  position: relative;
  min-height: max(560px, calc(100vh - var(--header-height)));
  display: flex;
  align-items: center;
  overflow: hidden;
  padding: 48px 0;
  background: var(--color-navy);
}

/* Sized 120%/-10% instead of a plain inset: 0 (added 2026-08-24,
   modernization pass) so initHeroParallax() (script.js) has vertical
   "slack" to translate this layer into on scroll without ever revealing
   a gap at the top/bottom edge — the parent .page-hero has overflow:
   hidden, so anything within that 10%-oversize margin stays invisible
   until scrolled into it. Every other hero-bg layer sitewide (7 total,
   1 per hero, this project's independent-classes convention means each
   gets its own separately-named rule rather than sharing this one) uses
   the identical 120%/-10% pattern — see .hero-video below for the same
   treatment on Home's video hero. will-change: transform is a
   performance hint for the scroll-linked transform, nothing else. */
.page-hero-bg {
  position: absolute;
  top: -20%;
  left: 0;
  right: 0;
  height: 140%;
  background-size: cover;
  background-position: center;
  opacity: 0.85;
  will-change: transform;
}

.page-hero-content {
  position: relative;
  z-index: 1;
  max-width: 760px;
  margin: 0 auto;
  text-align: center;
}

.page-hero-heading {
  color: #ffffff;
  font-weight: 800;
  font-size: clamp(34px, 5.2vw, 54px);
  line-height: 1.25;
  text-shadow: 0 2px 18px rgba(0, 0, 0, 0.35);
  margin-bottom: 20px;
}

.page-hero-text {
  display: inline-block;
  max-width: 640px;
  color: #ffffff;
  font-size: 19px;
  line-height: 1.7;
  background: radial-gradient(ellipse at center, rgba(0, 0, 0, 0.6) 0%, rgba(0, 0, 0, 0.45) 40%, rgba(0, 0, 0, 0.18) 70%, rgba(0, 0, 0, 0) 100%);
  padding: 56px 76px;
  margin-bottom: 64px;
}

.page-hero-btn {
  border: 1px solid rgba(255, 255, 255, 0.8);
}

@media (max-width: 640px) {
  .page-hero-text {
    padding: 36px 28px;
    margin-bottom: 44px;
  }
}

/* =========================================================
   About Us page — "Who We Are" intro section
   ========================================================= */

.intro-section {
  padding: 96px 0;
  background: var(--color-bg);
}

.intro-inner {
  display: flex;
  align-items: flex-start;
  gap: 64px;
}

.intro-images {
  flex: 1 1 42%;
  display: flex;
  flex-direction: column;
  gap: 24px;
}

.intro-image-frame {
  background: var(--gradient-brand);
  padding: 4px;
  border-radius: 14px;
  /* Soft lift shadow (added 2026-08-24, modernization pass) — same
     recipe as .industry-card's own shadow elsewhere on this site, so
     this photo frame reads as sitting above the page the same way the
     site's cards already do. */
  box-shadow: 0 14px 34px rgba(11, 37, 69, 0.1);
}

.intro-image-frame img {
  display: block;
  width: 100%;
  height: auto;
  border-radius: 10px;
}

.intro-content {
  flex: 1 1 58%;
}

.intro-heading {
  font-size: clamp(24px, 3vw, 34px);
  font-weight: 700;
  color: var(--color-navy);
  line-height: 1.3;
  margin-bottom: 20px;
}

.intro-text {
  color: var(--color-text-secondary);
  font-size: 16px;
  line-height: 1.7;
  margin-bottom: 20px;
}

.intro-stats {
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  gap: 24px;
  margin-top: 32px;
  padding-top: 32px;
  border-top: 1px solid var(--color-border);
}

.intro-stat-number {
  background: var(--gradient-logo);
  background-clip: text;
  -webkit-background-clip: text;
  color: transparent;
  font-size: 21px;
  font-weight: 800;
  line-height: 1.25;
  margin-bottom: 6px;
}

.intro-stat-label {
  color: var(--color-text-secondary);
  font-size: 13.5px;
  line-height: 1.5;
}

/* Scroll-linked toggle entrance (added 2026-08-25, follow-up fix, item
   11) — reuses the same single-threshold IntersectionObserver toggle
   pattern as .industries-grid's card-enter-left/-right and
   .process-grid's card-pop above (see initAboutIntroSection() in
   script.js): the 2 photos slide in from the left + fade, and the
   description paragraphs fade in place — both replay every time
   .intro-section scrolls into view and reset when it's ~90%+ scrolled
   out. The 4 stat numbers (.intro-stat-number above) count up/reset
   through the same shared counter helpers Home's hero stats use — no
   extra CSS needed for those, the count-up just changes text content
   inside the existing gradient-clipped element. The heading is
   deliberately untouched — no class, not part of this block. Uses
   `translate` (not `transform`) for the image slide for the same
   composability reason established elsewhere on this site (see the
   .card-enter-left/-right comment above) — neither `.intro-image-frame`
   nor `.intro-text` currently has a `transform`-based hover effect, but
   this keeps the convention consistent in case one is ever added. */
.intro-images .intro-image-frame.img-slide-in {
  opacity: 0;
  translate: -60px 0;
  transition: opacity 0.55s ease-out, translate 0.55s ease-out;
}

.intro-images .intro-image-frame.img-slide-in:nth-child(2) {
  transition-delay: 0.12s;
}

.intro-images .intro-image-frame.img-slide-in.is-visible {
  opacity: 1;
  translate: 0 0;
}

.intro-content .intro-text.text-fade-in {
  opacity: 0;
  transition: opacity 0.5s ease-out;
}

.intro-content .intro-text.text-fade-in.is-visible {
  opacity: 1;
}

@media (prefers-reduced-motion: reduce) {
  .intro-images .intro-image-frame.img-slide-in,
  .intro-content .intro-text.text-fade-in {
    opacity: 1;
    translate: 0 0;
    transition: none;
  }
}

@media (max-width: 860px) {
  .intro-inner {
    flex-direction: column;
  }

  .intro-images,
  .intro-content {
    width: 100%;
  }

  .intro-stats {
    grid-template-columns: repeat(2, 1fr);
  }

  .intro-section {
    padding: 64px 0;
  }
}

/* =========================================================
   About Us page — "Why trust GTT" section
   ========================================================= */

.trust-section {
  padding: 96px 0;
  background: linear-gradient(180deg, #ffffff 0%, var(--color-grey-medium) 50%, #ffffff 100%);
}

.trust-heading {
  font-size: clamp(26px, 3.4vw, 38px);
  font-weight: 800;
  text-align: center;
  max-width: 780px;
  line-height: 1.35;
  margin: 0 auto 56px;
}

.trust-heading .grad-seagreen-royal {
  background: var(--gradient-logo);
  background-clip: text;
  -webkit-background-clip: text;
  color: transparent;
}

/* NOT standardized to --gradient-logo, deliberately — see CLAUDE.md's
   gradient-text note. This pairs with .grad-seagreen-royal above as
   the "plain/dark" half of a two-tone heading (dark navy-charcoal
   alternating with a bright accent), not a second colorful highlight
   in the same family as the RFQ button — both spans already read as
   the same near-solid dark navy tone, so standardizing this one too
   would collapse the whole heading to one flat color and lose the
   alternating-emphasis design. Flagged to the user rather than
   changed silently. */
.trust-heading .grad-navy-charcoal {
  background: var(--gradient-navy-charcoal);
  background-clip: text;
  -webkit-background-clip: text;
  color: transparent;
}

.trust-grid {
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  gap: 28px;
}

.trust-card h3 {
  background: var(--gradient-logo);
  background-clip: text;
  -webkit-background-clip: text;
  color: transparent;
  font-weight: 800;
}

/* Scroll-linked stacked-center -> grid toggle (added 2026-08-25,
   follow-up fix, item 12) — reuses the same single-threshold
   IntersectionObserver toggle pattern as every other section above
   (see initTrustCardsStack() in script.js). Scoped to `.trust-grid`
   specifically (not `.trust-card` alone) so it only ever applies to
   this section's 4 cards, not the separate "Integrated Capabilities"
   section further down the page, which reuses the same `.trust-card`
   base class but lives in `.capabilities-grid` instead. `--stack-x`/
   `--stack-y` are per-card custom properties computed and written in
   JS (each card's own real distance back to the grid's center point —
   different for every card, unlike a fixed slide distance), with a
   0px fallback here only for the split-second before script.js's own
   init pass has run. Uses `translate`/`scale` (not `transform`) for
   the same hover-lift-composability reason as every other entrance
   effect on this site — `.trust-card` is also `.industry-card`, so it
   already has `.industry-card:hover`'s `transform: translateY(-4px)`. */
.trust-grid .trust-card.card-stack {
  opacity: 0;
  scale: 0.85;
  translate: var(--stack-x, 0px) var(--stack-y, 0px);
  transition: opacity 0.6s ease-out, scale 0.6s ease-out, translate 0.6s ease-out;
}

.trust-grid .trust-card.card-stack.is-visible {
  opacity: 1;
  scale: 1;
  translate: 0 0;
}

@media (prefers-reduced-motion: reduce) {
  .trust-grid .trust-card.card-stack {
    opacity: 1;
    scale: 1;
    translate: 0 0;
    transition: none;
  }
}

@media (max-width: 1024px) {
  .trust-grid {
    grid-template-columns: repeat(2, 1fr);
  }
}

@media (max-width: 560px) {
  .trust-grid {
    grid-template-columns: 1fr;
  }

  .trust-section {
    padding: 64px 0;
  }
}

/* =========================================================
   About Us page — Capabilities section
   ========================================================= */

.capabilities-grid {
  display: grid;
  grid-template-columns: 1fr;
  gap: 24px;
  position: relative;
}

/* Subtle vertical connecting line (added 2026-08-25, follow-up fix,
   item 15) — running behind the 4 stacked cards, reinforcing them as
   sequential lifecycle stages, in the same spirit as (but visually
   simpler than) the branching connector used in the 6-card process
   sections elsewhere on this page/site: this grid is a single column,
   so a plain vertical gradient line (no path/dot animation needed)
   already reads the same way. z-index'd behind the cards explicitly
   (see .capability-card below) so it only ever shows in the gaps
   between cards, never across their opaque white faces. */
.capabilities-grid::before {
  content: "";
  position: absolute;
  top: 0;
  bottom: 0;
  left: 50%;
  width: 2px;
  margin-left: -1px;
  background: linear-gradient(180deg, var(--color-navy), var(--color-seagreen));
  opacity: 0.22;
  z-index: 0;
}

.capability-card {
  padding: 18px 40px;
  border-radius: 32px;
  position: relative;
  z-index: 1;
}

/* Left-edge accent bar (added 2026-08-25, follow-up fix, item 15) —
   thin brand-gradient strip along each card's left edge, purely
   decorative. Matches the card's own border-radius (32px, overridden
   from .industry-card's base 14px, see the rule above) on its two left
   corners so it reads as part of the card's shape, not a separate box
   laid on top. */
.capability-card::before {
  content: "";
  position: absolute;
  left: 0;
  top: 0;
  bottom: 0;
  width: 5px;
  background: var(--gradient-brand);
  border-radius: 32px 0 0 32px;
}

.capability-card h3 {
  font-size: 22px;
}

.capability-card p {
  font-size: 16.5px;
  max-width: none;
}

/* Staggered fade + slide-up scroll toggle (added 2026-08-25, follow-up
   fix, item 15) — reuses the same single-threshold IntersectionObserver
   toggle infrastructure as every other staggered-entrance section above
   (JS-set per-card transition-delay, persistent state class, see
   initCapabilityCardsReveal() in script.js), with a fade + translateY
   visual specifically (per the brief) rather than the scale-based pop
   used for the 6-card sections — cards 1-4 rise into place one after
   another as the section scrolls into view, top to bottom, since this
   grid is always a single column. Uses `translate` (not `transform`)
   for the same hover-lift-composability reason as every other entrance
   effect on this site. */
.capabilities-grid .capability-card.card-slide-up {
  opacity: 0;
  translate: 0 32px;
  transition: opacity 0.55s ease-out, translate 0.55s ease-out;
}

.capabilities-grid .capability-card.card-slide-up.is-shown {
  opacity: 1;
  translate: 0 0;
}

@media (prefers-reduced-motion: reduce) {
  .capabilities-grid .capability-card.card-slide-up {
    opacity: 1;
    translate: 0 0;
    transition: none;
  }
}

/* =========================================================
   About Us page — Global Reach section
   ========================================================= */

.reach-section {
  padding: 90px 0 110px;
  background: linear-gradient(
    180deg,
    #ffffff 0%,
    var(--color-grey-medium) 6%,
    #22252c 20%,
    #22252c 58%,
    #4a4e57 74%,
    #9498a0 88%,
    var(--color-grey-light) 100%
  );
}

.reach-caption {
  text-align: center;
  max-width: 720px;
  margin: 0 auto 44px;
}

.reach-heading {
  color: #ffffff;
  font-size: clamp(26px, 3.4vw, 38px);
  font-weight: 800;
  line-height: 1.3;
  margin-bottom: 16px;
}

.reach-text {
  color: rgba(255, 255, 255, 0.82);
  font-size: 16px;
  line-height: 1.7;
}

.reach-image-wrap {
  padding-left: 48px;
  padding-right: 48px;
  margin-bottom: 36px;
}

.reach-map-frame {
  position: relative;
  border-radius: 16px;
  overflow: hidden;
}

.reach-image {
  display: block;
  width: 100%;
  height: auto;
  border-radius: 16px;
}

/* Animated map overlay (added 2026-08-25, follow-up fix, item 14) —
   reuses the same single-threshold IntersectionObserver toggle pattern
   as every other section above (see initReachMapAnimation() in
   script.js), observing the whole .reach-section so the map's own
   entrance and the "60+ Countries" count-up below it (which reuses the
   exact same animateCounter()/resetCounter() helpers Home's and About's
   intro stats already use) replay together. Color (#08abf3) was sampled
   directly from the underlying photo's own glowing line pixels so the
   new vector lines read as a continuation of the photo, not a mismatched
   overlay — see the HTML comment above this markup for the coordinate-
   sampling method. */
.reach-map-overlay {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  pointer-events: none;
}

.reach-line {
  fill: none;
  stroke: #08abf3;
  stroke-width: 2.5;
  stroke-linecap: round;
  opacity: 0.9;
  /* pathLength="1" on each <path> (see HTML) normalizes stroke-dasharray/
     -dashoffset to a 0-1 scale regardless of each line's real geometric
     length, so every line can share this exact same draw-in rule
     without needing its own individually-measured dash value. */
  stroke-dasharray: 1;
  stroke-dashoffset: 1;
  transition: stroke-dashoffset 1s ease-out;
}

.reach-map-overlay.is-visible .reach-line {
  stroke-dashoffset: 0;
}

/* The traveling pulse dots and the hub's breathing glow are gated on
   the same toggle via opacity only — their own looping animations
   (animateMotion / the keyframes below) keep running underneath at all
   times, same as this site's other continuous decorative loops (the
   process-section connector dot); only their visibility follows the
   scroll toggle, which is the simpler equivalent of "pause/resume" the
   brief allowed for a loop that isn't a one-shot entrance. */
.reach-pulses,
.reach-hub-pulse {
  opacity: 0;
  transition: opacity 0.5s ease-out;
}

.reach-map-overlay.is-visible .reach-pulses,
.reach-map-overlay.is-visible .reach-hub-pulse {
  opacity: 1;
}

.reach-pulse-dot {
  fill: #ffffff;
}

.reach-hub-ring {
  fill: none;
  stroke: #08abf3;
  stroke-width: 2.5;
  animation: reach-hub-pulse 2.4s ease-out infinite;
}

.reach-hub-ring-2 {
  animation-delay: 1.2s;
}

@keyframes reach-hub-pulse {
  0% {
    r: 20;
    opacity: 0.9;
  }
  100% {
    r: 60;
    opacity: 0;
  }
}

@media (prefers-reduced-motion: reduce) {
  .reach-line {
    stroke-dashoffset: 0;
    transition: none;
  }

  .reach-pulses,
  .reach-hub-pulse {
    display: none;
  }
}

.reach-stat {
  text-align: center;
  color: rgba(255, 255, 255, 0.85);
  font-size: 15px;
  font-weight: 500;
  margin-top: 8px;
}

.reach-stat-number {
  font-weight: 800;
  color: #ffffff;
}

@media (max-width: 640px) {
  .reach-section {
    padding: 64px 0 70px;
  }

  .reach-image-wrap {
    padding-left: 20px;
    padding-right: 20px;
  }
}

/* =========================================================
   Shared — gradient text utility (sitewide standard, matches RFQ button)
   ========================================================= */

/* Apply to any element (or inline <span> wrapping part of a heading)
   that needs gradient-clipped text matching the RFQ button's exact
   gradient. This is the standard, reusable way to add new gradient
   text going forward — existing gradient-text rules elsewhere in this
   file reference --gradient-logo directly in their own selectors
   instead of this class (so their other properties — font-size,
   weight, spacing, etc. — didn't need touching when they were
   standardized to this same gradient), but either approach yields an
   identical result since both point at the one shared variable. */
.grad-text {
  background: var(--gradient-logo);
  background-clip: text;
  -webkit-background-clip: text;
  color: transparent;
}

/* =========================================================
   Shared — small uppercase label above a section heading
   ========================================================= */

.section-eyebrow {
  display: block;
  color: var(--color-seagreen);
  font-size: 13px;
  font-weight: 700;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  margin-bottom: 14px;
}

/* =========================================================
   About Us page — Procurement performance / process section
   ========================================================= */

.perf-section {
  padding: 96px 0;
  background: #f4f7f9;
}

.perf-caption {
  text-align: center;
  max-width: 760px;
  margin: 0 auto 56px;
}

.perf-heading {
  font-size: clamp(26px, 3.4vw, 38px);
  font-weight: 800;
  color: var(--color-navy);
  line-height: 1.35;
  margin-bottom: 18px;
}

.perf-intro {
  color: var(--color-text-secondary);
  font-size: 16px;
  line-height: 1.7;
}

.perf-grid-wrap {
  position: relative;
}

.perf-connector {
  display: none;
}

.perf-process-grid {
  display: grid;
  grid-template-columns: 1fr;
  gap: 24px;
}

.perf-card {
  position: relative;
  border: 1px solid var(--color-border);
  border-radius: 16px;
  /* Soft lift shadow (added 2026-08-24, modernization pass) — this
     card had a border but no shadow, unlike .industry-card/
     .enquiry-form-panel elsewhere on this site; a small background
     tint was added alongside it (this card previously had none, just
     showing the section's own #f4f7f9 through it) since a shadow with
     nothing solid to "cast" from read oddly on its own. */
  background: var(--color-bg);
  box-shadow: 0 10px 26px rgba(11, 37, 69, 0.06);
}

.perf-card-head {
  width: 100%;
  display: flex;
  align-items: center;
  justify-content: space-between;
  margin-bottom: 16px;
}

.perf-number {
  background: var(--gradient-logo);
  background-clip: text;
  -webkit-background-clip: text;
  color: transparent;
  font-size: 24px;
  font-weight: 800;
}

.perf-icon-svg {
  width: 28px;
  height: 28px;
  fill: none;
  stroke: url(#perfIconGrad);
  stroke-width: 2.2;
  stroke-linecap: round;
  stroke-linejoin: round;
}

.perf-card h3 {
  font-size: 17px;
  font-weight: 700;
  color: var(--color-heading-dark);
  line-height: 1.35;
  margin-bottom: 10px;
}

.perf-desc {
  color: var(--color-text);
  font-size: 13.5px;
  line-height: 1.6;
  margin-bottom: 14px;
}

.perf-outcome {
  width: 100%;
  display: flex;
  align-items: center;
  gap: 6px;
  margin-top: auto;
  padding-top: 12px;
  border-top: 1px solid var(--color-border);
  color: #006b5e;
  font-size: 12.5px;
  font-weight: 700;
  line-height: 1.4;
}

.perf-outcome-icon {
  flex-shrink: 0;
  width: 15px;
  height: 15px;
  fill: none;
  stroke: #006b5e;
  stroke-width: 2.2;
  stroke-linecap: round;
  stroke-linejoin: round;
}

@media (min-width: 900px) {
  .perf-grid-wrap {
    max-width: 1100px;
    margin: 0 auto;
  }

  .perf-connector {
    display: block;
    position: absolute;
    inset: 0;
    width: 100%;
    height: 100%;
    pointer-events: none;
  }

  .perf-process-grid {
    grid-template-columns: repeat(3, 1fr);
    grid-template-rows: repeat(2, auto);
    column-gap: 3.6364%;
    row-gap: 64px;
  }

  .perf-card {
    margin: 0;
    height: 100%;
  }
}

@media (max-width: 899px) {
  .perf-process-grid {
    position: relative;
    padding-left: 34px;
  }

  .perf-process-grid::before {
    content: "";
    position: absolute;
    left: 4px;
    top: 6px;
    bottom: 6px;
    width: 2px;
    background: var(--gradient-brand);
  }

  .perf-card::before {
    content: "";
    position: absolute;
    left: -32px;
    top: 34px;
    width: 12px;
    height: 12px;
    border-radius: 50%;
    background: var(--gradient-brand);
    box-shadow: 0 0 0 4px #f4f7f9;
  }
}

.perf-impact {
  margin-top: 64px;
  padding-top: 44px;
  border-top: 1px solid var(--color-border);
  text-align: center;
}

.perf-impact-heading {
  font-size: 20px;
  font-weight: 800;
  color: var(--color-navy);
  margin-bottom: 28px;
}

.perf-impact-row {
  display: flex;
  justify-content: space-between;
  gap: 20px;
  flex-wrap: wrap;
}

.perf-impact-item {
  flex: 1 1 0;
  min-width: 140px;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 10px;
}

.perf-impact-icon {
  width: 30px;
  height: 30px;
  fill: none;
  /* Fixed 2026-08-25 (follow-up to item 16) — was url(#perfIconGrad),
     the navy->seagreen pair shared with the unrelated 6-card process
     icons (.perf-icon-svg above); at this icon's small size that
     particular gradient read as flat dark navy since both stops are
     similarly dark. #perfImpactIconGrad (defined in about.html, right
     after #perfIconGrad) matches .btn-primary's own literal gradient
     value exactly instead. */
  stroke: url(#perfImpactIconGrad);
  stroke-width: 1.8;
  stroke-linecap: round;
  stroke-linejoin: round;
  /* Draw-in (added 2026-08-25, follow-up fix, item 16) — same
     pathLength="1"-normalized stroke-dasharray/-dashoffset technique as
     the world-map connection lines (item 14). stroke-dasharray/
     -dashoffset are inheritable SVG presentation properties, same as
     `stroke` above (already relying on that exact inheritance through
     `<use>` to reach `url(#perfIconGrad)`), so setting them here on the
     outer <svg class="perf-impact-icon"> cascades down into each
     symbol's own <path>/<circle> sub-shapes (each individually given
     pathLength="1" where the symbols are defined, further up this same
     file) without needing to touch the <use> instances themselves. */
  stroke-dasharray: 1;
  stroke-dashoffset: 1;
  transition: stroke-dashoffset 0.6s ease-out;
}

.perf-impact-item.item-fade-up.is-shown .perf-impact-icon {
  stroke-dashoffset: 0;
}

.perf-impact-item span {
  color: var(--color-text);
  font-size: 14px;
  font-weight: 600;
}

/* Staggered fade + slide-up scroll toggle (added 2026-08-25, follow-up
   fix, item 16) — same single-threshold IntersectionObserver toggle
   infrastructure as every other staggered-entrance section on this page
   (JS-set per-item transition-delay, persistent state class, see
   initPerfImpactReveal() in script.js) — icon + label move together as
   one unit per item, left to right. Deliberately left the existing
   .perf-section.reveal .perf-impact whole-strip fade (further up this
   file) untouched rather than removing it — that's a broad section-
   level entrance, this is per-item detail on top of it, the same
   parent/child relationship already established for .trust-section's
   own whole-section fade coexisting with .trust-grid's per-card
   stack-to-center animation (item 12), not a literal duplicate of the
   same effect on the same element the way item 13's old per-card
   stagger was. */
.perf-impact-item.item-fade-up {
  opacity: 0;
  translate: 0 20px;
  transition: opacity 0.5s ease-out, translate 0.5s ease-out;
}

.perf-impact-item.item-fade-up.is-shown {
  opacity: 1;
  translate: 0 0;
}

@media (prefers-reduced-motion: reduce) {
  .perf-impact-icon {
    stroke-dashoffset: 0;
    transition: none;
  }

  .perf-impact-item.item-fade-up {
    opacity: 1;
    translate: 0 0;
    transition: none;
  }
}

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

  .perf-impact-row {
    justify-content: flex-start;
  }

  .perf-impact-item {
    flex: 1 1 42%;
    min-width: 0;
  }
}

/* Scroll-in stagger for the impact strip, layered on top of the shared
   .reveal fade (see "Scroll-in section reveal" above). Gated on .reveal
   so it only activates when JS/IntersectionObserver added that class —
   keeps the no-JS fallback fully visible. The 6 process cards
   themselves USED to have their own nested stagger here too
   (`.perf-section.reveal .perf-card` + `.card-1`-`.card-6` delays,
   opacity + translateY(28px), one-shot via the generic .reveal system)
   — removed 2026-08-25, follow-up fix, item 13, when this section was
   given the same scale+fade pop toggle as the homepage's equivalent
   6-card grid (see .perf-process-grid .perf-card.card-pop below and
   initCardPopGrid() in script.js). Keeping both would have double-
   animated every card — this translateY slide-up firing from the
   generic .reveal system watching `.perf-section`, at the same time as
   the new scale-based pop firing from its own separate observer
   watching `.perf-process-grid` — and the brief specifically asked for
   this section to look and behave identically to the homepage version,
   which never had this extra effect to begin with. */

.perf-section.reveal .perf-impact {
  opacity: 0;
  transform: translateY(24px);
  transition: opacity 0.6s cubic-bezier(0.22, 1, 0.36, 1) 0.65s, transform 0.6s cubic-bezier(0.22, 1, 0.36, 1) 0.65s;
}

.perf-section.reveal.is-visible .perf-impact {
  opacity: 1;
  transform: none;
}

@media (prefers-reduced-motion: reduce) {
  .perf-section.reveal .perf-impact {
    opacity: 1;
    transform: none;
    transition: none;
  }
}

/* =========================================================
   About Us page — Closing CTA section
   Deliberately standalone: fresh classes only, no dependency on the
   Home page's .cta / .cta-bg / .cta-heading / .grad-logo implementation.
   Visual values (colors, gradient stops, spacing) match the Home CTA by
   design, but nothing here references or extends it — editing one will
   never affect the other. (Shared sitewide primitives like --color-navy
   and the .btn/.btn-primary/.btn-hover-grow button system are reused
   deliberately, same as everywhere else on the site, including this
   page's own hero button above.)
   ========================================================= */

.about-cta {
  position: relative;
  background: var(--color-navy);
  overflow: hidden;
  padding: 120px 0;
}

.about-cta-bg {
  position: absolute;
  inset: 0;
  background-size: cover;
  background-position: center;
  opacity: 0.65;
}

.about-cta-content {
  position: relative;
  z-index: 1;
  max-width: 760px;
  margin: 0 auto;
  text-align: center;
}

.about-cta-heading {
  color: #ffffff;
  font-size: clamp(28px, 4vw, 42px);
  font-weight: 800;
  line-height: 1.3;
  margin-bottom: 24px;
}

.about-cta-grad {
  background: var(--gradient-logo);
  background-clip: text;
  -webkit-background-clip: text;
  color: transparent;
}

.about-cta-desc {
  color: #ffffff;
  font-size: 16px;
  line-height: 1.7;
  opacity: 0.92;
  margin-bottom: 36px;
}

@media (max-width: 640px) {
  .about-cta {
    padding: 80px 0;
  }
}

/* Silver glow border — the one intentional visual difference from the
   Home CTA button. Combined with .btn-hover-grow's own hover shadow in
   a single rule below since box-shadow values don't merge across
   separate rules (the later rule would otherwise silently replace it). */

.btn-glow-silver {
  box-shadow: 0 0 0 1px rgba(192, 192, 192, 0.5), 0 0 14px 2px rgba(192, 192, 192, 0.3);
}

.btn-glow-silver.btn-hover-grow:hover {
  box-shadow: 0 10px 24px rgba(0, 0, 0, 0.3), 0 0 0 1px rgba(192, 192, 192, 0.65), 0 0 20px 4px rgba(192, 192, 192, 0.4);
}

/* =========================================================
   Contact Us page — Hero
   ========================================================= */

.contact-hero {
  position: relative;
  min-height: 520px;
  display: flex;
  align-items: center;
  overflow: hidden;
  padding: 64px 0;
  background: var(--color-navy);
}

/* 120%/-10% oversize for scroll parallax slack — see .page-hero-bg's
   own comment above for the full explanation, same pattern here. */
.contact-hero-bg {
  position: absolute;
  top: -20%;
  left: 0;
  right: 0;
  height: 140%;
  background-size: cover;
  background-position: center;
  will-change: transform;
}

.contact-hero-overlay {
  position: absolute;
  inset: 0;
  background: linear-gradient(
    100deg,
    rgba(11, 37, 69, 0.94) 0%,
    rgba(11, 37, 69, 0.82) 35%,
    rgba(11, 37, 69, 0.5) 70%,
    rgba(11, 37, 69, 0.22) 100%
  );
}

.contact-hero-content {
  position: relative;
  z-index: 1;
  max-width: 620px;
}

.contact-hero .section-eyebrow {
  color: #7fdbda;
}

.contact-hero-heading {
  color: #ffffff;
  font-weight: 800;
  font-size: clamp(30px, 4.2vw, 46px);
  line-height: 1.3;
  margin: 14px 0 20px;
}

.contact-hero-grad {
  background: var(--gradient-logo);
  background-clip: text;
  -webkit-background-clip: text;
  color: transparent;
}

.contact-hero-desc {
  color: rgba(255, 255, 255, 0.92);
  font-size: 16.5px;
  line-height: 1.7;
  max-width: 560px;
  margin-bottom: 20px;
}

.contact-hero-trustline {
  color: rgba(255, 255, 255, 0.75);
  font-size: 13px;
  letter-spacing: 0.02em;
  margin-bottom: 32px;
}

@media (max-width: 640px) {
  .contact-hero {
    padding: 56px 0;
  }

  .contact-hero-overlay {
    background: linear-gradient(
      180deg,
      rgba(11, 37, 69, 0.94) 0%,
      rgba(11, 37, 69, 0.88) 60%,
      rgba(11, 37, 69, 0.75) 100%
    );
  }
}

/* =========================================================
   Contact Us page — Main Contact & Enquiry section
   ========================================================= */

.enquiry-section {
  padding: 96px 0;
  background: var(--color-bg);
}

.enquiry-caption {
  text-align: center;
  max-width: 720px;
  margin: 0 auto 56px;
}

.enquiry-heading {
  font-size: clamp(26px, 3.4vw, 38px);
  font-weight: 800;
  color: var(--color-navy);
  line-height: 1.35;
  margin-bottom: 18px;
}

.enquiry-intro {
  color: var(--color-text-secondary);
  font-size: 16px;
  line-height: 1.7;
}

.enquiry-caption-btn {
  margin-top: 28px;
}

/* Request a Quote page: the form is the only column now, centered with
   its own moderate max-width rather than the old 65%-of-1240px share. */
.quote-form-wrap {
  max-width: 760px;
  margin: 0 auto;
}

/* Contact Us page: Contact Information panel (65%) + the "Send Us a
   Message" form panel (35%). Uses this page's own 1240px/28px
   split-layout numbers rather than the generic .container's 1200px
   max-width. align-items: stretch (the flex default, stated explicitly
   here since a previous version of this rule set flex-start) makes
   both columns match the taller one's height, so the message-form
   card's white box lines up with the Contact Information panel's
   bottom edge even though it has less content. */
.contact-info-split {
  max-width: 1240px;
  margin: 0 auto;
  padding: 0 24px;
  display: flex;
  gap: 28px;
  align-items: stretch;
}

.contact-info-form-slot {
  flex: 0 0 35%;
  max-width: 35%;
}

/* --- Left panel: contact information --- */

.contact-info-panel {
  position: relative;
  overflow: hidden;
  flex: 0 0 65%;
  max-width: 65%;
  background: var(--gradient-blue);
  border-radius: 18px;
  padding: 44px 36px;
  color: #ffffff;
  /* Soft lift shadow (added 2026-08-24, modernization pass) — its
     sibling .enquiry-form-panel already had one; this panel didn't,
     so the two sides of the page read inconsistently "flat vs.
     floating." Same shadow color/softness as .industry-card and
     .enquiry-form-panel elsewhere, just a touch stronger to read
     against this panel's own dark gradient background. */
  box-shadow: 0 14px 34px rgba(11, 37, 69, 0.16);
}

/* Below this width the two columns stack, each full-width (the mobile
   flex-basis gotcha — see CLAUDE.md — otherwise the 65%/35% basis
   values would apply to height, not width, in column direction). This
   must come after the base .contact-info-split/.contact-info-panel
   rules above (same selector specificity), otherwise the unconditional
   65%/35% max-widths would win over this media query by source order
   alone, regardless of viewport — confirmed this was actually
   happening before being moved here. */
@media (max-width: 860px) {
  .contact-info-split {
    flex-direction: column;
  }

  .contact-info-panel,
  .contact-info-form-slot {
    max-width: 100%;
    width: 100%;
  }
}

.contact-info-graphic {
  position: absolute;
  top: -60px;
  right: -80px;
  width: 360px;
  height: 360px;
  fill: none;
  stroke: #ffffff;
  stroke-width: 1.2;
  opacity: 0.1;
  pointer-events: none;
}

.contact-info-label {
  position: relative;
  display: block;
  color: #7fdbda;
  font-size: 13px;
  font-weight: 700;
  letter-spacing: 0.14em;
  text-transform: uppercase;
}

.contact-info-accent-bar {
  position: relative;
  display: block;
  width: 40px;
  height: 3px;
  background: var(--color-seagreen);
  margin: 14px 0 18px;
}

.contact-info-heading {
  position: relative;
  color: #ffffff;
  font-size: clamp(22px, 2.4vw, 26px);
  font-weight: 800;
  line-height: 1.3;
  margin-bottom: 14px;
}

.contact-info-intro {
  position: relative;
  color: rgba(255, 255, 255, 0.85);
  font-size: 14.5px;
  line-height: 1.7;
  margin-bottom: 28px;
}

.contact-info-block {
  position: relative;
  padding-top: 18px;
  margin-top: 18px;
  border-top: 1px solid rgba(255, 255, 255, 0.14);
}

.contact-info-block:first-of-type {
  border-top: 1px solid rgba(255, 255, 255, 0.14);
}

.contact-info-block-label {
  color: rgba(255, 255, 255, 0.6);
  font-size: 12px;
  font-weight: 700;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  margin-bottom: 6px;
}

.contact-info-value {
  display: inline-block;
  color: #ffffff;
  font-size: 15px;
  font-weight: 600;
  line-height: 1.6;
}

.contact-info-link:hover {
  text-decoration: underline;
}

.contact-info-helper {
  color: rgba(255, 255, 255, 0.65);
  font-size: 13px;
  line-height: 1.5;
  margin-top: 5px;
}

.contact-hours-list {
  list-style: none;
}

.contact-hours-list li {
  display: flex;
  justify-content: space-between;
  gap: 12px;
  font-size: 14px;
  padding: 4px 0;
}

.contact-hours-list li span:first-child {
  color: rgba(255, 255, 255, 0.75);
}

.contact-hours-list li span:last-child {
  color: #ffffff;
  font-weight: 600;
  text-align: right;
}

/* WhatsApp CTA button — visual only for now, see the TODO comment
   beside its placeholder href in contact.html. The phone number is
   deliberately not shown as text anywhere in this block, only via
   this button once it's wired up to a real wa.me link. */
.whatsapp-btn {
  display: inline-flex;
  align-items: center;
  gap: 10px;
  padding: 10px 22px;
  border-radius: 999px;
  background: #25d366;
  color: #ffffff;
  font-size: 14px;
  font-weight: 700;
  letter-spacing: 0.02em;
  text-decoration: none;
  transition: transform 0.2s ease, box-shadow 0.2s ease, background 0.2s ease;
}

.whatsapp-btn:hover {
  background: #1ebe5a;
  transform: translateY(-2px);
  box-shadow: 0 10px 22px rgba(0, 0, 0, 0.25);
}

.whatsapp-icon {
  width: 18px;
  height: 18px;
  flex-shrink: 0;
}

.contact-info-trustnote {
  position: relative;
  margin-top: 28px;
  background: rgba(255, 255, 255, 0.08);
  border: 1px solid rgba(255, 255, 255, 0.16);
  border-radius: 12px;
  padding: 18px 20px;
}

.contact-info-trustnote h4 {
  color: #7fdbda;
  font-size: 14px;
  font-weight: 800;
  margin-bottom: 6px;
}

.contact-info-trustnote p {
  color: rgba(255, 255, 255, 0.82);
  font-size: 13.5px;
  line-height: 1.6;
}

/* --- Right panel (Contact Us page only): "Send Us a Message" form.
   Independently classed from .enquiry-form-panel below (that one
   belongs to request-a-quote.html's Request a Quote form) even though
   the visual recipe — white card, light-grey border, soft shadow — is
   the same, per this project's independent-classes convention. height:
   100% is what actually makes this card match the Contact Information
   panel's bottom edge; the equal-height behavior itself comes from
   .contact-info-split's align-items: stretch. --- */

.message-form-panel {
  height: 100%;
  background: #ffffff;
  border: 1px solid var(--color-border);
  border-radius: 18px;
  padding: 44px 36px;
  box-shadow: 0 10px 30px rgba(11, 37, 69, 0.08);
}

.message-form-heading {
  color: var(--color-navy);
  font-size: clamp(20px, 2.2vw, 24px);
  font-weight: 800;
  margin-top: 4px;
  margin-bottom: 8px;
}

.message-form-subtext {
  color: var(--color-text-secondary);
  font-size: 14px;
  line-height: 1.6;
  margin-bottom: 28px;
}

.message-submit-btn {
  display: block;
  width: 100%;
  margin-top: 8px;
  padding: 16px;
  font-size: 15px;
  text-align: center;
}

.message-form-note {
  margin-top: 20px;
  font-size: 12.5px;
  color: var(--color-text-secondary);
  line-height: 1.6;
}

.message-form-note a {
  color: var(--color-seagreen);
  font-weight: 700;
  text-decoration: none;
}

.message-form-note a:hover {
  text-decoration: underline;
}

/* --- Right panel (Request a Quote page): enquiry form --- */

.enquiry-form-panel {
  background: #ffffff;
  border: 1px solid var(--color-border);
  border-radius: 18px;
  padding: 44px 40px;
  box-shadow: 0 10px 30px rgba(11, 37, 69, 0.08);
}

.enquiry-form-heading {
  color: var(--color-navy);
  font-size: clamp(20px, 2.2vw, 24px);
  font-weight: 800;
  margin-bottom: 8px;
}

.enquiry-form-subtext {
  color: var(--color-text-secondary);
  font-size: 14px;
  line-height: 1.6;
  margin-bottom: 32px;
}

.form-grid-2 {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 4px 24px;
  margin-bottom: 20px;
}

.form-field {
  display: flex;
  flex-direction: column;
  margin-bottom: 20px;
}

.form-field label,
.field-label {
  font-size: 13.5px;
  font-weight: 700;
  color: var(--color-heading-dark);
  margin-bottom: 7px;
}

.required-mark {
  color: #c0392b;
}

/* Label + "?" tooltip icon, sitting on the same row above a field */

.field-label-row {
  display: flex;
  align-items: center;
  gap: 6px;
  margin-bottom: 7px;
}

.field-label-row label {
  margin-bottom: 0;
}

.tooltip-wrap {
  position: relative;
  display: inline-flex;
}

.tooltip-icon {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 16px;
  height: 16px;
  border-radius: 50%;
  border: 1px solid var(--color-border);
  background: var(--color-bg-alt);
  color: var(--color-text-secondary);
  font-size: 11px;
  font-weight: 700;
  line-height: 1;
  cursor: pointer;
  padding: 0;
}

.tooltip-icon:hover,
.tooltip-icon:focus-visible {
  background: var(--color-seagreen);
  border-color: var(--color-seagreen);
  color: #ffffff;
  outline: none;
}

.tooltip-popup {
  position: absolute;
  bottom: calc(100% + 8px);
  left: 50%;
  transform: translateX(-50%);
  width: max-content;
  max-width: min(230px, calc(100vw - 32px));
  background: var(--color-navy);
  color: #ffffff;
  font-size: 12px;
  font-weight: 500;
  line-height: 1.5;
  padding: 8px 12px;
  border-radius: 8px;
  box-shadow: 0 8px 20px rgba(11, 37, 69, 0.25);
  z-index: 30;
  opacity: 0;
  visibility: hidden;
  transform: translateX(-50%) translateY(4px);
  transition: opacity 0.15s ease, transform 0.15s ease, visibility 0.15s;
  pointer-events: none;
}

.tooltip-popup::after {
  content: "";
  position: absolute;
  top: 100%;
  left: 50%;
  transform: translateX(-50%);
  border: 5px solid transparent;
  border-top-color: var(--color-navy);
}

.tooltip-icon:hover + .tooltip-popup,
.tooltip-icon:focus-visible + .tooltip-popup,
.tooltip-wrap.is-open .tooltip-popup {
  opacity: 1;
  visibility: visible;
  transform: translateX(-50%) translateY(0);
}

.enquiry-form input[type="text"],
.enquiry-form input[type="email"],
.enquiry-form input[type="tel"],
.enquiry-form input[type="url"],
.enquiry-form input[type="date"],
.enquiry-form select,
.enquiry-form textarea {
  width: 100%;
  padding: 12px 14px;
  border: 1px solid var(--color-border);
  border-radius: 8px;
  background: #ffffff;
  color: var(--color-text);
  font-family: inherit;
  font-size: 14.5px;
  transition: border-color 0.2s ease, box-shadow 0.2s ease;
}

.enquiry-form input:focus,
.enquiry-form select:focus,
.enquiry-form textarea:focus {
  outline: none;
  border-color: var(--color-seagreen);
  box-shadow: 0 0 0 3px rgba(47, 111, 94, 0.15);
}

.enquiry-form input::placeholder,
.enquiry-form textarea::placeholder {
  color: var(--color-text-secondary);
  opacity: 0.75;
}

.enquiry-form input:disabled {
  background: var(--color-bg-alt);
  color: var(--color-text-secondary);
  cursor: not-allowed;
}

.enquiry-form textarea {
  resize: vertical;
}

.field-helper {
  font-size: 12.5px;
  color: var(--color-text-secondary);
  line-height: 1.5;
  margin-top: 6px;
}

/* Phone number with country-code selector */

.phone-input-group {
  display: flex;
  gap: 8px;
}

.phone-code-select {
  flex: 0 0 132px;
}

.phone-input-group input[type="tel"] {
  flex: 1;
}

/* Checkbox */

.checkbox-field {
  display: flex;
  align-items: center;
  gap: 8px;
  font-size: 13.5px;
  color: var(--color-text-secondary);
  margin-top: 10px;
  cursor: pointer;
}

.checkbox-field input[type="checkbox"] {
  width: 16px;
  height: 16px;
  accent-color: var(--color-seagreen);
}

/* File upload */

.file-upload-wrap {
  display: flex;
  align-items: center;
  gap: 14px;
  flex-wrap: wrap;
  padding: 14px 16px;
  border: 1.5px dashed var(--color-border);
  border-radius: 10px;
  background: var(--color-bg-alt);
  transition: border-color 0.2s ease, background 0.2s ease;
}

/* Drag-over state, set/cleared by initFileUpload() in script.js while a
   file is dragged over this field — same accent tint as the enquiry
   form's own input focus state (.enquiry-form input:focus above), so it
   reads as "this field is active" rather than a new visual language. */
.file-upload-wrap.is-dragover {
  border-color: var(--color-seagreen);
  background: rgba(47, 111, 94, 0.08);
}

.file-upload-wrap input[type="file"] {
  position: absolute;
  width: 1px;
  height: 1px;
  padding: 0;
  margin: -1px;
  overflow: hidden;
  clip: rect(0, 0, 0, 0);
  white-space: nowrap;
  border: 0;
}

.file-upload-btn {
  display: inline-flex;
  align-items: center;
  padding: 10px 20px;
  border: 1px solid var(--color-border);
  border-radius: 8px;
  background: var(--color-bg-alt);
  color: var(--color-navy);
  font-size: 13.5px;
  font-weight: 700;
  cursor: pointer;
  transition: background 0.2s ease;
}

.file-upload-btn:hover {
  background: var(--color-grey-medium);
}

.file-upload-hint {
  font-size: 12px;
  color: var(--color-text-secondary);
  line-height: 1.6;
}

/* Small helper note under Product Details/Special Requests — same
   font-size/color as .file-upload-hint above (independent class per
   this project's convention, values copied rather than shared, since
   this one needs its own margin-top to sit below a field rather than
   inline beside a button). */
.product-details-hint {
  margin-top: 8px;
  font-size: 12px;
  color: var(--color-text-secondary);
  line-height: 1.5;
}

.product-details-hint strong {
  font-weight: 700;
}

.file-list {
  list-style: none;
  margin-top: 12px;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: 6px;
}

.file-list li {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  font-size: 13px;
  color: var(--color-text);
  background: var(--color-bg-alt);
  padding: 8px 12px;
  border-radius: 6px;
}

.file-list-name {
  flex: 1;
  min-width: 0;
  word-break: break-word;
}

/* Per-row remove control. Reuses .file-error's #c0392b as the hover/
   focus color (same "removal" red already established on this exact
   field) rather than introducing a new accent color. */
.file-list-remove {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  flex-shrink: 0;
  width: 22px;
  height: 22px;
  padding: 0;
  border: none;
  border-radius: 50%;
  background: transparent;
  color: var(--color-text-secondary);
  font-size: 16px;
  line-height: 1;
  cursor: pointer;
  transition: background 0.2s ease, color 0.2s ease;
}

.file-list-remove:hover,
.file-list-remove:focus-visible {
  background: var(--color-grey-medium);
  color: #c0392b;
}

.file-error {
  color: #c0392b;
  font-size: 13px;
  margin-top: 10px;
}

.file-error[hidden] {
  display: none;
}

/* Honeypot (hidden from real visitors, left in the tab flow for bots
   that fill every field indiscriminately) */

.hp-field {
  position: absolute;
  left: -9999px;
  top: -9999px;
  width: 1px;
  height: 1px;
  overflow: hidden;
}

/* Email note + consent checkbox */

.form-consent-block {
  margin-bottom: 20px;
  padding-top: 4px;
}

.form-consent-block .field-helper {
  margin-top: 0;
  margin-bottom: 10px;
}

.form-consent-block .field-helper a {
  color: var(--color-seagreen);
  font-weight: 700;
}

.form-consent-block .field-helper a:hover {
  text-decoration: underline;
}

.form-consent-block .checkbox-field {
  margin-top: 0;
}

/* reCAPTCHA */

.recaptcha-wrap {
  margin-bottom: 20px;
}

.recaptcha-note {
  font-size: 12px;
  color: var(--color-text-secondary);
  margin-top: 6px;
}

/* Submit + response */

.enquiry-submit-btn {
  display: block;
  width: 100%;
  margin-top: 8px;
  padding: 16px;
  font-size: 15px;
  text-align: center;
}

.form-response {
  margin-top: 18px;
  font-size: 14px;
  font-weight: 600;
  text-align: center;
}

.form-response:empty {
  display: none;
}

.form-response.is-success {
  color: #1f7a4d;
  background: rgba(47, 111, 94, 0.08);
  border: 1px solid rgba(47, 111, 94, 0.3);
  border-radius: 8px;
  padding: 14px 16px;
}

.form-response.is-error {
  color: #b3261e;
  background: rgba(179, 38, 30, 0.08);
  border: 1px solid rgba(179, 38, 30, 0.3);
  border-radius: 8px;
  padding: 14px 16px;
}

/* Field-level error state (applied/removed by script.js on submit) */

.form-field.field-error input,
.form-field.field-error select,
.form-field.field-error textarea {
  border-color: #c0392b;
}

.field-error-text {
  color: #c0392b;
  font-size: 12.5px;
  margin-top: 6px;
}

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

  .contact-info-panel,
  .enquiry-form-panel,
  .message-form-panel {
    padding: 32px 24px;
  }

  .form-grid-2 {
    grid-template-columns: 1fr;
  }

  .phone-input-group {
    flex-direction: column;
  }

  .phone-code-select {
    flex-basis: auto;
    width: 100%;
  }
}

/* =========================================================
   Industries page — Hero
   ========================================================= */

.industries-hero {
  position: relative;
  min-height: max(560px, calc(100vh - var(--header-height)));
  display: flex;
  align-items: center;
  overflow: hidden;
  padding: 64px 0;
  background: var(--color-navy);
}

/* 120%/-10% oversize for scroll parallax slack — see .page-hero-bg's
   own comment (style.css, Hero section) for the full explanation. */
.industries-hero-bg {
  position: absolute;
  top: -20%;
  left: 0;
  right: 0;
  height: 140%;
  background-size: cover;
  background-position: center;
  will-change: transform;
}

.industries-hero-overlay {
  position: absolute;
  inset: 0;
  background: linear-gradient(180deg, rgba(11, 37, 69, 0.6) 0%, rgba(11, 37, 69, 0.4) 45%, rgba(11, 37, 69, 0.65) 100%);
}

.industries-hero-content {
  position: relative;
  z-index: 1;
  max-width: 820px;
  margin: 0 auto;
  text-align: center;
}

.industries-hero-heading {
  color: #ffffff;
  font-weight: 800;
  font-size: clamp(30px, 4.6vw, 50px);
  line-height: 1.3;
  text-shadow: 0 2px 18px rgba(0, 0, 0, 0.35);
  margin-bottom: 22px;
}

.industries-hero-grad {
  background: var(--gradient-logo);
  background-clip: text;
  -webkit-background-clip: text;
  color: transparent;
}

.industries-hero-desc {
  color: #ffffff;
  font-size: 17px;
  line-height: 1.75;
  max-width: 700px;
  margin: 0 auto 20px;
}

.industries-hero-features {
  color: rgba(255, 255, 255, 0.75);
  font-size: 13px;
  font-weight: 600;
  letter-spacing: 0.02em;
  margin-bottom: 36px;
}

.industries-hero-btn {
  border: 1px solid rgba(255, 255, 255, 0.8);
}

@media (max-width: 640px) {
  .industries-hero {
    padding: 56px 0;
  }

  .industries-hero-desc {
    font-size: 15.5px;
  }
}

/* =========================================================
   Industries page — "Industries We Serve" section
   ========================================================= */

.industries-list {
  background: var(--color-bg);
  padding: 110px 0 40px;
}

.industries-list-heading {
  text-align: center;
  color: var(--color-seagreen);
  font-size: clamp(22px, 2.6vw, 28px);
  font-weight: 800;
  letter-spacing: 0.01em;
  margin: 0 auto 90px;
}

.industries-blocks {
  max-width: 1100px;
}

.industry-block {
  position: relative;
  margin-bottom: 150px;
}

.industry-block:last-child {
  margin-bottom: 0;
}

.industry-block-media {
  position: relative;
  border-radius: 20px;
  overflow: hidden;
  background: var(--color-bg-alt);
}

.industry-block-media img {
  display: block;
  width: 100%;
  height: 460px;
  object-fit: cover;
}

.industry-block-card {
  position: absolute;
  top: 50%;
  transform: translateY(-50%);
  width: 420px;
  max-width: calc(100% - 48px);
  background: #ffffff;
  border-radius: 16px;
  padding: 44px 40px;
  box-shadow: 0 24px 50px rgba(11, 37, 69, 0.16), 0 4px 14px rgba(11, 37, 69, 0.07);
}

.industry-block--card-left .industry-block-card {
  left: 5%;
}

.industry-block--card-right .industry-block-card {
  right: 5%;
}

.industry-block-tag {
  display: block;
  color: var(--color-seagreen);
  /* Increased 13px -> 15px 2026-08-25, follow-up fix, item 18 — a
     static size-only change, unrelated to the block's slide-in
     animation above; color/weight/letter-spacing/case all untouched. */
  font-size: 15px;
  font-weight: 700;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  margin-bottom: 14px;
}

.industry-block-heading {
  background: var(--gradient-logo);
  background-clip: text;
  -webkit-background-clip: text;
  color: transparent;
  font-size: clamp(20px, 2.2vw, 25px);
  font-weight: 800;
  line-height: 1.35;
  margin-bottom: 16px;
}

.industry-block-desc {
  color: var(--color-text);
  font-size: 15px;
  line-height: 1.7;
}

@media (max-width: 900px) {
  .industry-block-card {
    width: 380px;
  }
}

@media (max-width: 860px) {
  .industries-list {
    padding: 72px 0 24px;
  }

  .industries-list-heading {
    margin-bottom: 8px;
  }

  .industry-block {
    margin-bottom: 64px;
  }

  .industry-block-media img {
    height: 280px;
  }

  .industry-block-card {
    position: relative;
    top: auto;
    left: auto;
    right: auto;
    transform: none;
    width: auto;
    max-width: none;
    margin: -48px 20px 0;
    padding: 32px 28px;
  }
}

@media (max-width: 480px) {
  .industry-block-card {
    margin: -40px 16px 0;
    padding: 28px 22px;
  }
}

/* Scroll-linked directional slide-in toggle (reworked 2026-08-25,
   follow-up fix, item 17 — replaces the old one-shot .industry-reveal/
   .is-revealed mechanism entirely, which deliberately never replayed;
   see PROGRESS.md's 2026-08-25 session log for the full account of why
   it was retired rather than kept alongside this). Card and image now
   animate as two SEPARATE elements, each sliding in from a different
   edge — the card from whichever side it's actually positioned on
   (left or right, per .industry-block--card-left/-right below), the
   image from the OPPOSITE side — and both replay every time their own
   block scrolls into view (independently per block, since each
   .industry-block is its own observed target — see
   initIndustryBlocksSlide() in script.js) and reset when it's ~90%+
   scrolled out, matching the toggle behavior every other section on
   the site now uses. Uses the standalone `translate` property rather
   than `transform` specifically because .industry-block-card already
   uses `transform: translateY(-50%)` for its own vertical centering
   (see the rule above) — `translate` composes independently alongside
   that with no conflict, the same reason it's used everywhere else on
   this site, but here it's not just a hover-composability nicety, it's
   required (transform is already spoken for). */

.industry-block-card.block-slide,
.industry-block-media.block-slide {
  opacity: 0;
  transition: opacity 0.6s ease-out, translate 0.6s ease-out;
}

.industry-block--card-left .industry-block-card.block-slide {
  translate: -70px 0;
}

.industry-block--card-right .industry-block-card.block-slide {
  translate: 70px 0;
}

.industry-block--card-left .industry-block-media.block-slide {
  translate: 70px 0;
}

.industry-block--card-right .industry-block-media.block-slide {
  translate: -70px 0;
}

.industry-block-card.block-slide.is-shown,
.industry-block-media.block-slide.is-shown {
  opacity: 1;
  translate: 0 0;
}

@media (prefers-reduced-motion: reduce) {
  .industry-block-card.block-slide,
  .industry-block-media.block-slide {
    opacity: 1;
    translate: 0 0;
    transition: none;
  }
}

/* =========================================================
   Industries page — closing CTA
   ========================================================= */

.industries-cta {
  position: relative;
  overflow: hidden;
  background: linear-gradient(180deg, #1e3a8a 0%, #2c2f33 100%);
  padding: 96px 0;
}

/* Decorative hex-grid texture, faded in from both edges and fully clear
   by the time it reaches the text column. The tile below is one
   seamless repeat unit of a flat-top/pointy-side hex grid (circumradius
   24px) — it includes the "wrap-around" hexagon copies needed so the
   ones that get clipped at the tile edge are completed by their
   neighbour, which is what makes background-repeat tile it with no
   seams. Opacity (0.45) is the "edge" strength from the brief; the
   mask-image gradient is what actually fades it to 0 toward the
   center — the two are independent so the fade is driven entirely by
   position, not a second opacity multiplying against the first.
   Purely decorative: aria-hidden, no interaction, sits behind the
   content via z-index. */
.industries-cta-hexpattern {
  position: absolute;
  inset: 0;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='72' height='42'%3E%3Cg fill='none' stroke='%23B9C2CB' stroke-width='1'%3E%3Cpolygon points='24,0 12,20.8 -12,20.8 -24,0 -12,-20.8 12,-20.8'/%3E%3Cpolygon points='96,0 84,20.8 60,20.8 48,0 60,-20.8 84,-20.8'/%3E%3Cpolygon points='24,41.6 12,62.4 -12,62.4 -24,41.6 -12,20.8 12,20.8'/%3E%3Cpolygon points='96,41.6 84,62.4 60,62.4 48,41.6 60,20.8 84,20.8'/%3E%3Cpolygon points='24,-41.6 12,-20.8 -12,-20.8 -24,-41.6 -12,-62.4 12,-62.4'/%3E%3Cpolygon points='96,-41.6 84,-20.8 60,-20.8 48,-41.6 60,-62.4 84,-62.4'/%3E%3Cpolygon points='60,20.8 48,41.6 24,41.6 12,20.8 24,0 48,0'/%3E%3C/g%3E%3C/svg%3E");
  background-repeat: repeat;
  background-size: 72px 42px;
  opacity: 0.45;
  -webkit-mask-image: linear-gradient(90deg, #000 0%, #000 8%, transparent 32%, transparent 68%, #000 92%, #000 100%);
  mask-image: linear-gradient(90deg, #000 0%, #000 8%, transparent 32%, transparent 68%, #000 92%, #000 100%);
  pointer-events: none;
}

.industries-cta-content {
  position: relative;
  z-index: 1;
  max-width: 680px;
  margin: 0 auto;
  text-align: center;
}

.industries-cta-heading {
  color: #ffffff;
  font-size: clamp(24px, 3vw, 34px);
  font-weight: 800;
  line-height: 1.3;
  margin-bottom: 18px;
}

.industries-cta-desc {
  color: rgba(255, 255, 255, 0.78);
  font-size: 16px;
  line-height: 1.7;
  margin-bottom: 32px;
}

@media (max-width: 640px) {
  .industries-cta {
    padding: 64px 0;
  }
}

/* Below this width the text column spans effectively the full section
   width (no unused "edge" space either side for the pattern to occupy),
   so the percentage-based fade zone shrinks along with it — tested at
   375px and confirmed the hex lines showed faintly behind the
   description/button, which the brief explicitly ruled out ("never
   crowds or touches the heading/description"). Hiding it here instead
   of trying to compensate with viewport-relative math for every width. */
@media (max-width: 860px) {
  .industries-cta-hexpattern {
    display: none;
  }
}

/* =========================================================
   Placeholder pages
   ========================================================= */

.placeholder-main {
  min-height: 60vh;
  display: flex;
  align-items: center;
  justify-content: center;
  text-align: center;
  padding: 80px 24px;
}

.placeholder-main h1 {
  font-size: clamp(26px, 4vw, 40px);
  color: var(--color-navy);
  margin-bottom: 12px;
}

.placeholder-main p {
  color: var(--color-text-secondary);
}

/* =========================================================
   Aviation & Aerospace page — Hero
   Independent classes, structurally matched to .industries-hero
   (same min-height/padding/overlay recipe) per this project's
   independent-classes convention — not a shared class with that page.
   ========================================================= */

.av-hero {
  position: relative;
  min-height: max(560px, calc(100vh - var(--header-height)));
  display: flex;
  align-items: center;
  overflow: hidden;
  padding: 64px 0;
  background: var(--color-navy);
}

/* 120%/-10% oversize for scroll parallax slack — see .page-hero-bg's
   own comment (style.css, Hero section) for the full explanation. */
.av-hero-bg {
  position: absolute;
  top: -20%;
  left: 0;
  right: 0;
  height: 140%;
  background-size: cover;
  background-position: center;
  will-change: transform;
}

.av-hero-overlay {
  position: absolute;
  inset: 0;
  background: linear-gradient(180deg, rgba(11, 37, 69, 0.6) 0%, rgba(11, 37, 69, 0.4) 45%, rgba(11, 37, 69, 0.65) 100%);
}

.av-hero-content {
  position: relative;
  z-index: 1;
  max-width: 820px;
  margin: 0 auto;
  text-align: center;
}

.av-hero-heading {
  color: #ffffff;
  font-weight: 800;
  font-size: clamp(30px, 4.6vw, 50px);
  line-height: 1.3;
  text-shadow: 0 2px 18px rgba(0, 0, 0, 0.35);
  margin-bottom: 16px;
}

/* Text-clip gradient — the exact logo gradient already used by
   .grad-logo (Home CTA), .about-cta-grad and .contact-hero-grad.
   Deliberately the SAME values (not the different, unrelated
   .industries-hero-grad), since the brief calls for the logo's own
   gradient specifically. background must come before background-clip
   in this rule (see CLAUDE.md's gradient-text gotcha) — copied that
   established order rather than risking the reset bug. */
.av-hero-subheading {
  background: var(--gradient-logo);
  background-clip: text;
  -webkit-background-clip: text;
  color: transparent;
  font-family: var(--font-heading);
  font-weight: 700;
  font-size: clamp(17px, 2.2vw, 22px);
  line-height: 1.4;
  margin-bottom: 22px;
}

.av-hero-desc {
  color: rgba(255, 255, 255, 0.85);
  font-size: 17px;
  line-height: 1.75;
  max-width: 700px;
  margin: 0 auto 36px;
}

/* Same logo gradient again, this time as a solid button background
   rather than text-clipped — per the brief, button text stays white. */
.av-hero-btn {
  background: linear-gradient(100deg, #0097b1 0%, #7ed957 100%);
  color: #ffffff;
}

@media (max-width: 640px) {
  .av-hero {
    padding: 56px 0;
  }

  .av-hero-desc {
    font-size: 15.5px;
  }
}

/* =========================================================
   Industrial & MRO page — Hero
   Independent classes, structurally matched to .av-hero (same
   min-height/padding/overlay recipe) per this project's
   independent-classes convention — not a shared class with that page.
   ========================================================= */

.imro-hero {
  position: relative;
  min-height: max(560px, calc(100vh - var(--header-height)));
  display: flex;
  align-items: center;
  overflow: hidden;
  padding: 64px 0;
  background: var(--color-navy);
}

/* 120%/-10% oversize for scroll parallax slack — see .page-hero-bg's
   own comment (style.css, Hero section) for the full explanation. */
.imro-hero-bg {
  position: absolute;
  top: -20%;
  left: 0;
  right: 0;
  height: 140%;
  background-size: cover;
  background-position: center;
  will-change: transform;
}

.imro-hero-overlay {
  position: absolute;
  inset: 0;
  background: linear-gradient(180deg, rgba(11, 37, 69, 0.6) 0%, rgba(11, 37, 69, 0.4) 45%, rgba(11, 37, 69, 0.65) 100%);
}

.imro-hero-content {
  position: relative;
  z-index: 1;
  max-width: 820px;
  margin: 0 auto;
  text-align: center;
}

.imro-hero-heading {
  color: #ffffff;
  font-weight: 800;
  font-size: clamp(30px, 4.6vw, 50px);
  line-height: 1.3;
  text-shadow: 0 2px 18px rgba(0, 0, 0, 0.35);
  margin-bottom: 16px;
}

/* Text-clip gradient — the exact logo gradient already used by
   .grad-logo (Home CTA), .about-cta-grad, .contact-hero-grad and
   .av-hero-subheading. Deliberately the SAME values (not the
   different, unrelated .industries-hero-grad), since the brief calls
   for the logo's own gradient specifically. background must come
   before background-clip in this rule (see CLAUDE.md's gradient-text
   gotcha) — copied that established order rather than risking the
   reset bug. */
.imro-hero-subheading {
  background: var(--gradient-logo);
  background-clip: text;
  -webkit-background-clip: text;
  color: transparent;
  font-family: var(--font-heading);
  font-weight: 700;
  font-size: clamp(17px, 2.2vw, 22px);
  line-height: 1.4;
  margin-bottom: 22px;
}

.imro-hero-desc {
  color: rgba(255, 255, 255, 0.85);
  font-size: 17px;
  line-height: 1.75;
  max-width: 700px;
  margin: 0 auto 36px;
}

/* Same logo gradient again, this time as a solid button background
   rather than text-clipped — per the brief, button text stays white. */
.imro-hero-btn {
  background: linear-gradient(100deg, #0097b1 0%, #7ed957 100%);
  color: #ffffff;
}

@media (max-width: 640px) {
  .imro-hero {
    padding: 56px 0;
  }

  .imro-hero-desc {
    font-size: 15.5px;
  }
}

/* =========================================================
   Automotive page — Hero
   Independent classes, structurally matched to .av-hero/.imro-hero
   (same min-height/padding/overlay recipe) per this project's
   independent-classes convention — not a shared class with those pages.
   ========================================================= */

.auto-hero {
  position: relative;
  min-height: max(560px, calc(100vh - var(--header-height)));
  display: flex;
  align-items: center;
  overflow: hidden;
  padding: 64px 0;
  background: var(--color-navy);
}

/* 120%/-10% oversize for scroll parallax slack — see .page-hero-bg's
   own comment (style.css, Hero section) for the full explanation. */
.auto-hero-bg {
  position: absolute;
  top: -20%;
  left: 0;
  right: 0;
  height: 140%;
  background-size: cover;
  background-position: center;
  will-change: transform;
}

.auto-hero-overlay {
  position: absolute;
  inset: 0;
  background: linear-gradient(180deg, rgba(11, 37, 69, 0.6) 0%, rgba(11, 37, 69, 0.4) 45%, rgba(11, 37, 69, 0.65) 100%);
}

.auto-hero-content {
  position: relative;
  z-index: 1;
  max-width: 820px;
  margin: 0 auto;
  text-align: center;
}

.auto-hero-heading {
  color: #ffffff;
  font-weight: 800;
  font-size: clamp(30px, 4.6vw, 50px);
  line-height: 1.3;
  text-shadow: 0 2px 18px rgba(0, 0, 0, 0.35);
  margin-bottom: 16px;
}

/* Text-clip gradient — the exact logo gradient already used by
   .grad-logo (Home CTA), .about-cta-grad, .contact-hero-grad,
   .av-hero-subheading and .imro-hero-subheading. Deliberately the SAME
   values (not the different, unrelated .industries-hero-grad), since
   the brief calls for the logo's own gradient specifically. background
   must come before background-clip in this rule (see CLAUDE.md's
   gradient-text gotcha) — copied that established order rather than
   risking the reset bug. */
.auto-hero-subheading {
  background: var(--gradient-logo);
  background-clip: text;
  -webkit-background-clip: text;
  color: transparent;
  font-family: var(--font-heading);
  font-weight: 700;
  font-size: clamp(17px, 2.2vw, 22px);
  line-height: 1.4;
  margin-bottom: 22px;
}

.auto-hero-desc {
  color: rgba(255, 255, 255, 0.85);
  font-size: 17px;
  line-height: 1.75;
  max-width: 700px;
  margin: 0 auto 36px;
}

/* Same logo gradient again, this time as a solid button background
   rather than text-clipped — per the brief, button text stays white. */
.auto-hero-btn {
  background: linear-gradient(100deg, #0097b1 0%, #7ed957 100%);
  color: #ffffff;
}

@media (max-width: 640px) {
  .auto-hero {
    padding: 56px 0;
  }

  .auto-hero-desc {
    font-size: 15.5px;
  }
}

/* =========================================================
   Electronics & Electrical page — Hero
   Independent classes, structurally matched to .av-hero/.imro-hero/
   .auto-hero (same min-height/padding/overlay recipe) per this
   project's independent-classes convention — not a shared class with
   those pages.
   ========================================================= */

.elec-hero {
  position: relative;
  min-height: max(560px, calc(100vh - var(--header-height)));
  display: flex;
  align-items: center;
  overflow: hidden;
  padding: 64px 0;
  background: var(--color-navy);
}

/* 120%/-10% oversize for scroll parallax slack — see .page-hero-bg's
   own comment (style.css, Hero section) for the full explanation. */
.elec-hero-bg {
  position: absolute;
  top: -20%;
  left: 0;
  right: 0;
  height: 140%;
  background-size: cover;
  background-position: center;
  will-change: transform;
}

.elec-hero-overlay {
  position: absolute;
  inset: 0;
  background: linear-gradient(180deg, rgba(11, 37, 69, 0.6) 0%, rgba(11, 37, 69, 0.4) 45%, rgba(11, 37, 69, 0.65) 100%);
}

.elec-hero-content {
  position: relative;
  z-index: 1;
  max-width: 820px;
  margin: 0 auto;
  text-align: center;
}

.elec-hero-heading {
  color: #ffffff;
  font-weight: 800;
  font-size: clamp(30px, 4.6vw, 50px);
  line-height: 1.3;
  text-shadow: 0 2px 18px rgba(0, 0, 0, 0.35);
  margin-bottom: 16px;
}

/* Text-clip gradient — the exact logo gradient already used by
   .grad-logo (Home CTA), .about-cta-grad, .contact-hero-grad,
   .av-hero-subheading, .imro-hero-subheading and .auto-hero-subheading.
   Deliberately the SAME values (not the different, unrelated
   .industries-hero-grad), since the brief calls for the logo's own
   gradient specifically. background must come before background-clip
   in this rule (see CLAUDE.md's gradient-text gotcha) — copied that
   established order rather than risking the reset bug. */
.elec-hero-subheading {
  background: var(--gradient-logo);
  background-clip: text;
  -webkit-background-clip: text;
  color: transparent;
  font-family: var(--font-heading);
  font-weight: 700;
  font-size: clamp(17px, 2.2vw, 22px);
  line-height: 1.4;
  margin-bottom: 22px;
}

.elec-hero-desc {
  color: rgba(255, 255, 255, 0.85);
  font-size: 17px;
  line-height: 1.75;
  max-width: 700px;
  margin: 0 auto 36px;
}

/* Same logo gradient again, this time as a solid button background
   rather than text-clipped — per the brief, button text stays white. */
.elec-hero-btn {
  background: linear-gradient(100deg, #0097b1 0%, #7ed957 100%);
  color: #ffffff;
}

@media (max-width: 640px) {
  .elec-hero {
    padding: 56px 0;
  }

  .elec-hero-desc {
    font-size: 15.5px;
  }
}

/* =========================================================
   Reusable "Subcategory" section — shared component
   Sits directly below the hero on every "Our Offerings" category page
   (Aviation & Aerospace, Industrial & MRO, Automotive, Electronics &
   Electrical), one instance per product subcategory (e.g. "Bearings &
   Mounted Units"). Unlike the page-specific hero blocks above (which
   deliberately copy values into independent per-page classes, per this
   project's independent-classes convention), THIS is a genuinely
   shared/generic component — like `.industry-card` — meant to be
   reused as-is. To add a new subcategory section anywhere on the site,
   copy this exact HTML structure (see aviation-aerospace.html's
   #av-categories section for the reference instance) and change only:
   the heading text, the description text, the line-heading text if
   needed, and the list of <a class="subcat-logo-cell"> entries inside
   .subcat-logo-grid. No new CSS should be needed for a typical reuse —
   if one is, consider whether it belongs here (shared) or as a
   one-off override on the specific instance.
   ========================================================= */

/* Background: white at top → lightest smoke grey through the middle →
   bluish smoke grey at the bottom. Literal hex values (not tokens) —
   these are one-off "smoke grey" shades not used elsewhere on the
   site, distinct from the neutral --color-grey-light/-medium tokens. */
.subcat-section {
  padding: 88px 0;
  background: linear-gradient(180deg, #ffffff 0%, #f2f4f6 50%, #c9d2db 100%);
}

.subcat-content {
  max-width: 900px;
  margin: 0 auto;
  text-align: center;
}

/* Two-line heading: line 1 is the offering-page name (plain dark
   charcoal grey), line 2 is the subcategory name (exact logo gradient
   text-clip — same values as .grad-logo/.about-cta-grad/
   .contact-hero-grad/the 4 offering-page hero subheadings). Each line
   is its own <span> inside the .subcat-heading <h2> — see
   .subcat-heading-offering/.subcat-heading-subcat below — so
   .subcat-heading itself only holds the shared size/weight/spacing. */
.subcat-heading {
  font-size: clamp(24px, 3vw, 34px);
  font-weight: 800;
  line-height: 1.3;
  margin-bottom: 20px;
}

.subcat-heading-offering {
  display: block;
  color: #333333;
}

/* background must come before background-clip (see CLAUDE.md's
   gradient-text gotcha) — copied that established order. */
.subcat-heading-subcat {
  display: block;
  background: var(--gradient-logo);
  background-clip: text;
  -webkit-background-clip: text;
  color: transparent;
}

.subcat-desc {
  color: var(--color-text);
  font-size: 16px;
  line-height: 1.75;
  max-width: 760px;
  margin: 0 auto 44px;
}

.subcat-logos-heading {
  color: #126E72;
  font-weight: 600;
  font-size: 15px;
  margin-bottom: 24px;
}

/* Hides the "Representative brands..." line heading whenever its
   section's .subcat-logo-grid is genuinely empty (no <a class=
   "subcat-logo-cell"> entries) — matches the actual DOM/brand list, not
   a manually-toggled flag, so this self-adjusts the same way the
   logo-grid centering rule below does as brand lists get populated or
   left empty. The grid itself already collapses to zero height when
   empty (see the "subcat-logo-grid can legitimately be empty" note in
   CLAUDE.md), so hiding just this heading is enough to remove the
   heading-above-a-blank-space look entirely. */
.subcat-logos-heading:has(+ .subcat-logo-grid:empty) {
  display: none;
}

.subcat-logo-grid {
  display: grid;
  grid-template-columns: repeat(5, 1fr);
  gap: 20px;
}

/* Center a genuinely partial row (fewer than 5 logos total, so the
   whole grid is just one under-full row) as a group, rather than
   leaving it pinned to the left with empty space on the right. Does
   NOT apply to a partial *final* row of an otherwise-multi-row grid
   (5+ logos total) — there, column alignment across rows takes
   priority, so that row stays left-aligned under the columns above
   (the plain grid rule above already does this by default; this
   selector simply never matches that case).

   :has(> .subcat-logo-cell:first-child:nth-last-child(-n + 4)) is a
   pure-CSS "total children ≤ 4" count check: it matches only when the
   grid's *first* logo cell is *also* among the *last 4* — which is
   only possible if there are 4 or fewer cells total. This is
   deliberately count-based, not a manually-toggled class per section,
   so it self-adjusts automatically as brand lists are added/changed —
   exactly the same self-adjusting reasoning as everywhere else this
   shared component is used.

   Switches to flex (grid-template-columns is simply ignored once
   display: flex is set, no need to unset it) purely so
   justify-content: center has a real effect — CSS Grid's 1fr tracks
   always stretch to fill 100% of the container's width regardless of
   how many are actually occupied, so justify-content would have no
   visible effect there. flex-basis below reproduces the exact same
   per-cell width the 5/3/2-column grid already produces at each
   breakpoint (see the matching gap-based calc() in each @media block),
   so cell size is unchanged — only the row's own alignment changes. */
.subcat-logo-grid:has(> .subcat-logo-cell:first-child:nth-last-child(-n + 4)) {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
}

.subcat-logo-grid:has(> .subcat-logo-cell:first-child:nth-last-child(-n + 4)) > .subcat-logo-cell {
  /* 5 columns, 4 gaps of 20px, matching repeat(5, 1fr) above exactly */
  flex: 0 0 calc((100% - 80px) / 5);
}

/* 1000x600 (5:3) cell aspect ratio, silver-grey border — the border
   color (#c0c0c0) is the same literal "silver" already used by
   .btn-glow-silver's rgba(192, 192, 192, ...) shadow ring elsewhere on
   this site, reused here as a solid border rather than a glow. No
   padding/background fill behind the image — the source logo files are
   pre-sized to exactly 1000x600, so the image fills the cell
   edge-to-edge itself (see .subcat-logo-img below); a transparent-
   background logo now shows the section's own background through it
   rather than an artificial white box. */
.subcat-logo-cell {
  display: flex;
  aspect-ratio: 5 / 3;
  overflow: hidden;
  border: 1px solid #c0c0c0;
  border-radius: 8px;
  transition: transform 0.25s ease, box-shadow 0.25s ease;
}

.subcat-logo-cell:hover,
.subcat-logo-cell:focus-visible {
  /* -3px → -4px 2026-08-24 (modernization pass) — the requested "-4px
     to -6px" lift range; scale(1.04) was already here and kept as-is,
     a complementary effect the brief didn't ask to remove. */
  transform: translateY(-4px) scale(1.04);
  box-shadow: 0 10px 22px rgba(11, 37, 69, 0.14);
}

/* object-fit: cover (not contain) — fills the cell edge-to-edge to
   match the source assets' exact 1000x600/5:3 sizing, rather than
   leaving letterboxed padding around it. border-radius: inherit keeps
   the image's corners following the cell's own rounded corners. */
.subcat-logo-img {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: cover;
  border-radius: inherit;
}

/* Small fine-print disclaimer below the logo grid — same recipe as
   .file-upload-hint/.product-details-hint elsewhere on the site (12px,
   var(--color-text-secondary)), reused here for consistency rather than
   inventing a new muted-text treatment. Inherits Inter from the sitewide
   body font automatically, same as .subcat-desc — no font-family
   override needed. */
.subcat-disclaimer {
  margin-top: 28px;
  font-size: 12px;
  line-height: 1.6;
  color: var(--color-text-secondary);
}

@media (max-width: 860px) {
  .subcat-logo-grid {
    grid-template-columns: repeat(3, 1fr);
  }

  /* 3 columns, 2 gaps of 20px, matching repeat(3, 1fr) above exactly */
  .subcat-logo-grid:has(> .subcat-logo-cell:first-child:nth-last-child(-n + 4)) > .subcat-logo-cell {
    flex: 0 0 calc((100% - 40px) / 3);
  }
}

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

  .subcat-logo-grid {
    grid-template-columns: repeat(2, 1fr);
  }

  /* 2 columns, 1 gap of 20px, matching repeat(2, 1fr) above exactly */
  .subcat-logo-grid:has(> .subcat-logo-cell:first-child:nth-last-child(-n + 4)) > .subcat-logo-cell {
    flex: 0 0 calc((100% - 20px) / 2);
  }
}

/* =========================================================
   Subcategory "locked full-page-fold" view — shared logic
   Applied by initSubcatFoldLock() in script.js when the URL hash is
   `#fold-<section-id>` (set only by the header's submenu subcategory
   links, never by a top-level category link or the hero's own
   "EXPLORE ... PRODUCTS" button, which stays untouched — see the JS
   function's own comment for why this is hash-prefix-based rather than
   a query param). Same background/heading/description/logo-grid/
   animations as a normal instance — only sizing/overflow/positioning
   changes here.

   position: fixed (not "scroll to the section, then freeze document
   scroll") is deliberate, fixing a real bug: the earlier version
   scrolled the target section into view, then set overflow: hidden on
   html/body — but that overflow change itself resets window scroll
   position to 0 in most browsers, since disabling scroll fundamentally
   changes what the "scrolling element" even means. The visible result
   was the hero (whatever sits at scroll position 0), locked, while the
   actually-requested section sat correctly class-flagged but scrolled
   far out of view. Pinning the section with position: fixed sidesteps
   this entirely — it renders in the same viewport position regardless
   of the underlying document's scrollY, so there's no scroll-then-freeze
   race to lose. `top: var(--header-height)` keeps it below the sticky
   header (`z-index: 100`) rather than covering it, matching how the
   section would sit if you'd scrolled to it normally.
   ========================================================= */

html.scroll-locked,
body.scroll-locked {
  overflow: hidden;
  height: 100%;
}

.subcat-section.is-locked-fold {
  position: fixed;
  top: var(--header-height);
  left: 0;
  right: 0;
  bottom: 0;
  z-index: 50;
  /* Fixed 2026-08-25 — was overflow: hidden with no internal scroll
     route (see CLAUDE.md's "Known limitation" note, now resolved),
     which clipped content with literally no way to reach it at
     high browser zoom (~150%+) or unusual window aspect ratios, since
     html/body's own overflow: hidden (.scroll-locked below) blocks all
     document-level scroll while this class is active. This section is
     now the scrollable region itself — overflow-y: auto lets it scroll
     internally when its own content is taller than the available
     space, while html/body staying locked still fully prevents
     escaping into sibling sections above/below (the fixed positioning
     means nothing else is ever reachable to scroll to regardless).
     overflow-x stays hidden — nothing here needs horizontal scroll and
     this avoids an accidental horizontal scrollbar at extreme zoom. */
  overflow-y: auto;
  overflow-x: hidden;
  display: flex;
  align-items: center;
  /* `safe` is the deliberate fix, not `center` alone: it tells browsers
     that support it (Firefox long-standing, Chrome/Edge 118+, Safari
     16.4+) to prefer top-alignment over centering *specifically when*
     centering would overflow the scrollable area — so an overflowing
     section starts scrolled to its own top, reachable by scrolling
     straight down, rather than opening pre-centered with the top half
     of its own content already scrolled past and needing to be
     scrolled *up* to see (technically still reachable either way, but
     `safe` is the far more discoverable, intuitive result). Older
     browsers that don't parse `safe center` simply ignore this whole
     declaration at parse time and keep the plain `center` above, so
     this is a pure progressive enhancement — never a regression. */
  align-items: safe center;
  /* This element is given a temporary tabindex and focused while locked
     (initSubcatFoldLock() in script.js) purely so keyboard scroll keys
     target it — not a real "user tabbed here" interaction, so suppress
     the default focus ring rather than showing one around the whole
     section. */
  outline: none;
}

.subcat-section.is-locked-fold .subcat-content {
  width: 100%;
}
