/* ==========================================================================
   TrackVia — Shared Design System
   Brand palette sourced from TrackVia Brand Refresh (AYR Creative):
   Navy #0a1a37 / Blue #2d76d4 / Slate-Teal #40635f / Gray #ebebeb / Gold #e1b145
   Typography: Nunito Sans — the flat-terminal sibling of the brand deck's
   Nunito, chosen for web over licensed Avenir (SIL OFL, no webfont licence)
   ========================================================================== */

/* The webfont is loaded from the document head by build/gen_fonts.py, not
   from here. An @import serialises three blocking round trips -- this file,
   then Google's CSS, then the woff2 -- and none of the URLs are visible to
   the preload scanner. Measured on the homepage, same build both ways: the
   webfont is ready at 396ms from the head, 3092ms through the import. */

:root {
  /* lets block-size animate to auto for the solutions drawer */
  interpolate-size: allow-keywords;
  /* Brand core (exact hex from brand presentation) */
  --navy: #0a1a37;
  --blue: #2d76d4;
  --teal: #40635f;
  --gold: #e1b145;
  --gray-brand: #ebebeb;

  /* Derived navy scale */
  --navy-950: #060e1e;
  --navy-900: #0a1a37;
  --navy-800: #102847;
  --navy-700: #17386a;
  --navy-600: #204a8a;

  /* Derived blue scale */
  --blue-600: #2159a8;
  --blue-500: #2d76d4;
  --blue-400: #5b95df;
  --sky-300: #9dc0ea;
  --sky-200: #c3daf3;
  --sky-100: #e8f0fa;
  /* the stats band ground; a touch deeper than sky-100 so the band reads as
     its own surface rather than a wash */
  --band-tint: #e3ebf7;

  /* Derived gold scale */
  --gold-600: #c6952f;
  --gold-700: #8a6410;  /* small text on light grounds; gold-600 is 2.51:1 there */
  --gold-500: #e1b145;
  --gold-100: #fbf1dd;

  /* Derived teal scale */
  --teal-600: #324f4b;
  --teal-500: #40635f;
  --teal-100: #e6ece9;

  --white: #ffffff;
  --gray-50: #f6f6f5;
  --gray-100: #efefee;
  --gray-200: #dcdedd;
  --gray-400: #9a9d9c;
  /* #75797a measured 4.40:1 on white and 4.07:1 on --gray-50, both under the
     4.5 floor, and it is the colour of the careers table headers, the case
     study fact labels, the success story card descriptions, the form notes
     and "More detail below". #696d6e clears 4.5 on both grounds. */
  --gray-500: #696d6e;
  --gray-600: #545a5d;
  --gray-800: #262f36;

  /* Semantic */
  --bg-page: var(--white);
  --text-body: #3b444c;
  --text-heading: var(--navy-900);

  --gradient-hero: linear-gradient(135deg, #060e1e 0%, #0a1a37 45%, #123561 80%, #2d76d4 130%);
  --gradient-cta: linear-gradient(135deg, #2d76d4 0%, #5b95df 100%);
  --gradient-band: linear-gradient(120deg, #0a1a37 0%, #17386a 100%);
  --gradient-gold: linear-gradient(135deg, #e1b145 0%, #edc673 100%);

  --radius-sm: 8px;
  --radius-md: 14px;
  --radius-lg: 22px;
  --radius-pill: 999px;

  --shadow-card: 0 1px 2px rgba(10, 26, 55, 0.06), 0 12px 32px -12px rgba(10, 26, 55, 0.18);

  --container: 1440px;
  --cta2-bg: var(--sky-100);
  --cta2-bg-hover: var(--sky-200);
  --cta2-ink: var(--blue-600);
  --cta2-edge: var(--sky-200);
  --cta2-edge-hover: var(--sky-300);
  --cta3-ink: var(--blue-600);
  --cta3-ink-hover: var(--navy-900);
}

* { box-sizing: border-box;}
html { scroll-behavior: smooth;}

body {
  margin: 0;
  font-family: 'Nunito Sans', -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
  color: var(--text-body);
  background: var(--bg-page);
  -webkit-font-smoothing: antialiased;
  line-height: 1.6;
  font-weight: 400;
}

h1, h2, h3, h4 {
  font-family: 'Nunito Sans', sans-serif;
  color: var(--text-heading);
  line-height: 1.15;
  margin: 0 0 0.5em 0;
  font-weight: 800;
  letter-spacing: -0.01em;
}
/* Sized deliberately. These previously inherited the UA defaults (1.5em /
   1.17em), which put unsized section H2s at 24px -- below the 30px H3 in the
   solutions accordion, and a very steep drop from the 62px H1. Rules that set
   their own size (.page-split, .prose, .sol-spot, .home-track-copy) still win. */
h2 { font-size: 34px;}
h3 { font-size: 19px;}

p { margin: 0 0 1em 0;}
a { color: inherit; text-decoration: none;}
img, svg { display: block; max-width: 100%;}
ul { margin: 0; padding: 0; list-style: none;}
button { font-family: inherit; cursor: pointer;}

.container {
  max-width: var(--container);
  margin: 0 auto;
  /* horizontal only — elements like .nav set their own vertical padding, and a
     shorthand here would reset it depending on rule order */
  padding-left: 28px;
  padding-right: 28px;
}

.section {
  padding: 96px 0;
}
.section--tight { padding: 64px 0;}
.section--dark {
  background: var(--gradient-band);
  color: var(--sky-100);
}
.section--dark h2, .section--dark h3, .section--dark h4 { color: var(--white);}
.section--soft { background: var(--gray-50);}

/* ---------- The stats band ----------
   Light rather than navy. It sits directly under the hero on every solution and
   industry page, and both heroes now carry the brand artwork -- so a navy slab
   butted into them with no transition, and on the industry pages put two dark
   bands back to back.

   The numerals had to change with it: the gold this band used to carry (#e1b145)
   measures 1.65:1 on this ground and is simply not readable. Everything else here
   inherits by dropping .section--dark rather than by overriding it -- headings
   take the default heading colour and the body copy is already gray-600.

   The numerals are blue-600, #2159a8, which is 5.72:1 on this tint and so clears
   AA at any size, not just at the 40px they ship at. Scoped to this band on
   purpose: .home-stat-num is navy by default and stays navy on the homepage. */
.section.stats-band {
  background: var(--band-tint);
  padding: 38px 0;
}
.section.stats-band .home-stat-num { color: var(--blue-600);}

.eyebrow {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  font-size: 13px;
  font-weight: 800;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--blue-600);
  background: var(--sky-100);
  padding: 6px 14px;
  border-radius: var(--radius-pill);
  margin-bottom: 18px;
}
.eyebrow.gold { color: var(--gold-600); background: var(--gold-100);}
.eyebrow.teal { color: var(--teal-600); background: var(--teal-100);}
.section--dark .eyebrow {
  color: var(--sky-200);
  background: rgba(255,255,255,0.08);
}

.section-head {
  max-width: 680px;
  margin: 0 0 48px 0;
}
/* Without this a heading breaks wherever it runs out of room and drops a
   single word onto its own line -- "What makes the platform technically /
   different". Balance splits the lines evenly instead, and it reflows with the
   viewport rather than being hard-coded at one width. */
.section-head h2, .section-head h3 { text-wrap: balance;}
.card-body h3, .card h3 { text-wrap: balance;}
/* Card #n19. A sweep of all 68 pages at 1440px found 49 headings whose last
   line held one or two words -- "…even offline", "…not a viewer for it",
   "…Get Paid Faster." text-wrap: balance evens the lines out instead, and the
   two rules above were only covering headings inside .section-head and cards.
   Applied to every heading rather than hunting them one at a time; balance is
   a no-op on a heading that already fits one line, and browsers cap it at a
   few lines so it costs nothing on long ones.
   Lede paragraphs get 'pretty' rather than 'balance': balance is limited to
   short blocks, and pretty is the one that targets the last-line orphan. */
h1, h2, h3, h4, h5,
.eyebrow, .sol-kicker { text-wrap: balance;}
.page-hero-lede, .sol-sub, .home-hero-sub, .story-lead-copy,
.section-head p, .home-rail-head p { text-wrap: pretty;}
.section-head.center { margin-left: auto; margin-right: auto; text-align: center;}
.section-head p { color: var(--gray-600); font-size: 17px;}
.section--dark .section-head p { color: var(--sky-200);}

/* ---------- Buttons ---------- */
.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 8px;
  padding: 14px 26px;
  border-radius: var(--radius-pill);
  font-weight: 800;
  font-size: 15px;
  border: 1px solid transparent;
  transition: transform 0.15s ease, box-shadow 0.15s ease, background 0.15s ease, color .15s ease;
  white-space: nowrap;
}
.btn:hover { transform: translateY(-1px);}
.btn-primary {
  /* Flat, not --gradient-gold. At the size these actually ship the two stops
     were indistinguishable, and the rest of the palette is flat. */
  background: var(--gold-500);
  color: var(--navy-900);
}
.btn-primary:hover { background: var(--gold-600);}
.btn-ghost-dark {
  background: transparent;
  border-color: rgba(255,255,255,0.35);
  color: var(--white);
}
.btn-ghost-dark:hover { background: rgba(255,255,255,0.1);}
.btn-outline {
  background: transparent;
  border-color: var(--navy-700);
  color: var(--navy-800);
}
.btn-outline:hover { background: var(--navy-900); color: var(--white);}
.btn-block { width: 100%;}
.btn-sm { padding: 10px 18px; font-size: 14px;}

/* ---------- CTA tiers ----------
   Three levels, in descending weight: a filled gold primary, a tinted
   secondary, and a text link. Each reads its colours from tokens so dark
   sections restyle all three by redefining the tokens once, rather than
   every component carrying its own on-dark override. */
.btn-secondary {
  background: var(--cta2-bg);
  color: var(--cta2-ink);
  border-color: var(--cta2-edge);
}
.btn-secondary:hover {
  background: var(--cta2-bg-hover);
  border-color: var(--cta2-edge-hover);
}

.cta-link {
  display: inline-flex;
  align-items: center;
  gap: 7px;
  font-weight: 800;
  font-size: 15px;
  color: var(--cta3-ink);
  transition: gap 0.15s ease, color 0.15s ease;
}
.cta-link::after {
  content: '\25B8';
  font-size: 0.9em;
  line-height: 1;
}
.cta-link:hover { gap: 12px; color: var(--cta3-ink-hover);}
/* Every section that paints a dark ground redefines the tokens once. */
.home-hero, .page-hero, .section--dark, .home-cases,
.home-promo, .home-quotes, .site-footer {
  --cta2-bg: rgba(255,255,255,0.14);
  --cta2-bg-hover: rgba(255,255,255,0.24);
  --cta2-ink: var(--white);
  --cta2-edge: rgba(255,255,255,0.24);
  --cta2-edge-hover: rgba(255,255,255,0.5);
  --cta3-ink: var(--white);
  --cta3-ink-hover: var(--sky-200);
}

/* The homepage hero moved off the dark photograph onto the light /solutions
   ground, so it must NOT take the white-on-dark CTA and focus tokens the
   other dark bands use. Reset to the page defaults. */
.home-hero {
  --cta2-bg: initial; --cta2-bg-hover: initial; --cta2-ink: initial;
  --cta2-edge: initial; --cta2-edge-hover: initial;
  --cta3-ink: initial; --cta3-ink-hover: initial;
  position: relative;
  background: var(--white);
  padding: 96px 0;
  overflow: hidden;
  /* 29vw kept the hero in proportion up to about 1900px and then ran away with
     it: 835px tall at 2880 and 998px at 3440, against 565px at 1440. Two people
     on two monitors were getting materially different pages. Capped at 700. */
  min-height: clamp(560px, 29vw, 700px);
  display: flex;
  flex-direction: column;
  justify-content: center;
}
.home-hero :focus-visible { outline-color: var(--blue-600);}

/* ---------- Focus ----------
   Hover is invisible to keyboard users; this is what they get instead.
   Applies to every interactive thing, not just buttons. */
/* A light card inside a dark section inherits that section's CTA tokens,
   which paints the text link white on a pale surface. Reset per surface. */
.home-case-body, .home-article, .card, .home-quote, .form-card {
  --cta2-bg: var(--sky-100);
  --cta2-bg-hover: var(--sky-200);
  --cta2-ink: var(--blue-600);
  --cta2-edge: var(--sky-200);
  --cta2-edge-hover: var(--sky-300);
  --cta3-ink: var(--blue-600);
  --cta3-ink-hover: var(--navy-900);
}

a:focus-visible,
button:focus-visible,
summary:focus-visible,
[tabindex]:focus-visible {
  outline: 3px solid var(--blue-500);
  outline-offset: 3px;
  border-radius: var(--radius-sm);
}
.home-hero :focus-visible, .page-hero :focus-visible, .section--dark :focus-visible,
.home-cases :focus-visible, .home-promo :focus-visible, .home-quotes :focus-visible,
.site-footer :focus-visible {
  outline-color: var(--white);
}

/* ---------- Reduced motion ----------
   Honour the OS setting rather than animating regardless. */
@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.01ms !important;
    scroll-behavior: auto !important;
}
  .btn:hover { transform: none;}
  .cta-link:hover { gap: 7px;}
}

/* ---------- Logo ---------- */
.logo { display: flex; align-items: center; gap: 10px; flex-shrink: 0;}
/* The official lockup is 5.07:1 where the old file was 3.18:1, so the same
   height is already 59% wider -- 44px here is 223px across, against 140px for
   the old file at the same height. 50px fit the row, but the wordmark is set
   heavy and at that size it outweighed the 15px/700 nav links beside it. */
.logo img { height: 44px; width: auto; display: block; object-fit: contain;}
/* object-fit guards the pair below: the mobile rules set a height AND a
   max-width, which a 5.07:1 image cannot honour at once. Rendered 132x36
   against a natural 1080x213, the wordmark was squeezed 28% narrow. */

/* ---------- Nav ---------- */
/* WCAG 2.4.1. Off-screen until focused, then it lands over the header so it
   is unmissable. Not display:none, which would take it out of the tab order
   and defeat the point. */
/* Fixed to the VIEWPORT, not to the header.

   Absolute + top:-64px only worked while the header started at y=0. A
   third-party script injects a stray text node at the top of <body> at
   runtime -- our own markup has nothing between <body> and <header> -- and
   that anonymous line box pushed the header down 26px. The link went with it
   and 8px of navy sat on screen above the logo.

   -100px against a 46px link leaves 54px of clearance, so it stays hidden even
   if the link grows. A percentage translate was the first attempt and is worse
   here: translateY(-120%) resolves against the element's own height, which is
   smaller before the web font loads, so it under-shot and the link flashed. */
.skip-link {
  position: fixed;
  left: 16px;
  top: -100px;
  z-index: 200;
  background: var(--navy-900);
  color: var(--white);
  font-weight: 700;
  font-size: 14px;
  padding: 12px 18px;
  border-radius: 0 0 var(--radius-md) var(--radius-md);
  transition: top 0.14s ease;
}
.skip-link:focus { top: 0;}

/* Present to a screen reader, absent to the eye. Used where a section
   genuinely has a heading but the page design does not show one. */
.vh {
  position: absolute;
  width: 1px; height: 1px;
  margin: -1px; padding: 0; border: 0;
  clip-path: inset(50%);
  overflow: hidden;
  white-space: nowrap;
}




.site-header {
  position: sticky;
  top: 0;
  z-index: 100;
  background: rgba(255,255,255,0.92);
  backdrop-filter: blur(10px);
  border-bottom: 1px solid var(--gray-200);
}
.nav {
  display: flex;
  align-items: center;
  justify-content: space-between;
  /* vertical only — horizontal inset comes from .container */
  padding-top: 16px;
  padding-bottom: 16px;
}
.nav-links {
  display: flex;
  align-items: center;
  gap: 34px;
}
.nav-links a {
  font-size: 15px;
  font-weight: 700;
  color: var(--gray-800);
  position: relative;
  padding: 6px 0;
}
.nav-links a:hover { color: var(--blue-600);}
.nav-links a.active { color: var(--blue-600);}
.nav-links a.active::after {
  content: '';
  position: absolute;
  left: 0; right: 0; bottom: -4px;
  height: 2px;
  background: var(--blue-500);
  border-radius: 2px;
}
.nav-cta { display: flex; align-items: center; gap: 14px;}
.nav-toggle {
  display: none;
  background: none;
  border: none;
  width: 40px; height: 40px;
  /* The three bars are ::before, the span and ::after. Without a direction
     flex laid them out in a row, so three 20px bars shrank to 9px each and
     sat side by side -- one short dash where a hamburger should be. */
  flex-direction: column;
  align-items: center;
  justify-content: center;
}
.nav-toggle span, .nav-toggle::before, .nav-toggle::after {
  content: '';
  display: block;
  width: 20px;
  height: 2px;
  background: var(--navy-900);
  position: relative;
  transition: 0.2s;
}
.nav-toggle span { margin: 5px 0;}

/* Login is a text link, not a button — the header's one button is the demo CTA */
.nav-login {
  font-size: 14px;
  font-weight: 700;
  color: var(--navy-800);
  white-space: nowrap;
}
.nav-login:hover { color: var(--blue-600);}

/* ---------- Nav dropdowns ---------- */
/* Solutions, Industries and Resources each open a menu rather than navigating.
   Solutions and Resources lead their menu with a link to their landing page;
   Industries has none, since /industries/ only redirects. */
.nav-group { position: relative; display: flex;}
.nav-group-toggle {
  font-family: inherit;
  font-size: 15px;
  font-weight: 700;
  color: var(--gray-800);
  background: none;
  border: 0;
  padding: 6px 0;
  display: inline-flex;
  align-items: center;
  gap: 7px;
  position: relative;
  white-space: nowrap;
}
.nav-group-toggle::after {
  content: '';
  width: 6px; height: 6px;
  border-right: 2px solid currentColor;
  border-bottom: 2px solid currentColor;
  transform: translateY(-2px) rotate(45deg);
  transition: transform 0.15s ease;
}
.nav-group-toggle[aria-expanded="true"]::after { transform: translateY(1px) rotate(-135deg);}
.nav-group-toggle:hover { color: var(--blue-600);}
.nav-group-toggle.active { color: var(--blue-600);}
.nav-group-toggle.active::before {
  content: '';
  position: absolute;
  left: 0; right: 13px; bottom: -4px;
  height: 2px;
  background: var(--blue-500);
  border-radius: 2px;
}

/* Two signals used to look the same. The bar means "the section you are in" and
   hover means "the pointer is here", and both went blue -- so with Solutions
   open on an industries page, Industries kept the bar and read as the thing you
   had selected. Opening a menu only flipped the chevron, which is not enough
   acknowledgement for a panel covering a third of the screen.

   While a menu is open the bar moves to the toggle you opened, and the location
   marker dims rather than disappearing, so it stays legible and returns on
   close. :has() keeps this in CSS -- neither the markup nor the script needs to
   know anything about it. */
.nav-group-toggle[aria-expanded="true"] { color: var(--blue-600);}
.nav-group-toggle[aria-expanded="true"]::before {
  content: '';
  position: absolute;
  left: 0; right: 13px; bottom: -4px;
  height: 2px;
  background: var(--blue-500);
  border-radius: 2px;
}
.nav-links:has(.nav-group-toggle[aria-expanded="true"])
  .nav-group-toggle.active:not([aria-expanded="true"])::before,
.nav-links:has(.nav-group-toggle[aria-expanded="true"]) a.active::after {
  opacity: 0.25;
  transition: opacity 0.15s ease;
}
.nav-group-menu {
  display: flex;
  flex-direction: column;
  position: absolute;
  top: calc(100% + 14px);
  left: -18px;
  min-width: 236px;
  background: var(--white);
  border: 1px solid var(--gray-200);
  border-radius: var(--radius-md);
  box-shadow: var(--shadow-card);
  padding: 10px;
  z-index: 120;
}
/* Author styles beat the UA rule for [hidden], so restate it or the menu
   never closes. */
.nav-group-menu[hidden] { display: none;}
/* Invisible bridge across the 14px gap above the panel. Without it the pointer
   leaves the group on its way from the trigger into the menu. */
.nav-group-menu::before {
  content: '';
  position: absolute;
  left: 0; right: 0; top: -14px;
  height: 14px;
}
.nav-group-menu:not(.nav-mega) a {
  font-size: 14.5px;
  font-weight: 700;
  color: var(--gray-800);
  padding: 9px 12px;
  border-radius: var(--radius-sm);
}
.nav-group-menu:not(.nav-mega) a:hover { background: var(--sky-100); color: var(--blue-600);}
/* Sections with a landing page lead their menu with a link to it; Industries
   has no landing page, so it has no lead link. */
.nav-group-lead {
  color: var(--blue-600) !important;
  border-bottom: 1px solid var(--gray-200);
  border-radius: 0 !important;
  margin-bottom: 6px;
  padding-bottom: 12px !important;
}

/* ---------- Solutions megamenu ---------- */
/* Solutions opens a wide panel rather than the narrow list the other two groups
   use. It is far wider than its trigger, so it can't hang off it:
   .nav-group--mega drops out of the positioning chain and the sticky
   .site-header becomes the containing block, which is what lets the panel
   centre on the page instead. Industries and Resources keep the compact
   dropdown until their own comps land. */
.nav-group--mega {
  position: static;
  /* Reaches the trigger's hit box down towards the panel; .nav-mega::before
     covers the rest. Both are needed -- see the note on that bridge. The
     negative margin cancels the padding, so nothing in the header moves, and
     20px keeps the box inside the header rather than over the page. */
  padding-bottom: 20px;
  margin-bottom: -20px;
}

/* Capped at the site container and centred, so it reads as a card under the
   header rather than a full-bleed band. */
.nav-mega {
  display: block;
  top: calc(100% + 10px);
  left: 50%;
  right: auto;
  transform: translateX(-50%);
  width: min(100% - 48px, var(--container));
  min-width: 0;
  padding: 0;
  background: var(--gray-100);
  border: 1px solid var(--gray-200);
  border-radius: var(--radius-lg);
}
/* The trigger's box ends ~22px above the panel, and the panel now sits 10px
   below the header on top of that. That strip belongs to .nav, not to the
   group, so without a bridge a pointer travelling from "Solutions" down into
   the panel leaves the group, starts the 160ms close, and the panel flashes
   shut and straight back open on arrival. This half only exists while the panel
   is visible, since [hidden] takes the pseudo with it -- which is why the group
   carries the other half. */
.nav-mega::before { top: -16px; height: 16px;}

.nav-mega-body {
  display: grid;
  grid-template-columns: minmax(0, 0.55fr) minmax(0, 1fr);
  gap: 36px;
  align-items: start;
  padding: 36px 36px 40px;
}

.nav-mega-head {
  font-size: 21px;
  font-weight: 800;
  line-height: 1.22;
  letter-spacing: -0.01em;
  color: var(--text-heading);
  margin: 0 0 14px;
}
.nav-mega-pitch p { font-size: 15px; margin: 0 0 24px;}
/* .nav-links a is a class *and* a type selector, so it outranks .btn and .btn-sm:
   the pill was losing its horizontal padding entirely and picking up the nav link's
   size, weight and colour. Restate the button's own values above it. */
.nav-links .nav-mega-cta {
  margin-top: 2px;
  padding: 10px 18px;
  font-size: 14px;
  font-weight: 800;
  color: var(--navy-900);
}
.nav-links .nav-mega-cta:hover { color: var(--navy-900);}

/* The rule in the comp runs the height of the panel content rather than just the
   rows, so it sits on the stretched column, not between grid tracks.
   Column-major: four rows down the first column, then the rest. */
.nav-mega-links {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  /* Row count is PER MENU, below. grid-auto-flow: column fills each column to
     the row count and then opens another one, so a list with more items than
     rows x 2 silently grows a third column and orphans the overflow. Solutions
     has seven links, Industries and Resources six each. */
  grid-template-rows: repeat(4, auto);
  grid-auto-flow: column;
  column-gap: 20px;
  row-gap: 18px;
  align-self: stretch;
  border-left: 1px solid var(--gray-400);
  padding-left: 32px;
}
/* Seven links: 4+3. */
#nav-solutions .nav-mega-links { grid-template-rows: repeat(4, auto);}
/* Six each: 3+3. Industries lost Education on 18 Sep; Resources never had a
   seventh. A link added to either wants this back at 4. */
#nav-industries .nav-mega-links,
#nav-resources .nav-mega-links { grid-template-rows: repeat(3, auto);}
.nav-mega-links a {
  display: flex;
  align-items: center;
  gap: 12px;
  font-size: 15px;
  font-weight: 800;
  color: var(--navy-900);
  padding: 4px 0;
}
/* A real element, not a pseudo, because these are placeholders for the icon set
   -- dropping an <img>/<svg> inside each span is then the only change needed. */
.nav-mega-ico {
  flex: 0 0 auto;
  display: grid;
  place-items: center;
  width: 26px;
  height: 26px;
  border-radius: 50%;
  background: var(--blue-500);
  transition: background 0.15s ease;
}
.nav-mega-ico > * { width: 15px; height: 15px;}

/* An item with an icon drops the dot: the SVG becomes a MASK, so the artwork
   takes its colour from this rule rather than from the file. That is what keeps
   the existing hover change working -- the shipped SVGs are all a fixed
   #2d76d4, which could not respond to anything. Items with no data-icon keep
   the plain dot, so nothing breaks if one is added to a menu without artwork. */
.nav-mega-ico[data-icon] {
  border-radius: 0;
  background: var(--blue-500);
  -webkit-mask: var(--nav-ico) center / contain no-repeat;
  mask: var(--nav-ico) center / contain no-repeat;
}
.nav-mega-ico[data-icon="industry_construction"] { --nav-ico: url(/assets/icons/industry_construction.svg);}
.nav-mega-ico[data-icon="industry_energy-and-utilities"] { --nav-ico: url(/assets/icons/industry_energy-and-utilities.svg);}
.nav-mega-ico[data-icon="industry_gaming"] { --nav-ico: url(/assets/icons/industry_gaming.svg);}
.nav-mega-ico[data-icon="industry_government"] { --nav-ico: url(/assets/icons/industry_government.svg);}
.nav-mega-ico[data-icon="industry_healthcare"] { --nav-ico: url(/assets/icons/industry_healthcare.svg);}
.nav-mega-ico[data-icon="industry_manufacturing"] { --nav-ico: url(/assets/icons/industry_manufacturing.svg);}
.nav-mega-ico[data-icon="resource_blog"] { --nav-ico: url(/assets/icons/resource_blog.svg);}
.nav-mega-ico[data-icon="resource_glossary"] { --nav-ico: url(/assets/icons/resource_glossary.svg);}
.nav-mega-ico[data-icon="resource_guides-and-whitepapers"] { --nav-ico: url(/assets/icons/resource_guides-and-whitepapers.svg);}
.nav-mega-ico[data-icon="resource_trackvia-university"] { --nav-ico: url(/assets/icons/resource_trackvia-university.svg);}
.nav-mega-ico[data-icon="resource_videos"] { --nav-ico: url(/assets/icons/resource_videos.svg);}
.nav-mega-ico[data-icon="resource_webinars-and-events"] { --nav-ico: url(/assets/icons/resource_webinars-and-events.svg);}
.nav-mega-ico[data-icon="solution_asset-and-site-operations"] { --nav-ico: url(/assets/icons/solution_asset-and-site-operations.svg);}
.nav-mega-ico[data-icon="solution_crew-and-vendor-management"] { --nav-ico: url(/assets/icons/solution_crew-and-vendor-management.svg);}
.nav-mega-ico[data-icon="solution_field-inspection"] { --nav-ico: url(/assets/icons/solution_field-inspection.svg);}
.nav-mega-ico[data-icon="solution_inventory"] { --nav-ico: url(/assets/icons/solution_inventory.svg);}
.nav-mega-ico[data-icon="solution_preventive-maintenance"] { --nav-ico: url(/assets/icons/solution_preventive-maintenance.svg);}
.nav-mega-ico[data-icon="solution_safety-and-compliance"] { --nav-ico: url(/assets/icons/solution_safety-and-compliance.svg);}
.nav-mega-ico[data-icon="solution_work-order-management"] { --nav-ico: url(/assets/icons/solution_work-order-management.svg);}
/* Borders rather than a glyph: a text caret won't sit on the baseline
   consistently next to the 800-weight label. */
.nav-mega-links a::after {
  content: '';
  flex: 0 0 auto;
  width: 0;
  height: 0;
  border-left: 8px solid var(--blue-500);
  border-top: 5px solid transparent;
  border-bottom: 5px solid transparent;
  transition: transform 0.15s ease;
}
.nav-mega-links a:hover { color: var(--blue-600);}
.nav-mega-links a:hover .nav-mega-ico { background: var(--blue-600);}
.nav-mega-links a:hover::after { transform: translateX(3px);}

/* Our link labels are longer than the comp's ("Inventory and Materials Control"
   against its "Inventory & Materials Control"), so the two columns need a floor
   to stay on one line. Below 1250px -- where the panel stops being capped at the
   container width and starts shrinking with the window -- the copy column gives
   the width up rather than the labels wrapping. Below 1025px the header
   collapses to the drawer anyway. */
@media (min-width: 1025px) and (max-width: 1249px) {
  .nav-mega-body {
    grid-template-columns: minmax(0, 0.42fr) minmax(0, 1fr);
    gap: 28px;
    padding: 32px 32px 36px;
}
  .nav-mega-links { padding-left: 28px; column-gap: 18px;}
}

/* ---------- Inner page furniture ---------- */
.page-hero {
  position: relative;
  overflow: hidden;
  background: var(--gradient-band);
  color: var(--sky-100);
  padding: 66px 0 62px;
  /* The band is full-bleed but its height is content-driven, so on a very wide
     display it stretches to a 5:1 letterbox and background-size:cover throws
     away most of the photograph. Above 1920 the height tracks the width
     instead, which pins the ratio at 3.80:1 at any size. This is what makes a
     single desktop crop briefable: 2.53:1 to 3.80:1 rather than open-ended. */
  min-height: max(505px, 26.3vw);
  display: flex;
  flex-direction: column;
  justify-content: center;
}
/* An individual post is not a section landing page: its hero matches the
   case study hero's 384px rather than the 505px the index pages use. Scoped
   to posts, because .page-hero also dresses /legal and the Resources indexes. */
.page-hero--post { min-height: 384px;}

/* a photo can now sit behind this band; the copy has to clear it */
.page-hero > .container {
  position: relative;
  z-index: 1;
  /* .page-hero is a flex column and .container carries margin:0 auto, and an
     auto margin on the cross axis opts an item OUT of stretch -- so the
     container was shrink-wrapping to its widest child and then centring itself.
     On the industry pages the copy is long enough to fill 1200px so it looked
     left-aligned; on a page with a short hero it collapsed and drifted to the
     middle. width:100% puts it back on the line, and max-width still caps it. */
  width: 100%;
}
.page-hero-bg { position: absolute; inset: 0;}
.page-hero h1 { color: var(--white); font-size: 44px; margin-bottom: 14px; text-wrap: balance;}
.page-hero .eyebrow { color: var(--sky-200); background: rgba(255,255,255,0.08);}
.page-hero-lede { color: var(--sky-200); font-size: 17.5px; max-width: 68ch; margin-bottom: 24px;}
.page-hero .btn { margin-top: 4px;}

/* ---------- Industry hero on the brand artwork ----------
   The supplied art is navy on the left with the industry photograph cut into the
   chevron on the right, so it needs neither the photo-bg scrim nor a second
   image on top. Measured on the files: the flat navy runs to 55.5% of the width
   at mid-height, which is the narrowest point of the shape and exactly where the
   copy sits, so the column is capped under that. */
.page-hero--art { background: var(--navy-900);}
.page-hero--art .page-hero-bg {
  background-size: cover;
  background-position: center right;
  background-repeat: no-repeat;
}
/* The art is 2.61:1. Below roughly 1320px the band is taller than the image is
   tall at that width, so cover scales up and crops horizontally -- which eats
   into the navy the headline sits on. This horizontal scrim is invisible over
   the artwork's own navy and only does work once that crop starts, so the copy
   stays legible at every width without a breakpoint. */
.page-hero--art .page-hero-bg::before {
  content: '';
  position: absolute;
  inset: 0;
  background: linear-gradient(90deg,
    rgba(6, 14, 30, 0.92) 0%,
    rgba(6, 14, 30, 0.74) 34%,
    rgba(6, 14, 30, 0.10) 58%,
    rgba(6, 14, 30, 0) 68%);
}
.page-hero--art h1,
.page-hero--art .page-hero-lede,
.page-hero--art .sol-facts,
.page-hero--art .ctarow { max-width: 52%;}

@media (max-width: 1000px) {
  /* The chevron point is off-screen by now; hold the photograph's subject in
     view and let the scrim carry the legibility. */
  .page-hero--art .page-hero-bg { background-position: center right -10%;}
  .page-hero--art h1,
  .page-hero--art .page-hero-lede,
  .page-hero--art .sol-facts,
  .page-hero--art .ctarow { max-width: 68%;}
}
@media (max-width: 720px) {
  .page-hero--art .page-hero-bg::before {
    background: linear-gradient(90deg,
      rgba(6, 14, 30, 0.94) 0%,
      rgba(6, 14, 30, 0.88) 55%,
      rgba(6, 14, 30, 0.62) 100%);
}
  .page-hero--art h1,
  .page-hero--art .page-hero-lede,
  .page-hero--art .sol-facts,
  .page-hero--art .ctarow { max-width: none;}
}

.card-grid > * { min-width: 0;}
.card {
  border: 1px solid var(--gray-200);
  border-radius: var(--radius-md);
  overflow: hidden;
  background: var(--white);
  display: flex;
  flex-direction: column;
  transition: box-shadow 0.15s ease, transform 0.15s ease;
}
.card:hover { box-shadow: var(--shadow-card); transform: translateY(-2px);}
/* A card that is not a link should not lift on hover -- the lift reads as
   "click me" and there is nothing to click. */
.card--static:hover { box-shadow: none; transform: none;}
.card > .ph { border-radius: 0; border-width: 0 0 1px 0;}
/* An article card's image carries the "Article" pill on top of it, so the
   photo is a background on the same box rather than a plain <img>. */
/* An <img loading="lazy">, not a background. A CSS background cannot be lazy
   loaded and is invisible to the preload scanner, so all 129 of these card
   thumbnails downloaded immediately on whatever page carried them, most of
   them far below the fold. Measured on the homepage before this: 2791KB
   transferred and 47 requests started before first paint. width/height are the
   intrinsic ratio so the box is reserved before the file arrives, which is
   also what stops the cards shifting as they load. */
/* Same reason as .card-shot below: the case reel cards on /solutions and
   /industries are flex columns too, and their photo was shrinking to make
   room for longer copy -- 235, 255 and 275px in one row. */
/* height:auto, not the .photo default of height:100%. A .card is a column
   flex box with overflow:hidden, so a photo told to fill 100% of the card
   takes the whole box and the .card-body underneath is clipped away -- the
   cards render as bare photographs with no module name or copy. Letting the
   inline aspect-ratio set the height leaves room for the text. */
.card > .photo, .card > img.photo { flex: none; height: auto;}

.card-shot {
  position: relative;
  flex: none;
  display: block;
  width: 100%;
  height: auto;
  aspect-ratio: 560 / 300;
  object-fit: cover;
  background-size: cover;
  background-position: center;
  border-bottom: 1px solid var(--gray-200);
}
/* A conference card carries the event's own logo, not a photo. Logos come in
   wildly different proportions (350x100 to 600x563) and two of the three are
   transparent while ULSCA's ships a baked-in white block -- so the box stays
   white and the mark is contained inside it rather than cropped to fill. */
.card-logo {
  aspect-ratio: 560 / 300;
  box-sizing: border-box;
  background: var(--white);
  border-bottom: 1px solid var(--gray-200);
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 26px 30px;
  /* Without this the box takes its automatic minimum size from a tall logo
     (PWX is near-square) and outgrows the ratio, so the three cards in the
     row stop lining up. */
  overflow: hidden;
}
.card-logo img {
  max-width: 100%;
  max-height: 100%;
  width: auto;
  min-height: 0;
  object-fit: contain;
}
.card-body { padding: 18px; display: flex; flex-direction: column; gap: 7px;}
/* .card-body is a column flex container, so a pill inside it stretches to the
   full card width instead of hugging its text. It is the eyebrow's own
   inline-flex that gets overridden, not its padding, so the fix is alignment
   rather than a width. Applies to every card pill: Webinar, Conference, Guide. */
.card-body .eyebrow {
  align-self: flex-start;
  margin-bottom: 0;
  /* Two pill components used to coexist in card bodies: .eyebrow on the
     Resources index pages and .home-article-type everywhere else. They now
     render the same, so a Guide pill and an Article pill match. */
  background: var(--white);
  border: 1px solid var(--gray-200);
  color: var(--gray-800);
  padding: 4px 14px;
  font-size: 12.5px;
  font-weight: 700;
  letter-spacing: 0;
  text-transform: none;
}
/* Wistia's popover trigger sits where a card's photo normally goes. Their own
   snippet ships an inline <style> for the pre-hydration state; it lives here
   instead so the page carries no inline styles. The :not(:defined) rule is
   what shows the video's own thumbnail before the custom element upgrades. */
.card-video { border-bottom: 1px solid var(--gray-200); line-height: 0;}
.card-video wistia-player { display: block; width: 100%;}
wistia-player[media-id='qmg9v7d2n8']:not(:defined) {
  background: center / contain no-repeat url('https://fast.wistia.com/embed/medias/qmg9v7d2n8/swatch');
  display: block;
  aspect-ratio: 16 / 9;
  filter: blur(5px);
}
.card-kind {
  font-size: 11px;
  font-weight: 800;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--gray-500);
}
.card-body h3 { font-size: 17.5px; margin: 0;}

.page-split {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 52px;
  align-items: center;
}
.page-split > * { min-width: 0;}
.page-split h2 { font-size: 30px;}

.page-list { margin: 0 0 24px;}
.page-list li {
  position: relative;
  padding-left: 20px;
  margin-bottom: 9px;
  color: var(--gray-800);
  font-size: 15.5px;
}
.page-list li::before {
  content: '';
  position: absolute;
  left: 2px; top: 10px;
  width: 6px; height: 6px;
  border-radius: 50%;
  background: var(--blue-500);
}

.link-row { display: flex; flex-wrap: wrap; gap: 10px;}
.link-row a {
  font-size: 14.5px;
  font-weight: 700;
  color: var(--blue-600);
  background: var(--white);
  border: 1px solid var(--gray-200);
  border-radius: var(--radius-pill);
  padding: 9px 18px;
}
.link-row a:hover { border-color: var(--blue-500);}

.prose { max-width: 68ch;}
/* Legal documents run the full container width. A contract is reference
   material people scan and search rather than read end to end, and its clause
   numbers already hang in the left margin -- the 68ch measure that suits an
   article leaves most of the page empty beside them. */
.prose--wide { max-width: none;}
.prose h2 { font-size: 25px; margin-top: 34px;}
.prose p { font-size: 17px; color: var(--gray-600);}
/* Lists and links inside prose. The global reset strips list markers and the
   .prose ul rule that puts them back was scoped to .post-body, so a list on any
   other prose page rendered as unmarked lines. Links were inheriting body
   colour with no underline, which on a legal page meant the cookie policy's
   link to the privacy policy was invisible. */
.prose ul, .prose ol { margin: 0 0 20px; padding-left: 22px; color: var(--gray-600);}
.prose ul { list-style: disc;}
.prose ol { list-style: decimal;}
.prose li { margin-bottom: 8px; font-size: 17px;}
.prose li::marker { color: var(--gray-400);}
.prose a { color: var(--blue-600); text-decoration: underline; text-underline-offset: 2px;}
.prose a:hover { color: var(--navy-900);}
/* A data table inside prose breaks out of the 68ch reading measure. Three
   columns of prose do not fit a paragraph width -- constrained to it, the
   privacy policy's collection table scrolled sideways on a desktop screen,
   which is the one place it has room not to. It stays inside the container,
   so it never goes full-bleed. */
/* Numbered contract clauses. The number sits in the margin so the clause text
   keeps a straight left edge down the page -- a wrapped clause should not run
   back under its own number. Two depths: 2.1 and (a). */
.prose .clause { position: relative; padding-left: 62px;}
.prose .clause-n {
  position: absolute;
  left: 0;
  top: 0;
  font-weight: 800;
  color: var(--navy-900);
  font-variant-numeric: tabular-nums;
  font-size: 15.5px;
}
.prose .clause--deep { padding-left: 104px;}
.prose .clause--deep .clause-n { left: 62px;}
/* A schedule or appendix is a break in the document, not another section. */
.prose h2.schedule {
  margin-top: 62px;
  padding-top: 30px;
  border-top: 2px solid var(--gray-200);
}
@media (max-width: 640px) {
  .prose .clause { padding-left: 46px;}
  .prose .clause--deep { padding-left: 74px;}
  .prose .clause--deep .clause-n { left: 46px;}
}

.prose .datatable-scroll {
  /* The 56px is .container's own 28px of padding on each side: .prose starts at
     the container's content edge, so a break-out of the full --container width
     would hang 28px past its right edge. */
  width: min(calc(100vw - 56px), calc(var(--container) - 56px));
  max-width: none;
  margin-block: 26px;
}

.chip-row { display: flex; flex-wrap: wrap; gap: 8px; margin-bottom: 14px;}
.chip-row span {
  font-size: 13.5px;
  font-weight: 700;
  color: var(--gray-500);
  background: var(--gray-50);
  border: 1px dashed var(--gray-200);
  border-radius: var(--radius-pill);
  padding: 7px 15px;
}
.index-note { font-size: 14px; color: var(--gray-500); margin-bottom: 26px;}

/* Provenance strip at the foot of every stub — what governs this page and
   what is still missing. Goes away as pages are finished. */
/* Unused since 18 Sep, when the stub banner was removed for the public site.
   Kept only so a stub page can be marked again during a build if that is ever
   wanted; nothing on the site emits .stub-meta now. */
.stub-meta {
  border-top: 1px dashed var(--gold-500);
  background: var(--gold-100);
  padding: 26px 0 30px;
}
.stub-path {
  font-family: ui-monospace, 'SF Mono', Menlo, Consolas, monospace;
  font-size: 13px;
  color: var(--gold-600);
  margin-bottom: 8px;
}
.stub-note { font-size: 15px; color: var(--gray-600); max-width: 70ch; margin-bottom: 10px;}
.stub-status {
  display: inline-block;
  font-size: 12px;
  font-weight: 800;
  letter-spacing: 0.05em;
  text-transform: uppercase;
  color: var(--gold-600);
  margin: 0;
}

@media (max-width: 820px) {
  .page-hero h1 { font-size: 33px;}
  .story-hero--story h1 { font-size: 33px;}
  .page-split { grid-template-columns: 1fr; gap: 30px;}
}

/* ---------- Hero ---------- */
.hero {
  background: var(--gradient-hero);
  color: var(--white);
  position: relative;
  overflow: hidden;
  padding: 100px 0 90px;
}
.hero::before {
  content: '';
  position: absolute;
  inset: -20% -10% auto auto;
  width: 640px; height: 640px;
  background: radial-gradient(circle, rgba(45,118,212,0.30) 0%, rgba(45,118,212,0) 70%);
  pointer-events: none;
}
.hero::after {
  content: '';
  position: absolute;
  left: -12%; bottom: -30%;
  width: 500px; height: 500px;
  background: radial-gradient(circle, rgba(225,177,69,0.16) 0%, rgba(225,177,69,0) 70%);
  pointer-events: none;
}
.hero-grid {
  position: relative;
  z-index: 1;
  display: grid;
  grid-template-columns: 1fr 0.95fr;
  gap: 56px;
  align-items: center;
}
.hero h1 {
  font-size: 48px;
  color: var(--white);
  margin-bottom: 22px;
}
.hero h1 .accent { color: var(--white);}
.hero p.lead {
  font-size: 18px;
  color: var(--sky-100);
  max-width: 560px;
  margin-bottom: 34px;
}
.hero-actions { display: flex; gap: 16px; flex-wrap: wrap; margin-bottom: 38px;}
.hero-microcopy { font-size: 13px; color: var(--sky-200); display: flex; gap: 20px; flex-wrap: wrap;}
.hero-microcopy span { display: inline-flex; align-items: center; gap: 6px;}
.dot { width: 6px; height: 6px; border-radius: 50%; background: var(--gold); display: inline-block;}

.hero-photo {
  position: relative;
  border-radius: var(--radius-lg);
  overflow: hidden;
  box-shadow: 0 24px 60px -16px rgba(0,0,0,0.5);
  border: 1px solid rgba(255,255,255,0.12);
}
.hero-photo img { width: 100%; height: auto; display: block;}

.hero-panel {
  position: relative;
  background: rgba(255,255,255,0.06);
  border: 1px solid rgba(255,255,255,0.14);
  border-radius: var(--radius-lg);
  padding: 22px;
  backdrop-filter: blur(6px);
}
.hero-panel-header { display: flex; align-items: center; gap: 8px; margin-bottom: 16px;}
.hero-panel-header .dotrow { display: flex; gap: 6px;}
.hero-panel-header .dotrow span { width: 9px; height: 9px; border-radius: 50%; background: rgba(255,255,255,0.25);}
.hero-panel-title { font-size: 12px; color: var(--sky-200); font-weight: 700; letter-spacing: 0.04em; text-transform: uppercase; margin-left: 6px;}
.hero-stat-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 14px;}
.hero-stat {
  background: rgba(6,14,30,0.4);
  border: 1px solid rgba(255,255,255,0.1);
  border-radius: var(--radius-md);
  padding: 18px;
}
.hero-stat .num {
  font-family: 'Nunito Sans', sans-serif;
  font-size: 26px;
  font-weight: 800;
  color: var(--white);
  display: block;
}
.hero-stat .label { font-size: 12px; color: var(--sky-200);}
.hero-panel-bar {
  margin-top: 14px;
  height: 10px;
  border-radius: 6px;
  background: rgba(255,255,255,0.08);
  overflow: hidden;
}
.hero-panel-bar > div { height: 100%; background: linear-gradient(90deg, var(--blue-500), var(--gold));}

/* ---------- Logo strip ---------- */
.logo-strip {
  padding: 44px 0;
  border-bottom: 1px solid var(--gray-200);
}
.logo-strip .label {
  text-align: center;
  font-size: 13px;
  font-weight: 800;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--gray-500);
  margin-bottom: 26px;
}
.logo-row {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: 14px;
}
.logo-chip {
  padding: 10px 20px;
  border-radius: var(--radius-pill);
  background: var(--gray-50);
  border: 1px solid var(--gray-200);
  color: var(--gray-600);
  font-weight: 800;
  font-size: 14.5px;
  font-family: 'Nunito Sans', sans-serif;
}
.logo-chip a { color: inherit;}

/* ---------- Pillars / feature trio ---------- */
.pillars { display: grid; grid-template-columns: repeat(3, 1fr); gap: 24px;}
.pillar {
  background: var(--white);
  border: 1px solid var(--gray-200);
  border-radius: var(--radius-lg);
  padding: 32px;
  box-shadow: var(--shadow-card);
}
.pillar .num-badge {
  width: 42px; height: 42px;
  border-radius: 12px;
  background: var(--gradient-cta);
  color: var(--white);
  display: flex; align-items: center; justify-content: center;
  font-weight: 800;
  font-family: 'Nunito Sans', sans-serif;
  margin-bottom: 18px;
}
.pillar h3 { font-size: 19px;}
.pillar p { color: var(--gray-600); font-size: 15px; margin-bottom: 0;}
.pillar .kicker { font-size: 12px; font-weight: 800; letter-spacing: 0.06em; text-transform: uppercase; color: var(--blue-600); display: block; margin-bottom: 6px;}

/* ---------- Card grid (solutions/use-cases) ---------- */
.card-grid {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 22px;
}
/* Four short cards in a three-column grid leave one stranded on its own row.
   The 980px and below media queries still take this down to two, then one. */
.card-grid--4up { grid-template-columns: repeat(4, 1fr);}
/* Four LONG cards do the opposite: three across leaves one stranded below and
   the text is too cramped at a third each. Two by two gives each one room and
   squares the block off. */
.card-grid--2up { grid-template-columns: repeat(2, 1fr);}
.solution-card {
  background: var(--white);
  border: 1px solid var(--gray-200);
  border-radius: var(--radius-lg);
  padding: 28px;
  transition: transform 0.18s ease, box-shadow 0.18s ease, border-color .18s ease;
}
.solution-card:hover {
  transform: translateY(-4px);
  box-shadow: var(--shadow-card);
  border-color: var(--sky-300);
}
.solution-icon {
  width: 46px; height: 46px;
  border-radius: 12px;
  background: var(--sky-100);
  display: flex; align-items: center; justify-content: center;
  margin-bottom: 18px;
}
.solution-icon svg { width: 24px; height: 24px;}
.solution-card h3 { font-size: 18px; margin-bottom: 8px;}
.solution-card p { font-size: 14.5px; color: var(--gray-600); margin-bottom: 0;}
.tag-row { display: flex; flex-wrap: wrap; gap: 8px; margin-top: 14px;}
.tag {
  font-size: 12px;
  font-weight: 800;
  color: var(--blue-600);
  background: var(--sky-100);
  padding: 4px 10px;
  border-radius: var(--radius-pill);
}
.tag.tag-teal { color: var(--teal-600); background: var(--teal-100);}
.tag.tag-gold { color: var(--gold-600); background: var(--gold-100);}

.more-solutions {
  margin-top: 40px;
  padding-top: 36px;
  border-top: 1px solid var(--gray-200);
  text-align: center;
}
.more-solutions p { font-size: 13.5px; font-weight: 800; text-transform: uppercase; letter-spacing: 0.06em; color: var(--gray-500); margin-bottom: 16px;}
.more-solutions .tag-row { justify-content: center;}
.more-solutions .tag { font-size: 13.5px; padding: 9px 18px; background: var(--white); border: 1px solid var(--gray-200); color: var(--navy-800);}

/* ---------- Stats band ---------- */
.stats-band {
  background: var(--gradient-band);
  color: var(--white);
  padding: 64px 0;
}
.stats-grid {
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  gap: 24px;
  text-align: center;
}
.stat-block .num {
  font-family: 'Nunito Sans', sans-serif;
  font-size: 40px;
  font-weight: 900;
  color: var(--gold);
  display: block;
  margin-bottom: 6px;
}
.stat-block .label { font-size: 14.5px; color: var(--sky-100);}
.stat-block .source { font-size: 12px; color: var(--sky-300); margin-top: 4px; display: block;}

/* ---------- Testimonial ---------- */
.testimonial {
  background: var(--white);
  border: 1px solid var(--gray-200);
  border-radius: var(--radius-lg);
  padding: 48px;
  box-shadow: var(--shadow-card);
  display: grid;
  grid-template-columns: auto 1fr;
  gap: 32px;
  align-items: center;
}
.testimonial .quote-mark {
  font-family: 'Nunito Sans', sans-serif;
  font-size: 72px;
  color: var(--gold);
  line-height: 1;
}
.testimonial blockquote {
  font-size: 22px;
  font-weight: 700;
  color: var(--navy-900);
  margin: 0 0 18px 0;
}
.testimonial .attribution { font-size: 14.5px; color: var(--gray-600);}
.testimonial .attribution b { color: var(--navy-900);}
.testimonial .attribution a { color: var(--blue-600); font-weight: 700;}

/* ---------- Brand photo band (triangle-mask device) ---------- */
.brand-band {
  position: relative;
  background: var(--navy-900);
  overflow: hidden;
}
.brand-band img { width: 100%; height: auto; display: block; opacity: 0.95;}
.brand-band-caption {
  position: absolute;
  left: 0; right: 0; bottom: 0;
  padding: 28px;
  background: linear-gradient(0deg, rgba(6,14,30,0.85) 0%, rgba(6,14,30,0) 100%);
  color: var(--white);
  text-align: center;
}
.brand-band-caption p { margin: 0; font-weight: 800; font-size: 16px; letter-spacing: 0.01em;}
.brand-band-caption span { font-size: 12.5px; color: var(--sky-200); font-weight: 600;}

/* ---------- Case study cards ---------- */
.case-grid {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 24px;
  align-items: start;
}
.case-card {
  background: var(--white);
  border: 1px solid var(--gray-200);
  border-radius: var(--radius-lg);
  overflow: hidden;
  box-shadow: var(--shadow-card);
  display: flex;
  flex-direction: column;
  scroll-margin-top: 110px;
}
.case-card-top {
  padding: 26px 26px 22px;
  background: var(--gradient-band);
  color: var(--white);
  position: relative;
}
.case-card-top .tag {
  background: rgba(255,255,255,0.14);
  color: var(--sky-200);
  margin-bottom: 14px;
  display: inline-block;
}
.case-card-top .result-num {
  font-family: 'Nunito Sans', sans-serif;
  font-size: 32px;
  font-weight: 900;
  color: var(--gold);
}
.case-card-top .result-label { font-size: 13.5px; color: var(--sky-100);}
.case-card-body { padding: 24px 26px 28px; flex: 1; display: flex; flex-direction: column;}
.case-card-body h3 { font-size: 17.5px; margin-bottom: 10px;}
.case-card-body p { font-size: 14.5px; color: var(--gray-600); flex: 1;}
.case-card-body .case-link { font-size: 14px; font-weight: 800; color: var(--blue-600); margin-top: 14px; display: inline-flex; align-items: center; gap: 6px; cursor: pointer; background: none; border: none; padding: 0; font-family: inherit;}
.case-card-body .quote { font-size: 13.5px; font-style: italic; color: var(--navy-800); border-left: 3px solid var(--gold); padding-left: 12px; margin: 12px 0;}
.case-card-body .case-meta { font-size: 12.5px; color: var(--gray-500); margin: -6px 0 12px;}

.case-details { margin-top: 8px;}
.case-details summary {
  cursor: pointer;
  list-style: none;
  font-size: 14px;
  font-weight: 800;
  color: var(--blue-600);
  display: inline-flex;
  align-items: center;
  gap: 6px;
}
.case-details summary::-webkit-details-marker { display: none;}
.case-details summary::after { content: '\2193'; font-size: 11px; transition: transform .15s ease; display: inline-block;}
.case-details[open] summary::after { transform: rotate(180deg);}
.case-full {
  padding-top: 16px;
  margin-top: 14px;
  border-top: 1px solid var(--gray-200);
}
.case-full h4 { font-size: 14px; text-transform: uppercase; letter-spacing: 0.04em; color: var(--blue-600); margin: 16px 0 6px;}
.case-full h4:first-child { margin-top: 0;}
.case-full p { font-size: 14px; color: var(--gray-600);}

.filter-row { display: flex; gap: 10px; margin-bottom: 40px; flex-wrap: wrap;}
.filter-btn {
  border: 1px solid var(--gray-200);
  background: var(--white);
  padding: 10px 20px;
  border-radius: var(--radius-pill);
  font-weight: 700;
  font-size: 14px;
  color: var(--gray-600);
}
.filter-btn.active { background: var(--navy-900); color: var(--white); border-color: var(--navy-900);}

.gov-proof-note {
  background: var(--teal-100);
  border: 1px solid var(--teal-500);
  border-radius: var(--radius-lg);
  padding: 32px;
  text-align: center;
  color: var(--teal-600);
}
.gov-proof-note h3 { color: var(--navy-900); margin-bottom: 10px;}
.gov-proof-note p { color: var(--teal-600); max-width: 520px; margin: 0 auto 20px;}

/* ---------- Compliance badges (gov) ---------- */
.badge-row {
  display: flex;
  flex-wrap: wrap;
  gap: 16px;
  justify-content: center;
}
.compliance-badge {
  display: flex;
  align-items: center;
  gap: 12px;
  background: rgba(255,255,255,0.06);
  border: 1px solid rgba(255,255,255,0.16);
  border-radius: var(--radius-md);
  padding: 16px 22px;
  min-width: 200px;
}
.compliance-badge .icon {
  width: 38px; height: 38px;
  border-radius: 10px;
  background: var(--gradient-cta);
  display: flex; align-items: center; justify-content: center;
  flex-shrink: 0;
}
.compliance-badge .icon svg { width: 20px; height: 20px;}
.compliance-badge .name { font-weight: 800; color: var(--white); font-size: 14.5px;}
.compliance-badge .desc { font-size: 12.5px; color: var(--sky-200);}

/* ---------- Segment chips (gov) ---------- */
.segment-row { display: flex; flex-wrap: wrap; gap: 10px; margin-top: 22px;}
.segment-chip {
  font-size: 13px;
  font-weight: 700;
  color: var(--navy-800);
  background: var(--white);
  border: 1px solid var(--gray-200);
  padding: 8px 16px;
  border-radius: var(--radius-pill);
}

/* ---------- Split section (why gov) ---------- */
.split {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 64px;
  align-items: center;
}
.check-list li {
  display: flex;
  gap: 14px;
  margin-bottom: 20px;
  align-items: flex-start;
}
.check-list .check-icon {
  width: 26px; height: 26px;
  border-radius: 50%;
  background: var(--sky-100);
  color: var(--blue-600);
  display: flex; align-items: center; justify-content: center;
  flex-shrink: 0;
  font-size: 14px;
  font-weight: 800;
  margin-top: 2px;
}
.check-list h4 { font-size: 16.5px; margin-bottom: 4px;}
.check-list p { font-size: 14.5px; color: var(--gray-600); margin-bottom: 0;}

.tracker-mock {
  background: var(--navy-900);
  border-radius: var(--radius-lg);
  padding: 24px;
  color: var(--white);
  box-shadow: var(--shadow-card);
}
.tracker-mock .row {
  display: flex;
  justify-content: space-between;
  align-items: center;
  padding: 12px 0;
  border-bottom: 1px solid rgba(255,255,255,0.08);
  font-size: 14px;
}
.tracker-mock .row:last-child { border-bottom: none;}
.tracker-mock .status {
  font-size: 11.5px;
  font-weight: 800;
  padding: 4px 10px;
  border-radius: var(--radius-pill);
}
.status--green { background: rgba(225,177,69,0.18); color: var(--gold);}
.status--amber { background: rgba(255,193,87,0.16); color: #ffc157;}
.status--blue { background: rgba(45,118,212,0.22); color: #8ec3ff;}

/* ---------- Forms ---------- */
.form-section {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 56px;
  align-items: flex-start;
}
.form-card {
  background: var(--white);
  border-radius: var(--radius-lg);
  padding: 36px;
  box-shadow: var(--shadow-card);
  border: 1px solid var(--gray-200);
}
.form-card h3 { font-size: 21px; margin-bottom: 6px;}
.form-card .sub { color: var(--gray-600); font-size: 14.5px; margin-bottom: 26px;}
.field { margin-bottom: 18px;}
.field label {
  display: block;
  font-size: 13.5px;
  font-weight: 800;
  color: var(--navy-800);
  margin-bottom: 7px;
}
.field input, .field select, .field textarea {
  width: 100%;
  padding: 12px 14px;
  border: 1px solid var(--gray-200);
  border-radius: var(--radius-sm);
  font-size: 14.5px;
  font-family: inherit;
  color: var(--text-body);
  background: var(--gray-50);
  transition: border-color .15s ease, box-shadow .15s ease;
}
.field input:focus, .field select:focus, .field textarea:focus {
  outline: none;
  border-color: var(--blue-500);
  box-shadow: 0 0 0 3px rgba(45,118,212,0.15);
  background: var(--white);
}
.field-row { display: grid; grid-template-columns: 1fr 1fr; gap: 14px;}
.form-note { font-size: 12px; color: var(--gray-500); margin-top: 14px;}
.hubspot-slot {
  border: 1px dashed var(--gray-400);
  border-radius: var(--radius-sm);
  padding: 10px 12px;
  font-size: 12px;
  color: var(--gray-500);
  margin-bottom: 18px;
  background: var(--gray-50);
}
.form-error {
  background: var(--red-50, #fbe9e4);
  border: 1px solid var(--red-300, #e8bdb0);
  color: var(--red-700, #a3341a);
  border-radius: var(--radius-sm);
  padding: 12px 14px;
  font-size: 14px;
  font-weight: 600;
  margin-top: 14px;
}
.form-success {
  display: none;
  background: var(--gold-100);
  border: 1px solid var(--gold);
  color: var(--navy-800);
  border-radius: var(--radius-sm);
  padding: 14px 16px;
  font-size: 14px;
  font-weight: 700;
  margin-bottom: 18px;
}

/* ---------- CTA banner ---------- */
.cta-banner {
  background: var(--gradient-hero);
  border-radius: var(--radius-lg);
  padding: 56px;
  color: var(--white);
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 32px;
  flex-wrap: wrap;
  position: relative;
  overflow: hidden;
}
.cta-banner::after {
  content: '';
  position: absolute;
  right: -10%; top: -40%;
  width: 380px; height: 380px;
  background: radial-gradient(circle, rgba(225,177,69,0.3), transparent 70%);
}
.cta-banner h2 { color: var(--white); margin-bottom: 8px; position: relative; z-index: 1;}
.cta-banner p { color: var(--sky-100); margin-bottom: 0; position: relative; z-index: 1;}
.cta-banner .cta-actions { position: relative; z-index: 1; display: flex; gap: 14px;}

/* ---------- Footer ---------- */
.site-footer {
  background: var(--navy-950);
  color: var(--sky-100);
  padding: 64px 0 28px;
}
.footer-grid {
  display: grid;
  grid-template-columns: 1.3fr 1fr 1fr 1fr 1fr;
  gap: 32px;
  margin-bottom: 40px;
}
.footer-grid p { color: var(--gray-400); font-size: 14px; max-width: 280px;}
.footer-col h5, .footer-col .footer-head {
  font-size: 13px;
  text-transform: uppercase;
  letter-spacing: 0.06em;
  color: var(--sky-300);
  margin-bottom: 16px;
}
.footer-col a, .footer-col li {
  display: block;
  font-size: 14.5px;
  color: var(--gray-400);
  margin-bottom: 12px;
}
.footer-col a:hover { color: var(--white);}
.footer-legal {
  display: flex;
  flex-wrap: wrap;
  gap: 22px;
  justify-content: center;
  padding-top: 20px;
  margin-top: 20px;
  border-top: 1px solid rgba(255,255,255,0.08);
}
.footer-legal a { font-size: 13px; color: var(--gray-400);}
.footer-legal a:hover { color: var(--white);}
/* HubSpot's cookie-settings control. It reopens the consent banner rather than
   going anywhere, so it has to be a button, but it sits in a row of links and
   should read as one: the chrome comes off and it inherits the row's type. */
.footer-legal button {
  font: inherit;
  font-size: 13px;
  color: var(--gray-400);
  background: none;
  border: 0;
  padding: 0;
  cursor: pointer;
}
.footer-legal button:hover { color: var(--white);}

.footer-bottom {
  border-top: 1px solid rgba(255,255,255,0.08);
  padding-top: 24px;
  display: flex;
  justify-content: space-between;
  align-items: center;
  flex-wrap: wrap;
  gap: 12px;
  font-size: 13px;
  /* was --gray-500, which measured 4.38:1 on the navy footer */
  color: var(--gray-200);
}
.footer-badges { display: flex; gap: 10px;}
.footer-badges span {
  border: 1px solid rgba(255,255,255,0.15);
  border-radius: var(--radius-pill);
  padding: 5px 12px;
  font-size: 11.5px;
  color: var(--sky-200);
}

/* ---------- Utility ---------- */
.mt-0 { margin-top: 0;}
.text-center { text-align: center;}
.pill-note {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  background: var(--sky-100);
  color: var(--blue-600);
  font-size: 12.5px;
  font-weight: 800;
  padding: 8px 16px;
  border-radius: var(--radius-pill);
  margin-bottom: 20px;
}
/* Unused since 18 Sep, when the prototype notice was removed for the public
   launch. Kept so the banner can be put back for an internal review build
   without redesigning it. */
.proto-banner {
  background: var(--gold-100);
  color: #6b4a02;
  text-align: center;
  font-size: 13px;
  font-weight: 700;
  padding: 8px 16px;
}
.proto-banner b { font-weight: 800;}

/* ---------- Responsive ---------- */
/* The desktop nav needs ~950px for the logo, links and CTAs to sit side by
   side, so it collapses to the menu button well before the 720px breakpoint. */
@media (max-width: 1024px) {
  .nav-links, .nav-cta .btn-outline-hide { display: none;}
  .nav-toggle { display: flex;}
  .nav-links.open {
    display: flex;
    flex-direction: column;
    align-items: flex-start;
    gap: 16px;
    position: absolute;
    top: 100%;
    left: 0;
    right: 0;
    padding: 20px 28px;
    background: var(--white);
    border-bottom: 1px solid var(--gray-200);
    box-shadow: var(--shadow-card);
}
  /* Inside the mobile panel the dropdown expands in place rather than
     floating over the page. */
  .nav-links.open .nav-group { flex-direction: column; align-items: flex-start; width: 100%;}
  .nav-links.open .nav-group-menu {
    position: static;
    min-width: 0;
    width: 100%;
    padding: 6px 0 2px 14px;
    background: none;
    border: 0;
    border-left: 2px solid var(--gray-200);
    border-radius: 0;
    box-shadow: none;
    margin-top: 10px;
}
  /* :not(.nav-mega-cta) -- this rule is for the plain dropdown LINKS, but it also
     matched the megamenu's pill button, at (0,3,1) against the button's own
     (0,2,0). The pill lost its horizontal padding entirely and its label spilled
     out of the gold. Same shape as the .nav-links-outranks-.btn note above, just
     in the phone drawer. */
  .nav-links.open .nav-group-menu a:not(.nav-mega-cta) { padding: 7px 0;}
  .nav-links.open .nav-mega-cta {
    padding: 10px 18px;
    align-self: flex-start;
    margin-top: 10px;
}
  .nav-links.open .nav-group-menu a:hover { background: none;}
  /* The mega panel folds down to the same plain list the other groups use --
     the pitch copy is desktop furniture. The CTA survives as the
     last row, so the overview page is still reachable from the phone menu. */
  .nav-links.open .nav-group--mega { padding-bottom: 0; margin-bottom: 0;}
  /* The drawer stacks in flow, so the centring transform and the hover bridge
     both have to come off -- the bridge would otherwise position itself against
     the open drawer and sit invisibly over the header. */
  .nav-links.open .nav-mega { transform: none;}
  .nav-links.open .nav-mega::before { content: none;}
  .nav-links.open .nav-mega-body {
    display: flex;
    flex-direction: column;
    gap: 0;
    max-width: none;
    padding: 0;
}
  .nav-links.open .nav-mega-head,
  .nav-links.open .nav-mega-pitch p { display: none;}
  .nav-links.open .nav-mega-pitch { order: 2; margin-top: 14px;}
  .nav-links.open .nav-mega-links {
    grid-auto-flow: row;
    grid-template-columns: 1fr;
    grid-template-rows: none;
    row-gap: 0;
    border-left: 0;
    padding-left: 0;
}
  .nav-links.open .nav-mega-links a {
    gap: 10px;
    padding: 7px 0;
    font-size: 14.5px;
    font-weight: 700;
    color: var(--gray-800);
}
  /* The phone drawer shrinks these to an 8px dot. An icon is unreadable at that
     size, so a masked one drops its mask here and goes back to being a dot --
     the drawer was designed around a dot, not artwork. */
  .nav-links.open .nav-mega-ico { width: 8px; height: 8px;}
  .nav-links.open .nav-mega-ico[data-icon] {
    -webkit-mask: none;
    mask: none;
    border-radius: 50%;
}
  .nav-links.open .nav-mega-links a::after { content: none;}
}
@media (max-width: 980px) {
  .hero-grid, .split, .form-section { grid-template-columns: 1fr;}
  .pillars, .card-grid, .case-grid { grid-template-columns: repeat(2, 1fr);}
  .stats-grid { grid-template-columns: repeat(2, 1fr); gap: 32px;}
  .footer-grid { grid-template-columns: 1fr 1fr;}
  .hero h1 { font-size: 38px;}
}
@media (max-width: 720px) {
  .container { padding-left: 20px; padding-right: 20px;}
  .nav-links.open { padding: 20px;}
  .logo img { height: 36px;}
  .nav-cta { gap: 8px;}
  .section { padding: 64px 0;}
  .pillars, .card-grid, .case-grid { grid-template-columns: 1fr;}
  .stats-grid { grid-template-columns: 1fr 1fr;}
  .testimonial { grid-template-columns: 1fr; text-align: center;}
  .testimonial .quote-mark { display: none;}
  .cta-banner { flex-direction: column; text-align: center;}
  .footer-grid { grid-template-columns: 1fr;}
  .field-row { grid-template-columns: 1fr;}
  .hero h1 { font-size: 30px;}
  .form-card { padding: 24px;}
}
/* Narrowest phones: the logo, header CTA and menu button need the last few px.
   At 375px the three of them wanted ~389px, so the logo and the CTA ended up
   touching with no gap at all and read as overlapping. A floor on the gap keeps
   them apart, and the logo gives up the few pixels that buys. */
/* The header row overflowed the viewport between 381px and about 430px: the
   smallest breakpoint was 380, so the phones most people actually carry, the
   iPhone 12/13/14 at 390 and the Pro Max at 430, fell in the gap and every
   page carried a horizontal scrollbar. */
@media (max-width: 440px) {
  .nav-cta { min-width: 0;}
  .nav-cta .btn {
    white-space: nowrap;
    font-size: 13px;
    padding-left: 12px;
    padding-right: 12px;
}
  .logo img { max-width: 132px; height: auto;}
}

@media (max-width: 380px) {
  .container { padding-left: 16px; padding-right: 16px;}
  .nav-cta { gap: 4px;}
  .nav-cta .btn-sm { padding: 9px 12px; font-size: 13px;}
  /* height:auto for the same reason as the 132px cap above: with a width
     cap already in force, a second fixed height just re-creates the box
     the image cannot fill. */
  .logo img { height: auto;}
  .container.nav { gap: 10px;}
  .logo { min-width: 0; flex: 0 1 auto;}
}

/* amplify auto-deploy verification test - 2026-08-17 - safe to remove */

/* Footer column headings link to their section landing where one exists */
.footer-col h5 a, .footer-col .footer-head a { color: inherit;}
.footer-col h5 a:hover, .footer-col .footer-head a:hover { color: var(--white);}

/* ==========================================================================
   Homepage — built to TrackVia_Home_v5.pdf. Imagery is placeholdered until
   the designer supplies assets; every .ph carries the intended subject and
   pixel size in data-ph so swapping in a real <img> is mechanical.
   ========================================================================== */

.ph {
  position: relative;
  background:
    repeating-linear-gradient(45deg,
      var(--gray-100) 0 10px, var(--gray-50) 10px 20px);
  border: 1px dashed var(--gray-400);
  border-radius: var(--radius-md);
  display: flex;
  align-items: center;
  justify-content: center;
  min-height: 60px;
}
.ph::after {
  content: attr(data-ph);
  font-size: 12px;
  font-weight: 700;
  color: var(--gray-500);
  text-align: center;
  padding: 10px 14px;
  line-height: 1.45;
  max-width: 90%;
}
.ph--dark {
  background:
    repeating-linear-gradient(45deg,
      rgba(255,255,255,0.05) 0 10px, rgba(255,255,255,0.02) 10px 20px),
    var(--navy-900);
  border-color: rgba(255,255,255,0.28);
}
.ph--dark::after { color: var(--sky-200);}
/* When a placeholder wraps real content, the caption moves out of the way and
   gets its own chip so it stays legible over whatever is behind it. */
.ph:has(> *)::after {
  position: absolute;
  left: 8px; bottom: 8px; top: auto; right: auto;
  max-width: calc(100% - 16px);
  padding: 4px 9px;
  font-size: 10.5px;
  background: rgba(10,26,55,0.74);
  color: #fff;
  border-radius: 6px;
}
.ph--logo {
  aspect-ratio: 150 / 56;
  border-radius: var(--radius-sm);
  min-height: 0;
}
.ph--logo::after { font-size: 11.5px;}

/* Customer marks. They run from 2:1 to 9:1, so they are contained in the slot
   rather than filled into it -- a cropped or stretched logo is a wrong logo. */
.logo-mark {
  display: block;
  width: 100%;
  height: 56px;
  object-fit: contain;
  object-position: center;
}
.home-hero::after {
  content: '';
  position: absolute;
  inset: 0;
  /* The homepage has its own artwork now -- the duotone field crew rather than
     the abstract chevron the solution and company heroes share. */
  background-image: url(/assets/img/home-hero-bg.webp);
  /* Scaled to the hero's HEIGHT and pinned right, not cover. cover fills the
     band by width, so on a wide screen the band is a thin slice through the
     middle of a 2.61:1 picture: at 3182px only 57% of the image's height
     survives and at 3440 only 53%, which is why the chevron disappears and
     two enormous hard hats are all that is left. auto 100% keeps the whole
     picture, at the same scale relative to the hero, at every width.

     What runs off the left is the artwork's own white half, and the hero is
     painted white, so there is no seam and nothing to see. */
  background-size: auto 100%;
  background-position: right center;
  background-repeat: no-repeat;
}
.home-hero-bg {
  position: absolute;
  inset: 0;
  border-radius: 0;
  border-width: 0 0 1px 0;
}
.home-hero-bg::after { align-self: flex-start; margin-top: 18px;}
/* width:100% for the same reason .page-hero > .container needs it -- the hero
   is a flex column and .container carries margin:0 auto, so an auto margin on
   the cross axis opts it out of stretch and it shrink-wraps to its widest
   child and then centres itself. */
.home-hero-inner {
  position: relative;
  z-index: 2;
  width: 100%;
  text-align: left;
}
.home-hero-eyebrow {
  color: var(--blue-600);
  font-size: 13px;
  font-weight: 800;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  margin-bottom: 14px;
}
.home-hero h1 {
  color: var(--navy-900);
  font-size: 62px;
  max-width: 17ch;
  letter-spacing: -0.025em;
  margin-bottom: 16px;
  text-wrap: balance;
}
.home-hero-sub {
  color: var(--gray-800);
  font-size: 20px;
  font-weight: 700;
  max-width: 56ch;
  margin: 0 0 30px;
}

/* ---------- Customer logos ---------- */
.home-logos { padding: 40px 0; border-bottom: 1px solid var(--gray-200);}
/* The homepage reel. Same marquee as /connect/: full bleed, running off both
   edges of the viewport rather than stopping at the text column. It sits
   outside .container for that reason -- inside one it would be boxed to 1200px
   and the loop would visibly start and stop at the copy's margins. The one
   difference from /connect/ is that it turns slower. */
.home-logo-marquee { overflow: hidden;}
/* two classes, because .demo-logo-track's `animation` SHORTHAND sits later
   in this file and would otherwise reset the duration back to 46s */
.home-logos .home-logo-track { animation-duration: 64s;}
.home-logos .logo-mark { width: auto; height: calc(38px * var(--k, 1));}
@media (max-width: 760px) {
  .home-logos .logo-mark { height: calc(30px * var(--k, 1));}
  .home-logos .demo-logo-set > li { margin-right: 44px;}
}

.home-logo-row {
  display: flex;
  gap: 26px;
  align-items: center;
}
/* Equal-width cells sized these by width, so height came out inversely
   proportional to each mark's aspect: a 10:1 wordmark rendered at 14px beside
   a 2.8:1 mark at 50px. Giving each mark width in proportion to its own ratio
   lands them all on one height instead. --ar is set per logo in the markup.
   --k is an optical nudge on top of that, for marks whose visual weight does
   not match their measured height -- NBCUniversal reads heavy at a common
   height, CU Denver and GE read light. */
.home-logo-row .logo-mark {
  flex: calc(var(--ar, 4) * var(--k, 1)) 1 0;
  width: auto;
  height: auto;
  max-height: 46px;
  min-width: 0;
}
/* Grid items default to min-width:auto, so a long placeholder label would
   push its track wider than its share and scroll the page sideways. */
.home-logo-row > *, .home-stats > *, .home-promo-grid > *,
.home-industry-strip > *, .home-track-grid > *, .home-acc-body > * { min-width: 0;}

/* ---------- Stats ---------- */
.home-stats {
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  gap: 34px;
}
.home-stat h3, .home-stat .home-stat-lab { font-size: 19px; margin-bottom: 6px;}
.home-stat p { color: var(--gray-600); font-size: 15px; margin-bottom: 14px;}
.home-stat-num {
  font-size: 56px;
  font-weight: 900;
  letter-spacing: -0.03em;
  color: var(--navy-900);
  line-height: 1;
  font-variant-numeric: tabular-nums;
}

/* ---------- Track everything ---------- */
.home-track {
  background: var(--gray-100);
  position: relative;
  overflow: hidden;
  padding: 86px 0;
}
/* Mirrored: the wedge, the columns and the phone all moved to the other side
   together. The clip-path points are the old ones with x flipped (100 - x), so
   the diagonal still runs from the outer edge in towards the copy rather than
   away from it. */
.home-track::after {
  content: '';
  position: absolute;
  left: 0; top: 0; bottom: 0;
  width: 48%;
  background: linear-gradient(160deg, var(--blue-500) 0%, var(--navy-800) 100%);
  clip-path: polygon(0 0, 62% 0, 100% 100%, 0 100%);
  z-index: 0;
}
.home-track-grid {
  position: relative;
  z-index: 1;
  display: grid;
  /* media column first and still the wider of the two */
  grid-template-columns: 1.05fr 1fr;
  gap: 56px;
  align-items: center;
}
/* order rather than moving the markup: the copy stays first in the DOM, which
   is the reading order this section wants once it stacks. */
.home-track-media { order: 1;
  position: relative; padding-left: 90px;}
.home-track-copy { order: 2;}
.home-track-copy h2 { font-size: 34px; margin-bottom: 16px;}
.home-track-copy > p { color: var(--gray-600); margin-bottom: 18px;}
.home-track-list { margin-bottom: 26px;}
.home-track-list li {
  position: relative;
  padding-left: 20px;
  margin-bottom: 9px;
  color: var(--gray-800);
  font-size: 15.5px;
}
.home-track-list li::before {
  content: '';
  position: absolute;
  left: 2px; top: 10px;
  width: 6px; height: 6px;
  border-radius: 50%;
  background: var(--navy-900);
}
.home-track-device {
  position: absolute;
  left: 0; top: 50%;
  transform: translateY(-50%);
  width: 220px;
  /* height:auto is load-bearing. These carry width/height attributes for CLS,
     and an attribute is a presentational hint that only CSS overrides. With a
     width set here and no height, the height hint applied at full natural size
     and object-fit:fill squashed the screenshot to ~62% of its true width.
     This lets height compute from the ratio while the box is still reserved. */
  height: auto;
  box-shadow: var(--shadow-card);
  background-color: var(--white);
  /* the .ph placeholder supplied this; the real screenshot needs it too */
  border-radius: var(--radius-md);
}

/* ---------- Solutions accordion ---------- */
.home-accordion {
  max-width: 980px;
  margin: 0 auto;
  display: flex;
  flex-direction: column;
  /* 4px when these were grey blocks butted together; shadows need air. */
  gap: 12px;
}
.home-acc {
  /* A card by its shadow rather than a grey fill: the section behind is white,
     so a soft lift reads as a raised surface without a second ground colour. */
  background: var(--white);
  border-radius: var(--radius-md);
  overflow: hidden;
  box-shadow: 0 1px 2px rgba(10, 26, 55, 0.05), 0 6px 18px -10px rgba(10, 26, 55, 0.18);
  transition: box-shadow 0.18s ease;
}
.home-acc:hover { box-shadow: 0 1px 2px rgba(10, 26, 55, 0.06), 0 10px 26px -12px rgba(10, 26, 55, 0.26);}
.home-acc summary {
  list-style: none;
  cursor: pointer;
  padding: 18px 26px;
  font-size: 19px;
  font-weight: 800;
  color: var(--navy-900);
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 16px;
}
.home-acc summary::-webkit-details-marker { display: none;}
.home-acc summary::after {
  content: '';
  width: 9px; height: 9px;
  border-right: 2.5px solid var(--blue-500);
  border-bottom: 2.5px solid var(--blue-500);
  transform: translateY(-3px) rotate(45deg);
  flex-shrink: 0;
  transition: transform 0.18s ease;
}
.home-acc[open] summary::after { transform: translateY(2px) rotate(-135deg);}
.home-acc[open] { background: var(--white);
  border-left: 4px solid var(--blue-500);}
.home-acc-body {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 44px;
  align-items: center;
  /* 6px left the photograph touching the tab above it in the stacked layout. */
  padding: 18px 26px 34px;
  animation: acc-open 0.3s cubic-bezier(.22,.61,.36,1);
}
.home-acc-media { position: relative; padding-right: 70px;}
/* Mirrored so the subject faces into the copy rather than off the edge.
   Scoped with a class rather than flipping the file, because the same
   photograph is also used on /solutions/ where it is not mirrored. */
.home-acc-media .photo.is-mirrored { transform: scaleX(-1); }

.home-acc-device {
  position: absolute;
  right: 0; top: 50%;
  transform: translateY(-50%);
  width: 168px;
  /* height:auto is load-bearing. These carry width/height attributes for CLS,
     and an attribute is a presentational hint that only CSS overrides. With a
     width set here and no height, the height hint applied at full natural size
     and object-fit:fill squashed the screenshot to ~62% of its true width.
     This lets height compute from the ratio while the box is still reserved. */
  height: auto;
  box-shadow: var(--shadow-card);
  background-color: var(--white);
  /* the .ph placeholder supplied this; the real screenshot needs it too */
  border-radius: var(--radius-md);
}

/* Below 1080 the phone is not allowed to overhang. The artwork is 302x606, so
   at 168px wide it renders 337px tall against a photo row of about 222px --
   it reached 57px above the photo and landed on the summary heading. The wide
   layout affords that overhang deliberately and pads 62px for it; here there
   is no such clearance, so the phone is capped to the row instead. */
@media (max-width: 1079px) {
  .home-acc-device { width: auto; max-height: 100%;}
}

.home-acc-copy h3 { font-size: 24px; margin-bottom: 12px;}
.home-acc-copy p { color: var(--gray-600); margin-bottom: 22px;}

/* ---------- Horizontal rails (case studies, resources) ---------- */
.home-rail-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 20px;
  margin-bottom: 14px;
}
.home-rail-word {
  /* "RESOURCES" is a single unbreakable word: at a fixed size it cannot
     shrink, so space-between pushed the rail arrows past the container.
     Fluid size + min-width:0 lets it give way instead. */
  font-size: clamp(24px, 7vw, 60px);
  min-width: 0;
  font-weight: 900;
  letter-spacing: -0.03em;
  text-transform: uppercase;
  color: rgba(10,26,55,0.08);
  line-height: 1;
}
.home-rail-nav { display: flex; gap: 10px; flex: none;}
/* The in-text CTA under each homepage rail. The circles move the row; this
   is the way out of it, for anyone who would rather see the whole list. */
.rail-more { margin: 20px 0 0;}
.rail-more .cta-link { font-size: 15.5px; font-weight: 700;}

.home-rail-btn {
  width: 42px; height: 42px;
  border-radius: 50%;
  border: 0;
  background: var(--gray-200);
  color: var(--gray-600);
  font-size: 20px;
  line-height: 1;
  display: flex;
  align-items: center;
  justify-content: center;
  transition: background 0.18s ease, transform 0.18s ease;
}
.home-rail-btn.is-on { background: var(--blue-500); color: var(--white);}
.home-rail {
  display: grid;
  grid-auto-flow: column;
  grid-auto-columns: minmax(288px, 1fr);
  gap: 22px;
  overflow-x: auto;
  scroll-snap-type: x mandatory;
  padding-bottom: 10px;
  /* No native scrollbar track under the cards. The rail still scrolls by wheel,
     trackpad, drag and keyboard -- this hides the bar only, and the arrow
     buttons beside the heading are the visible affordance. */
  scrollbar-width: none;
  -ms-overflow-style: none;
  scroll-behavior: smooth;
}
.home-rail::-webkit-scrollbar { display: none;}
.home-rail > * { scroll-snap-align: start;}

/* case study cards */
.home-cases { background: var(--navy-900);}
.home-cases h2, .home-cases .section-head h2 { color: var(--white);}
.home-cases .section-head p { color: var(--sky-200);}
/* The rail label was an oversized ghosted watermark; it reads as the same
   eyebrow used everywhere else now, so the two rails stop being a one-off. */
.home-cases .eyebrow { color: var(--sky-200); background: rgba(255,255,255,0.10);}
.home-rail-head .eyebrow { margin-bottom: 0;}
.home-cases .home-rail-btn { background: rgba(255,255,255,0.16); color: var(--white);}
.home-cases .home-rail-btn.is-on { background: var(--blue-500);}
.home-case {
  background: var(--gray-100);
  border-radius: var(--radius-md);
  overflow: hidden;
  display: flex;
  flex-direction: column;
}
.home-case-top {
  /* .ph supplied these while this was a placeholder box */
  position: relative;
  display: flex;
  aspect-ratio: 560 / 300;
  border-radius: 0;
  border: 0;
  align-items: flex-end;
  justify-content: flex-start;
  padding: 18px;
  text-align: right;
}
/* Same treatment as the cards on the success-stories index: the photo sits
   behind the brand scrim, so the white customer block on top stays readable. */
.home-case-top.has-photo { background-size: cover; background-position: center;}
.home-case-top.has-photo::before {
  content: '';
  position: absolute; inset: 0;
  background: linear-gradient(200deg, rgba(45,118,212,0.82), rgba(10,26,55,0.86));
}
/* Keep the placeholder caption clear of the right-aligned customer block. */
.home-case-top::after { position: absolute; top: 10px; left: 12px; right: auto; max-width: 52%; text-align: left;}
.home-case-id { position: relative; z-index: 1; margin-left: auto;}
.home-case-id h3 { color: var(--white); font-size: 21px; margin-bottom: 4px;}
.home-case-id p { color: var(--sky-200); font-size: 12.5px; margin-bottom: 10px; line-height: 1.4;}
.home-case-tags { display: flex; gap: 6px; flex-wrap: wrap; justify-content: flex-end;}
.home-case-tags span {
  font-size: 10.5px;
  font-weight: 700;
  color: var(--white);
  border: 1px solid rgba(255,255,255,0.6);
  border-radius: var(--radius-pill);
  padding: 3px 10px;
  white-space: nowrap;
}
.home-case-body { padding: 22px; display: flex; flex-direction: column; gap: 18px; flex: 1;}
.home-case-body h4 { font-size: 19px; margin: 0;}
.home-case-link { color: var(--blue-600); font-weight: 800; font-size: 14.5px; margin-top: auto;}

/* ---------- Promo pair ---------- */
.home-promo {
  background: linear-gradient(180deg, var(--navy-900) 0%, var(--navy-800) 55%, var(--blue-600) 100%);
  position: relative; overflow: hidden;
  clip-path: polygon(0 0, 100% 0, 100% 100%, 0 86%);
  padding-bottom: 116px;
}
.home-promo .section-head h2 { color: var(--white);}
.home-promo .section-head p { color: var(--sky-200);}
.home-promo-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 34px;}
.home-promo-card {
  /* .ph used to supply the box; with a photo in it these have to be stated */
  position: relative;
  display: flex;
  justify-content: center;
  aspect-ratio: 640 / 500;
  align-items: flex-end;
  border-radius: var(--radius-lg);
  overflow: hidden;
  padding: 34px;
}
.home-promo-card::after { position: absolute; top: 14px; left: 50%; transform: translateX(-50%);}
.home-promo-inner { position: relative; z-index: 1; text-align: center; width: 100%;}
.home-promo-inner h3 { color: var(--white); font-size: 23px; margin-bottom: 20px;}

/* ---------- Industry strip ---------- */
.home-industry-strip {
  display: grid;
  /* SIX, not seven. Education came off the reel on 18 Sep and this rule was not
     moved with it, so the strip reserved a seventh column that nothing filled --
     the tiles stopped short of the right edge with a blank slot after them. */
  grid-template-columns: repeat(6, 1fr);
  gap: 0;
}
.home-industry > .ph { border-radius: 0; border-width: 1px 0 1px 1px;
  transition: transform 0.35s cubic-bezier(.22,.61,.36,1), filter 0.35s ease;}
.home-industry:last-child > .ph { border-right-width: 1px;}
/* :last-of-type, not `span` -- the media wrapper is also a span, and a bare
   descendant selector handed it the label's padding, which is what pushed the
   scrim off the edges of the photograph. */
.home-industry > span:last-of-type {
  display: block;
  text-align: center;
  /* Seven tiles across leaves about 130px of label at the tightest desktop
     width, so the longest name wraps. Balanced rather than stopped: the row
     stretches to match, and every label still starts on the same line. */
  text-wrap: balance;
  padding: 16px 7px;
  font-size: 14.5px;
  font-weight: 700;
  color: var(--blue-600);
}
.home-industry:hover > span:last-of-type { color: var(--navy-900);}

/* ---------- Testimonials ---------- */
.home-quotes { background: var(--blue-500); padding: 60px 0;}
.home-quote {
  background: var(--white);
  border-radius: var(--radius-lg);
  margin: 0;
  max-width: 720px;
  text-align: center;
  position: relative;
  display: flex;
  flex-direction: column;
  justify-content: center;
  /* Sized down on 19 Sep: the section was 642px for nine quotes whose height
     is set by the longest one. No min-height -- the ring measures the tallest
     card and sets the track, so a floor here only padded the short ones. */
  padding: 40px 46px 36px;
}
.home-quote::before, .home-quote::after {
  content: '\201C';
  position: absolute;
  font-size: 82px;
  line-height: 1;
  color: var(--blue-500);
  font-weight: 900;
}
.home-quote::before {
  top: 24px; left: 30px;
}
.home-quote::after {
  content: '\201D';
  bottom: 2px; right: 30px;
}
.home-quote p {
  font-size: 19px; font-weight: 700; color: var(--navy-900);
  margin-bottom: 20px;
}
.home-quote footer { display: flex; flex-direction: column; gap: 2px;}
.home-quote footer strong { font-size: 15px; color: var(--navy-900);}
.home-quote footer span { font-size: 14.5px; color: var(--gray-600);}

/* ---------- Latest articles ---------- */
.home-article {
  background: var(--white);
  border: 1px solid var(--gray-200);
  border-radius: var(--radius-md);
  overflow: hidden;
  display: flex;
  flex-direction: column;
}
.home-article > .ph { border-radius: 0; border-width: 0 0 1px 0;}
/* Option C, agreed 21 Sep: the asset-type pill sits in the card body above the
   title rather than floating on the photograph. One pill treatment for every
   listing on the site, and it never lands on a busy part of an image. The
   homepage case reel and the /success-stories index keep their own layout. */
.home-article-type {
  align-self: flex-start;
  background: var(--white);
  border: 1px solid var(--gray-200);
  border-radius: var(--radius-pill);
  padding: 4px 14px;
  font-size: 12.5px;
  font-weight: 700;
  color: var(--gray-800);
}
.home-article-body { padding: 20px; display: flex; flex-direction: column; gap: 16px; flex: 1;}
.home-article-body h3 { font-size: 18px; margin: 0;}
.home-article-meta {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 6px;
  margin-top: auto;
  font-size: 14px;
}
.home-article-meta time { color: var(--gray-600);}
.home-article-meta a { color: var(--blue-600); font-weight: 800;}
/* A cta-link that has to be a <button> because it opens a player rather
   than going anywhere. Same type as the links beside it. */
.home-article-meta .video-play {
  font: inherit;
  color: var(--blue-600);
  font-weight: 800;
  background: none;
  border: 0;
  padding: 0;
  cursor: pointer;
}

/* ---------- Homepage responsive ---------- */
@media (max-width: 1024px) {
  .home-hero h1 { font-size: 48px;}
  h2 { font-size: 30px;}
  /* was grid-template-columns; the row is flex now. Fix each mark's width at
     its ratio x a target height and let the row wrap -- shrinking instead
     would keep all seven on one squashed line. */
  .home-logo-row { flex-wrap: wrap; justify-content: center; row-gap: 22px;}
  .home-logo-row .logo-mark { flex: 0 0 calc(var(--ar, 4) * var(--k, 1) * 32px);}
  .home-stats { grid-template-columns: repeat(2, 1fr);}
  /* Six tiles divide evenly now, so 3 + 3. (It was 4 + 3 for seven.) */
  .home-industry-strip { grid-template-columns: repeat(3, 1fr);}
  .home-industry > .ph { border-width: 1px;}
}
@media (max-width: 820px) {
  .home-hero { padding-top: 64px; padding-bottom: 72px;}
  .home-hero h1 { font-size: 38px;}
  h2 { font-size: 27px;}
  .home-hero-sub { font-size: 17px;}
  .home-track-grid, .home-acc-body, .home-promo-grid { grid-template-columns: 1fr;}
  .home-track::after { display: none;}
  /* stacked: copy first, as it reads */
  .home-track-media, .home-track-copy { order: 0;}
  .home-track-media, .home-acc-media { padding-right: 0; padding-left: 0;}
  .home-track-device, .home-acc-device {
    position: static;
    transform: none;
    width: 190px;
    margin: -60px auto 0;
}
  .home-quote { padding: 34px 26px;}
}
@media (max-width: 560px) {
  .home-stats { grid-template-columns: 1fr;}
  /* smaller target height again, so it stays two-ish to a line */
  .home-logo-row .logo-mark { flex: 0 0 calc(var(--ar, 4) * var(--k, 1) * 26px);}
  .home-industry-strip { grid-template-columns: repeat(2, 1fr);}
}

/* Footer social + "ask AI" row. LinkedIn and YouTube use their own glyphs;
   the AI destinations are text pills rather than approximated brand marks. */
.footer-connect {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: 18px;
  padding-top: 22px;
  /* Matching padding BELOW the icons. Without it this block ended where the
     icons ended, so the next block's border-top sat flush against them --
     measured 23px of air above and 0 below. */
  padding-bottom: 22px;
  margin-top: 8px;
  border-top: 1px solid rgba(255,255,255,0.08);
}
.footer-address {
  font-style: normal;
  font-size: 14.5px;
  line-height: 1.6;
  color: var(--gray-400);
  margin-top: 14px;
}
.footer-social { display: flex; gap: 10px;}
.footer-social a {
  width: 38px; height: 38px;
  border-radius: 50%;
  background: rgba(255,255,255,0.10);
  display: flex;
  align-items: center;
  justify-content: center;
}
.footer-social a:hover { background: var(--blue-500);}
/* Separates the social profiles from the app store listings. They sit in the
   same row and the same discs, but they are different kinds of destination --
   one is somewhere to follow us, the other is somewhere to install something. */
.footer-social-sep {
  width: 1px;
  height: 22px;
  background: rgba(255, 255, 255, 0.22);
  margin: 0 4px;
  flex: none;
  /* The row is a flex container with no align-items, so the default stretch
     applies -- and an item with a fixed height cannot stretch, which parks it at
     the top. The 38px discs fill the row and hide the problem; this 22px rule
     sat 8px above their centre line. */
  align-self: center;
}
.footer-social svg { width: 19px; height: 19px; fill: var(--white); display: block;}

.footer-ai { display: flex; flex-wrap: wrap; align-items: center; gap: 9px;}
.footer-ai span {
  font-size: 12px;
  font-weight: 800;
  letter-spacing: 0.07em;
  text-transform: uppercase;
  color: var(--sky-300);
  margin-right: 2px;
}
.footer-ai a {
  font-size: 13px;
  font-weight: 700;
  color: var(--gray-400);
  border: 1px solid rgba(255,255,255,0.18);
  border-radius: var(--radius-pill);
  padding: 6px 14px;
}
.footer-ai a:hover { color: var(--white); border-color: rgba(255,255,255,0.5);}
/* The supplied brand marks, painted through currentColor so they sit on navy
   and brighten on hover exactly like the LinkedIn and YouTube icons above. */
.footer-ai .ai-icon {
  display: inline-grid;
  place-items: center;
  width: 36px; height: 36px;
  padding: 0;
  color: var(--sky-300);
}
.footer-ai .ai-icon svg { width: 20px; height: 20px; fill: currentColor; display: block;}
.footer-ai .ai-icon:hover { color: var(--white);}
.footer-ai .ai-icon:focus-visible { outline: 3px solid var(--blue-400); outline-offset: 2px;}

@media (max-width: 640px) {
  .footer-connect { flex-direction: column; align-items: flex-start;}
}


/* ---------- Mobile QA fixes ---------- */
@media (max-width: 820px) {
  /* Text links and footer links were 23-24px tall — too small for a thumb.
     Padding grows the hit area without moving anything visually. */
  .cta-link { padding: 10px 0;}
  .footer-col a { padding: 7px 0; margin-bottom: 4px;}
  .footer-legal { gap: 14px 22px;}
  .footer-legal a { padding: 6px 0;}
  .footer-ai a { padding: 9px 16px;}
}
@media (max-width: 560px) {
  /* 38px is still a lot of heading on a 375px screen. */
  .home-hero h1 { font-size: 31px;}
  h2 { font-size: 24px;}
  /* sets its own 34px, so it needs its own step down too */
  .home-track-copy h2 { font-size: 26px;}
  .home-hero-sub { font-size: 16px;}
  .page-hero h1 { font-size: 28px;}
  .story-hero--story h1 { font-size: 28px;}
  .home-stat-num { font-size: 44px;}
  .home-acc summary { font-size: 17px; padding: 16px 18px;}
  .home-acc-body { padding: 16px 18px 26px;}
}

/* ==========================================================================
   Motion
   Three ideas only, each tied to something the page is actually doing:
   sections arriving as you reach them, the stat figures counting up once,
   and small nudges that hint a control is interactive. Everything here is
   switched off wholesale by the prefers-reduced-motion block above.
   ========================================================================== */

/* 1 — Reveal on scroll.
   Elements start slightly low and fade up as they enter. The no-JS and
   pre-observer state is fully visible, so nothing is ever stranded
   invisible if the script fails to run. */
.reveal { opacity: 1; transform: none;}
.js-motion .reveal {
  opacity: 0;
  transform: translateY(18px);
  transition: opacity 0.55s cubic-bezier(.22,.61,.36,1),
              transform 0.55s cubic-bezier(.22,.61,.36,1);
}
.js-motion .reveal.is-in { opacity: 1; transform: none;}
/* Children of a revealed group arrive in sequence rather than together. */
.js-motion .reveal-group > * {
  opacity: 0;
  transform: translateY(16px);
  transition: opacity 0.5s cubic-bezier(.22,.61,.36,1),
              transform 0.5s cubic-bezier(.22,.61,.36,1);
  transition-delay: calc(var(--i, 0) * 70ms);
}
.js-motion .reveal-group.is-in > * { opacity: 1; transform: none;}
.home-industry:hover > .ph { transform: scale(1.04); filter: saturate(1.08);}
.home-industry span { transition: gap 0.2s ease, color 0.2s ease;}

.home-case, .home-article { transition: transform 0.25s cubic-bezier(.22,.61,.36,1), box-shadow 0.25s ease;}
.home-case:hover, .home-article:hover { transform: translateY(-4px); box-shadow: var(--shadow-card);}
.home-rail-btn:hover { transform: scale(1.08);}
.home-rail.is-rotating { cursor: grab;}
.rail-hint {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  font-size: 12px;
  font-weight: 700;
  letter-spacing: 0.02em;
  color: var(--gray-500);
  margin-left: 14px;
  opacity: 0;
  transition: opacity 0.3s ease;
}
.home-cases .rail-hint { color: var(--sky-300);}
.home-rail-head:hover .rail-hint,
.home-rail-head:focus-within .rail-hint { opacity: 1;}
@keyframes acc-open {
  from { opacity: 0; transform: translateY(-6px); }
  to   { opacity: 1; transform: none; }
}

/* ==========================================================================
   Solution pages — built to TrackVia_Interior-Templates. The hero carries a
   rail of sibling solutions on the left, headline and CTAs in the middle,
   and product screens on the right.
   ========================================================================== */

.sol-hero {
  position: relative;
  background: var(--navy-950);
  overflow: hidden;
  min-height: 560px;
  display: flex;
  align-items: stretch;
}
.sol-hero-bg { position: absolute; inset: 0; border-radius: 0; border-width: 0;}
.sol-hero-bg::after { align-self: flex-start; margin-top: 16px;}
.sol-hero-inner {
  position: relative;
  z-index: 1;
  width: 100%;
  max-width: 1600px;
  margin: 0 auto;
  display: grid;
  grid-template-columns: 300px minmax(0, 1fr) minmax(0, 1.05fr);
  align-items: center;
  gap: 40px;
}

/* the rail */
.sol-rail {
  align-self: stretch;
  display: flex;
  flex-direction: column;
  justify-content: center;
  background: rgba(255,255,255,0.93);
  backdrop-filter: blur(6px);
}
.sol-rail a {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  padding: 17px 22px;
  font-size: 16px;
  font-weight: 700;
  color: var(--navy-900);
  border-bottom: 1px solid var(--gray-200);
  transition: background 0.16s ease, color 0.16s ease, padding-left 0.16s ease;
}
.sol-rail a:last-child { border-bottom: 0;}
.sol-rail a::after {
  content: '';
  width: 7px; height: 7px;
  border-right: 2px solid var(--blue-500);
  border-bottom: 2px solid var(--blue-500);
  transform: rotate(-45deg);
  flex-shrink: 0;
  transition: transform 0.16s ease;
}
.sol-rail a:hover { background: var(--sky-100); padding-left: 26px;}
.sol-rail a:hover::after { transform: rotate(-45deg) translate(2px, -2px);}
.sol-rail a.is-here { background: var(--navy-900); color: var(--white);}
.sol-rail a.is-here::after { border-color: var(--white);}

.sol-hero-copy { padding: 60px 0;}
.sol-hero-copy h1 {
  color: var(--white);
  font-size: 62px;
  line-height: 1.04;
  letter-spacing: -0.028em;
  margin-bottom: 18px;
  text-wrap: balance;
}
.sol-lede { color: var(--white); font-size: 19px; max-width: 46ch; margin-bottom: 28px;}
.sol-hero-cta { display: flex; gap: 14px; flex-wrap: wrap;}

/* the video CTA is its own thing: blue, with a play disc */
.btn-video { background: var(--blue-500); color: var(--white);}
.btn-video:hover { background: var(--blue-600);}
.btn-video svg {
  width: 21px; height: 21px;
  flex-shrink: 0;
  display: block;
}

.sol-hero-screens { position: relative; padding: 50px 40px 50px 0;}
.sol-hero-phone {
  position: absolute;
  right: 0; top: 50%;
  transform: translateY(-50%);
  width: 190px;
  background-color: var(--white);
  box-shadow: var(--shadow-card);
}

/* flipped split, so consecutive sections alternate rather than repeat */
.page-split--flip > *:first-child { order: 2;}
@media (max-width: 820px) { .page-split--flip > *:first-child { order: 0;} }

/* the singled-out capability */
.sol-spot { background: var(--gradient-band); color: var(--sky-100); padding: 82px 0;}
.sol-spot h2 { color: var(--white); font-size: 32px;}
.sol-spot p { color: var(--sky-200); font-size: 17px;}
.sol-spot-inner { display: grid; grid-template-columns: 1fr 0.85fr; gap: 52px; align-items: center;}
.sol-spot-inner > * { min-width: 0;}

/* proof points */
.proof-grid {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 30px 26px;
}
.proof-grid > * { min-width: 0; display: flex; flex-direction: column; gap: 4px;}
.pf-n {
  font-size: 40px;
  font-weight: 900;
  letter-spacing: -0.03em;
  color: var(--navy-900);
  font-variant-numeric: tabular-nums;
  line-height: 1;
}
.pf-k { font-size: 14.5px; color: var(--gray-600);}

@media (max-width: 1180px) {
  .sol-hero-inner { grid-template-columns: 260px minmax(0,1fr);}
  .sol-hero-screens { display: none;}
  .sol-hero-copy h1 { font-size: 48px;}
}
@media (max-width: 820px) {
  /* The rail becomes a scrolling strip above the copy rather than a column
     seven items tall pushing the headline off the screen. */
  .sol-hero { min-height: 0;}
  .sol-hero-inner { grid-template-columns: 1fr; gap: 0;}
  .sol-rail {
    flex-direction: row;
    overflow-x: auto;
    background: rgba(255,255,255,0.95);
    scroll-snap-type: x proximity;
    /* Must be reset from the desktop rule: with justify-content:center a
       flex row that overflows spills past BOTH edges, and scrollLeft cannot
       go negative -- so the first items become permanently unreachable. */
    justify-content: flex-start;
}
  .sol-rail a {
    border-bottom: 0;
    border-right: 1px solid var(--gray-200);
    white-space: nowrap;
    font-size: 14.5px;
    padding: 14px 18px;
    scroll-snap-align: start;
}
  .sol-rail a:hover { padding-left: 18px;}
  .sol-hero-copy { padding: 40px 22px 44px;}
  .sol-hero-copy h1 { font-size: 34px;}
  .sol-lede { font-size: 16.5px;}
  .sol-spot-inner { grid-template-columns: 1fr; gap: 30px;}
  .proof-grid { grid-template-columns: repeat(2, 1fr);}
  .pf-n { font-size: 32px;}
}
@media (max-width: 560px) {
  .proof-grid { grid-template-columns: 1fr;}
}

/* ==========================================================================
   Success stories and the resources hub, built to TrackVia_Interior-Templates
   ========================================================================== */

.sr-only {
  position: absolute; width: 1px; height: 1px;
  padding: 0; margin: -1px; overflow: hidden;
  clip: rect(0 0 0 0); white-space: nowrap; border: 0;
}

/* shared interior hero */
.story-hero {
  position: relative;
  background: var(--navy-950);
  padding: 74px 0 78px;
  overflow: hidden;
  text-align: center;
  /* Every section landing page and case study shares this hero, and their
     titles run from one word ("Company") to a full sentence. Left to the
     content they range from 288px to 384px, so the pages look inconsistent
     next to each other. The floor is the tallest of them; the grid centring
     keeps the shorter ones from sitting at the top of their own box. */
  min-height: 384px;
  display: grid;
  align-content: center;
}
.story-hero-bg { position: absolute; inset: 0; border-radius: 0; border-width: 0;}
.story-hero-bg::after { align-self: flex-start; margin-top: 14px;}
.story-hero-inner { position: relative; z-index: 1;}
.story-hero h1 {
  color: var(--white);
  font-size: 58px;
  letter-spacing: -0.028em;
  margin-bottom: 12px;
  text-wrap: balance;
}
/* The section landings keep their centred hero. An individual story reads
   like an article, so its eyebrow and headline align left. */
/* Exactly the .page-hero > .container bug, and it arrived when .story-hero
   became a grid for the min-height work: a grid item carrying margin:0 auto
   opts out of stretch, so the container shrink-wrapped to its widest child and
   then centred itself. A long headline filled 1200px and looked correctly
   left-aligned; a short one -- "Complex Guest Operations & Compliance" -- came
   out 872px wide and sat in the middle of the page. width:100% puts it back on
   the line and max-width still caps it. */
.story-hero > .container { width: 100%;}
.story-hero--story { text-align: left;}
/* A story headline is whatever the customer's headline happens to be, and some
   run long. At a flat 58px the longest ones sat on one line and ran past the
   right edge between the 900px breakpoint and a wide desktop. Scaling with the
   viewport holds 58px on a large screen and comes down smoothly below it. */
/* Matched to .page-hero h1 exactly -- same 44/33/28 ladder at the same
   breakpoints -- so a case study hero and a blog post hero are the same
   height and the same weight. The clamp here used to peak at 58px, which
   pushed a three-line headline to 415px against the blog post's 384px. */
.story-hero--story h1 { font-size: 44px;}
.story-hero p {
  color: var(--white);
  font-size: 18px;
  font-weight: 700;
  max-width: 44ch;
  margin: 0 auto;
}

.story-back { padding-top: 18px; padding-bottom: 4px; text-align: left;}
.cta-link--back::after { content: none;}
.cta-link--back::before {
  content: '\25C2';
  font-size: 0.9em;
  line-height: 1;
}

/* index grid */
.story-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(300px, 1fr));
  gap: 26px;
}
.story-grid > * { min-width: 0;}
.story-card {
  background: var(--gray-100);
  border-radius: var(--radius-md);
  overflow: hidden;
  display: flex;
  flex-direction: column;
  transition: transform 0.22s cubic-bezier(.22,.61,.36,1), box-shadow 0.22s ease;
}
.story-card:hover { transform: translateY(-4px); box-shadow: var(--shadow-card);}
/* flex:none so the aspect ratio decides the height, not the leftover space.
   These cards are a flex column stretched to the tallest in the row; the photo
   block was a shrinkable flex item, so a card with more copy under it got a
   SHORTER photo. Measured across a row on /success-stories/: 176, 194 and
   207px against an identical 378px card height. The slack now falls in the
   body, where it is invisible, instead of in the picture, where it is not. */
.story-top {
  position: relative;
  flex: none;
  /* 560/300 was the photo's ratio, but this box also carries the customer name,
     the descriptor and the tag chips on top of the photo, and on the cards with
     the longest of those the CONTENT was taller than the ratio -- so the photo
     block grew and a row came out at 176, 194 and 207px against an identical
     card height. Lindsay's call was that a bigger container is fine as long as
     it does not throw the design off, so the ratio now accommodates the tallest
     overlay instead of the shortest. Anything still taller is clamped below. */
  aspect-ratio: 560 / 360;
  border-radius: 0;
  border: 0;
  display: flex;
  align-items: flex-end;
  padding: 18px;
}
/* The card caption is --sky-200 at 12.5px over a different photograph on every
   card, and the first stop of this gradient was light enough that the lightest
   photographs showed through: measured 3.56:1 at 390 and 3.32:1 at 1440 across
   50 runs of text, against a 4.5 floor. Darkening the first stop from
   rgba(45,118,212,0.82) to rgba(31,86,160,0.88) takes the worst card to 5.40
   and 5.07. Raising only the alpha was tried first and reached 4.10, still
   short -- the blue itself was too light, not just too transparent. */
.story-top::before {
  content: '';
  position: absolute; inset: 0;
  background: linear-gradient(200deg, rgba(31,86,160,0.88), rgba(10,26,55,0.92));
}
.story-top::after { position: absolute; top: 9px; left: 12px; max-width: 52%;}
/* Two lines is what the tallest card needs; a third would push the photo
   block past the ratio above and put the row back out of step. */
.story-id p { display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical;
  overflow: hidden;
}
.story-id {
  position: relative;
  z-index: 1;
  margin-left: auto;
  text-align: right;
  display: flex;
  flex-direction: column;
  gap: 6px;
}
.story-id strong { color: var(--white); font-size: 21px; font-weight: 800; letter-spacing: -0.01em;}
.story-id em { color: var(--sky-200); font-size: 12.5px; font-style: normal; line-height: 1.45;}
.story-tags { display: flex; gap: 6px; flex-wrap: wrap; justify-content: flex-end;}
.story-tags span {
  font-size: 10.5px;
  font-weight: 700;
  color: var(--white);
  border: 1px solid rgba(255,255,255,0.6);
  border-radius: var(--radius-pill);
  padding: 3px 10px;
  white-space: nowrap;
}
.story-body { padding: 22px; display: flex; flex-direction: column; gap: 16px; flex: 1;}
.story-body h3 { font-size: 19px; margin: 0;}
.story-body .cta-link { margin-top: auto;}

/* story detail */
.story-lead { padding-top: 26px;}
.story-lead .container { position: relative;}
/* Facts card and the story headline sit side by side beneath the image, as
   in the interior template, rather than the headline dropping below the fold. */
.story-lead-grid {
  display: grid;
  /* Copy first, fact panel on the right. The panel stays first in the DOM so
     that on one column it is still the thing that lifts over the hero. */
  grid-template-columns: minmax(0, 1fr) 300px;
  gap: 44px;
  align-items: start;
}
/* Where the customer is named in the copy but we cannot use their mark, the
   name takes the logo's place at the top of the facts card, so the card
   still says WHO before it says what. Sized and spaced to sit where the
   logo sits rather than reading as another fact row. */
.story-client {
  margin: 0 0 16px;
  text-align: center;
  font-size: 19px;
  font-weight: 800;
  line-height: 1.25;
  color: var(--navy-900);
  text-wrap: balance;
}
.story-facts { order: 2;
  margin-top: -140px;
  background: var(--white);
  border: 1px solid var(--gray-200);
  border-radius: var(--radius-md);
  box-shadow: var(--shadow-card);
  padding: 26px 24px;
  width: 300px;
  position: relative;
  z-index: 1;}
.story-lead-grid > * { min-width: 0;}
.story-lead-copy { padding-top: 26px;}
.story-lead-copy > p { color: var(--gray-600); margin-bottom: 14px;}
.story-lead-img {
  aspect-ratio: 1200 / 420;
  border-radius: var(--radius-md);
  border-width: 0;
  /* it is an <img> now that the real photography is in, not a placeholder box */
  display: block;
  width: 100%;
  object-fit: cover;
}
/* The card top keeps its own blue scrim (.story-top::before), so the photo goes
   behind as a background. .photo-bg is deliberately not used here -- its own
   ::before would replace that scrim with the dark hero one. */
.story-top.has-photo { background-size: cover; background-position: center;}
.story-facts .ph--logo { margin-bottom: 18px; aspect-ratio: 150/56;}
/* Where the customer is named and we hold their mark, the fact panel carries
   the real logo. Contained, not filled -- same reasoning as the rows. The
   anonymised stories keep the placeholder on purpose: putting a logo on one
   would undo the anonymisation. */
.story-facts .story-logo {
  display: block;
  margin: 0 auto 18px;
  max-width: 150px;
  max-height: 46px;
  object-fit: contain;
}
/* A square mark contained in a 150x46 box comes out 46px wide, which reads
   much smaller than a wordmark filling the same box. KS Industries is the only
   round one, so it gets a taller allowance to sit at a comparable weight. */
.story-facts .story-logo--square { max-height: 58px; max-width: 58px;}
/* LifeCenter and Lifeline of Ohio are two organizations in one story, so that
   panel carries two marks. They share the row and each takes half the height
   the single logo gets, which keeps the panel's top the same as every other. */
.story-logo-pair {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 14px;
  margin-bottom: 18px;
}
.story-logo-pair .story-logo { margin: 0; max-width: 104px; max-height: 38px;}
.story-facts dl { margin: 0; display: flex; flex-direction: column; gap: 14px; text-align: center;}
.story-facts dt { font-size: 12.5px; color: var(--gray-500); margin-bottom: 2px;}
.story-facts dd { margin: 0; font-size: 16px; font-weight: 800; color: var(--navy-900);}

/* The fact panel is the tall item in the lead grid, so IT set the row height
   and the body copy started below it -- leaving a hole between the lead line
   and the first heading that ran from 30px to 391px depending on how short the
   lead copy was. Taking the panel out of flow lets the row be as tall as the
   copy and the body copy follow straight on. It is safe because .prose is
   capped at 68ch and the panel sits in the right 300px: at 1000px, the widest
   the prose gets is still 24px clear of the panel. Below 901px the grid is one
   column and the panel goes back in flow, above the copy. */
@media (min-width: 901px) {
  .story-lead-grid { position: relative;}
  .story-facts { position: absolute; top: 0; right: 0;}
}

/* 44px of section padding met the first h2's own 36px top margin, which with
   the lead paragraph's margin left a 110px hole. The first heading does not
   need its margin here: the section padding is the separation. Trimmed again
   on 22 Sep: the lead line and the first heading read as one run of copy, and
   26px of section padding on top of the paragraph's own margin still left 64px
   between them. */
.story-body-copy { padding-top: 26px;}
.story-body-copy .prose > h2:first-child { margin-top: 0;}

/* ---------- Solution page, direction D ---------- */
/* Light hero carrying the angled band, so the page opens on the light ground
   rather than a navy slab. Doubled class: .page-hero/.sol-hero rules later in
   this file share specificity and would otherwise win. */
.sol-hero--d {
  position: relative;
  overflow: hidden;
  background: var(--white);
  padding: 92px 0;
  /* Same reasoning as .page-hero: pins the band at 3.20:1 above 1920 so
     whatever artwork lands here has a bounded shape to be designed against. */
  min-height: max(600px, 31.25vw);
  display: flex;
  flex-direction: column;
  justify-content: center;
}
.sol-hero--d::after {
  content: '';
  position: absolute;
  inset: 0;
  background-image: url(/assets/img/solution-hero-bg.webp);
  background-size: cover;
  background-position: center right;
  background-repeat: no-repeat;
}
.sol-hero--d > .container { position: relative; z-index: 1;}
.sol-hero-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 48px; align-items: center;}
.sol-hero-grid > * { min-width: 0;}
.sol-hero--d h1 { color: var(--navy-900); font-size: 48px; line-height: 1.08; letter-spacing: -0.022em;}
/* the primary keyword rides inside the H1 so the heading carries both it and
   the reason to keep reading; gold fails on this light ground, so it is blue */
.sol-kicker {
  display: block;
  font-size: 13px; font-weight: 800; letter-spacing: 0.08em;
  text-transform: uppercase; color: var(--blue-600); margin-bottom: 14px;
}
/* The hero shot is product UI on a pale brand ground, so it needs lifting off
   the background the way the cards elsewhere are. Scoped to the hero: the
   -why and -further photographs on the same page sit flat. */
.sol-hero-grid .photo {
  box-shadow: 0 2px 6px rgba(10, 26, 55, 0.08), 0 24px 48px -18px rgba(10, 26, 55, 0.28);
}
.sol-sub { font-size: 19px; font-weight: 700; color: var(--gray-800); max-width: 56ch; margin: 0 0 18px;}
.sol-facts { display: flex; flex-wrap: wrap; gap: 10px 26px; margin: 0 0 22px; padding: 0; list-style: none;}
.sol-facts li { display: flex; align-items: center; gap: 9px; font-size: 14.5px; font-weight: 700; color: var(--gray-600);}
.sol-facts li::before { content: ''; width: 7px; height: 7px; border-radius: 50%; background: var(--blue-600); flex: none;}

.sol-split { display: grid; grid-template-columns: 1fr 1.05fr; gap: 52px; align-items: center;}
.sol-split > * { min-width: 0;}
.sol-list { margin: 0 0 26px; padding: 0; list-style: none;}
.sol-list li { position: relative; padding-left: 20px; margin-bottom: 11px; color: var(--gray-800); font-size: 15.5px;}
.sol-list li::before { content: ''; position: absolute; left: 0; top: 9px; width: 8px; height: 8px; border-radius: 50%; background: var(--gold-500);}
.sol-list b { display: block; color: var(--navy-900); font-weight: 800;}

/* marks where the page stops selling and starts explaining */
.sol-depth { border-top: 1px solid var(--gray-200); background: var(--gray-50);}
.sol-depth-mark { max-width: var(--container); margin: 0 auto; padding: 0 28px; display: flex; align-items: center; transform: translateY(-11px);}
.sol-depth-mark span {
  background: var(--gray-50); border: 1px solid var(--gray-200); color: var(--gray-500);
  font-size: 11px; font-weight: 800; letter-spacing: 0.13em; text-transform: uppercase;
  padding: 4px 13px; border-radius: var(--radius-pill);
}

@media (max-width: 1024px) {
  .sol-hero--d h1 { font-size: 42px;}
}
/* The homepage artwork is pinned to the right edge and crops off the left as
   the window narrows, so the narrower the window the further left the picture
   reaches. With the abstract chevron the copy started colliding with it below
   1200px; the field-crew photograph is busier and reaches further, and the
   headline measures 2.4:1 at 1280 and 1.1:1 at 1200 against a 4.5 floor.
   Measured on the real glyph boxes over the real background: 1300px is the
   first width that comes back clean, at 5.6:1.

   Scaling the artwork down and pinning it bottom right was tried instead. It
   passes the contrast check from 1200 up, but the picture's own white ground
   stops short of the hero and leaves a grey band across the top with a hard
   edge where the image begins -- the same stray-corner failure described for
   .sol-hero--d below. So the artwork goes, and the hero loses its 560px floor
   with it, or the copy sits in a tall empty field. */
/* The artwork stays pinned to the viewport and bleeds off the right edge.

   Anchoring it to the container's centre line was tried, to hold the gap
   between the copy and the picture constant as the screen grows. It does hold
   that gap from 1100 to 1680, and it fails everywhere else. The panel it
   creates is tall and narrow -- 0.73:1 at 1100 against the artwork's own
   2.61:1 -- so background-size: cover magnifies the image 3.6x at 1100 and
   3.2x at 1208, which crops the brand chevron out of the frame entirely and
   leaves two enormous hard hats. And above 1680 it makes the drift worse, not
   better: measured copy-to-picture gaps of -76, 164, 357, 698 at 1440/1920/
   2560/3440, against -79, 6, 101, 231 for the full-bleed version. A panel
   whose left edge is fixed grows faster than one that scales with the
   viewport, so the picture runs away from the copy sooner.

   The real fix is artwork proportioned for a panel rather than for a band.
   Until that exists, full bleed is both the better-looking and the more
   stable of the two. */
/* 1279 rather than 1299: sizing to height instead of cover buys a little room
   at the bottom end too, and 1280 now measures 5.58:1. Below that the copy
   reaches the photograph itself -- 1.66:1 at 1200, 1.23 at 1100 -- so it still
   has to go. */
@media (max-width: 1279px) {
  .home-hero::after { display: none;}
  .home-hero { min-height: 0; padding: 76px 0 80px;}
}
@media (max-width: 900px) {
  /* The artwork comes off entirely once this stacks.
     It is a wide composition -- almost white on the left, the blue chevron on
     the right -- so there is no crop of it that reads as anything but a stray
     corner at this width. The previous attempt painted it 140px tall pinned
     top right, which put a 366px graphic in the corner above 168px of empty
     ground, and that is what it looked like. The hero keeps its light band and
     the product shot underneath carries the section. */
  .sol-hero--d::after { display: none;}
  /* The homepage artwork is already hidden from 1299px down, in its own block
     above. What is left here is the phone padding. */
  .home-hero { min-height: 0; padding: 56px 0 60px;}
  .sol-hero-grid, .sol-split { grid-template-columns: 1fr; gap: 28px;}
  .sol-hero--d { padding: 56px 0 60px; min-height: 0;}
  .sol-hero--d h1 { font-size: 36px;}
}
@media (max-width: 560px) {
  .sol-hero--d h1 { font-size: 30px;}
}

/* ---------- Real photography ---------- */
/* A photograph behind hero type. The scrim is not decoration: white text over
   an uncontrolled photograph has no contrast guarantee, and these are the same
   slots that used to hold a flat dark placeholder. */
.photo-bg {
  background-size: cover;
  background-position: center;
  border: 0;
  border-radius: 0;
  /* Scrim strength, per photograph. --scrim is the middle stop, where the
     copy sits; the other two move with it. Overridden by file name below. */
  --scrim: 0.68;
  --scrim-top: calc(var(--scrim) + 0.14);
  --scrim-foot: calc(var(--scrim) + 0.20);
}
/* One value cannot serve this set. 0.68 was tuned to the brightest
   photographs and holds them at 4.6:1 against a 4.5 floor, so it cannot come
   down for them -- but it was giving the darkest 13.4:1, about three times
   what it needs. Measured over text-sized patches where hero copy sits.
   Keyed on the file name, since that is where the page sets the image, and
   the default above is the heaviest value: an image that is renamed or
   swapped falls back to the safe end rather than to an unreadable one. */
.photo-bg[style*="header-blog.webp"]        { --scrim: 0.60;}
.photo-bg[style*="hero-home.webp"]          { --scrim: 0.52;}
.photo-bg[style*="header-demo.webp"]        { --scrim: 0.45;}
.photo-bg[style*="home-track-offline.webp"] { --scrim: 0.45;}
.photo-bg[style*="header-case-studies.webp"]{ --scrim: 0.58;}
/* The four resources landings use photo-bg--high on the SAME photograph, and
   that crop puts a much lighter part of it behind the copy: still 3.88 at 390
   and 4.23 at 1440 with 0.58. 0.68 brings them to about 5.6. Scoped to the
   --high crop so the pages using the standard crop keep the lighter 0.58. */
.photo-bg--high[style*="header-case-studies.webp"] { --scrim: 0.68;}
/* 0.45 measured 3.74:1 at 1440 and 4.76 at 390 on /success-stories/, and
   3.88 and 3.70 on /resources/glossary/ -- three of those four under the
   4.5 floor. This photograph is lighter than the scrim was tuned for.
   0.58 gives 5.57 and 6.81 on the first and 5.51 and 5.24 on the second.
   One rule, six pages: the two above plus the blog, guides, videos and
   webinars landings, which all share this header. */
/* The guide cover is artwork rather than a scene, and the default 0.68 flattened
   it to a grey rectangle. 0.55 is the lightest value that still clears 4.5:1 for
   the white heading at its worst pixel -- measured, not guessed. */
.photo-bg[style*="home-best-practices-guide.webp"] { --scrim: 0.55;}
/* Measured on the photograph itself, over the heading band: 0.68 was giving
   this card 8.7:1 against white, nearly double the 4.5 floor, and burying a
   photograph nobody could see. 0.52 holds 5.2:1 and gives the picture back.
   The guide card above cannot follow it down -- the bright cover art sits
   directly behind its heading and 0.55 already measures 4.66:1. */
.photo-bg[style*="home-see-in-action.webp"] { --scrim: 0.52;}

.photo-bg::before {
  content: '';
  position: absolute;
  inset: 0;
  background: linear-gradient(180deg,
    rgba(6, 14, 30, var(--scrim-top)) 0%,
    rgba(6, 14, 30, var(--scrim)) 45%,
    rgba(10, 26, 55, var(--scrim-foot)) 100%);
}
.photo-bg::after { content: none;}

/* These photos put faces in the upper third, and the hero band is far wider
   than the 3:2 source, so a centred crop cuts heads off. Bias the crop up.
   30% suits most of them; --crop-y tunes the odd photo that needs more or
   less headroom, set next to that photo's own background-image. */
.photo-bg--high { background-position: center var(--crop-y, 30%);}
/* The two Ready-made for your industry panels are .photo-bg, which sets
   border-radius: 0 and, coming later at the same specificity, defeated the
   radius .home-promo-card already asked for. Same 14px as the page's other
   images. */
.home-promo-card.photo-bg { border-radius: var(--radius-md);}
/* The +0.14 top stop exists for heroes, where copy sits high in the band. On a
   wide promo card nothing is up there -- the heading starts at 63% of the card
   -- so that stop was only darkening the half of the photograph anyone
   actually looks at. Pulling it below --scrim brightens the image and cannot
   reach the text, which sits well past the 45% stop.

   The gate is 1000px because the heading's position is a function of card
   height, not of the grid being two up. Measured across 375-1920: the card is
   434px wide-screen and the heading starts at 0.628, but at 821px -- still two
   up -- it is 286px and the heading climbs to 0.343, straight into this stop,
   where -0.18 puts the two headings at 3.76:1 and 4.01:1 against a 4.5 floor.
   At 1000px the card is 355px, the heading is at 0.474 and both measure 4.66:1
   and 5.2:1. Below 1000 the card keeps the hero value. */
@media (min-width: 1000px) {
  .home-promo-card.photo-bg { --scrim-top: calc(var(--scrim) - 0.18);}
}

/* Content photography: fills its slot without distorting. */
.photo {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: cover;
  border-radius: var(--radius-md);
}
.home-industry .photo { border-radius: var(--radius-md); aspect-ratio: 400/500;
  transition: transform 0.32s cubic-bezier(.2,.7,.3,1);}

/* ---------- Blog post (T9) ---------- */
.post-meta {
  display: flex;
  flex-wrap: wrap;
  gap: 8px 14px;
  align-items: center;
  color: var(--sky-200);
  font-size: 14.5px;
  margin: 0;
}
.post-meta span::before { content: '\00B7'; margin-right: 14px; color: rgba(255,255,255,0.5);}
.post-body { padding-top: 40px;}
.post-lead-img {
  aspect-ratio: 1200 / 520;
  border-radius: var(--radius-md);
  margin-bottom: 40px;
  /* an <img> now the real photography is in, not a placeholder box */
  display: block;
  width: 100%;
  object-fit: cover;
}
/* a comfortable measure for long-form reading, centred in the container */
.post-body .prose { max-width: 72ch; margin: 0 auto;}

/* Post contents, built by script.js from the post's own h2s.

   Hidden by default and shown only from 1000px, which is the width at which
   the centred 72ch measure leaves a real gutter to put it in. The three-column
   track is 1fr / the measure / 1fr, so the article sits in exactly the same
   place and at exactly the same width as it does without a contents rail --
   the rail occupies space that was empty. */
.post-toc { display: none;}
@media (min-width: 1040px) {
  .post-body > .container {
    display: grid;
    /* Equal outer tracks, so the measure stays centred exactly where it sits
       without a rail. A 1fr track that grows to fit the rail is what pushed the
       article 26px right of where it had always been. */
    grid-template-columns: 190px minmax(0, 72ch) 190px;
    column-gap: 36px;
    justify-content: center;
    align-items: start;
}
  .post-body > .container > .prose { grid-column: 2; grid-row: 1; margin: 0; max-width: none;}
  .post-body > .container > .post-back { grid-column: 2; grid-row: 2;}
  .post-toc {
    display: block;
    grid-column: 1;
    grid-row: 1;
    justify-self: end;
    width: 190px;
    position: sticky;
    top: 104px;
    max-height: calc(100vh - 140px);
    overflow-y: auto;
    overscroll-behavior: contain;
}
  /* clears the sticky site header when a contents link jumps to a heading */
  .post-body .prose h2 { scroll-margin-top: 96px;}
}
.post-toc-h {
  margin: 0 0 10px;
  font-size: 11.5px;
  font-weight: 900;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--gray-500);
}
.post-toc ul { list-style: none; margin: 0; padding: 0; border-left: 2px solid var(--gray-200);}
.post-toc li { margin: 0;}
.post-toc a {
  display: block;
  padding: 6px 0 6px 14px;
  margin-left: -2px;
  border-left: 2px solid transparent;
  font-size: 13.5px;
  line-height: 1.4;
  color: var(--gray-600);
  text-decoration: none;
}
.post-toc a:hover { color: var(--navy-900);}
.post-toc a.is-here {
  color: var(--navy-900);
  font-weight: 700;
  border-left-color: var(--blue-600);
}
.post-body .prose ul { margin: 0 0 20px 0; padding-left: 22px; color: var(--text-body);}
.post-body .prose li { margin-bottom: 8px;}
/* Aligned with the paragraph above it rather than centred, and closer to it. */
/* .post-body .prose is centred at 72ch, so a sibling sitting at the container
   edge would be ~226px to its left. Same measure, same left edge. */
.post-back { text-align: left; padding-top: 18px; max-width: 72ch; margin: 0 auto;}
.post-more .card-grid { margin-top: 26px;}

/* Closing band: the four proof stats plus a single CTA strip, matching the
   interior template. Reuses .home-stats so the numerals stay identical. */
.story-cta { text-align: center;}
.story-cta .home-stats { margin-top: 38px; text-align: center;}
.story-cta .home-stat h3, .story-cta .home-stat .home-stat-lab { color: var(--white);}
.story-cta .home-stat p { color: var(--sky-200);}
.story-strip {
  margin: 40px auto 0;
  max-width: 720px;
  background: var(--white);
  border-radius: var(--radius-md);
  padding: 18px 22px;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 18px;
  flex-wrap: wrap;
}
.story-strip p { margin: 0; color: var(--navy-900); font-weight: 800; font-size: 16.5px;}
.story-headline { font-size: 32px; margin-bottom: 10px;}
.story-tags--inline { display: flex; gap: 7px; flex-wrap: wrap; margin-bottom: 26px;}
/* These only ever set colour, on the assumption of a base .story-tags span
   rule that does not reach them: no padding, radius or border-width, so they
   rendered as text on a coloured background rather than as pills. */
.story-tags--inline span {
  font-size: 11.5px;
  font-weight: 700;
  color: var(--blue-600);
  background: var(--sky-100);
  border: 1px solid var(--sky-200);
  border-radius: var(--radius-pill);
  padding: 4px 12px;
  white-space: nowrap;
}
.story-body-copy .prose h2 { font-size: 24px; margin-top: 36px;}
.story-body-copy .prose p { font-size: 16.5px;}

/* resources hub search */
.res-search { display: flex; max-width: 640px; margin: 0 auto 26px;}
.res-search input {
  flex: 1;
  min-width: 0;
  font-family: inherit;
  font-size: 16px;
  padding: 14px 18px;
  border: 1px solid var(--gray-200);
  border-right: 0;
  border-radius: var(--radius-sm) 0 0 var(--radius-sm);
  background: var(--white);
  color: var(--text-body);
}
.res-search input:focus { outline: 2px solid var(--blue-500); outline-offset: -2px;}
.res-search button {
  border: 0;
  background: var(--blue-500);
  border-radius: 0 var(--radius-sm) var(--radius-sm) 0;
  padding: 0 22px;
  display: flex;
  align-items: center;
}
.res-search button:hover { background: var(--blue-600);}
.res-search svg { width: 21px; height: 21px; display: block;}

.chip-row--btns { justify-content: center; margin-bottom: 18px;}
.chip-btn {
  font-family: inherit;
  font-size: 14px;
  font-weight: 700;
  color: var(--blue-600);
  background: var(--white);
  border: 1px solid var(--sky-200);
  border-radius: var(--radius-pill);
  padding: 9px 18px;
}
.chip-btn:hover { background: var(--sky-100);}
.chip-btn.is-on { background: var(--navy-900); color: var(--white); border-color: var(--navy-900);}
/* .card is a grid item with its own display, which beats the bare [hidden]
   UA rule, so the filter has to say so explicitly. */
.card-grid .card[hidden] { display: none;}
/* ---------- Open roles table ----------
   Seven columns, and two of them (role and salary) carry long strings, so on a
   narrow screen it scrolls sideways inside its own region rather than being
   restyled into blocks -- which would strip the table semantics a screen reader
   relies on. The region is focusable and labelled so it can be reached and
   scrolled from the keyboard, which is what makes that trade acceptable. */
.jobs-scroll,
.datatable-scroll {
  overflow-x: auto;
  -webkit-overflow-scrolling: touch;
  border: 1px solid var(--gray-200);
  border-radius: var(--radius-md);
}
.jobs-scroll:focus-visible,
.datatable-scroll:focus-visible { outline: 3px solid var(--blue); outline-offset: 2px;}
.jobs,
.datatable {
  width: 100%;
  border-collapse: collapse;
  font-size: 14.5px;
  min-width: 860px;
}
.jobs thead th,
.datatable thead th {
  text-align: left;
  padding: 13px 16px;
  background: var(--gray-50);
  border-bottom: 1px solid var(--gray-200);
  font-size: 11.5px;
  font-weight: 900;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--gray-500);
  white-space: nowrap;
}
.jobs tbody th,
.jobs tbody td,
.datatable tbody th,
.datatable tbody td {
  padding: 16px;
  border-top: 1px solid var(--gray-100);
  vertical-align: top;
  color: var(--gray-600);
  line-height: 1.5;
}
.jobs tbody th,
.datatable tbody th {
  text-align: left;
  font-weight: 800;
  color: var(--navy-900);
  font-size: 15px;
  min-width: 240px;
}
.jobs tbody tr:hover,
.datatable tbody tr:hover { background: var(--sky-100);}
.jobs .jobs-apply { white-space: nowrap;}
/* Same treatment, neutral name, for a data table on any page -- the privacy
   policy's "what we collect and why" matrix uses it. Three wide prose columns
   rather than the jobs table's six narrow ones, so it needs less room before
   it has to scroll. */
.datatable { min-width: 680px;}
.datatable tbody th { min-width: 180px;}
.jobs-note {
  margin-top: 14px;
  font-size: 13.5px;
  color: var(--gray-500);
}
/* The featured reel sits directly under the hero, so a full 96px of section
   padding on top of the hero's own left a hole before any content. It is also
   a skim strip rather than a section to read, so its cards carry a shallower
   image than the feed's below it. */
.res-featured { padding: 40px 0 52px;}
.res-featured .card-shot { aspect-ratio: 16 / 7;}
.res-featured .tv-carousel-head { margin-bottom: 18px;}

.res-more { display: flex; justify-content: center; margin-top: 34px;}
/* Third time in this file: an author display: rule beats the UA style for
   [hidden], so the button stayed on screen reading "Show more (0 left)"
   once the feed ran out. See .nav-group-menu and .card-grid .card. */
.res-more[hidden] { display: none;}
.res-empty {
  margin: 30px 0 0;
  text-align: center;
  color: var(--gray-500);
  font-size: 15.5px;
}

@media (max-width: 900px) {
  .story-lead-grid { grid-template-columns: 1fr; gap: 24px;}
  /* One column: the fact panel goes back above the copy, where it started,
     and the -110px the old two-column rule set was already dead here -- the
     shorthand below it reset the whole margin. */
  .story-facts { order: 0; width: auto; margin: -70px 0 0;}
  .story-lead-copy { padding-top: 0;}
  .story-strip { flex-direction: column; text-align: center;}
  .story-hero { min-height: 343px;}
  .story-hero h1 { font-size: 40px;}
}
@media (max-width: 560px) {
  /* On a phone the hero is never seen beside another one, and a floor here
     just pushes the page content below the fold. */
  .story-hero { padding: 48px 0 52px; min-height: 0;}
  .story-hero h1 { font-size: 31px;}
  .story-hero p { font-size: 16px;}
  .story-headline { font-size: 25px;}
  .story-facts { margin-top: -40px; padding: 20px 18px;}
}

/* ==========================================================================
   Solution pages, second pass. Three things the first build got wrong, and
   three components it was missing.
   ========================================================================== */

/* The stats strip is authored for the homepage, where it sits on white. The
   solution pages put it inside .section--dark, so the number was navy-900 on
   a navy-900 gradient -- 1:1, invisible -- and the subline was gray-600 on
   the same. Repaint both for the dark ground. */
.section--dark .home-stat p { color: var(--sky-200);}
.section--dark .home-stat-num { color: var(--gold-500);}

/* Related solutions. The markup shipped with .solgrid but the rule never came
   across from the mockup, so these rendered as bare anchors in a card grid. */
.solgrid { grid-template-columns: repeat(3, 1fr);}
.solgrid a {
  display: flex;
  flex-direction: column;
  gap: 6px;
  padding: 20px;
  text-decoration: none;
  background: var(--white);
  border: 1px solid var(--gray-200);
  border-radius: var(--radius-md);
  transition: border-color 0.18s ease, transform 0.18s ease;
}
.solgrid a:hover { border-color: var(--blue-400); transform: translateY(-2px);}
.solgrid b { color: var(--navy-900); font-size: 15.5px;}
.solgrid span { color: var(--gray-600); font-size: 13.5px; line-height: 1.45;}

/* ---------- Block 1: the photograph with the app beside it ----------
   Same pairing the homepage accordion uses, carried onto each module page so
   the product actually appears above the fold-and-a-bit rather than only in
   a photograph of someone holding a tablet. The column is wider here than on
   the homepage, so the device is scaled up to match. */
.sol-why-media { position: relative; padding-right: 78px;}
.sol-why-device {
  position: absolute;
  right: 0;
  top: 50%;
  transform: translateY(-50%);
  width: 188px;
  /* height:auto is load-bearing. These carry width/height attributes for CLS,
     and an attribute is a presentational hint that only CSS overrides. With a
     width set here and no height, the height hint applied at full natural size
     and object-fit:fill squashed the screenshot to ~62% of its true width.
     This lets height compute from the ratio while the box is still reserved. */
  height: auto;
  border-radius: var(--radius-md);
  background-color: var(--white);
  box-shadow: var(--shadow-card);
}
@media (max-width: 900px) {
  .sol-why-media { padding-right: 58px;}
  .sol-why-device { width: 142px;}
}
@media (max-width: 520px) {
  .sol-why-media { padding-right: 44px;}
  .sol-why-device { width: 112px;}
}

/* ---------- Connector: related solutions + the industries rail ----------
   A solution page was a dead end for anyone who arrived wanting to know whether
   it fits their sector. The rail rides alongside the block that already does
   the cross-linking rather than adding a section of its own. */
.sol-connect {
  display: grid;
  /* Even, and both halves carry their own heading. It was 1fr / 210px, which
     is not really a ratio problem: the industries were a bare list against a
     hairline while the solutions were cards, so they read as an afterthought
     whatever the width. No divider between them -- with bordered cards on
     both sides a rule is a third line weight in the same place, and the gap
     plus two headings already say these are two lists. */
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 56px;
  align-items: start;
  margin-top: 52px;
}
/* Three related solutions, stacked. At half width three across would be about
   170px each, too narrow for the line under each name. */
.solrows { display: flex; flex-direction: column; gap: 12px;}
.solrows a {
  display: flex;
  align-items: baseline;
  gap: 14px;
  padding: 14px 18px;
  text-decoration: none;
  background: var(--white);
  border: 1px solid var(--gray-200);
  border-radius: var(--radius-md);
  transition: border-color 0.18s ease, transform 0.18s ease;
}
.solrows a:hover { border-color: var(--blue-400); transform: translateY(-2px);}
/* flex: 0 0 auto meant this label could not shrink, so "Crew and Vendor
   Management" beside its description pushed the row -- and the page --
   past a 320px viewport. It may shrink now, and below 560px the label and
   its description stack instead of sharing a line, which is the only way
   both read on a phone. */
.solrows b { flex: 0 1 auto; color: var(--navy-900); font-size: 15.5px;}
.solrows span { color: var(--gray-600); font-size: 13.5px; line-height: 1.45;}

@media (max-width: 560px) {
  .solrows a { flex-direction: column; align-items: stretch; gap: 4px;}
}

.indchips { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 12px;}
.indchips a {
  display: block;
  padding: 13px 16px;
  font-size: 14.5px;
  font-weight: 700;
  color: var(--navy-900);
  text-decoration: none;
  background: var(--white);
  border: 1px solid var(--gray-200);
  border-radius: var(--radius-sm);
  transition: border-color 0.18s ease, transform 0.18s ease;
}
.indchips a::after { content: ' \25B8'; color: var(--blue-500);}
.indchips a:hover { border-color: var(--blue-400); transform: translateY(-2px);}
@media (max-width: 640px) {
  /* 163px each at phone width, which wraps "Casinos and Resorts" onto two lines. */
  .indchips { grid-template-columns: 1fr;}
}
.sol-connect > div > .section-head { margin-top: 0;}

@media (max-width: 900px) {
  .sol-connect { grid-template-columns: 1fr; gap: 28px; margin-top: 40px;}
}

/* How it works. A sequence, not a set: the numeral leads and the rule under it
   carries the eye along the row. Distinct from .card on purpose -- the
   platform overview below uses those, and the two should not read alike. */
.sol-steps {
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  gap: 30px;
  counter-reset: step;
}
.sol-step { position: relative; padding-top: 18px;}
.sol-step::before {
  content: '';
  position: absolute;
  top: 0;
  left: 0;
  width: 46px;
  height: 3px;
  background: var(--blue-500);
  border-radius: 2px;
}
.sol-step-n {
  display: block;
  font-size: 13px;
  font-weight: 800;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--blue-600);
  margin-bottom: 10px;
}
.sol-step h3 { font-size: 19px; margin-bottom: 8px;}
.sol-step p { color: var(--gray-600); font-size: 15px; line-height: 1.6;}

/* Platform overview: four capabilities, each with the features that sit under
   it. Shared by every solution page. */
.sol-caps { display: grid; grid-template-columns: repeat(2, 1fr); gap: 26px;}
.sol-cap {
  padding: 30px;
  background: var(--white);
  border: 1px solid var(--gray-200);
  border-radius: var(--radius-md);
}
.sol-cap h3 { font-size: 21px; margin-bottom: 10px;}
.sol-cap > p { color: var(--gray-600); font-size: 15px; line-height: 1.6;}
.sol-cap ul { list-style: none; margin: 18px 0 0; padding: 0;}
.sol-cap li {
  padding: 11px 0 0;
  border-top: 1px solid var(--gray-100);
  margin-top: 11px;
  font-size: 14.5px;
  line-height: 1.55;
  color: var(--gray-600);
}
.sol-cap li:first-child { border-top: 0; margin-top: 0; padding-top: 0;}
.sol-cap li b { display: block; color: var(--navy-900); font-size: 15px; margin-bottom: 3px;}

/* ---------- Platform overview, as open/close rows ----------
   The block moved up the page, so height became the constraint: four cards of
   features ran about a third of the viewport on their own. Closed, each
   capability is one row and its lede; the features are still real content, one
   click away, not a tooltip. First row ships open so the section never reads as
   a wall of shut doors. */
.sol-acc {
  background: var(--white);
  border: 1px solid var(--gray-200);
  border-radius: var(--radius-md);
  overflow: hidden;
}
.sol-acc details { border-top: 1px solid var(--gray-100);}
.sol-acc details:first-child { border-top: 0;}
.sol-acc summary {
  list-style: none;
  cursor: pointer;
  padding: 19px 24px;
  display: flex;
  align-items: flex-start;
  gap: 18px;
  font-size: 19px;
  font-weight: 800;
  line-height: 1.3;
  color: var(--navy-900);
}
/* The title holds its own column so the wrapped lede has a straight left edge
   down the block rather than starting at a different x on every row. */
.sol-acc summary > span:first-child { flex: 0 0 168px;}
.sol-acc summary::-webkit-details-marker { display: none;}
.sol-acc-lede {
  flex: 1;
  min-width: 0;
  font-size: 14px;
  font-weight: 400;
  line-height: 1.55;
  color: var(--gray-600);
}
.sol-acc-chev {
  flex: none;
  width: 26px; height: 26px;
  border-radius: 50%;
  background: var(--sky-100);
  color: var(--blue-600);
  display: grid;
  place-items: center;
  font-size: 13px;
  font-weight: 900;
  transition: transform 0.18s ease;
}
.sol-acc details[open] > summary .sol-acc-chev { transform: rotate(45deg);}
.sol-acc details[open] > summary { padding-bottom: 6px;}

/* Option D: each capability is its own card with real gaps, rather than rows
   divided by 1px of gray-100 -- #efefee on white, the faintest line in the
   palette, which is why they ran together. The open one takes the homepage
   accordion's blue rule, so there is finally something marking which is open
   besides the chevron. */
.sol-acc--cards {
  background: transparent;
  border: 0;
  border-radius: 0;
  overflow: visible;
  display: flex;
  flex-direction: column;
  gap: 12px;
}
.sol-acc--cards details,
.sol-acc--cards details:first-child {
  background: var(--white);
  border: 1px solid var(--gray-200);
  border-radius: var(--radius-md);
  overflow: hidden;
}
.sol-acc--cards details[open] { border-left: 4px solid var(--blue-500);}
.sol-acc-body { padding: 4px 24px 26px;}
.sol-acc-body ul {
  list-style: none;
  margin: 0;
  padding: 0;
  display: grid;
  grid-template-columns: repeat(2, 1fr);
  gap: 0 26px;
}
.sol-acc-body li {
  padding: 16px 0 0;
  border-top: 1px solid var(--gray-100);
  margin-top: 16px;
  font-size: 14px;
  line-height: 1.65;
  color: var(--gray-600);
}
.sol-acc-body li b { display: block; color: var(--navy-900); margin-bottom: 5px;}

/* ---------- Common questions ----------
   The FAQ on the eight solution and seven industry pages carried NO styling at
   all and was rendering on browser defaults: answers set solid, and one Q&A
   running straight into the next with nothing between them. Two things matter
   here — leading inside an answer, and separation between blocks. */
/* Full container width, so the question rows line up with the two-column
   block underneath instead of stopping 320px short of it. The answers keep
   their own measure below -- the rows span, the prose does not. */
.faq { max-width: none;}
.faq h2 { margin-bottom: 8px;}
.faq details {
  border-top: 1px solid var(--gray-200);
}
.faq details:last-of-type { border-bottom: 1px solid var(--gray-200);}
.faq summary {
  list-style: none;
  cursor: pointer;
  display: flex;
  align-items: flex-start;
  gap: 16px;
  padding: 20px 0;
  font-size: 18px;
  font-weight: 800;
  line-height: 1.45;
  color: var(--navy-900);
}
.faq summary::-webkit-details-marker { display: none;}
.faq summary::after {
  content: '+';
  flex: none;
  margin-left: auto;
  width: 26px;
  height: 26px;
  border-radius: 50%;
  background: var(--sky-100);
  color: var(--blue-600);
  display: grid;
  place-items: center;
  font-size: 15px;
  font-weight: 900;
  transition: transform 0.18s ease;
}
.faq details[open] > summary { padding-bottom: 10px;}
.faq details[open] > summary::after { transform: rotate(45deg);}

/* QA item 4.3. With every row closed, nothing said these were expandable until
   someone hovered. As the block arrives each plus turns clockwise to 45deg and
   comes straight back, staggered 70ms down the list.

   Scoped to details:not([open]) on purpose. An open row's plus already RESTS at
   45deg, so animating it would swing it away from where it belongs and back.

   45deg is also the angle that marks a row as open, chosen with that in mind:
   the cue is a preview of what opening does, and it returns to 0.

   No reduced-motion query needed -- .js-motion is only added by script.js after
   it has checked prefers-reduced-motion, so this block is already opt-in. */
@keyframes chev-hint {
  0% { transform: rotate(0);}
  50% { transform: rotate(45deg);}
  100% { transform: rotate(0);}
}
.js-motion .sol-acc.is-in details:not([open]) .sol-acc-chev,
.js-motion .faq.is-in details:not([open]) > summary::after {
  /* The block fades in over 0.55s. Firing the cue underneath that fade wastes
     it, so it waits until the plus signs are actually legible. */
  animation: chev-hint 0.46s cubic-bezier(0.4, 0, 0.2, 1) 0.42s both;
}
.js-motion .sol-acc.is-in details:nth-of-type(2) .sol-acc-chev,
.js-motion .faq.is-in details:nth-of-type(2) > summary::after { animation-delay: 490ms;}
.js-motion .sol-acc.is-in details:nth-of-type(3) .sol-acc-chev,
.js-motion .faq.is-in details:nth-of-type(3) > summary::after { animation-delay: 560ms;}
.js-motion .sol-acc.is-in details:nth-of-type(4) .sol-acc-chev,
.js-motion .faq.is-in details:nth-of-type(4) > summary::after { animation-delay: 630ms;}
/* The FAQ runs longer than four, so the stagger caps here rather than leaving a
   thirteen-question page still rippling most of a second after it arrived. */
.js-motion .faq.is-in details:nth-of-type(n+5) > summary::after { animation-delay: 700ms;}
.faq-body {
  padding: 0 42px 26px 0;
  font-size: 16px;
  line-height: 1.8;
  color: var(--gray-600);
  /* The row spans the container; the sentence should not. Measured at 1440 with
     every answer open: 68ch gave 76 characters a line, 80ch gives 81, and no cap
     at all gives 154 with the longest answer at 185, which puts most answers on
     one line spanning 1384px. 80ch is Lindsay's pick of the three. */
  max-width: 80ch;
}
@media (prefers-reduced-motion: reduce) {
  .faq summary::after { transition: none;}
}
@media (max-width: 700px) {
  .faq summary { font-size: 16.5px; padding: 17px 0; gap: 12px;}
  .faq-body { padding-right: 0; font-size: 15.5px;}
}

/* ---------- The business case that caps the platform block ----------
   Navy on a light section, so it reads as the conclusion of the block rather
   than a fifth capability. */
.sol-impact {
  position: relative;
  overflow: hidden;
  text-align: center;
  margin-top: 22px;
  padding: 28px 30px;
  border-radius: var(--radius-md);
  /* A soft corner glow rather than a hard-edged shape. The straight left edge
     of a rounded square read as a visible seam at this box height, and the
     header shape language is being reworked separately -- this does not
     presuppose it. The peak is held at 0.30: at 0.38 the gold eyebrow measured
     4.36:1 against the brightest point, under the 4.5:1 floor. */
  background:
    radial-gradient(118% 150% at 100% 0%, rgba(45, 118, 212, 0.30) 0%, rgba(45, 118, 212, 0) 62%),
    var(--navy-900);
  color: var(--sky-100);
}
.sol-impact > * { position: relative; z-index: 1;}
.sol-impact .eyebrow { color: var(--gold-500); background: rgba(225, 177, 69, 0.14);}
.sol-impact h2 { font-size: 23px; color: var(--white); margin-bottom: 9px; max-width: 26ch;
  margin-inline: auto;}
/* Same widening as the FAQ answers above, and for the same reason: use more of
   the card without letting the line run away. 74ch measured 86 characters over
   two lines; no cap put it on ONE line of 172 and collapsed the card's height.
   90ch keeps two lines and a card that still balances the section beside it. */
.sol-impact p { margin: 0 auto; font-size: 15.5px; line-height: 1.62; color: var(--sky-200);
  max-width: 90ch;}

@media (max-width: 880px) {
  .sol-acc-body ul { grid-template-columns: 1fr;}
  .sol-acc summary {
    padding: 16px 18px;
    font-size: 17px;
    /* Title over lede, with the chevron parked top-right, rather than three
       things fighting for one row. The lede used to be hidden here because it
       truncated to nothing useful; it wraps now, so it earns its place. */
    display: grid;
    grid-template-columns: minmax(0, 1fr) 26px;
    gap: 6px 12px;
}
  .sol-acc summary > span:first-child { flex: none; grid-column: 1;}
  .sol-acc-lede { grid-column: 1; font-size: 13.5px;}
  .sol-acc-chev { grid-column: 2; grid-row: 1;}
  .sol-acc-body { padding: 0 18px 20px;}
  .sol-impact { padding: 22px 20px;}
  .sol-impact h3 { font-size: 20px;}
}

/* Carousel. One component, two uses: story cards three-up, quotes one-up.
   Scroll-snap does the positioning so the responsive breakpoints only have to
   change how many slides are in view. */
.tv-carousel { position: relative; 
  /* Let the browser keep vertical panning and hand us horizontal gestures.
     Without this it claims the whole gesture for scrolling and fires
     pointercancel instead of pointerup, so a real swipe on a phone did
     nothing -- synthetic pointer events worked, which is what hid it. */
  touch-action: pan-y;
}
.tv-carousel-head {
  display: flex;
  align-items: flex-end;
  justify-content: space-between;
  gap: 20px;
  margin-bottom: 26px;
}
.tv-carousel-head .section-head { margin-bottom: 0;}
.tv-nav { display: flex; gap: 10px; flex: none;}
.tv-btn {
  width: 42px;
  height: 42px;
  border-radius: 50%;
  border: 1px solid var(--gray-200);
  background: var(--white);
  color: var(--navy-900);
  font-size: 19px;
  line-height: 1;
  cursor: pointer;
  transition: border-color 0.18s ease, background 0.18s ease, opacity 0.18s ease;
}
.tv-btn:hover:not(:disabled) { border-color: var(--blue-400); background: var(--sky-100);}
.tv-btn:disabled { opacity: 0.35; cursor: default;}
.tv-track {
  display: flex;
  gap: 22px;
  overflow-x: auto;
  scroll-snap-type: x mandatory;
  /* The animation lives here, not in the JS. The buttons assign scrollLeft
     directly: if a browser animates this it glides, and if it does not it
     jumps, but either way it lands. Passing behavior:'smooth' from script
     instead makes the arrows do nothing at all wherever that API is a no-op. */
  scroll-behavior: smooth;
  scrollbar-width: none;
  -webkit-overflow-scrolling: touch;
}
.tv-track::-webkit-scrollbar { display: none;}
.tv-track > * { scroll-snap-align: start; flex: 0 0 calc((100% - 44px) / 3);}
.tv-carousel--single .tv-track > * { flex: 0 0 100%;}
.tv-dots { display: flex; justify-content: center; gap: 8px; margin-top: 26px;}
.tv-dots button {
  width: 8px;
  height: 8px;
  padding: 0;
  border: 0;
  border-radius: 50%;
  background: var(--gray-200);
  cursor: pointer;
  transition: background 0.18s ease, transform 0.18s ease;
}
.tv-dots button.is-on { background: var(--blue-500); transform: scale(1.25);}
.section--dark .tv-btn { background: transparent; border-color: rgba(255,255,255,0.25); color: var(--white);}
.section--dark .tv-btn:hover:not(:disabled) { background: rgba(255,255,255,0.1); border-color: var(--sky-300);}
.section--dark .tv-dots button { background: rgba(255,255,255,0.25);}
.section--dark .tv-dots button.is-on { background: var(--gold-500);}

@media (max-width: 1024px) {
  .sol-steps { grid-template-columns: repeat(2, 1fr); gap: 26px;}
  .solgrid { grid-template-columns: repeat(3, 1fr); gap: 16px;}
  .tv-track > * { flex: 0 0 calc((100% - 22px) / 2);}
}
@media (max-width: 820px) {
  .sol-caps { grid-template-columns: 1fr;}
  .solgrid { grid-template-columns: 1fr;}
  .tv-carousel-head { flex-direction: column; align-items: flex-start;}
}
@media (max-width: 560px) {
  .sol-steps { grid-template-columns: 1fr;}
  .tv-track > * { flex: 0 0 100%;}
  .sol-cap { padding: 22px;}
}

/* The testimonial card was sized for a centred stage (max-width 720px). Inside
   a carousel track it is a flex item at 100% of the viewport, so the cap has
   to come off or every slide shrinks away from its snap point. */
.home-quotes .tv-track > .home-quote { max-width: none;}
.home-quotes .tv-carousel-head { align-items: center;
  justify-content: flex-start;}
.home-quotes .eyebrow { color: var(--white); background: rgba(255,255,255,0.16);}
.home-quotes .tv-btn {
  background: var(--white);
  border-color: transparent;
  color: var(--blue-600);
}
.home-quotes .tv-btn:hover:not(:disabled) { background: var(--sky-100); border-color: transparent;}
.home-quotes .tv-dots button { background: rgba(255,255,255,0.4);}
.home-quotes .tv-dots button.is-on { background: var(--white);}

/* Stat block with the figure leading, used on the solution pages where the
   number is the proof and the label reads off it. The homepage and the success
   stories keep the label-first order: their figures are benefit claims, which
   land better as the payoff. Only the spacing needs to flip with the order. */
.home-stats--lead .home-stat-num { margin-bottom: 10px;}
.home-stats--lead .home-stat h3, .home-stats--lead .home-stat .home-stat-lab { margin-bottom: 4px;}
.home-stats--lead .home-stat p { margin-bottom: 0;}

/* The hero facts sit on a photograph on the industry pages rather than the light
   ground of a solution hero, so they need the band's own palette. Gold marker
   rather than blue-600, which disappears against the scrim. */
.page-hero .sol-facts--onphoto li { color: var(--sky-100);}
.page-hero .sol-facts--onphoto li::before { background: var(--gold-500);}
.page-hero .ctarow { display: flex; flex-wrap: wrap; gap: 12px;}
/* The secondary button is drawn for a light ground. On the photo band its
   0.24-alpha border came out at 2.13:1 against the scrim and its fill at 1.5:1,
   so nothing defined the control's edge -- under the 3:1 WCAG 1.4.11 wants for a
   boundary. Raise both so the button reads as a button. */
.page-hero .btn-secondary {
  background: rgba(255, 255, 255, 0.16);
  border-color: rgba(255, 255, 255, 0.5);
  color: var(--white);
}
.page-hero .btn-secondary:hover { background: rgba(255, 255, 255, 0.26);}

/* ==========================================================================
   Industry pages: the lifecycle spine.
   The whole argument is visible at rest -- six phases of a construction job,
   with the ones TrackVia actually runs lit and the ones it does not greyed and
   attributed. No interaction required to read it.
   ========================================================================== */
.lifecycle {
  display: grid;
  grid-template-columns: repeat(6, 1fr);
  border: 1px solid var(--gray-200);
  border-radius: var(--radius-md);
  overflow: hidden;
  background: var(--white);
}
.lc-phase {
  padding: 20px 16px 22px;
  border-right: 1px solid var(--gray-200);
  background: var(--gray-50);
  display: flex;
  flex-direction: column;
  gap: 6px;
  min-width: 0;
}
.lc-phase:last-child { border-right: 0;}
.lc-num {
  font-size: 11px;
  font-weight: 800;
  letter-spacing: 0.1em;
  color: var(--gray-600);
  font-variant-numeric: tabular-nums;
}
.lc-name { font-size: 16px; font-weight: 800; color: var(--navy-900); line-height: 1.2;}
.lc-who { font-size: 13px; color: var(--gray-600); line-height: 1.45; margin-top: auto; padding-top: 6px;}

/* the phases TrackVia runs */
.lc-phase--ours { background: var(--navy-900); border-right-color: var(--navy-700);}
.lc-phase--ours .lc-num { color: var(--gold-500);}
.lc-phase--ours .lc-name { color: var(--white);}
.lc-phase--ours .lc-who { color: var(--sky-200);}
/* the one it only partly touches */
.lc-phase--part { background: var(--sky-100);}
.lc-phase--part .lc-num { color: var(--blue-600);}

.lc-key {
  display: flex;
  flex-wrap: wrap;
  gap: 10px 26px;
  margin-top: 16px;
  font-size: 13.5px;
  color: var(--gray-600);
}
.lc-key span { display: flex; align-items: center; gap: 8px;}
.lc-key i {
  width: 13px; height: 13px; border-radius: 3px; flex: none;
  border: 1px solid var(--gray-200);
}
.lc-key .k-ours { background: var(--navy-900); border-color: var(--navy-900);}
.lc-key .k-part { background: var(--sky-100);}
.lc-key .k-not { background: var(--gray-50);}

/* ---- the day on site: deliberately brief, one strip, five stops ---- */
.daystrip {
  display: grid;
  grid-template-columns: repeat(5, 1fr);
  gap: 22px;
  margin: 26px 0 0;
  padding: 0;
  list-style: none;
}
.day-stop { border-top: 3px solid var(--blue-500); padding-top: 13px; min-width: 0;}
.day-stop:last-child { border-top-color: var(--gold-700);}
.day-time {
  display: block;
  font-size: 12.5px;
  font-weight: 800;
  letter-spacing: 0.06em;
  color: var(--blue-600);
  font-variant-numeric: tabular-nums;
  margin-bottom: 5px;
}
.day-stop:last-child .day-time { color: var(--gold-700);}
.day-what { font-size: 14.5px; color: var(--gray-600); line-height: 1.5; margin: 0;}
.day-what b { color: var(--navy-900); display: block; font-size: 15px; margin-bottom: 3px;}


@media (max-width: 1024px) {
  .lifecycle { grid-template-columns: repeat(3, 1fr);}
  .lc-phase:nth-child(3) { border-right: 0;}
  .lc-phase:nth-child(-n+3) { border-bottom: 1px solid var(--gray-200);}
  .daystrip { grid-template-columns: repeat(3, 1fr); gap: 20px;}
}
@media (max-width: 620px) {
  .lifecycle { grid-template-columns: repeat(2, 1fr);}
  .lc-phase { border-right: 1px solid var(--gray-200); border-bottom: 1px solid var(--gray-200);}
  .lc-phase:nth-child(2n) { border-right: 0;}
  .lc-phase:nth-last-child(-n+2) { border-bottom: 0;}
  .daystrip { grid-template-columns: 1fr; gap: 16px;}
}


/* ================= Homepage clean-up ================= */

/* ---------- Solutions accordion: a narrow list, panel opens to the right ----
   The list holds a readable column on the left and the open panel escapes it
   rightward into space the layout was not using, so opening a row reads as the
   answer arriving rather than the page growing. Below 1080px there is no room
   for that, and it falls back to the full-width stack. */
@media (min-width: 1080px) {
  .home-solutions .section-head.center {
    text-align: left;
    margin-left: 0;
    margin-right: 0;
    max-width: 520px;
}
  /* The rows are narrow, the drawer is not. Rather than sizing the panel by a
     fixed overhang, the <details> runs the full container width and only the
     summary is held to a column -- so the drawer is simply 100% and spans the
     page whatever the viewport does. */
  .home-accordion { max-width: none; margin: 0;}
  .home-acc { background: transparent; overflow: visible; box-shadow: none;}
  .home-acc:hover { box-shadow: none;}
  .home-acc > summary {
    max-width: 460px;
    background: var(--white);
    border-radius: var(--radius-md);
    box-shadow: 0 1px 2px rgba(10, 26, 55, 0.05), 0 6px 18px -10px rgba(10, 26, 55, 0.18);
    transition: box-shadow 0.18s ease;
}
  .home-acc:hover > summary {
    box-shadow: 0 1px 2px rgba(10, 26, 55, 0.06), 0 10px 26px -12px rgba(10, 26, 55, 0.26);
}
  .home-acc[open] > summary {
    background: var(--white);
    border-radius: var(--radius-md) var(--radius-md) 0 0;
    /* No shadow on the open tab: it and the drawer below it are one card, and
       a shadow here draws a seam straight through the middle of it. */
    box-shadow: none;
}
  .home-acc[open]:hover > summary { box-shadow: none;}
  .home-acc[open] { position: relative; z-index: 2;
  /* No spread here: drop-shadow() takes x, y, blur and colour only, and a
       fourth length makes the whole declaration invalid and silently dropped.
       The box-shadow this replaces used -22px of spread, so the blur is
       pulled in to compensate. */
    filter: drop-shadow(0 2px 4px rgba(10, 26, 55, 0.06))
            drop-shadow(0 16px 28px rgba(10, 26, 55, 0.16));}
  .home-acc[open] .home-acc-body {
    position: relative;
    width: 100%;
    /* Both columns capped and the slack pushed between them. Capping the
       media with max-width instead needs an auto margin, and an auto margin on
       a grid item drops it out of stretch -- it then shrink-wraps to content,
       which collapsed the photograph to about 300px. */
    grid-template-columns: minmax(0, 400px) minmax(0, 520px);
    justify-content: space-between;
    /* Top padding carries the clearance. The device is sized off the photo at
       112% and overhangs it upward by about 19px, so against the 40px this used
       to hold there were only 15px between the phone and the tab above it and
       the two read as touching. 62px gives the phone the same clearance the
       photograph already had. Set in the shorthand rather than beside it --
       a separate padding-top above this line is simply overwritten. */
    padding: 62px 38px 44px;
    background: var(--white);
    border-radius: 0 var(--radius-md) var(--radius-md) var(--radius-md);
    /* The shadow moved to the wrapper as a filter -- see below. A box-shadow
       here only ever wrapped the drawer, so the tab above it sat flat. */
    gap: 44px;
}
  /* Copy holds the list column; the photograph is what slides out to the
     right, into the space the drawer just opened up. */
  .home-acc[open] .home-acc-copy { order: 1;}
  .home-acc[open] .home-acc-media {
    order: 2;
    /* The column is capped above; this is just the room the device overlaps
       into on the right. */
    padding-right: 84px;
}
  /* The device is a fixed 168px wide, which at this column width made it half
     again taller than the photograph it sits against and it broke out of the
     panel top and bottom. Sized off the photo instead, so it overlaps by a
     little on purpose rather than a lot by accident. */
  .home-acc[open] .home-acc-device { width: auto; height: 112%;}

  /* Actually slide, where the browser can. <details> has no animatable state
     of its own, so this only runs where ::details-content is supported and is
     an instant open everywhere else. */
  @supports selector(::details-content) {
    .home-acc::details-content {
      block-size: 0;
      overflow: hidden;
      transition: block-size .34s cubic-bezier(.2,.7,.3,1), content-visibility .34s;
      transition-behavior: allow-discrete;
}
    .home-acc[open]::details-content { block-size: auto;}
  }
}

/* ---------- Industry reel: scrim on every photo, and a hover ----------
   Three motions are defined; the strip carries one of the modifier classes.
   --rise is the default: the photo lifts a few pixels and the scrim clears.
   Swap to --zoom or --slide on .home-industry-strip to try the others. */
.home-industry-media {
  display: block;
  position: relative;
  overflow: hidden;
}
.home-industry-media .photo { border-radius: 0; display: block;}
.home-industry-media::after {
  content: '';
  position: absolute;
  inset: 0;
  /* Bottom only, rising. Written `to top` so the intent is the reading order:
     it starts at the foot of the photo and is gone by just past halfway, with
     nothing at all across the top.
     Near-neutral rather than navy-950 -- at this strength a blue-heavy scrim
     casts the whole lower half of every photograph, which read as a colour
     grade instead of a shadow. */
  background: linear-gradient(to top,
    rgba(10, 12, 16, 0.62) 0%,
    rgba(10, 12, 16, 0.34) 17%,
    rgba(10, 12, 16, 0.11) 36%,
    rgba(10, 12, 16, 0) 55%);
  transition: opacity 0.28s ease, transform 0.32s cubic-bezier(.2,.7,.3,1);
}
.home-industry:hover .home-industry-media::after,
.home-industry:focus-visible .home-industry-media::after { opacity: 0.55;}

.home-industry-strip--rise .home-industry:hover .photo,
.home-industry-strip--rise .home-industry:focus-visible .photo,
.home-industry-strip--rise .home-industry:hover .home-industry-media::after,
.home-industry-strip--rise .home-industry:focus-visible .home-industry-media::after {
  transform: translateY(-5px) scale(1.05);
}

.home-industry-strip--zoom .home-industry:hover .photo,
.home-industry-strip--zoom .home-industry:focus-visible .photo,
.home-industry-strip--zoom .home-industry:hover .home-industry-media::after,
.home-industry-strip--zoom .home-industry:focus-visible .home-industry-media::after {
  transform: scale(1.09);
}

.home-industry-strip--slide .home-industry:hover .photo,
.home-industry-strip--slide .home-industry:focus-visible .photo,
.home-industry-strip--slide .home-industry:hover .home-industry-media::after,
.home-industry-strip--slide .home-industry:focus-visible .home-industry-media::after {
  transform: translateX(-5%) scale(1.12);
}

@media (prefers-reduced-motion: reduce) {
  .home-industry .photo { transition: none;}
  .home-industry-strip--rise .home-industry:hover .photo,
  .home-industry-strip--zoom .home-industry:hover .photo,
  .home-industry-strip--slide .home-industry:hover .photo,
  .home-industry .home-industry-media::after { transform: none;}
}
/* One quiet fold inside the band so the dark area is not flat behind the
   cards, running parallel to the cut. */
.home-promo::before {
  content: '';
  position: absolute;
  left: 0; right: 0; bottom: 0;
  height: 74%;
  z-index: 0;
  background: rgba(255, 255, 255, 0.055);
  clip-path: polygon(0 46%, 100% 100%, 0 100%);
}
.home-promo > .container { position: relative; z-index: 1;}
.home-quotes .tv-carousel--ring .tv-track {
  position: relative;
  display: block;
  /* full-bleed, so the neighbours run off the screen rather than stopping at
     the container edge */
  width: 100vw;
  margin-left: calc(50% - 50vw);
  overflow: hidden;
  padding: 0;
  gap: 0;
  scroll-snap-type: none;
  -webkit-mask-image: none;
  mask-image: none;
}
.home-quotes .tv-carousel--ring .home-quote {
  position: absolute;
  top: 0;
  left: 50%;
  width: min(660px, 82vw);
  max-width: none;
  margin: 0;
  opacity: 0.34;
  transform-origin: center center;
  transition: transform 0.55s cubic-bezier(.32,.78,.35,1), opacity 0.45s ease;
  will-change: transform;
}
.home-quotes .tv-carousel--ring .home-quote.is-on { opacity: 1;}
/* a veiled card is a target: click it to bring it in */
.home-quotes .tv-carousel--ring .home-quote:not(.is-on) { cursor: pointer;}
.home-quotes .tv-carousel--ring .home-quote:not(.is-on):hover { opacity: 0.52;}

@media (max-width: 860px) {
  .home-quotes .tv-carousel--ring .home-quote {
    width: min(660px, 88vw);
    padding: 44px 30px 40px;
    min-height: 0;
}
  .home-quote::before { left: 14px;}
  .home-quote::after { right: 14px;}
}

/* ==========================================================================
   Request a demo (/connect/) — built against TrackVia_Demo_v1
   Geometry, type sizes and colours below were measured off the comp; the
   palette needed no new tokens (it is already navy / blue / gold / #ebebeb).
   ========================================================================== */

/* The hero is a blue field with the grey brand wedge clipped on top of it, so
   the blue reads in three places at once: the notch at the top right, the band
   down the right edge behind the form card, and the diagonal along the foot.
   The clip corners are the comp's, as fractions of the hero box. */
.demo-hero {
  position: relative;
  isolation: isolate;
  overflow: hidden;
  background: linear-gradient(188deg, #2d76d4 0%, #2d76d4 32%, #1c4886 100%);
  padding: 50px 0;
}
.demo-hero::before {
  content: '';
  position: absolute;
  inset: 0;
  z-index: -1;
  background: var(--gray-brand);
  /* The comp sets these against the form card, not the window: the notch bites
     in at 89% across the card, the bend sits at 54% across it, and the foot of
     the bottom diagonal lands just inside its left edge. So derive the card's
     own box here rather than pinning the diagonals to the centre line -- pinned
     to the centre, the notch slides off the card below about 1250px, where the
     container stops being its full width and starts tracking the window.
     0.5283 is the card's share of the row: 1.12 / (1 + 1.12), matching
     .demo-hero-grid; 104px is the container padding (56) plus the grid gap.
     Both of these read --container rather than a written-in number: they used
     to say 1200px and 600px, and went wrong the moment the container moved. */
  --card-w: calc((min(var(--container), 100%) - 104px) * 0.5283);
  --card-l: calc(min(50% + (var(--container) / 2), 100%) - 28px - var(--card-w));
  clip-path: polygon(
    0 0,
    calc(var(--card-l) + var(--card-w) * 0.89) 0,
    calc(var(--card-l) + var(--card-w) * 0.537) 64.5%,
    calc(var(--card-l) + var(--card-w) * 0.059) 100%,
    0 100%
  );
}
.demo-hero-grid {
  position: relative;
  display: grid;
  /* the comp gives the card the larger share — 52% of the content width */
  grid-template-columns: minmax(0, 1fr) minmax(0, 1.12fr);
  gap: 48px;
  align-items: center;
}

.demo-hero-copy h1 {
  /* The comp's 66px is set against its wider content column; held to this
     site's 1200px container that is the same headline at 55px. Balanced so it
     breaks where the comp breaks it -- after "Connect the" and "Work. Control"
     -- rather than running the first two words onto one line. */
  font-size: clamp(30px, 3.5vw, 55px);
  line-height: 1.04;
  letter-spacing: -0.025em;
  text-wrap: balance;
  color: var(--navy-900);
  margin-bottom: 30px;
}
.demo-hero-points {
  display: flex;
  flex-direction: column;
  gap: 20px;
}
.demo-hero-points li {
  display: flex;
  align-items: flex-start;
  gap: 17px;
  color: var(--navy-900);
  font-size: 17px;
  line-height: 1.4;
}
/* the comp's marker is the brand arrowhead, not a bullet or a check */
.demo-hero-points li::before {
  content: '';
  flex: 0 0 auto;
  width: 18px;
  height: 23px;
  margin-top: 1px;
  background: var(--blue-500);
  clip-path: polygon(0 0, 100% 50%, 0 100%);
}

.demo-form-card {
  background: var(--white);
  border-radius: var(--radius-md);
  padding: clamp(22px, 2.8vw, 42px);
  box-shadow: 0 18px 44px -22px rgba(10, 26, 55, 0.35);
}
/* the comp's fields are white on a white card, held by a hairline, and its
   labels are regular weight — both differ from the shared .field defaults */
.demo-form-card .field { margin-bottom: 18px;}
.demo-form-card .field label {
  font-size: 13.5px;
  font-weight: 400;
  color: var(--navy-900);
  margin-bottom: 7px;
}
.demo-form-card .field input,
.demo-form-card .field textarea {
  background: var(--white);
  border-color: var(--gray-brand);
  padding: 12px 14px;
  font-size: 14.5px;
}
.demo-form-card .field textarea {
  min-height: 46px;
  resize: vertical;
}
.demo-form-card .field-row { gap: 22px;}
.demo-form-card .req {
  color: #d7413c;
  margin-left: 1px;
}
.demo-form-actions {
  display: flex;
  justify-content: center;
  margin-top: 10px;
}
/* square-ish, not the pill the rest of the prototype uses — this is the comp's
   button shape, and it is deliberately kept local to this page for now */
.demo-form-actions .btn {
  border-radius: 5px;
  padding: 12px 28px;
}
.demo-form-card .form-note {
  text-align: center;
  margin-top: 16px;
}

/* Customer logos run off both edges in the comp, which is what a marquee looks
   like stopped. Each item carries its own right margin rather than the row
   carrying a gap, so one copy of the set is exactly 50% of the track and the
   loop has no seam. */
.demo-logos {
  padding: 42px 0;
  overflow: hidden;
  background: var(--white);
}
.demo-logo-track {
  display: flex;
  width: max-content;
  animation: demo-logo-marquee 46s linear infinite;
}
.demo-logo-set {
  display: flex;
  align-items: center;
}
.demo-logo-set > li {
  margin-right: 66px;
}
/* Same problem the homepage row had: a fixed-width cell with object-fit:
   contain sizes each mark by width, so a 10:1 wordmark came out a third the
   height of a 2.8:1 one. The track is max-content, so here the marks can just
   be given a common height and take whatever width they need. --k is the same
   optical nudge used on the homepage. */
.demo-logos .logo-mark {
  width: auto;
  height: calc(38px * var(--k, 1));
}
@keyframes demo-logo-marquee {
  to { transform: translateX(-50%); }
}
@media (prefers-reduced-motion: reduce) {
  .demo-logo-track { animation: none;}
}

@media (max-width: 980px) {
  .demo-hero { padding: 44px 0 0;}
  .demo-hero-grid {
    grid-template-columns: 1fr;
    gap: 34px;
}
  /* stacked, the wedge can only read as a foot — the card covers the rest */
  .demo-hero::before { clip-path: polygon(0 0, 100% 0, 100% 92%, 0 100%);}
  .demo-hero-copy h1 { margin-bottom: 28px;}
  .demo-form-card { margin-bottom: 58px;}
}
@media (max-width: 560px) {
  .demo-hero-points li { font-size: 16px; gap: 13px;}
  .demo-hero-points li::before { width: 15px; height: 19px;}
  .demo-form-card .field-row { grid-template-columns: 1fr; gap: 0;}
  .demo-logo-set > li { width: 124px; margin-right: 44px;}
}


/* ==========================================================================
   Closing CTA
   One block, on every page that ends by asking for the meeting. A tinted
   band with a white card on it, rather than the old navy band whose only
   bright element was a white strip carrying four words. The band matters:
   the navy version sat against the near-black footer at 1.12:1 and the two
   merged, which is what the strip was really compensating for.
   ========================================================================== */
.cta-band { background: var(--band-tint);}
.cta-card {
  background: var(--white);
  border: 1px solid var(--gray-200);
  border-radius: var(--radius-lg);
  padding: 44px 48px;
  display: flex;
  align-items: center;
  gap: 56px;
  text-align: left;
  box-shadow: var(--shadow-card);
}
.cta-card-copy { flex-grow: 1; min-width: 0; display: flex; flex-direction: column; gap: 12px;}
.cta-eyebrow {
  margin: 0;
  font-size: 13.5px;
  font-weight: 700;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  /* 4.51:1 on the card. blue-500 is the brightest blue that clears 4.5 here. */
  color: var(--blue-500);
}
.cta-card h2 {
  margin: 0;
  font-size: 36px;
  line-height: 1.15;
  letter-spacing: -0.015em;
  /* explicit, so .section--dark h2 does not turn it white on the success
     stories, where the card sits on the navy band */
  color: var(--navy-900);
}
.cta-card .cta-sub { margin: 0; font-size: 17px; line-height: 1.5; color: var(--text-body); max-width: 52ch;}
.cta-card .btn { flex-shrink: 0;}

/* Success stories keep the navy band and their stat row; only the block that
   asks for the meeting changes. .story-cta centres its children, so the card
   sets its alignment back. */
.cta-reel-head { text-align: center; margin: 0 0 34px;}
.cta-band--dark .home-stats { margin-top: 0; margin-bottom: 44px;}
/* The numerals were blue-600 on the navy band: 1.69:1 against the lighter end
   of the gradient, so the figures were all but invisible. */
.cta-band--dark .home-stat-num { color: var(--sky-300);}

@media (max-width: 860px) {
  .cta-card { flex-direction: column; align-items: flex-start; gap: 28px; padding: 32px 26px;}
  .cta-card h2 { font-size: 28px;}
}


/* ---------- Partner page ---------- */
/* The Second Front quote sits beside its paragraph rather than under it, so the
   claim and the partner backing it read as one thing. */
.partner-split {
  display: grid;
  grid-template-columns: minmax(0, 1.25fr) minmax(0, 1fr);
  gap: 44px;
  align-items: start;
}
.partner-split .prose { max-width: none;}
.partner-quote {
  max-width: none;
  text-align: left;
  border: 1px solid var(--gray-200);
  padding: 56px 34px 40px;
}
.partner-quote p { font-size: 17px; line-height: 1.55;}
/* The drawn quote marks are 82px, sized for the wide homepage card whose copy
   is centred and so never runs under them. This card is narrower and its copy
   is left aligned, which put the opening mark straight on the first word, so
   the marks scale down and the top padding clears the ink properly. */
.partner-quote::before,
.partner-quote::after { font-size: 46px;}
.partner-quote::before { top: 12px; left: 24px;}
.partner-quote::after { bottom: 0; right: 24px;}
@media (max-width: 980px) {
  .partner-split { grid-template-columns: 1fr; gap: 28px;}
}

/* Six proof points read as 3x2 rather than 4 + an orphan row of 2. */
.home-stats--six { grid-template-columns: repeat(3, 1fr); row-gap: 44px;}
/* The consequence line under a problem, set apart from the description. */
.card-impact { margin-top: 2px; font-weight: 700; color: var(--navy-700);}
/* Two columns of text, not copy beside an image: centring them vertically
   puts their headings on different lines whenever the columns differ in
   height. Scoped, because .sol-split is copy-and-image on 13 other pages. */
.sol-split--top { align-items: start;}

/* A testimonial that sits on the tinted band with no card around it, the way
   the success stories put their reel above the ask. */
.cta-testimonial { max-width: 760px; margin: 0 auto 46px; text-align: center;}
.cta-testimonial blockquote { margin: 0;}
.cta-testimonial p {
  margin: 0 0 18px;
  font-size: 21px;
  line-height: 1.5;
  font-weight: 700;
  color: var(--navy-900);
}
.cta-testimonial figcaption { display: flex; flex-direction: column; gap: 2px; font-size: 14.5px;}
.cta-testimonial figcaption strong { color: var(--navy-900);}
.cta-testimonial figcaption span { color: var(--gray-600);}

/* The homepage promo treatment as a reusable band: navy into blue, a white
   wash rising from the left, and the section itself ending on a diagonal. */
.promo-band {
  position: relative;
  overflow: hidden;
  background: linear-gradient(180deg, var(--navy-900) 0%, var(--navy-800) 55%, var(--blue-600) 100%);
  clip-path: polygon(0 0, 100% 0, 100% 100%, 0 86%);
  padding-bottom: 116px;
}
.promo-band::before {
  content: '';
  position: absolute;
  left: 0; right: 0; bottom: 0;
  height: 74%;
  z-index: 0;
  background: rgba(255, 255, 255, 0.055);
  clip-path: polygon(0 46%, 100% 100%, 0 100%);
}
.promo-band > .container { position: relative; z-index: 1;}
.promo-band .section-head h2 { color: var(--white);}
.promo-band .section-head p,
.promo-band .prose p { color: var(--sky-200);}
.promo-band .prose a { color: var(--white);}

/* Company pages borrow the solutions hero ground, which is light, so the
   lede and eyebrow that were written for the navy band need their own
   colours here. */
.page-hero--light .page-hero-lede { color: var(--text-body);}
.page-hero--light .eyebrow { color: var(--blue-600); background: var(--sky-100);}
/* The hero is a column flex container and .container carries margin: 0 auto.
   An auto margin on a flex item beats align-items: stretch, so the container
   shrank to the width of its own text and the copy floated into the middle of
   the page. The solutions heroes never showed it: their container holds a
   two-column grid, so it fills the 1200 on its own. */
.page-hero--light > .container { width: 100%;}
/* That ground was sized for a hero carrying a photograph beside the copy.
   These hold an eyebrow, a line and a lede, and 600px left most of it empty.
   Height only -- the padding is left alone, because below 768px it is what
   drops the copy clear of the artwork once that moves to the top right. */

/* Nothing separated this hero from the white section under it: the ground is
   gray-100 but you never see it, because solution-hero-bg.webp is fully
   opaque and pure white across its left side and background-size: cover
   paints it over the whole section. So the hero is white, sitting on white.
   A grey rule draws the edge, in the same grey the cards and tables use. */
.page-hero--light {
  /* The ground is drawn for a hero with a photograph beside the copy; these
     carry an eyebrow, a line and a lede, and 600px left most of it empty. */
  min-height: max(420px, 22vw);
  border-bottom: 3px solid var(--gray-200);
}
/* why-trackvia opens straight into its own content and does not want it. */
.page-hero--norule { border-bottom: 0;}

/* ---------- Glossary ---------- */
.gloss-nav {
  display: flex;
  flex-wrap: wrap;
  gap: 6px;
  margin-bottom: 40px;
  padding-bottom: 22px;
  border-bottom: 1px solid var(--gray-200);
}
.gloss-nav a {
  display: grid;
  place-items: center;
  min-width: 34px;
  height: 34px;
  padding: 0 6px;
  border-radius: var(--radius-sm);
  background: var(--gray-50);
  color: var(--blue-600);
  font-size: 14px;
  font-weight: 800;
  text-decoration: none;
}
.gloss-nav a:hover { background: var(--sky-100);}
.gloss-group { margin-bottom: 34px; scroll-margin-top: 90px;}
.gloss-group h2 {
  font-size: 15px;
  font-weight: 900;
  letter-spacing: 0.1em;
  color: var(--blue-600);
  padding-bottom: 8px;
  margin-bottom: 14px;
  border-bottom: 2px solid var(--sky-100);
}
.gloss dl { margin: 0; display: flex; flex-direction: column; gap: 16px;}
.gloss dt { font-size: 16.5px; font-weight: 800; color: var(--navy-900);}
.gloss dd {
  margin: 4px 0 0;
  font-size: 15.5px;
  line-height: 1.55;
  color: var(--text-body);
  /* a definition is a single sentence or two; keep it to a readable measure */
  max-width: 68ch;
}
.gloss dd a { font-weight: 700; white-space: nowrap;}

/* ---------- Leadership, on About ---------- */
/* The seven headshots arrive at three different proportions -- square, 4:3 and
   2:3 -- so the frame is fixed at 1:1 and the photo is contained by the frame
   rather than the other way round. object-position sits above centre because a
   centred square crop of a portrait headshot cuts the top of the head. */
/* Seven people. auto-fit packed five onto the first row and left two adrift on
   the second; four and three reads as a group rather than a leftover. Fixed
   track counts rather than auto-fit for exactly that reason -- auto-fit will
   always fill the row. */
.leader-grid {
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  gap: 28px 24px;
  margin-top: 34px;
}
@media (max-width: 900px) { .leader-grid { grid-template-columns: repeat(3, 1fr);} }
@media (max-width: 640px)  { .leader-grid { grid-template-columns: repeat(2, 1fr);} }
.leader { margin: 0;}
.leader img {
  display: block;
  width: 100%;
  /* the width/height attributes are a presentational hint with more weight
     than aspect-ratio, so without this the frame is 720px tall */
  height: auto;
  aspect-ratio: 1;
  object-fit: cover;
  object-position: 50% 22%;
  border-radius: var(--radius-md);
  background: var(--gray-100);
}
.leader figcaption { padding-top: 14px;}
.leader b { display: block; font-size: 16.5px; color: var(--navy-900);}
.leader-role {
  display: block;
  margin-top: 2px;
  font-size: 12.5px;
  font-weight: 800;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--blue-600);
}
.leader figcaption p { margin: 8px 0 0; font-size: 14.5px; color: var(--gray-600);}

/* A single line pulled out of the run of prose around it. Not a blockquote --
   it is our own sentence, not a quotation. */
.prose .callout {
  margin: 22px 0;
  padding: 2px 0 2px 20px;
  border-left: 3px solid var(--blue-600);
  font-size: 19px;
  font-weight: 700;
  line-height: 1.45;
  color: var(--navy-900);
}

/* The stat a case study card leads with on Why TrackVia. It is the reason to
   read the card, so it sits above the customer name rather than under it. */
.result-stat {
  margin: 10px 0 2px;
  font-size: 23px;
  font-weight: 900;
  line-height: 1.15;
  letter-spacing: -0.01em;
  color: var(--blue-600);
}
.result-card h3 { font-size: 16px; color: var(--gray-600); font-weight: 700;}

/* ---------- Site search (board #n25) ---------- */
.nav-search-btn {
  display: inline-flex;
  align-items: center;
  gap: 7px;
  border: 1px solid var(--gray-200);
  border-radius: 999px;
  background: var(--white);
  color: var(--gray-600);
  font: inherit;
  font-size: 14px;
  font-weight: 700;
  cursor: pointer;
  transition: border-color 0.16s ease, color 0.16s ease;
  padding: 0; width: 36px; height: 36px; justify-content: center;
}
.nav-search-btn:hover { border-color: var(--blue-400); color: var(--navy-900);}
.nav-search-btn svg {
  width: 16px; height: 16px; flex: 0 0 auto;
  fill: none; stroke: currentColor; stroke-width: 2; stroke-linecap: round;
}
/* Glyph only, at every width. The word "Search" beside it was competing with
   Log In and Request a Demo for the same corner, and a magnifier needs no
   label. The accessible name is on the button, so nothing is lost to a screen
   reader. */
.nav-search-label {
  position: absolute; width: 1px; height: 1px;
  margin: -1px; padding: 0; border: 0;
  clip-path: inset(50%); overflow: hidden; white-space: nowrap;
}

.site-search {
  position: fixed;
  inset: 0;
  z-index: 300;
  background: rgba(10, 26, 55, 0.45);
  display: flex;
  justify-content: center;
  padding: max(12vh, 64px) 16px 24px;
}
/* an author display: rule beats the UA style for [hidden] */
.site-search[hidden] { display: none;}
.search-open { overflow: hidden;}

.site-search-panel {
  width: 100%;
  max-width: 640px;
  max-height: 100%;
  display: flex;
  flex-direction: column;
  background: var(--white);
  border-radius: var(--radius-md);
  box-shadow: 0 24px 60px rgba(10, 26, 55, 0.3);
  overflow: hidden;
}
.site-search-bar {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 14px 16px;
  border-bottom: 1px solid var(--gray-200);
}
.site-search-ico {
  width: 18px; height: 18px; flex: 0 0 auto;
  fill: none; stroke: var(--gray-400); stroke-width: 2; stroke-linecap: round;
}
.site-search-bar input {
  flex: 1 1 auto;
  min-width: 0;
  border: 0;
  padding: 4px 0;
  font: inherit;
  font-size: 17px;
  color: var(--navy-900);
  background: transparent;
}
.site-search-bar input:focus { outline: none;}
.site-search-close {
  flex: 0 0 auto;
  border: 1px solid var(--gray-200);
  border-radius: var(--radius-sm);
  background: var(--gray-50);
  padding: 4px 9px;
  font: inherit;
  font-size: 11.5px;
  font-weight: 800;
  letter-spacing: 0.06em;
  color: var(--gray-500);
  cursor: pointer;
}
.site-search-status {
  margin: 0;
  padding: 10px 16px;
  font-size: 12.5px;
  font-weight: 700;
  color: var(--gray-500);
}
.site-search-results {
  list-style: none;
  margin: 0;
  padding: 0 8px 10px;
  overflow-y: auto;
  overscroll-behavior: contain;
}
.site-search-results a {
  display: block;
  padding: 11px 12px;
  border-radius: var(--radius-sm);
  text-decoration: none;
  color: inherit;
}
.site-search-results a:hover,
.site-search-results a:focus-visible { background: var(--gray-50); outline: none;}
.site-search-results a:focus-visible { box-shadow: inset 0 0 0 2px var(--blue-600);}
.sr-kind {
  display: block;
  font-size: 10.5px;
  font-weight: 800;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--blue-600);
}
.sr-title { display: block; margin-top: 2px; font-size: 15.5px; font-weight: 700; color: var(--navy-900);}
.sr-desc { display: block; margin-top: 3px; font-size: 13.5px; line-height: 1.45; color: var(--gray-600);}
.site-search-results mark { background: #fdf1d6; color: inherit; border-radius: 2px;}

/* The Business Impact card is its own band between two padded sections, so it
   needs no vertical padding of its own: 96px above from the section that ends,
   96px below from the one that begins. With .section's own 96 plus the card's
   22px margin on top of those, the card sat 214px clear of the content above
   and read as adrift rather than as that section's closing statement. */
.sol-impact-band { padding-block: 0;}
.sol-impact-band .sol-impact { margin-top: 0;}
