/* ==========================================================================
   Norvenzia — public design system
   One file, custom properties, no framework.
   Light-only, editorial register. Monochrome navy — navy is the entire
   accent system; in-body links are underlined since colour alone can't
   distinguish them (WCAG 1.4.1).
   ========================================================================== */

/* -------------------------------------------------------------- 1. Fonts */

@font-face {
  font-family: 'Montserrat';
  font-style: normal;
  font-weight: 100 900;
  font-display: swap;
  src: url('/fonts/montserrat-var-latin.woff2') format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA,
    U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193,
    U+2212, U+2215, U+FEFF, U+FFFD;
}

@font-face {
  font-family: 'Manrope';
  font-style: normal;
  font-weight: 200 800;
  font-display: swap;
  src: url('/fonts/manrope-var-latin.woff2') format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA,
    U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193,
    U+2212, U+2215, U+FEFF, U+FFFD;
}

@font-face {
  font-family: 'JetBrains Mono';
  font-style: normal;
  font-weight: 100 800;
  font-display: swap;
  src: url('/fonts/jetbrains-mono-var-latin.woff2') format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA,
    U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193,
    U+2212, U+2215, U+FEFF, U+FFFD;
}

/* ------------------------------------------------------------- 2. Tokens */

:root {
  /* Brand palette v2 — navy is the entire accent system. Cobalt and cyan are
     retired; the site is monochrome navy in the register of Linear or Stripe.
     The old accent token NAMES are kept as navy-family aliases so the ~1,700
     lines below resolve unchanged — only their values moved to navy. */
  --navy: #002d5c;
  --navy-deep: #001d3c;
  --navy-soft: #0a3a6b;      /* mid-navy, for gradient depth only */
  --white: #ffffff;
  --ground-alt: #f4f7fa;     /* alternate section background (replaces old ice) */
  --ice: #f4f7fa;            /* retired alias → ground-alt */
  --ice-warm: #eef2f7;       /* retired alias */
  --slate: #5e729a;
  --mist: #a7c0ce;
  --slate-light: #a7c0ce;    /* retired alias → mist */
  --grey: #bcbdc3;

  /* Accents folded into navy */
  --cobalt: #002d5c;         /* retired alias → navy */
  --cobalt-dark: #001d3c;    /* retired alias → navy-deep */
  --cyan: #5e729a;           /* retired alias → slate (keeps gradients monochrome) */

  /* Functional colours */
  --error: #98342e;
  --warn: #8a5a1f;
  --ok: #2e6a4f;

  /* Applied roles */
  --ink: #002d5c;            /* text is navy throughout — a deliberate monochrome */
  --ink-muted: #4a5c7e;
  --ink-faint: var(--slate);
  --display: var(--navy);
  --body: var(--ink);
  --muted: var(--slate);
  --ground: var(--white);
  --rule: var(--grey);
  --rule-soft: rgba(0, 45, 92, 0.12);
  --rule-strong: rgba(0, 45, 92, 0.22);

  /* Type */
  --font-display: 'Montserrat', system-ui, -apple-system, 'Segoe UI', sans-serif;
  --font-body: 'Manrope', system-ui, -apple-system, 'Segoe UI', sans-serif;
  --font-mono: 'JetBrains Mono', ui-monospace, 'SF Mono', Menlo, monospace;

  /* Layout */
  --shell: 1240px;
  --shell-narrow: 760px;
  --gutter: clamp(1.25rem, 4vw, 3rem);
  --section-y: clamp(4.5rem, 9vw, 8.5rem);

  /* Header height and the logo it's sized around. The mobile nav drops from
     exactly --header-h, so these must stay in step — hence the variable. */
  --header-h: 112px;
  --logo-h: 68px;

  /* Motion */
  --ease: cubic-bezier(0.22, 1, 0.36, 1);
}

/* --------------------------------------------------- 2b. Homepage redesign tokens
   New tokens for the bynar.io/terminal-industries-inspired homepage sections
   (dark hero, tabs, tiers, accordion, stats, quote block). Added alongside the
   tokens above rather than replacing them — --navy/--ink/--rule/etc. above are
   still what every other page (What We Do, Industries, How We Work, Who We Are,
   Contact, Legal, Live, Story, /admin) renders with, so this file stays a single
   shared stylesheet without reskinning pages outside this redesign's scope.

   The homepage previously ran its own typefaces (Space Grotesk/Inter) via a
   body.home override, kept separate from --font-display/--font-body above —
   removed for site-wide font consistency. Montserrat/Manrope/JetBrains Mono
   from the Tokens block now apply everywhere, the homepage included. */
:root {
  /* Backgrounds */
  --color-bg-dark: #0a0f1a;
  --color-bg-mid: #0f1b2d;
  --color-bg-light: #ffffff;
  --color-bg-paper: #f7f8fa;

  /* Text */
  --color-text-primary: #10192b;
  --color-text-secondary: #5b6472;
  --color-text-white: #ffffff;
  --color-text-dim: rgba(255, 255, 255, 0.6);

  /* Accent */
  --color-accent: #2f6fed;
  --color-accent-dark: #1e4fcb;

  /* Borders */
  --color-border: #e2e5ea;
  --color-border-dark: rgba(255, 255, 255, 0.1);

  /* Spacing */
  --section-padding: clamp(4rem, 8vw, 7rem) clamp(1.5rem, 5vw, 4rem);
  --max-width: 1120px;

  /* Radius */
  --radius-sm: 4px;
  --radius-md: 8px;
  --radius-lg: 16px;

  /* Transitions */
  --transition-fast: 160ms ease;
  --transition-base: 280ms ease;
}

/* --------------------------------------------------------- 3. Base reset */

*,
*::before,
*::after {
  box-sizing: border-box;
}

html {
  -webkit-text-size-adjust: 100%;
  scroll-behavior: smooth;
}

body {
  margin: 0;
  background: var(--ground);
  color: var(--ink);
  font-family: var(--font-body);
  font-size: 1.0625rem;
  line-height: 1.65;
  font-weight: 400;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  overflow-x: hidden;
}

img,
svg,
video {
  max-width: 100%;
  height: auto;
  display: block;
}

h1,
h2,
h3,
h4 {
  font-family: var(--font-display);
  font-weight: 800;
  letter-spacing: -0.03em;
  line-height: 1.04;
  margin: 0;
  text-wrap: balance;
}

p {
  margin: 0;
}

/* <picture> is inline by default -- with only an absolutely-positioned
   <img> inside (every hero/backdrop photo, see .hero__photo etc.), an
   unstyled <picture> still generates a line box from its own font
   metrics, leaving a few px of stray gap below the image. Block removes
   that; the img inside is still positioned by its own absolute rules. */
picture {
  display: block;
}

ul,
ol {
  margin: 0;
  padding: 0;
  list-style: none;
}

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

/* WCAG 1.4.1 — with navy the only accent, an in-body link can't be signalled
   by colour alone, so running-prose links carry an underline. Structural
   links (nav, buttons, cards, arrow-links) are distinguished by shape and
   position and set their own affordances, so they're excluded here. */
main p a,
main li a:not(.btn):not(.card):not(.card--link),
.lede a,
.legal-body a,
.faq__answer a {
  text-decoration: underline;
  text-underline-offset: 0.15em;
  text-decoration-thickness: 1px;
}

button {
  font: inherit;
  cursor: pointer;
}

:focus-visible {
  outline: 3px solid var(--cobalt);
  outline-offset: 3px;
  border-radius: 2px;
}

.skip-link {
  position: absolute;
  left: -9999px;
  top: 0;
  z-index: 200;
  background: var(--navy);
  color: #fff;
  padding: 0.85rem 1.4rem;
  font-family: var(--font-mono);
  font-size: 0.8rem;
}

.skip-link:focus {
  left: 1rem;
  top: 1rem;
}

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

/* ------------------------------------------------------- 4. Type helpers */

.display-1 {
  font-size: clamp(1.95rem, 4.8vw, 3.825rem);
  line-height: 0.98;
}

.display-2 {
  font-size: clamp(2rem, 4.2vw, 3.4rem);
  line-height: 1.03;
}

.display-3 {
  font-size: clamp(1.35rem, 2.1vw, 1.75rem);
  line-height: 1.15;
  letter-spacing: -0.02em;
}

.eyebrow {
  font-family: var(--font-mono);
  font-size: 0.72rem;
  font-weight: 600;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--cobalt);
  display: block;
  margin-bottom: 1.4rem;
}

.eyebrow--onNavy {
  color: var(--slate-light);
}

.lede {
  font-size: clamp(1.075rem, 1.45vw, 1.28rem);
  line-height: 1.6;
  color: var(--ink-muted);
  max-width: 62ch;
}

.muted {
  color: var(--ink-muted);
}

/* Small follow-up link under a hero lede -- used to cross-link the two
   "Intelligence" pages (/intelligence and /insights/sweden-trade) to each other. */
.crosslink {
  margin-top: 1.4rem;
}

/* ---------------------------------------------------------- 5. Structure */

/* Full-bleed: no max-width cap, so content spans the actual viewport rather than
   sitting in a centered column with visible gutters on wide monitors. Only the
   side padding (--gutter) keeps text off the literal edge. .shell--narrow below
   is unaffected — that one is a deliberate reading-width cap for legal copy. */
.shell {
  width: 100%;
  margin-inline: auto;
  padding-inline: var(--gutter);
}

.shell--narrow {
  max-width: var(--shell-narrow);
}

.section {
  padding-block: var(--section-y);
  position: relative;
}

.section--tight {
  padding-block: clamp(3rem, 6vw, 5rem);
}

.section--alt {
  background: var(--ground-alt);
}

.section--warm {
  background: var(--ice-warm);
}

.section--navy {
  background: var(--navy);
  color: #fff;
}

.section--navy .lede,
.section--navy .muted {
  color: rgba(255, 255, 255, 0.72);
}

.section-head {
  max-width: 58ch;
  margin-bottom: clamp(2.5rem, 5vw, 4rem);
}

.section-head p {
  margin-top: 1.4rem;
}

/* Accenture-style hairline that runs the full section width */
.rule {
  height: 1px;
  background: var(--rule);
  border: 0;
  margin: 0;
}

.grid {
  display: grid;
  gap: clamp(1.5rem, 3vw, 2.75rem);
}

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

.grid--3 {
  grid-template-columns: repeat(3, minmax(0, 1fr));
}

.grid--4 {
  grid-template-columns: repeat(4, minmax(0, 1fr));
}

@media (max-width: 960px) {
  .grid--3,
  .grid--4 {
    grid-template-columns: repeat(2, minmax(0, 1fr));
  }
}

@media (max-width: 640px) {
  .grid--2,
  .grid--3,
  .grid--4 {
    grid-template-columns: minmax(0, 1fr);
  }
}

/* ------------------------------------------------------------- 6. Header */

.header {
  position: sticky;
  top: 0;
  z-index: 100;
  background: rgba(255, 255, 255, 0.86);
  backdrop-filter: saturate(180%) blur(14px);
  -webkit-backdrop-filter: saturate(180%) blur(14px);
  border-bottom: 1px solid var(--rule);
}

.header__inner {
  display: flex;
  align-items: center;
  gap: 2rem;
  min-height: var(--header-h);
}

.header__logo img {
  height: var(--logo-h);
  width: auto;
}

.header__nav {
  margin-left: auto;
  display: flex;
  align-items: center;
  gap: clamp(1rem, 2.2vw, 2.1rem);
}

.header__link {
  font-size: 0.945rem;
  font-weight: 600;
  color: var(--ink);
  padding-block: 0.35rem;
  position: relative;
  transition: color 0.2s var(--ease);
}

.header__link::after {
  content: '';
  position: absolute;
  left: 0;
  right: 0;
  bottom: -0.15rem;
  height: 2px;
  background: var(--cobalt);
  transform: scaleX(0);
  transform-origin: left;
  transition: transform 0.28s var(--ease);
}

.header__link:hover {
  color: var(--cobalt);
}

.header__link:hover::after,
.header__link.is-current::after {
  transform: scaleX(1);
}

.header__cta {
  margin-left: 0.5rem;
}

.header__toggle {
  display: none;
  margin-left: auto;
  background: none;
  border: 1px solid var(--rule-strong);
  border-radius: 3px;
  width: 44px;
  height: 40px;
  padding: 0;
  align-items: center;
  justify-content: center;
}

.header__toggle span {
  display: block;
  width: 18px;
  height: 1.5px;
  background: var(--navy);
  position: relative;
}

.header__toggle span::before,
.header__toggle span::after {
  content: '';
  position: absolute;
  left: 0;
  width: 18px;
  height: 1.5px;
  background: var(--navy);
}

.header__toggle span::before {
  top: -6px;
}

.header__toggle span::after {
  top: 6px;
}

/* The hamburger media query (nav position, link sizing, the mobile --logo-h /
   --header-h override) lives entirely in views/layout.ejs, not here. Its
   breakpoint has to shift with whatever logo height is set in the admin panel —
   a wider logo needs a wider viewport before the nav fits on one line — and CSS
   media queries can't reference a custom property, so the breakpoint has to be
   computed server-side and the ruleset rendered with it, together, in one place. */

/* Homepage-only: starts transparent over the dark hero (white text/logo, no
   border), becomes the standard solid header once nav.js adds .nav--scrolled
   past 80px of scroll — matching what every other page looks like by default. */
.header--transparent {
  position: fixed;
  top: 0;
  left: 0;
  right: 0;
  background: transparent;
  backdrop-filter: none;
  -webkit-backdrop-filter: none;
  border-bottom: 1px solid transparent;
  transition: background-color var(--transition-base), border-color var(--transition-base),
    backdrop-filter var(--transition-base);
}

.header--transparent .header__link {
  color: var(--color-text-white);
}

.header--transparent .header__link:hover {
  color: var(--color-text-dim);
}

/* The mobile dropdown (views/layout.ejs's inline media query) always renders
   on a solid white background regardless of header transparency, so its
   links need dark text even while .header--transparent is still active
   (i.e. the menu is opened before the visitor has scrolled) — otherwise
   this is white text on white, invisible. */
.header--transparent .header__nav.is-open .header__link {
  color: var(--ink);
}

.header--transparent .header__nav.is-open .header__link:hover {
  color: var(--cobalt);
}

.header--transparent .header__toggle {
  border-color: rgba(255, 255, 255, 0.4);
}

.header--transparent .header__toggle span,
.header--transparent .header__toggle span::before,
.header--transparent .header__toggle span::after {
  background: var(--color-text-white);
}

.header--transparent.nav--scrolled {
  position: sticky;
  background: rgba(255, 255, 255, 0.86);
  backdrop-filter: saturate(180%) blur(14px);
  -webkit-backdrop-filter: saturate(180%) blur(14px);
  border-bottom: 1px solid var(--rule);
}

.header--transparent.nav--scrolled .header__link {
  color: var(--ink);
}

.header--transparent.nav--scrolled .header__link:hover {
  color: var(--cobalt);
}

.header--transparent.nav--scrolled .header__toggle {
  border-color: var(--rule-strong);
}

.header--transparent.nav--scrolled .header__toggle span,
.header--transparent.nav--scrolled .header__toggle span::before,
.header--transparent.nav--scrolled .header__toggle span::after {
  background: var(--navy);
}

/* Since .header--transparent starts position:fixed (not sticky, so it can sit
   over the hero with nothing pushing content down), the homepage needs the
   hero itself to sit under it rather than get pushed down — handled in the
   hero's own top padding, not here. */

/* ---------------------------------------------------- 7. Buttons & links */

.btn {
  display: inline-flex;
  align-items: center;
  gap: 0.6rem;
  font-family: var(--font-body);
  font-size: 0.95rem;
  font-weight: 700;
  letter-spacing: 0.01em;
  padding: 0.92rem 1.6rem;
  border-radius: 2px;
  border: 1.5px solid transparent;
  transition: background 0.22s var(--ease), color 0.22s var(--ease),
    border-color 0.22s var(--ease), transform 0.22s var(--ease);
}

.btn:hover {
  transform: translateY(-1px);
}

.btn--primary {
  background: var(--cobalt);
  color: #fff;
  border-color: var(--cobalt);
}

.btn--primary:hover {
  background: var(--cobalt-dark);
  border-color: var(--cobalt-dark);
}

.btn--ghost {
  background: transparent;
  color: var(--navy);
  border-color: var(--rule-strong);
}

.btn--ghost:hover {
  border-color: var(--navy);
  background: var(--navy);
  color: #fff;
}

.btn--onNavy {
  background: #fff;
  color: var(--navy);
  border-color: #fff;
}

.btn--onNavy:hover {
  background: var(--cobalt);
  border-color: var(--cobalt);
  color: #fff;
}

/* Compact variant for tight spaces (the War Room popup panel, section 29) --
   same modifier pattern as --primary/--ghost/--onNavy above. */
.btn--sm {
  padding: 0.5rem 0.9rem;
  font-size: 0.78rem;
}

/* The chevron text-link that carries most navigation weight in this register */
.arrow-link {
  display: inline-flex;
  align-items: center;
  gap: 0.55rem;
  font-weight: 700;
  font-size: 0.95rem;
  color: var(--cobalt);
  transition: gap 0.24s var(--ease);
}

.arrow-link::after {
  content: '';
  width: 0.55rem;
  height: 0.55rem;
  border-right: 2px solid currentColor;
  border-top: 2px solid currentColor;
  transform: rotate(45deg);
  flex: none;
}

.arrow-link:hover {
  gap: 0.95rem;
}

.arrow-link--onNavy {
  color: var(--cyan);
}

.btn-row {
  display: flex;
  flex-wrap: wrap;
  gap: 0.9rem;
  margin-top: 2.4rem;
}

/* --------------------------------------------------------------- 9. Hero */

.hero {
  padding-block: clamp(3.5rem, 7vw, 6.5rem) clamp(3rem, 6vw, 5.5rem);
  position: relative;
  overflow: hidden;
}

/* Photographic backdrop behind the home hero. Masked to fade out over the copy
   column so navy text stays legible against the white section background, and
   strongest behind the figure on the right. Hidden on small screens where the
   hero stacks and there's no room for it behind the copy — and static, so
   nothing here needs a prefers-reduced-motion guard. */
.hero__bg {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  object-position: right center;
  z-index: 0;
  pointer-events: none;
  mask-image: linear-gradient(to right, transparent 0%, transparent 30%, black 65%);
  -webkit-mask-image: linear-gradient(to right, transparent 0%, transparent 30%, black 65%);
}

/* On mobile the grid stacks to a single column and the home hero has no
   figure to reveal the image behind (figureImage is unset in content/
   home.js — the desktop view shows the artwork in the empty right-hand
   grid column, not behind a graphic), so there's no room in the text flow
   to fade it in without sitting under a paragraph. Instead: reserve a
   dedicated strip below the CTA buttons (:has keeps this scoped to the
   home hero only — inner-page heroes have no .hero__bg) and reveal the
   image only there, faded top-to-bottom, so it still reads as the same
   photographic backdrop as desktop instead of disappearing on mobile. */
@media (max-width: 900px) {
  .hero:has(.hero__bg) {
    padding-bottom: clamp(9rem, 32vw, 11.5rem);
  }

  .hero__bg {
    object-position: center;
    mask-image: linear-gradient(to bottom, transparent 0%, transparent 74%, black 92%);
    -webkit-mask-image: linear-gradient(to bottom, transparent 0%, transparent 74%, black 92%);
  }
}

/* Cursor-reactive glow. A radial-gradient layer positioned by --hero-mx/--hero-my
   (set in main.js from pointermove), under the copy and figure. Off by default —
   .has-pointer only lands on fine-pointer devices with motion allowed (see
   main.js and the reduced-motion guard at the bottom of this file), so touch,
   keyboard and reduced-motion visitors never see it move. Monochrome navy. */
.hero__spotlight {
  position: absolute;
  inset: 0;
  z-index: 0;
  pointer-events: none;
  opacity: 0;
  transition: opacity 0.5s var(--ease);
  background: radial-gradient(
    560px circle at var(--hero-mx, 50%) var(--hero-my, 38%),
    rgba(0, 45, 92, 0.10),
    rgba(94, 114, 154, 0.06) 42%,
    transparent 72%
  );
}

.hero.has-pointer .hero__spotlight {
  opacity: 1;
}

.hero__grid {
  display: grid;
  grid-template-columns: minmax(0, 1.05fr) minmax(0, 0.95fr);
  gap: clamp(2rem, 5vw, 4.5rem);
  align-items: center;
  position: relative;
  z-index: 1;
}

.hero__body {
  margin-top: 1.9rem;
}

/* Copy renders before the figure in DOM order — a visitor should read what the
   company does before seeing an abstract graphic of it. */
.hero__figure {
  position: relative;
}

@media (max-width: 900px) {
  .hero__grid {
    grid-template-columns: minmax(0, 1fr);
  }

  .hero__figure {
    order: 2;
  }
}

/* Inner pages: single column, no diagram */
.hero--inner {
  padding-block: clamp(3.5rem, 7vw, 6rem) clamp(2.5rem, 5vw, 4rem);
  border-bottom: 1px solid var(--rule);
}

/* rem, not em: em here would resolve against the wrapper's inherited body size,
   not the display heading inside it, and collapse to a narrow column. */
.hero--inner .hero__copy {
  max-width: min(100%, 46rem);
}

.hero--inner .lede {
  max-width: 58ch;
  margin-top: 1.9rem;
}

/* Topic pages share the same editorial text placement: navigation first,
   then a compact title block, then the explanatory paragraph. */
.topic-hero__inner {
  width: min(100%, 580px);
  max-width: none;
  margin-inline: auto;
  display: flex;
  flex-direction: column;
  align-items: flex-start;
}

.topic-hero__back {
  display: inline-block;
  margin-bottom: 1.5rem;
}

.topic-hero__headline {
  max-width: 15ch;
}

.topic-hero__body {
  max-width: 50ch;
  margin-top: 1.5rem;
}

/* ------------------------------------------------------------ 10. Factbar */

.factbar {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  border-top: 1px solid var(--rule);
}

.factbar__item {
  padding: clamp(2rem, 3.5vw, 3rem) clamp(1.25rem, 2.5vw, 2.4rem);
  border-right: 1px solid var(--rule);
}

.factbar__item:first-child {
  padding-left: 0;
}

.factbar__item:last-child {
  border-right: 0;
  padding-right: 0;
}

.factbar__title {
  font-family: var(--font-display);
  font-weight: 800;
  font-size: 1.16rem;
  letter-spacing: -0.02em;
  margin-bottom: 0.85rem;
  line-height: 1.2;
}

.factbar__body {
  color: var(--ink-muted);
  font-size: 0.97rem;
  line-height: 1.62;
}

@media (max-width: 860px) {
  .factbar {
    grid-template-columns: minmax(0, 1fr);
  }

  .factbar__item {
    border-right: 0;
    border-bottom: 1px solid var(--rule);
    padding-inline: 0;
  }

  .factbar__item:last-child {
    border-bottom: 0;
  }
}

/* -------------------------------------------------------------- 11. Cards */

.card {
  background: #fff;
  border: 1px solid var(--rule);
  border-radius: 3px;
  padding: clamp(1.6rem, 2.4vw, 2.2rem);
  display: flex;
  flex-direction: column;
  gap: 1rem;
  height: 100%;
}

.card__title {
  font-family: var(--font-display);
  font-weight: 800;
  font-size: 1.2rem;
  letter-spacing: -0.02em;
  line-height: 1.2;
}

.card__body {
  color: var(--ink-muted);
  font-size: 0.97rem;
}

.card--link {
  transition: transform 0.3s var(--ease), box-shadow 0.3s var(--ease),
    border-color 0.3s var(--ease);
}

.card--link:hover {
  transform: translateY(-4px);
  border-color: rgba(46, 123, 255, 0.4);
  box-shadow: 0 22px 40px -26px rgba(10, 27, 60, 0.45);
}

/* Image-topped card — the workhorse of this register */
.card--media {
  padding: 0;
  overflow: hidden;
  gap: 0;
}

.card--media .card__media {
  aspect-ratio: 16 / 10;
  overflow: hidden;
  background: var(--ice);
  position: relative;
}

.card--media .card__media img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  transition: transform 0.7s var(--ease);
}

.card--media:hover .card__media img {
  transform: scale(1.045);
}

.card--media .card__inner {
  padding: clamp(1.4rem, 2.2vw, 1.9rem);
  display: flex;
  flex-direction: column;
  gap: 0.85rem;
  flex: 1;
}

.card__icon {
  width: 44px;
  height: 44px;
  color: var(--cobalt);
}

.card__icon svg,
.card__icon img {
  width: 100%;
  height: 100%;
  object-fit: contain;
}

.pain-list {
  display: flex;
  flex-direction: column;
  gap: 0.7rem;
  margin-top: 0.2rem;
}

.pain-list li {
  position: relative;
  padding-left: 1.15rem;
  font-size: 0.925rem;
  line-height: 1.58;
  color: var(--ink-muted);
}

.pain-list li::before {
  content: '';
  position: absolute;
  left: 0;
  top: 0.62em;
  width: 5px;
  height: 1.5px;
  background: var(--cobalt);
}

/* ------------------------------------------- 11b. Industries — fit criteria
   A 2x2 grid of checklist cards instead of a plain bordered list — each
   criterion reads as a self-contained "yes, this is you" card rather than a
   row in a table. */
.fit-grid {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: clamp(1rem, 2vw, 1.5rem);
  margin-top: clamp(2rem, 4vw, 3rem);
}

.fit-card {
  display: flex;
  align-items: flex-start;
  gap: 1.1rem;
  padding: clamp(1.5rem, 2.5vw, 2rem);
  border: 1px solid var(--rule);
  border-radius: var(--radius-lg);
  background: var(--bg, #fff);
  transition: border-color 0.25s var(--ease), transform 0.25s var(--ease), box-shadow 0.25s var(--ease);
}

.fit-card:hover {
  border-color: var(--cobalt);
  transform: translateY(-3px);
  box-shadow: 0 22px 40px -28px rgba(0, 45, 92, 0.35);
}

.fit-card__check {
  flex-shrink: 0;
  width: 2.25rem;
  height: 2.25rem;
  border-radius: 50%;
  background: var(--ice);
  color: var(--cobalt);
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 1.05rem;
  font-weight: 700;
  transition: background 0.25s var(--ease), color 0.25s var(--ease);
}

.fit-card:hover .fit-card__check {
  background: var(--cobalt);
  color: #fff;
}

.fit-card__number {
  display: block;
  font-family: var(--font-mono);
  font-size: 0.7rem;
  letter-spacing: 0.12em;
  color: var(--ink-muted);
  margin-bottom: 0.35rem;
}

.fit-card__text {
  font-weight: 500;
  color: var(--ink);
  font-size: 1.02rem;
  line-height: 1.5;
}

@media (max-width: 700px) {
  .fit-grid {
    grid-template-columns: minmax(0, 1fr);
  }
}

/* ---------------------------------------------------------------- 11c. FAQ */

.faq {
  border-top: 1px solid var(--rule);
}

.faq__item {
  border-bottom: 1px solid var(--rule);
}

.faq__q {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 1.5rem;
  padding-block: clamp(1.1rem, 2.2vw, 1.5rem);
  font-family: var(--font-display);
  font-weight: 800;
  font-size: clamp(1.02rem, 1.6vw, 1.18rem);
  letter-spacing: -0.02em;
  line-height: 1.3;
  cursor: pointer;
  list-style: none;
  transition: color 0.2s var(--ease);
}

/* Safari still paints the default disclosure triangle without this. */
.faq__q::-webkit-details-marker {
  display: none;
}

.faq__q::after {
  content: '+';
  flex: none;
  font-family: var(--font-mono);
  font-weight: 400;
  font-size: 1.4rem;
  line-height: 1;
  color: var(--cobalt);
  transition: transform 0.25s var(--ease);
}

.faq__item[open] .faq__q::after {
  transform: rotate(45deg);
}

.faq__q:hover {
  color: var(--cobalt);
}

.faq__q:focus-visible {
  outline: 2px solid var(--cobalt);
  outline-offset: 3px;
}

.faq__a {
  max-width: 78ch;
  margin: 0;
  padding-bottom: clamp(1.1rem, 2.2vw, 1.5rem);
  color: var(--ink-muted);
}

/* -------------------------------------------------------- 12. Step / index */

.steps {
  display: grid;
  gap: 0;
  border-top: 1px solid var(--rule);
}

.step {
  display: grid;
  grid-template-columns: 6rem minmax(0, 1fr) minmax(0, 1.3fr);
  gap: clamp(1rem, 3vw, 2.5rem);
  padding-block: clamp(1.1rem, 2.2vw, 1.5rem);
  border-bottom: 1px solid var(--rule);
  align-items: start;
  transition: background 0.3s var(--ease);
}

.step:hover {
  background: var(--ice-warm);
}

.step__number {
  font-family: var(--font-mono);
  font-size: 0.8rem;
  font-weight: 600;
  letter-spacing: 0.1em;
  color: var(--cobalt);
  padding-top: 0.35rem;
}

.step__title {
  font-family: var(--font-display);
  font-weight: 800;
  font-size: 1.28rem;
  letter-spacing: -0.02em;
  line-height: 1.18;
}

.step__body {
  color: var(--ink-muted);
  font-size: 1rem;
}

@media (max-width: 780px) {
  .step {
    grid-template-columns: 3.5rem minmax(0, 1fr);
  }

  .step__body {
    grid-column: 2;
  }
}

/* ---------------------------------------------------- 13. Division blocks */

.division {
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
  gap: clamp(2rem, 5vw, 4.5rem);
  padding-block: clamp(2.5rem, 5vw, 4rem);
  border-top: 1px solid var(--rule);
  align-items: start;
}

.division__index {
  font-family: var(--font-mono);
  font-size: 0.78rem;
  letter-spacing: 0.14em;
  color: var(--cobalt);
  margin-bottom: 1.1rem;
  display: block;
}

.division__name {
  font-size: clamp(1.9rem, 3.4vw, 2.7rem);
  line-height: 1.05;
  display: flex;
  align-items: center;
  gap: 1rem;
  flex-wrap: wrap;
}

.division__summary {
  margin-top: 1.5rem;
  color: var(--ink-muted);
  font-size: 1.06rem;
  max-width: 46ch;
}

.capability-list {
  display: grid;
  gap: 0;
}

.capability-list li {
  padding-block: 0.85rem;
  border-bottom: 1px solid var(--rule);
  font-weight: 600;
  font-size: 1rem;
  display: flex;
  align-items: center;
  gap: 0.8rem;
}

.capability-list li::before {
  content: '';
  width: 6px;
  height: 6px;
  background: var(--cobalt);
  flex: none;
  transform: rotate(45deg);
}

.capability-list li:first-child {
  border-top: 1px solid var(--rule);
}

@media (max-width: 860px) {
  .division {
    grid-template-columns: minmax(0, 1fr);
  }
}

/* -------------------------------------------------------------- 14. Table */

.data-table {
  width: 100%;
  border-collapse: collapse;
  font-size: 0.98rem;
}

.table-scroll {
  overflow-x: auto;
  -webkit-overflow-scrolling: touch;
}

.data-table th {
  text-align: left;
  font-family: var(--font-mono);
  font-size: 0.68rem;
  font-weight: 700;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--ink-faint);
  background: var(--ice);
  padding: 1rem 1.2rem;
  border-bottom: 1px solid var(--rule);
  white-space: nowrap;
}

.data-table td {
  padding: 1.15rem 1.2rem;
  border-bottom: 1px solid var(--rule);
  vertical-align: top;
  color: var(--ink-muted);
}

.data-table td:first-child {
  font-family: var(--font-display);
  font-weight: 800;
  color: var(--ink);
  letter-spacing: -0.01em;
  white-space: nowrap;
}

.data-table tr:hover td {
  background: var(--ice-warm);
}

/* -------------------------------------------------------------- 15. Tiers */

.tier {
  background: #fff;
  border: 1px solid var(--rule);
  border-top: 3px solid var(--navy);
  border-radius: 0 0 3px 3px;
  padding: clamp(1.8rem, 2.6vw, 2.4rem);
  display: flex;
  flex-direction: column;
  gap: 1.1rem;
  height: 100%;
  transition: transform 0.3s var(--ease), box-shadow 0.3s var(--ease);
}

.tier:hover {
  transform: translateY(-4px);
  box-shadow: 0 24px 44px -30px rgba(10, 27, 60, 0.5);
}

.tier__name {
  font-family: var(--font-display);
  font-weight: 800;
  font-size: 1.55rem;
  letter-spacing: -0.025em;
}

.tier__summary {
  color: var(--ink-muted);
  font-size: 0.97rem;
}

.tier__includes {
  display: flex;
  flex-direction: column;
  gap: 0.65rem;
  margin-top: 0.3rem;
  padding-top: 1.2rem;
  border-top: 1px solid var(--rule);
}

.tier__includes li {
  padding-left: 1.5rem;
  position: relative;
  font-size: 0.94rem;
  color: var(--ink-muted);
}

.tier__includes li::before {
  content: '';
  position: absolute;
  left: 0;
  top: 0.42em;
  width: 0.42rem;
  height: 0.72rem;
  border-right: 2px solid var(--cobalt);
  border-bottom: 2px solid var(--cobalt);
  transform: rotate(45deg);
}

.tier__cta {
  margin-top: auto;
  padding-top: 1.4rem;
}

/* -------------------------------------------------------- 16. Split panel */

.split {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: clamp(1.5rem, 3vw, 2.5rem);
}

.panel {
  background: #fff;
  border: 1px solid var(--rule);
  border-radius: 3px;
  overflow: hidden;
  display: flex;
  flex-direction: column;
}

/* aspect-ratio height is 0.7x the previous 16/9 box (9 * 0.7 = 6.3), so the
   box itself is shorter while staying full width — the --ice ground still
   fills the panel edge-to-edge with no white margins. */
.panel__media {
  aspect-ratio: 16 / 6.3;
  overflow: hidden;
  background: var(--ice);
  display: flex;
  align-items: center;
  justify-content: center;
}

/* Box runs full width so the --ice ground fills the panel with no white
   margins on the sides. Source files are pre-cropped to their content
   bounding box (the vemaps.com watermark was physically cropped out), so
   object-fit: contain shows the whole flag-map with no cropping of the
   country outline, then scaled down by 0.7x within the ice box. */
.panel__media img {
  width: 100%;
  height: 100%;
  object-fit: contain;
  transform: scale(0.7);
}

.panel__inner {
  padding: clamp(1.6rem, 2.4vw, 2.2rem);
  display: flex;
  flex-direction: column;
  gap: 0.8rem;
  flex: 1;
}

.panel__label {
  font-family: var(--font-mono);
  font-size: 0.68rem;
  letter-spacing: 0.15em;
  text-transform: uppercase;
  color: var(--cobalt);
}

.panel__place {
  font-family: var(--font-display);
  font-weight: 800;
  font-size: 1.75rem;
  letter-spacing: -0.025em;
  line-height: 1.1;
}

.panel__body {
  color: var(--ink-muted);
  font-size: 0.99rem;
}

.panel--plain .panel__inner {
  padding-top: clamp(1.6rem, 2.4vw, 2.2rem);
}

/* The Model's hub/reach cards — the same rounded, lifting card chrome used
   for tier/product/fit cards elsewhere on the site, scoped so the Security
   section's intentionally plain panels (no photos, no hover) are untouched. */
.panel--model {
  border-radius: var(--radius-lg);
  transition: border-color 0.25s var(--ease), transform 0.25s var(--ease), box-shadow 0.25s var(--ease);
}

.panel--model:hover {
  border-color: var(--cobalt);
  transform: translateY(-4px);
  box-shadow: 0 22px 40px -28px rgba(0, 45, 92, 0.35);
}

.panel--model .panel__media {
  aspect-ratio: 16 / 9;
}

.panel--model .panel__media img {
  transform: scale(0.85);
  transition: transform 0.4s var(--ease);
}

.panel--model:hover .panel__media img {
  transform: scale(0.92);
}

/* The cards now sit in their own column beside the copy (see
   .model-layout below) rather than stretching full-width underneath it,
   so they no longer need their own width cap — just the tightened inner
   padding that keeps them compact. */
.split--model .panel__inner {
  padding: clamp(1.1rem, 1.6vw, 1.4rem);
}

/* Puts the model copy and the two country cards side by side instead of
   stacked — stacking left a wide dead strip beside the ~40ch-wide copy
   block at desktop widths, since the cards only ever needed the same
   width as the text column above them. */
.model-layout {
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
  gap: clamp(2rem, 4vw, 3.5rem);
  align-items: center;
}

@media (max-width: 900px) {
  .model-layout {
    grid-template-columns: minmax(0, 1fr);
  }
}

.panel--roadmap {
  background: var(--ice-warm);
  border-style: dashed;
}

.posture-list {
  display: flex;
  flex-direction: column;
  gap: 0.85rem;
  margin-top: 0.4rem;
}

.posture-list li {
  position: relative;
  padding-left: 1.7rem;
  font-size: 0.96rem;
  color: var(--ink-muted);
  line-height: 1.58;
}

.posture-list--yes li::before {
  content: '';
  position: absolute;
  left: 0;
  top: 0.36em;
  width: 0.44rem;
  height: 0.76rem;
  border-right: 2px solid var(--cobalt);
  border-bottom: 2px solid var(--cobalt);
  transform: rotate(45deg);
}

.posture-list--no li::before {
  content: '';
  position: absolute;
  left: 0;
  top: 0.72em;
  width: 0.7rem;
  height: 2px;
  background: var(--slate);
}

@media (max-width: 780px) {
  .split {
    grid-template-columns: minmax(0, 1fr);
  }
}

/* ------------------------------------------------------------- 17. People */

.people {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: clamp(1.5rem, 3vw, 2.5rem);
}

.person {
  display: grid;
  grid-template-columns: 84px minmax(0, 1fr);
  gap: 1.4rem;
  align-items: start;
  background: #fff;
  border: 1px solid var(--rule);
  border-radius: 3px;
  padding: clamp(1.5rem, 2.4vw, 2rem);
}

.person__portrait {
  width: 84px;
  height: 84px;
  border-radius: 50%;
  overflow: hidden;
  background: var(--navy);
  color: #fff;
  display: grid;
  place-items: center;
  font-family: var(--font-display);
  font-weight: 800;
  font-size: 1.55rem;
  letter-spacing: -0.02em;
  flex: none;
}

.person__portrait img {
  width: 100%;
  height: 100%;
  object-fit: cover;
}

.person__name {
  font-family: var(--font-display);
  font-weight: 800;
  font-size: 1.16rem;
  letter-spacing: -0.02em;
}

.person__role {
  font-family: var(--font-mono);
  font-size: 0.7rem;
  letter-spacing: 0.09em;
  text-transform: uppercase;
  color: var(--ink-faint);
  margin-top: 0.42rem;
  line-height: 1.5;
}

.person__quote {
  margin-top: 1rem;
  color: var(--ink-muted);
  font-size: 0.96rem;
  line-height: 1.6;
}

.person__link {
  margin-top: 1rem;
  display: inline-flex;
  font-size: 0.87rem;
  font-weight: 700;
  color: var(--cobalt);
}

.person__link:hover {
  text-decoration: underline;
}

@media (max-width: 780px) {
  .people {
    grid-template-columns: minmax(0, 1fr);
  }
}

/* ------------------------------------------------------- 18. Founder block */

.founder {
  display: grid;
  grid-template-columns: minmax(0, 0.24fr) minmax(0, 1.2fr);
  gap: clamp(2rem, 5vw, 4rem);
  align-items: start;
}

.founder__portrait {
  aspect-ratio: 4 / 5;
  background: var(--navy);
  border-radius: 3px;
  overflow: hidden;
  display: grid;
  place-items: center;
  color: #fff;
  font-family: var(--font-display);
  font-weight: 800;
  font-size: clamp(3rem, 7vw, 5rem);
  letter-spacing: -0.03em;
}

.founder__portrait img {
  width: 100%;
  height: 100%;
  object-fit: cover;
}

.founder__story p {
  font-size: 1.09rem;
  line-height: 1.68;
  color: var(--ink-muted);
  margin-bottom: 1.3rem;
}

.founder__story p:first-of-type {
  font-size: 1.22rem;
  color: var(--ink);
  font-weight: 500;
}

.founder__meta {
  margin-top: 1.8rem;
  padding-top: 1.4rem;
  border-top: 1px solid var(--rule);
}

/* The real source of the large gap: .founder__story p (margin-bottom:
   1.3rem, further up this file) has higher specificity (class+type) than
   .person__role (class only) and .founder__meta's name <p> has no class
   at all, so that 1.3rem bottom margin was winning on BOTH paragraphs
   and dominating the margin-collapse regardless of what margin-top got
   set to here -- which is why the first two rounds of shrinking
   margin-top barely moved the visible gap. Zeroing it here (specificity
   ties with .founder__story p but this rule comes later in the file, so
   it wins) is what actually closes it up. */
.founder__meta p {
  margin-bottom: 0;
}

/* Tighter than the shared .person__role/.person__link spacing (which the
   Team section's cards still use as-is) -- scoped here so this only
   affects the founder's name/role/LinkedIn stack. line-height on the role
   also tightened: at 0.7rem type, the shared 1.5 line-height was adding
   several px of leading above/below the text on top of the margin. */
.founder__meta .person__role {
  margin-top: 0.05rem;
  line-height: 1.15;
}

.founder__meta .person__link {
  margin-top: 0.2rem;
  /* 0.3x larger than the shared .person__link size (0.87rem). */
  font-size: calc(0.87rem * 1.3);
}

@media (max-width: 860px) {
  .founder {
    grid-template-columns: minmax(0, 1fr);
  }

  .founder__portrait {
    max-width: 260px;
  }
}

/* Draft / review notices — amber, deliberately visible */
.notice {
  display: flex;
  gap: 0.85rem;
  align-items: flex-start;
  background: #fff8e6;
  border: 1px solid #f0d091;
  border-left: 3px solid #d99b13;
  border-radius: 2px;
  padding: 0.95rem 1.2rem;
  font-size: 0.9rem;
  color: #6b4c07;
  margin-top: 1.6rem;
}

.notice__label {
  font-family: var(--font-mono);
  font-size: 0.66rem;
  letter-spacing: 0.13em;
  text-transform: uppercase;
  font-weight: 700;
  white-space: nowrap;
  padding-top: 0.15rem;
}

/* -------------------------------------------------------------- 19. Quote */

.pullquote {
  font-family: var(--font-display);
  font-weight: 800;
  font-size: clamp(1.7rem, 3.9vw, 3.1rem);
  line-height: 1.14;
  letter-spacing: -0.03em;
  max-width: 22ch;
}

.pullquote--wide {
  max-width: 26ch;
}

/* ----------------------------------------------------------- 20. Contact */

.contact-grid {
  display: grid;
  grid-template-columns: minmax(0, 1.15fr) minmax(0, 0.85fr);
  gap: clamp(2rem, 5vw, 4rem);
  align-items: start;
}

@media (max-width: 900px) {
  .contact-grid {
    grid-template-columns: minmax(0, 1fr);
  }
}

.field {
  margin-bottom: 1.05rem;
}

.field__label {
  display: block;
  font-family: var(--font-mono);
  font-size: 0.7rem;
  font-weight: 600;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--ink-faint);
  margin-bottom: 0.4rem;
}

.field__req {
  color: var(--cobalt);
}

.field input,
.field textarea,
.field select {
  width: 100%;
  font: inherit;
  font-size: 1rem;
  color: var(--ink);
  background: #fff;
  border: 1px solid var(--rule-strong);
  border-radius: 2px;
  padding: 0.55rem 0.8rem;
  transition: border-color 0.2s var(--ease), box-shadow 0.2s var(--ease);
}

.field input:focus,
.field textarea:focus,
.field select:focus {
  outline: none;
  border-color: var(--cobalt);
  box-shadow: 0 0 0 3px rgba(46, 123, 255, 0.16);
}

.field textarea {
  min-height: 110px;
  resize: vertical;
}

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

.field__error {
  display: block;
  margin-top: 0.5rem;
  font-size: 0.87rem;
  color: #c0392b;
  font-weight: 600;
}

/* Honeypot — off-screen rather than display:none so bots still see it */
.field--trap {
  position: absolute;
  left: -9999px;
  width: 1px;
  height: 1px;
  overflow: hidden;
}

.form-error-summary {
  background: #fdecea;
  border: 1px solid #f5c2bd;
  border-left: 3px solid #c0392b;
  border-radius: 2px;
  padding: 1rem 1.2rem;
  margin-bottom: 2rem;
  color: #86261a;
  font-size: 0.94rem;
  font-weight: 600;
}

.aside-card {
  background: var(--ice);
  border: 1px solid var(--rule);
  border-radius: 3px;
  padding: clamp(1.6rem, 2.4vw, 2.1rem);
  margin-bottom: 1.5rem;
}

.aside-card__title {
  font-family: var(--font-display);
  font-weight: 800;
  font-size: 1.12rem;
  letter-spacing: -0.02em;
  margin-bottom: 0.8rem;
}

.aside-card__body {
  color: var(--ink-muted);
  font-size: 0.95rem;
}

.aside-card dl {
  margin: 0;
}

.aside-card dt {
  font-family: var(--font-mono);
  font-size: 0.66rem;
  letter-spacing: 0.13em;
  text-transform: uppercase;
  color: var(--ink-faint);
  margin-top: 1.1rem;
}

.aside-card dt:first-child {
  margin-top: 0;
}

.aside-card dd {
  margin: 0.35rem 0 0;
  font-weight: 600;
  font-size: 0.97rem;
}

.booking-frame {
  width: 100%;
  min-height: 620px;
  border: 0;
  border-radius: 3px;
}

.success-panel {
  border: 1px solid var(--rule);
  border-top: 3px solid var(--cobalt);
  border-radius: 0 0 3px 3px;
  background: #fff;
  padding: clamp(2rem, 4vw, 3.2rem);
  max-width: 44rem;
}

/* --------------------------------------------------------------- 21. Legal */

.legal-body h2 {
  font-size: 1.32rem;
  margin-top: 3rem;
  margin-bottom: 1rem;
  letter-spacing: -0.02em;
}

.legal-body h2:first-child {
  margin-top: 0;
}

.legal-body p {
  color: var(--ink-muted);
  margin-bottom: 1.1rem;
  line-height: 1.72;
}

.legal-meta {
  font-family: var(--font-mono);
  font-size: 0.72rem;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--ink-faint);
  margin-top: 1.4rem;
}

/* ------------------------------------------------------------ 22. CTA band */

.cta-band {
  background: var(--navy);
  color: #fff;
  position: relative;
  overflow: hidden;
  padding-block: clamp(3.5rem, 7vw, 6rem);
}

.cta-band__mark {
  position: absolute;
  right: -3%;
  top: 50%;
  transform: translateY(-50%);
  width: min(38%, 420px);
  opacity: 0.06;
  pointer-events: none;
  user-select: none;
}

.cta-band__inner {
  position: relative;
  display: grid;
  grid-template-columns: minmax(0, 1.4fr) auto;
  gap: clamp(1.5rem, 4vw, 3.5rem);
  align-items: center;
}

.cta-band__title {
  font-size: clamp(1.7rem, 3.4vw, 2.7rem);
  line-height: 1.1;
}

.cta-band__body {
  margin-top: 1.1rem;
  color: rgba(255, 255, 255, 0.74);
  max-width: 52ch;
}

@media (max-width: 780px) {
  .cta-band__inner {
    grid-template-columns: minmax(0, 1fr);
  }
}

/* -------------------------------------------------------------- 23. Footer */

.footer {
  /* Abstract data-wave photo instead of flat black — a lighter gradient
     than before sits between the photo and the text, so the image reads
     more clearly while contrast still holds regardless of where the
     image's brighter bands land. */
  background: linear-gradient(180deg, rgba(10, 15, 26, 0.6) 0%, rgba(10, 15, 26, 0.78) 100%),
    var(--color-bg-dark) url('/img/photos/footer-bg.jpg') center/cover no-repeat;
  color: rgba(255, 255, 255, 0.72);
  padding-block: clamp(2.5rem, 5vw, 4rem) 1.6rem;
  font-size: 0.93rem;
}

/* Below 900px the footer's five sections stack into one tall column (see
   .footer__grid's breakpoints below) -- the box becomes portrait, roughly
   9:16 at typical phone/tablet widths, nothing like footer-bg.jpg's wide
   1920x1072 landscape source. background-size: cover was scaling that
   landscape image up ~2.5x to cover the height, which read as blur. A
   purpose-shot portrait crop swaps in instead, same as the mobile hero/
   contact photos elsewhere on the site. */
@media (max-width: 900px) {
  .footer {
    background-image: linear-gradient(180deg, rgba(10, 15, 26, 0.6) 0%, rgba(10, 15, 26, 0.78) 100%),
      url('/img/photos/footer-bg-mobile.jpg');
  }
}

.footer__grid {
  display: grid;
  grid-template-columns: minmax(0, 1.4fr) minmax(0, 1fr) minmax(0, 1fr) minmax(0, 1fr);
  /* column-gap stays wide (columns sit side by side on one row, so this never
     stacks); row-gap is deliberately much smaller -- once the grid drops to
     fewer columns and each section stacks under the last (see the
     max-width media queries below), this is the space between sections,
     and the old single `gap` value used the wide desktop column spacing
     there too, stacking into the tall gaps the mobile screenshot showed. */
  column-gap: clamp(2rem, 4vw, 3rem);
  row-gap: clamp(1.6rem, 3vw, 2.2rem);
  padding-bottom: 1.8rem;
}

.footer__logo img {
  height: 64px;
  width: auto;
  margin-bottom: 1.3rem;
}

.footer__tagline {
  font-family: var(--font-display);
  font-weight: 700;
  font-size: 1.06rem;
  color: #fff;
  letter-spacing: -0.015em;
  margin-bottom: 1rem;
}

.footer__heading {
  font-family: var(--font-mono);
  font-size: 0.66rem;
  letter-spacing: 0.15em;
  text-transform: uppercase;
  color: var(--slate-light);
  margin-bottom: 0.75rem;
}

.footer__list {
  display: flex;
  flex-direction: column;
  gap: 0.35rem;
}

.footer__list a:hover {
  color: #fff;
  text-decoration: underline;
}

.footer__bottom {
  border-top: 1px solid rgba(255, 255, 255, 0.13);
  padding-top: 1.3rem;
  display: flex;
  flex-wrap: wrap;
  gap: 1rem 2rem;
  justify-content: space-between;
  align-items: center;
  font-size: 0.85rem;
  color: rgba(255, 255, 255, 0.55);
}

.footer__cookie-btn {
  background: none;
  border: 0;
  padding: 0;
  color: inherit;
  font-size: inherit;
  text-decoration: underline;
  font-family: inherit;
}

.footer__cookie-btn:hover {
  color: #fff;
}

@media (max-width: 1100px) {
  .footer__grid {
    grid-template-columns: repeat(3, minmax(0, 1fr));
  }

  .footer__brand {
    grid-column: 1 / -1;
  }
}

@media (max-width: 820px) {
  .footer__grid {
    grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
  }
}

@media (max-width: 520px) {
  .footer__grid {
    grid-template-columns: minmax(0, 1fr);
  }
}

/* ------------------------------------------------------ 24. Cookie banner */

.cookie-banner {
  position: fixed;
  left: 0;
  right: 0;
  bottom: 0;
  z-index: 150;
  background: #fff;
  border-top: 1px solid var(--rule-strong);
  box-shadow: 0 -12px 34px -22px rgba(10, 27, 60, 0.5);
  transform: translateY(110%);
  transition: transform 0.45s var(--ease);
}

.cookie-banner.is-visible {
  transform: translateY(0);
}

.cookie-banner__inner {
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto;
  gap: 1.5rem;
  align-items: center;
  padding-block: 1.3rem;
}

.cookie-banner__text {
  font-size: 0.92rem;
  color: var(--ink-muted);
  max-width: 68ch;
}

.cookie-banner__text a {
  color: var(--cobalt);
  text-decoration: underline;
}

.cookie-banner__actions {
  display: flex;
  gap: 0.7rem;
  flex-wrap: wrap;
}

/* Reject carries the same visual weight as Accept — a GDPR requirement,
   not just an accessibility nicety. */
.cookie-banner__actions .btn {
  padding: 0.72rem 1.35rem;
  font-size: 0.9rem;
}

@media (max-width: 780px) {
  .cookie-banner__inner {
    grid-template-columns: minmax(0, 1fr);
  }
}

/* ------------------------------------------------------------ 25. Errors */

.error-page {
  padding-block: clamp(5rem, 12vw, 10rem);
  text-align: center;
}

.error-page__code {
  font-family: var(--font-mono);
  font-size: 0.8rem;
  letter-spacing: 0.2em;
  color: var(--cobalt);
  margin-bottom: 1.6rem;
}

.error-page .lede {
  margin-inline: auto;
  margin-top: 1.5rem;
}

.error-page .btn-row {
  justify-content: center;
}

/* ------------------------------------------------- 26. Motion / reveal */

/* Hidden only when JS is present to reveal it again — see /js/js-on.js. With
   JavaScript disabled the .js class is never set and content stays visible. */
.js .reveal {
  opacity: 0;
  transform: translateY(18px);
  transition: opacity 0.875s var(--ease), transform 0.875s var(--ease);
  /* --reveal-i is set per-child by main.js on grid/list containers (cards,
     columns, etc.) so siblings fade in staggered rather than all at once.
     Elements without children indexed this way just get 0 — no delay. */
  transition-delay: calc(var(--reveal-i, 0) * 88ms);
}

.js .reveal.is-visible {
  opacity: 1;
  transform: none;
}

/* Scroll parallax: main.js sets --parallax-y (in px) on scroll for any
   element carrying [data-parallax]. Desktop/tablet (min-width: 768px) and
   motion-allowed only — see main.js. */
.js [data-parallax] {
  transform: translate3d(0, var(--parallax-y, 0), 0);
  will-change: transform;
}

/* Photos need a little overscan so the parallax shift never uncovers an
   edge of their (overflow: hidden) frame. --parallax-scale defaults to 1
   (no extra scale at all) and is only set to 1.06 by main.js when
   parallax is actually active (desktop/tablet, motion allowed) — on
   mobile this rule now applies zero extra scale, so photos render at
   their normal cover-fit sharpness instead of stacking a second upscale
   on top of an already-tall portrait crop. */
.js img[data-parallax] {
  transform: scale(var(--parallax-scale, 1)) translate3d(0, var(--parallax-y, 0), 0);
}

/* Grid/list wrappers where the *wrapper* carries the reveal state but it's
   the individual children (cards/tiers/rows) that should fade in staggered
   — main.js indexes each child's --reveal-i. Unlike .reveal, the wrapper
   itself is never hidden; only its children are. */
.js .reveal-group > * {
  opacity: 0;
  transform: translateY(18px);
  transition: opacity 0.875s var(--ease), transform 0.875s var(--ease);
  transition-delay: calc(var(--reveal-i, 0) * 88ms);
}

.js .reveal-group.is-visible > * {
  opacity: 1;
  transform: none;
}

/* Directional reveals: slide in from a side, from below, or pop from a
   slight scale-down — for images specifically, so the motion reads as the
   picture arriving into place rather than the generic fade+lift every
   other element uses. Same is-visible/IntersectionObserver wiring as
   .reveal (see main.js) and the same easing curve, just a longer duration
   and a bigger travel distance so it reads as deliberate motion, not a
   twitch. */
.js .reveal-left {
  opacity: 0;
  transform: translateX(-70px);
  transition: opacity 1.125s var(--ease), transform 1.125s var(--ease);
  transition-delay: calc(var(--reveal-i, 0) * 88ms);
}

.js .reveal-left.is-visible {
  opacity: 1;
  transform: none;
}

.js .reveal-right {
  opacity: 0;
  transform: translateX(70px);
  transition: opacity 1.125s var(--ease), transform 1.125s var(--ease);
  transition-delay: calc(var(--reveal-i, 0) * 88ms);
}

.js .reveal-right.is-visible {
  opacity: 1;
  transform: none;
}

.js .reveal-up {
  opacity: 0;
  transform: translateY(70px);
  transition: opacity 1.125s var(--ease), transform 1.125s var(--ease);
  transition-delay: calc(var(--reveal-i, 0) * 88ms);
}

.js .reveal-up.is-visible {
  opacity: 1;
  transform: none;
}

.js .reveal-pop {
  opacity: 0;
  transform: scale(0.82);
  transition: opacity 0.875s var(--ease), transform 0.875s var(--ease);
}

.js .reveal-pop.is-visible {
  opacity: 1;
  transform: none;
}

/* Slower, more deliberate pop — for a single standout element (the
   contact card) rather than the general-purpose .reveal-pop. */
.js .reveal-pop-slow {
  opacity: 0;
  transform: scale(0.85);
  transition: opacity 1.6s var(--ease), transform 1.6s var(--ease);
}

.js .reveal-pop-slow.is-visible {
  opacity: 1;
  transform: none;
}

/* Grid-wrapper variant of .reveal-up, for card grids where the wrapper
   carries the reveal state and its children are what should rise (see
   .reveal-group above for the same pattern) — used by What We Do's
   product-suite "roadmap" cards. */
.js .reveal-group--up > * {
  opacity: 0;
  transform: translateY(70px);
  transition: opacity 1.125s var(--ease), transform 1.125s var(--ease);
  transition-delay: calc(var(--reveal-i, 0) * 112ms);
}

.js .reveal-group--up.is-visible > * {
  opacity: 1;
  transform: none;
}

/* Cursor parallax: each .pz group carries its own --depth (0 = fixed,
   1 = furthest travel), set inline per-group in hero-figure.ejs. --hero-px/
   --hero-py (-1..1, set in main.js) inherit down from .hero, so one
   pointermove listener drives every layer at a different rate — the control
   point (deepest) leads, the background grid barely moves. */
.hero-figure .pz {
  transition: transform 0.5s var(--ease);
  transform: translate3d(
    calc(var(--hero-px, 0) * var(--depth, 0) * 22px),
    calc(var(--hero-py, 0) * var(--depth, 0) * 22px),
    0
  );
}

/* The hero figure's own animation, paused when motion is reduced */
.hero-figure__pulse {
  animation: mx-pulse 5.625s ease-in-out infinite;
  transform-origin: center;
}

@keyframes mx-pulse {
  0%,
  100% {
    opacity: 0.35;
  }
  50% {
    opacity: 1;
  }
}

.hero-figure__dash {
  stroke-dasharray: 5 7;
  animation: mx-flow 4s linear infinite;
}

@keyframes mx-flow {
  to {
    stroke-dashoffset: -24;
  }
}

.motion-media {
  border-radius: 3px;
  overflow: hidden;
  background: var(--ice);
}

.motion-media img,
.motion-media video {
  width: 100%;
  height: 100%;
  object-fit: cover;
}

/* Full-bleed image band. Decorative: hidden from assistive tech. */
.motion-band {
  background: var(--navy);
  overflow: hidden;
  line-height: 0;
  position: relative;
}

/* Scaled slightly past the frame so the cursor parallax shift never exposes
   an edge. The translate is driven by --band-px/--band-py, set in main.js
   from pointermove; both default to 0, so with no pointer the image sits
   exactly where it did before. */
.motion-band img {
  width: 100%;
  height: clamp(200px, 26vw, 340px);
  object-fit: cover;
  object-position: center;
  transform: scale(1.06)
    translate3d(calc(var(--band-px, 0) * -14px), calc(var(--band-py, 0) * -10px), 0);
  transition: transform 0.35s var(--ease);
  will-change: transform;
}

.motion-band__wave,
.motion-band__flow,
.motion-band__glow {
  position: absolute;
  inset: 0;
  pointer-events: none;
}

/* --band-t drives both the sheen and the wave from a single 0->1 sweep,
   registered as an animatable number so calc() can place the gradients'
   centre at an exact, continuous position — no background-size/tiling
   arithmetic, so the sweep is visible for its entire on-screen travel
   instead of fading in and out at unpredictable points. -20% to 120% means
   the centre starts and ends fully off-canvas, so it enters and leaves
   cleanly at the edges rather than popping in mid-band. */
@property --band-t {
  syntax: '<number>';
  inherits: false;
  initial-value: 0;
}

@keyframes band-sweep {
  from { --band-t: 0; }
  to { --band-t: 1; }
}

/* Pixel-displacement wave. A duplicate of the band image, revealed only
   through a narrow window that sweeps left to right in step with the sheen.
   The duplicate uses the exact same scale as the base image (only main.js's
   cursor offset plus one small constant shift), so the two stay a fixed
   distance apart at every point in the frame — a uniform ripple, not a
   mismatch that grows toward the edges. Same image, so no colour is
   introduced. */
.motion-band__wave {
  overflow: hidden;
  line-height: 0;
  mask-image: linear-gradient(
    100deg,
    transparent calc(-20% + var(--band-t) * 140% - 16%),
    rgba(0, 0, 0, 0.5) calc(-20% + var(--band-t) * 140% - 8%),
    #000 calc(-20% + var(--band-t) * 140%),
    rgba(0, 0, 0, 0.5) calc(-20% + var(--band-t) * 140% + 8%),
    transparent calc(-20% + var(--band-t) * 140% + 16%)
  );
  -webkit-mask-image: linear-gradient(
    100deg,
    transparent calc(-20% + var(--band-t) * 140% - 16%),
    rgba(0, 0, 0, 0.5) calc(-20% + var(--band-t) * 140% - 8%),
    #000 calc(-20% + var(--band-t) * 140%),
    rgba(0, 0, 0, 0.5) calc(-20% + var(--band-t) * 140% + 8%),
    transparent calc(-20% + var(--band-t) * 140% + 16%)
  );
  animation: band-sweep 11.25s linear infinite;
}

.motion-band__wave img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  object-position: center;
  transform: scale(1.06)
    translate3d(calc(var(--band-px, 0) * -14px + 9px), calc(var(--band-py, 0) * -10px + 4px), 0);
  transition: transform 0.35s var(--ease);
  will-change: transform;
}

/* Live-data sheen: a narrow translucent-white band sweeping left to right in
   the same 9s loop as the wave, reading as throughput moving through the
   tunnel. Additive white only — it lightens, never recolours, so the
   artwork's palette is intact. */
.motion-band__flow {
  background: linear-gradient(
    100deg,
    transparent calc(-20% + var(--band-t) * 140% - 11%),
    rgba(255, 255, 255, 0.05) calc(-20% + var(--band-t) * 140% - 5%),
    rgba(255, 255, 255, 0.13) calc(-20% + var(--band-t) * 140%),
    rgba(255, 255, 255, 0.05) calc(-20% + var(--band-t) * 140% + 5%),
    transparent calc(-20% + var(--band-t) * 140% + 11%)
  );
  animation: band-sweep 11.25s linear infinite;
}

/* Cursor-follow glow, same idea as the hero spotlight. Fades in only once
   main.js confirms a fine pointer with motion allowed. */
.motion-band__glow {
  opacity: 0;
  transition: opacity 0.5s var(--ease);
  background: radial-gradient(
    420px circle at var(--band-mx, 50%) var(--band-my, 50%),
    rgba(255, 255, 255, 0.10),
    rgba(255, 255, 255, 0.04) 45%,
    transparent 72%
  );
}

.motion-band.has-pointer .motion-band__glow {
  opacity: 1;
}

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

  .js .reveal,
  .js .reveal-group > *,
  .js .reveal-left,
  .js .reveal-right,
  .js .reveal-up,
  .js .reveal-pop,
  .js .reveal-pop-slow,
  .js .reveal-group--up > * {
    opacity: 1;
    transform: none;
    transition: none;
    transition-delay: 0s;
  }

  .js [data-parallax] {
    transform: none;
  }

  .who-serve__accent {
    animation: none;
  }

  .hero-figure__pulse,
  .hero-figure__dash {
    animation: none;
  }

  .hero__spotlight {
    display: none;
  }

  .motion-band__wave,
  .motion-band__flow,
  .motion-band__glow {
    display: none;
  }

  .motion-band img {
    transform: scale(1.06);
    transition: none;
  }

  .hero-figure .pz {
    transition: none;
    transform: none;
  }

  .btn:hover,
  .card--link:hover,
  .tier:hover,
  .fit-card:hover {
    transform: none;
  }

  .card--media:hover .card__media img {
    transform: none;
  }

  *,
  *::before,
  *::after {
    transition-duration: 0.01ms !important;
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
  }
}

/* ------------------------------------------------------------- 27. Print */

@media print {
  .header,
  .cookie-banner,
  .cta-band,
  .footer {
    display: none;
  }

  body {
    color: #000;
  }
}

/* ------------------------------------------------------- 28. Live monitor */

/* Severity behaves like a threat/status level (project44-style ops
   dashboard reference), not a generic magnitude -- a status-style
   green -> amber -> red ramp, distinct from the blue sequential ramp used
   for other magnitude data elsewhere on the site. Never the only channel:
   every marker also carries a numeric label and a text tooltip. */
:root {
  --severity-1: #22c55e;
  --severity-2: #84cc16;
  --severity-3: #fbbf24;
  --severity-4: #f97316;
  --severity-5: #ef4444;
}

.live-unavailable {
  background: var(--ground-alt);
  border: 1px solid var(--rule);
  border-radius: 3px;
  padding: clamp(2rem, 5vw, 3.5rem);
  text-align: center;
}

.world-map {
  position: relative;
  width: 100%;
  aspect-ratio: 960 / 480;
  min-height: 320px;
  /* A section of the page, not the whole screen -- aspect-ratio alone
     would make this taller than the viewport on wide desktop windows. */
  max-height: 60vh;
  border-radius: 6px;
  /* CARTO's dark-matter basemap paints its own background layer once
     loaded; this is only the fallback color shown for the instant before
     the style/tiles finish their first fetch. */
  background: #0e0e0e;
  /* Not overflow:hidden: the War Room investigate section (section 29) can
     make .world-map__popup taller than the map box itself, especially near
     the bottom edge -- clipping it there would silently hide the results
     panel. The canvas gets its own border-radius immediately below so the
     map still reads as rounded; only children that intentionally extend
     past the box (popups) are now allowed to. */
  overflow: visible;
  margin-bottom: 1rem;
}

.world-map .maplibregl-map {
  width: 100%;
  height: 100%;
  font-family: var(--font-body);
}

/* MapLibre adds its own "maplibregl-map" class to this same element at
   init and maplibre-gl.css sets `.maplibregl-map { overflow: hidden }` --
   same specificity as .world-map's own rule above, loaded after it in the
   document, so it would otherwise win and re-clip popups. Two classes on
   one selector beats one class regardless of source order. */
.world-map.maplibregl-map {
  overflow: visible;
}

.world-map .maplibregl-canvas {
  border-radius: 6px;
}

/* Top-left (not bottom-left): MapLibre's own AttributionControl now lives
   at bottom-left, required for the CARTO basemap. */
.world-map__updated {
  position: absolute;
  left: 0.75rem;
  top: 0.75rem;
  margin: 0;
  color: rgba(255, 255, 255, 0.6);
  font-size: 0.7rem;
  font-family: var(--font-mono);
  z-index: 1;
}

/* Distinct shape (arrow) and hue (cyan) from the disruption markers
   (colored circles) -- two different entities never share one visual
   channel. MapLibre's own Marker.setRotation() rotates the whole marker
   wrapper (map-aligned), so no manual heading CSS var is needed here. */
.world-map__vessel {
  width: 24px;
  height: 24px;
  padding: 0;
  border: none;
  background: transparent;
  cursor: pointer;
  display: flex;
  align-items: center;
  justify-content: center;
}

.world-map__vessel::before {
  content: '';
  width: 9px;
  height: 9px;
  background: var(--cyan);
  clip-path: polygon(50% 0%, 100% 100%, 50% 78%, 0% 100%);
  filter: drop-shadow(0 0 3px var(--cyan));
}

.world-map__vessel:hover::before,
.world-map__vessel:focus-visible::before {
  filter: drop-shadow(0 0 5px #fff);
}

.world-map__legend-vessel {
  width: 0;
  height: 0;
  border-left: 5px solid transparent;
  border-right: 5px solid transparent;
  border-bottom: 9px solid var(--cyan);
}

.world-map__tooltip {
  position: absolute;
  translate: -50% calc(-100% - 10px);
  max-width: 260px;
  background: #fff;
  color: var(--ink);
  border-radius: 3px;
  padding: 0.75rem 0.9rem;
  box-shadow: 0 16px 32px -16px rgba(6, 15, 38, 0.5);
  pointer-events: none;
  z-index: 2;
}

.world-map__tooltip-severity {
  display: block;
  font-size: 0.9rem;
}

.world-map__tooltip-meta {
  font-size: 0.75rem;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  color: var(--ink-faint);
  margin-top: 0.15rem;
}

.world-map__tooltip-summary {
  margin: 0.4rem 0 0;
  font-size: 0.85rem;
  color: var(--ink-muted);
}

/* Click popup for the native MapLibre circle event layer (events-dots).
   maplibre-gl.css supplies the tip/close-button/positioning mechanics for
   .maplibregl-popup; this just re-skins the content box to match the
   site's dark surfaces instead of MapLibre's white default. */
.world-map__popup .maplibregl-popup-content {
  background: var(--navy-deep, #0a1628);
  color: #fff;
  border-radius: 4px;
  padding: 0.75rem 0.9rem;
  box-shadow: 0 16px 32px -16px rgba(6, 15, 38, 0.6);
}

.world-map__popup .maplibregl-popup-tip {
  border-top-color: var(--navy-deep, #0a1628);
  border-bottom-color: var(--navy-deep, #0a1628);
}

.world-map__popup .maplibregl-popup-close-button {
  color: rgba(255, 255, 255, 0.7);
}

.world-map__popup-severity {
  display: block;
  font-size: 0.9rem;
}

.world-map__popup-meta {
  font-size: 0.75rem;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  color: rgba(255, 255, 255, 0.55);
  margin-top: 0.15rem;
}

.world-map__popup-summary {
  margin: 0.4rem 0 0;
  font-size: 0.85rem;
  color: rgba(255, 255, 255, 0.85);
}

.world-map__popup-source {
  display: inline-block;
  margin-top: 0.4rem;
  font-size: 0.78rem;
  color: var(--cyan);
}

.world-map__legend {
  position: absolute;
  right: 0.75rem;
  bottom: 0.75rem;
  display: flex;
  align-items: center;
  gap: 0.9rem;
  background: rgba(6, 15, 38, 0.6);
  backdrop-filter: blur(4px);
  border: 1px solid rgba(255, 255, 255, 0.08);
  border-radius: 4px;
  padding: 0.4rem 0.7rem;
}

.world-map__legend-group {
  display: flex;
  align-items: center;
  gap: 0.35rem;
}

.world-map__legend-label {
  color: #fff;
  font-size: 0.7rem;
  text-transform: uppercase;
  letter-spacing: 0.05em;
  margin-right: 0.15rem;
}

.world-map__legend-swatch {
  width: 1.1rem;
  height: 1.1rem;
  border-radius: 50%;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  font-size: 0.65rem;
  font-family: var(--font-mono);
}

.world-map__legend-swatch--1 { background: var(--severity-1); color: #fff; }
.world-map__legend-swatch--2 { background: var(--severity-2); color: #fff; }
.world-map__legend-swatch--3 { background: var(--severity-3); color: #fff; }
.world-map__legend-swatch--4 { background: var(--severity-4); color: var(--navy); }
.world-map__legend-swatch--5 { background: var(--severity-5); color: var(--navy); }

.world-map__legend-line {
  width: 1.1rem;
  height: 0;
  border-top: 1px solid #5b7699;
}

.world-map__caption {
  font-size: 0.8rem;
  color: var(--ink-faint);
  margin: 0 0 clamp(2rem, 4vw, 3rem);
}

/* ---- Map-level filter chips (views/live.ejs / public/js/live.js) ----
   Deliberately plain/light like .live-feed__search/.live-feed__sort below
   (this sits in the page's light chrome, not inside the dark map canvas)
   -- a distinct control surface from that feed search/sort so the two
   "filter the map" vs "filter the list" affordances never read as one
   control. */
.live-map-filters {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.6rem 1.5rem;
  margin: 0 0 1rem;
}

.live-map-filters__intro {
  font-family: var(--font-mono);
  font-size: 0.68rem;
  text-transform: uppercase;
  letter-spacing: 0.08em;
  color: var(--ink-faint);
}

.live-map-filters__group {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.4rem;
}

.live-map-filters__label {
  font-family: var(--font-mono);
  font-size: 0.68rem;
  text-transform: uppercase;
  letter-spacing: 0.06em;
  color: var(--ink-faint);
  margin-right: 0.1rem;
}

.live-map-filters__chip {
  font: inherit;
  font-size: 0.78rem;
  text-transform: capitalize;
  padding: 0.3rem 0.75rem;
  border-radius: 999px;
  border: 1px solid var(--rule-strong);
  background: #fff;
  color: var(--ink-muted);
  cursor: pointer;
  transition: border-color 0.15s var(--ease), background-color 0.15s var(--ease), color 0.15s var(--ease);
}

.live-map-filters__chip:hover {
  border-color: var(--navy);
  color: var(--navy);
}

.live-map-filters__chip:focus-visible {
  outline: 2px solid var(--navy);
  outline-offset: 2px;
}

.live-map-filters__chip[aria-pressed="true"] {
  background: var(--navy);
  border-color: var(--navy);
  color: #fff;
}

/* Severity chips borrow the same ramp as the map legend/feed badges once
   active, so "I filtered to severity 4" reads with the same color meaning
   used everywhere else severity appears on this page. Category has no
   established color channel here, so those chips stay plain navy. */
.live-map-filters__chip--severity[data-filter-value="1"][aria-pressed="true"] { background: var(--severity-1); border-color: var(--severity-1); color: #fff; }
.live-map-filters__chip--severity[data-filter-value="2"][aria-pressed="true"] { background: var(--severity-2); border-color: var(--severity-2); color: #fff; }
.live-map-filters__chip--severity[data-filter-value="3"][aria-pressed="true"] { background: var(--severity-3); border-color: var(--severity-3); color: #fff; }
.live-map-filters__chip--severity[data-filter-value="4"][aria-pressed="true"] { background: var(--severity-4); border-color: var(--severity-4); color: var(--navy); }
.live-map-filters__chip--severity[data-filter-value="5"][aria-pressed="true"] { background: var(--severity-5); border-color: var(--severity-5); color: var(--navy); }

/* display:flex here would otherwise beat the [hidden] attribute's own
   UA-stylesheet display:none, since same-specificity author rules win over
   user-agent defaults regardless of source order -- this restates it
   explicitly so toggling the `hidden` property in live.js actually hides
   the row. */
.live-map-filters__status-row[hidden] {
  display: none;
}

.live-map-filters__status-row {
  display: flex;
  align-items: center;
  gap: 0.6rem;
  font-size: 0.8rem;
  color: var(--ink-faint);
  flex-basis: 100%;
}

.live-map-filters__clear {
  font: inherit;
  font-size: 0.78rem;
  color: var(--navy);
  text-decoration: underline;
  background: none;
  border: none;
  padding: 0;
  cursor: pointer;
}

.live-map-filters__clear:hover,
.live-map-filters__clear:focus-visible {
  color: var(--navy-deep);
}

.live-feed__header {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: 1rem;
}

.live-feed__controls {
  display: flex;
  flex-wrap: wrap;
  gap: 0.6rem;
}

.live-feed__search,
.live-feed__sort {
  font: inherit;
  font-size: 0.9rem;
  color: var(--ink);
  background: #fff;
  border: 1px solid var(--rule-strong);
  border-radius: 2px;
  padding: 0.55rem 0.8rem;
  transition: border-color 0.2s var(--ease), box-shadow 0.2s var(--ease);
}

.live-feed__search {
  min-width: 15rem;
}

.live-feed__search:focus,
.live-feed__sort:focus {
  outline: none;
  border-color: var(--cobalt);
  box-shadow: 0 0 0 3px rgba(46, 123, 255, 0.16);
}

.live-feed__count {
  margin: 0.9rem 0 0;
  font-size: 0.8rem;
  color: var(--ink-faint);
}

/* Capped height + internal scroll: with a live feed that can carry
   hundreds of events, an uncapped list would push everything below it
   (the War Room explainer moved above this section specifically to avoid
   that, but the feed itself still needs its own bound) an unbounded
   distance down the page. */
.live-feed__list {
  list-style: none;
  display: grid;
  gap: 1rem;
  margin-top: 1rem;
  max-height: 640px;
  overflow-y: auto;
  padding-right: 0.4rem;
}

/* role="button"/tabindex on the <li> itself (see live.ejs / live.js
   renderFeed): clicking or Enter/Space-ing an item flies the map to it and
   opens its popup, so this needs visible interactive affordance. */
.live-feed__item {
  display: grid;
  grid-template-columns: auto auto 1fr;
  align-items: baseline;
  gap: 0.5rem 0.75rem;
  padding: 1rem 1.25rem;
  border: 1px solid var(--rule);
  border-radius: 3px;
  background: #fff;
  cursor: pointer;
  transition: border-color 0.15s var(--ease), box-shadow 0.15s var(--ease);
}

.live-feed__item:hover,
.live-feed__item:focus-visible {
  border-color: var(--cyan);
  box-shadow: 0 0 0 1px var(--cyan);
  outline: none;
}

.live-feed__severity {
  font-family: var(--font-mono);
  font-weight: 700;
  width: 1.75rem;
  height: 1.75rem;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  border-radius: 50%;
  color: #fff;
  font-size: 0.85rem;
}

.live-feed__severity--1 { background: var(--severity-1); }
.live-feed__severity--2 { background: var(--severity-2); }
.live-feed__severity--3 { background: var(--severity-3); }
.live-feed__severity--4 { background: var(--severity-4); color: var(--navy); }
.live-feed__severity--5 { background: var(--severity-5); color: var(--navy); }

.live-feed__category {
  text-transform: uppercase;
  font-size: 0.75rem;
  letter-spacing: 0.06em;
  color: var(--ink-faint);
}

.live-feed__location {
  font-weight: 600;
  grid-column: 3;
}

.live-feed__summary {
  grid-column: 1 / -1;
  color: var(--ink-muted);
  margin: 0;
}

.live-feed__source {
  grid-column: 1 / -1;
  justify-self: start;
  font-size: 0.85rem;
}

/* Real event date + "reported late" staleness badge (public/js/live.js's
   formatEventDate()/isStaleEvent()) -- spans the full item width like
   .live-feed__summary/-source above, placed right after location/before
   the summary paragraph. */
.live-feed__occurred {
  grid-column: 1 / -1;
  font-size: 0.78rem;
  color: var(--ink-faint);
}

.live-feed__stale-badge {
  display: inline-block;
  margin-left: 0.35rem;
  padding: 0.05rem 0.4rem;
  border-radius: 2px;
  font-size: 0.68rem;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  /* Tint of --severity-4, not a new color -- "reported late" reads as a
     caution flag, not a fifth ramp step. */
  background: rgba(249, 115, 22, 0.14);
  color: #b45309;
}

/* Same popup-side treatment, on the dark .world-map__popup surface. */
.world-map__popup-occurred {
  margin-top: 0.3rem;
  font-size: 0.75rem;
  color: rgba(255, 255, 255, 0.6);
}

.world-map__popup-stale {
  display: inline-block;
  margin-left: 0.3rem;
  padding: 0.05rem 0.4rem;
  border-radius: 2px;
  font-size: 0.65rem;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  background: rgba(249, 115, 22, 0.25);
  color: #fdba74;
}

/* -- at-a-glance stats strip (category + severity counts), computed
   entirely client-side from the same `events` array already in memory
   (public/js/live.js's renderStatsStrip()) -- no new network call. Sits on
   the page's normal light background like the feed section below it, not
   the dark map/popup surfaces above. */
.live-stats {
  display: flex;
  flex-wrap: wrap;
  gap: clamp(2rem, 5vw, 4rem);
}

.live-stats__empty {
  color: var(--ink-faint);
  margin: 0;
}

.live-stats__group {
  flex: 1 1 260px;
  min-width: 0;
}

.live-stats__heading {
  margin: 0 0 0.6rem;
  font-family: var(--font-mono);
  font-size: 0.72rem;
  text-transform: uppercase;
  letter-spacing: 0.06em;
  color: var(--ink-faint);
}

.live-stats__categories {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  flex-wrap: wrap;
  gap: 0 0.5rem;
  font-size: 0.9rem;
  color: var(--ink);
}

/* Decorative separator only -- each <li> already carries its own full
   "count category" text, so a screen reader loses nothing without this. */
.live-stats__categories li:not(:last-child)::after {
  content: '\00b7';
  margin-left: 0.5rem;
  color: var(--ink-faint);
}

.live-stats__severity {
  list-style: none;
  margin: 0;
  padding: 0;
  display: grid;
  gap: 0.4rem;
}

.live-stats__severity-row {
  display: grid;
  grid-template-columns: 0.9rem 6rem 1fr;
  align-items: center;
  gap: 0.5rem;
}

.live-stats__severity-swatch {
  width: 0.9rem;
  height: 0.9rem;
  border-radius: 50%;
}

.live-stats__severity-track {
  display: block;
  height: 0.45rem;
  border-radius: 2px;
  background: var(--rule-soft);
  overflow: hidden;
}

.live-stats__severity-fill {
  display: block;
  height: 100%;
  border-radius: 2px;
}

.live-stats__severity-text {
  font-size: 0.85rem;
  color: var(--ink-muted);
}

/* ---------------------------------------------------------- 29. War room */

/* "Investigate" section appended to the disruption popup
   (public/js/live-warroom.js). It lives inside the same dark
   .world-map__popup surface as the rest of the popup (section 28 above),
   so it inherits that dark-on-navy treatment rather than introducing a
   second visual language inside one popup. Gating states (locked teaser,
   unlock form, progress, results) are separate blocks below in that order.
   See docs/internal/WARROOM_BUILD_PLAN.md §10 for the access model. */

.warroom-panel {
  margin-top: 0.65rem;
  padding-top: 0.65rem;
  border-top: 1px solid rgba(255, 255, 255, 0.12);
}

/* -- locked teaser + request-access/demo CTAs -------------------------- */

.warroom-teaser__title {
  display: block;
  font-size: 0.85rem;
  color: #fff;
}

.warroom-teaser__body {
  margin: 0.35rem 0 0.7rem;
  font-size: 0.8rem;
  line-height: 1.5;
  color: rgba(255, 255, 255, 0.75);
}

.warroom-teaser__actions {
  display: flex;
  flex-wrap: wrap;
  gap: 0.5rem;
}

/* -- "already have a code?" inline unlock form --------------------------- */

.warroom-unlock {
  margin-top: 0.6rem;
}

.warroom-unlock__toggle {
  background: none;
  border: none;
  padding: 0;
  font: inherit;
  font-size: 0.76rem;
  color: var(--cyan);
  text-decoration: underline;
  cursor: pointer;
}

.warroom-unlock-form {
  display: flex;
  flex-wrap: wrap;
  gap: 0.5rem;
  margin-top: 0.6rem;
}

/* The class rule above sets display:flex at the same specificity as the
   browser's default `[hidden] { display: none }` -- without this, setting
   the `hidden` attribute in live-warroom.js would stop actually hiding
   the form once this stylesheet loads. */
.warroom-unlock-form[hidden] {
  display: none;
}

.warroom-unlock-form__input {
  flex: 1 1 120px;
  min-width: 0;
  font: inherit;
  font-size: 0.85rem;
  padding: 0.5rem 0.7rem;
  border-radius: 2px;
  border: 1px solid rgba(255, 255, 255, 0.25);
  background: rgba(255, 255, 255, 0.06);
  color: #fff;
}

.warroom-unlock-form__input::placeholder {
  color: rgba(255, 255, 255, 0.45);
}

.warroom-unlock-form__input:focus {
  outline: none;
  border-color: var(--cyan);
}

.warroom-unlock-form__error {
  flex-basis: 100%;
  margin: 0.2rem 0 0;
  font-size: 0.75rem;
  color: #ff9d9d;
}

/* -- unlocked idle / in-flight states ------------------------------------ */

.warroom-investigate-btn {
  width: 100%;
  justify-content: center;
}

.warroom-progress {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 0.3rem;
}

.warroom-spinner {
  width: 1rem;
  height: 1rem;
  border: 2px solid rgba(255, 255, 255, 0.25);
  border-top-color: var(--cyan);
  border-radius: 50%;
  animation: warroom-spin 0.8s linear infinite;
}

@keyframes warroom-spin {
  to {
    transform: rotate(360deg);
  }
}

.warroom-progress__text {
  margin: 0;
  font-size: 0.85rem;
  color: #fff;
}

.warroom-progress__note {
  margin: 0;
  font-size: 0.75rem;
  color: rgba(255, 255, 255, 0.6);
}

/* -- unavailable / rate-limited / failed messages ------------------------ */

.warroom-message p {
  margin: 0;
  font-size: 0.82rem;
  color: rgba(255, 255, 255, 0.8);
}

.warroom-message--error p {
  color: #ff9d9d;
}

/* -- completed results panel: summary, tables, actions, citations ------- */

.warroom-results {
  max-height: 340px;
  overflow-y: auto;
  padding-right: 0.3rem;
}

.warroom-results__capped {
  margin: 0 0 0.5rem;
  padding: 0.5rem 0.65rem;
  border-radius: 2px;
  background: rgba(251, 191, 36, 0.15);
  color: #fde68a;
  font-size: 0.75rem;
}

.warroom-results__summary {
  margin: 0 0 0.75rem;
  font-size: 0.85rem;
  line-height: 1.5;
  color: rgba(255, 255, 255, 0.92);
}

/* -- impact badge: at-a-glance signal above the summary -------------------
   Backgrounds are low-opacity tints of the map's own severity ramp (section
   28) -- reusing the validated palette rather than inventing new hues, same
   posture as the confidence pills below. "none" gets the identical treatment
   as every other level (icon + colored surface + subtitle), not a bare
   empty state -- a real no-impact result should read as confident, not
   shallow. Icon foreground colors mirror .world-map__legend-swatch's own
   contrast choices (white on green/lime/amber, navy on red). */
.warroom-impact-badge {
  display: flex;
  align-items: flex-start;
  gap: 0.6rem;
  margin: 0 0 0.75rem;
  padding: 0.55rem 0.7rem;
  border-radius: 4px;
}

.warroom-impact-badge__icon {
  flex: 0 0 auto;
  width: 1.5rem;
  height: 1.5rem;
  display: flex;
  align-items: center;
  justify-content: center;
  border-radius: 50%;
  font-family: var(--font-mono);
  font-size: 0.8rem;
  font-weight: 700;
  line-height: 1;
}

.warroom-impact-badge__label {
  display: block;
  font-size: 0.85rem;
  color: #fff;
}

.warroom-impact-badge__reason {
  margin: 0.15rem 0 0;
  font-size: 0.76rem;
  line-height: 1.4;
  color: rgba(255, 255, 255, 0.75);
}

.warroom-impact-badge--none {
  background: rgba(34, 197, 94, 0.15);
}

.warroom-impact-badge--none .warroom-impact-badge__icon {
  background: var(--severity-1);
  color: #fff;
}

.warroom-impact-badge--low {
  background: rgba(132, 204, 22, 0.15);
}

.warroom-impact-badge--low .warroom-impact-badge__icon {
  background: var(--severity-2);
  color: #fff;
}

.warroom-impact-badge--moderate {
  background: rgba(251, 191, 36, 0.15);
}

.warroom-impact-badge--moderate .warroom-impact-badge__icon {
  background: var(--severity-3);
  color: #fff;
}

.warroom-impact-badge--severe {
  background: rgba(239, 68, 68, 0.15);
}

.warroom-impact-badge--severe .warroom-impact-badge__icon {
  background: var(--severity-5);
  color: var(--navy);
}

/* -- at-a-glance impact strip: collapsed entry point into the category
   tables below (section further down), instead of always showing every
   table stacked open. */
.warroom-impact-strip {
  display: flex;
  flex-wrap: wrap;
  gap: 0.4rem;
  margin: 0 0 0.75rem;
}

.warroom-impact-strip__item {
  display: flex;
  align-items: center;
  gap: 0.35rem;
  padding: 0.3rem 0.55rem;
  border-radius: 3px;
  border: 1px solid rgba(255, 255, 255, 0.18);
  background: rgba(255, 255, 255, 0.04);
  color: #fff;
  font: inherit;
  cursor: pointer;
}

.warroom-impact-strip__item:hover,
.warroom-impact-strip__item:focus-visible {
  border-color: var(--cyan);
  background: rgba(70, 211, 255, 0.1);
  outline: none;
}

.warroom-impact-strip__item[aria-expanded='true'] {
  border-color: var(--cyan);
  background: rgba(70, 211, 255, 0.16);
}

.warroom-impact-strip__tag {
  font-family: var(--font-mono);
  font-size: 0.62rem;
  letter-spacing: 0.05em;
  color: rgba(255, 255, 255, 0.55);
}

.warroom-impact-strip__count {
  font-weight: 700;
  font-size: 0.85rem;
}

.warroom-impact-strip__label {
  font-size: 0.72rem;
  color: rgba(255, 255, 255, 0.75);
}

.warroom-table {
  margin-bottom: 0.75rem;
}

.warroom-table__heading {
  margin: 0 0 0.35rem;
  font-family: var(--font-mono);
  font-size: 0.66rem;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: rgba(255, 255, 255, 0.55);
}

.warroom-table__list,
.warroom-actions__list {
  list-style: none;
  margin: 0;
  padding: 0;
  display: grid;
  gap: 0.4rem;
}

.warroom-table__row {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: 0.4rem;
  font-size: 0.8rem;
}

.warroom-table__name {
  font-weight: 600;
  color: #fff;
}

/* Reuses the severity ramp's semantics (green/amber/muted), not its
   literal role -- confidence isn't severity, but "good/caution/unclear"
   maps cleanly onto the same three-step ramp already established. */
.warroom-table__confidence {
  font-size: 0.66rem;
  text-transform: uppercase;
  letter-spacing: 0.05em;
  padding: 0.05rem 0.4rem;
  border-radius: 8px;
  border: 1px solid currentColor;
}

.warroom-table__confidence--high {
  color: var(--severity-1);
}

.warroom-table__confidence--medium {
  color: var(--severity-3);
}

.warroom-table__confidence--low {
  color: rgba(255, 255, 255, 0.55);
}

/* Every table row carries its own citation link -- never a claim with no
   visible source (WARROOM_API_CONTRACT.md hard rule). */
.warroom-table__source {
  flex-basis: 100%;
  font-size: 0.75rem;
  color: var(--cyan);
}

.warroom-actions__item {
  padding-top: 0.4rem;
  border-top: 1px solid rgba(255, 255, 255, 0.08);
}

.warroom-actions__item:first-child {
  padding-top: 0;
  border-top: none;
}

.warroom-actions__urgency {
  display: inline-block;
  margin-bottom: 0.2rem;
  font-size: 0.62rem;
  text-transform: uppercase;
  letter-spacing: 0.08em;
  padding: 0.05rem 0.4rem;
  border-radius: 8px;
}

.warroom-actions__urgency--high {
  background: var(--severity-5);
  color: #fff;
}

.warroom-actions__urgency--medium {
  background: var(--severity-3);
  color: var(--navy);
}

.warroom-actions__urgency--low {
  background: rgba(255, 255, 255, 0.15);
  color: #fff;
}

.warroom-actions__action {
  margin: 0;
  font-size: 0.82rem;
  color: #fff;
}

.warroom-actions__rationale {
  margin: 0.15rem 0 0;
  font-size: 0.76rem;
  color: rgba(255, 255, 255, 0.65);
}

/* -- sources panel: source-diversity signal computed client-side from
   result.findings (sourceType breakdown) -- a first-class element, not a
   citation link buried on each row. */
.warroom-sources {
  margin-bottom: 0.75rem;
}

.warroom-sources__chips {
  display: flex;
  flex-wrap: wrap;
  gap: 0.35rem;
}

.warroom-sources__chip {
  font-size: 0.72rem;
  padding: 0.15rem 0.5rem;
  border-radius: 8px;
  background: rgba(255, 255, 255, 0.08);
  color: rgba(255, 255, 255, 0.85);
}

/* -- related incidents: informational chips only, no click behavior --
   there's no cross-event navigation mechanism in this popup system yet, and
   building one is out of scope here (v1 is just the titles). */
.warroom-related {
  margin-bottom: 0.75rem;
}

.warroom-related__heading {
  margin: 0 0 0.35rem;
  font-family: var(--font-mono);
  font-size: 0.66rem;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: rgba(255, 255, 255, 0.55);
}

.warroom-related__chips {
  display: flex;
  flex-wrap: wrap;
  gap: 0.35rem;
}

.warroom-related__chip {
  font-size: 0.72rem;
  padding: 0.15rem 0.5rem;
  border-radius: 8px;
  border: 1px solid rgba(255, 255, 255, 0.18);
  color: rgba(255, 255, 255, 0.8);
}

/* -- investigation metadata: a trust signal (cost/depth/elapsed time), kept
   deliberately secondary -- caption-sized and muted, never competing with
   the summary or the actions above it. */
.warroom-results__meta {
  margin: 0.5rem 0 0;
  font-size: 0.68rem;
  color: rgba(255, 255, 255, 0.45);
}

.warroom-results__disclaimer {
  margin: 0.75rem 0 0;
  padding-top: 0.5rem;
  border-top: 1px solid rgba(255, 255, 255, 0.12);
  font-size: 0.7rem;
  font-style: italic;
  color: rgba(255, 255, 255, 0.5);
}

/* ------------------------------------------------ 30. War room explainer */

/* The "Investigate" action only lives inside a clicked event's popup
   (section 29) -- a visitor who never clicks a marker has no way to know
   it exists. This section makes the capability visible on the page itself,
   in the site's normal light editorial register. The preview card on the
   right holds a 4-slide carousel: slide 1 is a real screenshot of the live
   disruption map. Slides 2-4 (.warroom-slider__slide--panel) are a dark
   surface (same --navy-deep as the real popup, section 28) so the real
   .warroom-impact-badge/-strip/-sources/-table/-actions classes nested
   inside them render pixel-identical to production instead of needing more
   screenshots or any stock imagery (this site's "No photography"
   constraint applies here too) -- it's the product's own real UI with
   static, clearly-labeled illustrative content. */
.section--warroom-intro {
  border-top: 1px solid var(--rule);
}

.warroom-intro {
  display: grid;
  grid-template-columns: minmax(0, 1.05fr) minmax(0, 0.95fr);
  gap: clamp(2rem, 5vw, 4rem);
  align-items: start;
}

.warroom-intro__features {
  list-style: none;
  margin: 1.6rem 0 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: 0.85rem;
}

.warroom-intro__features li {
  padding-left: 1.4rem;
  position: relative;
  color: var(--ink-muted);
  font-size: 0.95rem;
  line-height: 1.5;
}

.warroom-intro__features li::before {
  content: '';
  position: absolute;
  left: 0;
  top: 0.55em;
  width: 6px;
  height: 6px;
  border-radius: 50%;
  background: var(--cobalt);
}

.warroom-intro__features strong {
  color: var(--ink);
}

.warroom-intro__cta {
  margin-top: 1.8rem;
  display: flex;
  flex-wrap: wrap;
  gap: 0.75rem;
}

.warroom-intro__preview {
  background: var(--navy-deep, #0a1628);
  border-radius: 4px;
  padding: 1.1rem 1.2rem 1.3rem;
  box-shadow: 0 16px 32px -16px rgba(6, 15, 38, 0.6);
}

/* Slide 1 is a real product screenshot (not stock imagery -- see live.ejs's
   comment on this block); slides 2-4 are illustrative UI panels, not
   screenshots (see .warroom-slider__slide--panel below). Scroll-snap
   carousel so it works with zero JS as a bare swipeable/scrollable strip;
   the prev/next buttons (wired in live.js) are a progressive enhancement on
   top, not load-bearing. The image slide is a plain white card floating on
   the dark preview surface -- it has its own white chrome (browser/page
   background), so a white card frame reads as intentional, not a
   mismatched background. */
.warroom-slider {
  display: flex;
  align-items: center;
  gap: 0.5rem;
}

.warroom-slider__track {
  display: flex;
  gap: 0.85rem;
  overflow-x: auto;
  scroll-snap-type: x mandatory;
  scrollbar-width: none;
  border-radius: 4px;
}

.warroom-slider__track::-webkit-scrollbar {
  display: none;
}

.warroom-slider__slide {
  flex: 0 0 100%;
  scroll-snap-align: start;
  margin: 0;
  background: #fff;
  border-radius: 4px;
  overflow: hidden;
}

.warroom-slider__slide img {
  display: block;
  width: 100%;
  height: 300px;
  object-fit: cover;
  object-position: top center;
  background: #fff;
}

.warroom-slider__slide figcaption {
  padding: 0.65rem 0.9rem;
  font-size: 0.78rem;
  color: var(--ink-muted);
  border-top: 1px solid var(--rule);
}

/* Slides 2-4: illustrative UI panels instead of screenshots (see the
   section comment above and live.ejs). Same dark surface as
   .warroom-intro__preview/the real popup (--navy-deep, section 28) so the
   real .warroom-impact-badge/-strip/-sources/-table/-actions classes
   render pixel-identical to production. min-height matches the image
   slide's fixed 300px height so the scroll-snap carousel doesn't jump in
   height when it reaches these slides. */
.warroom-slider__slide--panel {
  display: flex;
  flex-direction: column;
  min-height: 300px;
  padding: 1.1rem 1.2rem;
  background: var(--navy-deep, #0a1628);
}

.warroom-slider__slide-label {
  margin: 0 0 0.85rem;
  font-size: 0.7rem;
  text-transform: uppercase;
  letter-spacing: 0.08em;
  color: rgba(255, 255, 255, 0.45);
}

.warroom-slider__slide--panel figcaption {
  margin-top: auto;
  padding: 0.65rem 0 0;
  border-top: 1px solid rgba(255, 255, 255, 0.12);
  color: rgba(255, 255, 255, 0.55);
}

.warroom-slider__nav {
  flex: 0 0 auto;
  width: 2.1rem;
  height: 2.1rem;
  border-radius: 50%;
  border: 1px solid rgba(255, 255, 255, 0.22);
  background: rgba(255, 255, 255, 0.08);
  color: #fff;
  font-size: 1.3rem;
  line-height: 1;
  cursor: pointer;
  transition: background 0.2s var(--ease);
}

.warroom-slider__nav:hover,
.warroom-slider__nav:focus-visible {
  background: rgba(255, 255, 255, 0.18);
  outline: none;
}

@media (max-width: 900px) {
  .warroom-intro {
    grid-template-columns: minmax(0, 1fr);
  }
}

/* ============================================================================
   31. Homepage redesign — dark hero / bynar.io + terminal-industries mix
   All net-new component classes for the homepage sections (§3.1-3.10 of the
   redesign brief). Scoped to new class names throughout, so nothing here
   changes how any other page renders — see the tokens note in §2b above.
   ========================================================================== */

/* ---- 31.1 Dark hero ---- */

.hero--dark {
  position: relative;
  overflow: hidden;
  min-height: 100vh;
  display: flex;
  align-items: center;
  background: var(--color-bg-dark);
  /* .header--transparent is position:fixed (removed from flow), so the hero
     needs its own top padding to clear it instead of relying on layout push. */
  padding-top: var(--header-h);
}

/* Forcing the hero to fill the full viewport height on a narrow phone
   asks object-fit: cover to stretch a 1920x1072 photo across whatever
   tall box 100vh happens to be there (often 800-950+ CSS px, i.e.
   2000-2800+ physical px at typical phone pixel density) -- multiple
   times the source's native resolution, which is what was reading as
   blur on mobile specifically (desktop viewports rarely need anywhere
   near that much stretch). Capping it to a shorter, content-appropriate
   height keeps the photo far closer to its native resolution. */
@media (max-width: 640px) {
  .hero--dark {
    min-height: 0;
  }
}

/* Below 640px the <picture> in hero.ejs swaps in a purpose-shot portrait
   crop (public/img/photos/*-mobile.jpg) instead of the desktop landscape
   photo -- real photography framed for a tall mobile viewport, rather
   than this same rule's native-resolution cap (below) having to crop a
   landscape source down to a sliver to avoid upscaling it. Every other
   full-bleed hero/backdrop image in this file (.work-hero__img,
   .banner-photo__img, .contact-photo-hero__img) gets the same <picture>
   treatment in its own view.

   top/left/right instead of inset: 0 (no forced bottom edge), plus
   max-height, so main.js can cap this at whichever photo (desktop or
   mobile) actually loaded's own native resolution -- see noUpscaleEls in
   main.js. Below that cap the image just doesn't reach the bottom of the
   section; the section's own dark background (already close to the
   scrim's own darkest value) shows through rather than the photo being
   stretched past its real pixels. */
.hero__photo {
  position: absolute;
  top: 0;
  left: 0;
  right: 0;
  width: 100%;
  height: 100%;
  max-height: var(--photo-cap-h, 100%);
  object-fit: cover;
}

/* Darkens the photo so the white headline/eyebrow stay legible — same
   treatment as .banner-photo__scrim. */
.hero__scrim {
  position: absolute;
  inset: 0;
  background: linear-gradient(180deg, rgba(10, 15, 26, 0.55) 0%, rgba(10, 15, 26, 0.82) 100%);
}

.hero__dark-inner {
  position: relative;
  z-index: 1;
  padding-block: clamp(2.5rem, 6vw, 4rem);
}

/* --color-accent (a mid-saturation blue) reads fine on the flat dark
   sections elsewhere, but over the busy, often-bright hero photo it loses
   contrast against the sky/highlight areas. --slate-light is the token the
   rest of the site already uses for eyebrow text on dark backgrounds (see
   .eyebrow--onNavy) -- reusing it here instead of introducing a new colour. */
.hero--dark .eyebrow {
  color: var(--slate-light);
}

.hero__headline {
  color: var(--color-text-white);
  font-size: clamp(2.6rem, 7vw, 5.5rem);
}

/* Reserves layout space equal to the final rendered text before the letter
   reveal starts, so nothing reflows as words wrap in. em-relative and
   unscoped to any one heading class, so it applies correctly wherever
   data-letter-reveal is used -- the homepage hero and every other page's
   hero heading alike. */
[data-letter-reveal] {
  min-height: 1.04em;
}

/* Letter-by-letter reveal (see public/js/letter-reveal.js): each character
   slides in from the right, staggered left-to-right via --char-i. Words are
   their own inline-block/nowrap wrapper so the headline still only ever
   breaks lines at real word boundaries, exactly like the plain text would.
   Skipped entirely under reduced motion -- the script never touches the DOM
   in that case, so the plain final text renders with no animation classes
   to speak of. */
.letter-reveal__word {
  display: inline-block;
  white-space: nowrap;
}

.letter-reveal__char {
  display: inline-block;
  opacity: 0;
  transform: translateX(0.6em);
  transition: opacity 0.5s var(--ease), transform 0.5s var(--ease);
  transition-delay: calc(var(--char-i, 0) * 22ms);
}

[data-letter-reveal].is-revealed .letter-reveal__char {
  opacity: 1;
  transform: none;
}

.hero__body--dim {
  color: var(--color-text-dim);
  max-width: 46ch;
}

.hero--dark .btn--primary {
  background: var(--color-accent);
  border-color: var(--color-accent);
}

.hero--dark .btn--primary:hover {
  background: var(--color-accent-dark);
  border-color: var(--color-accent-dark);
}

.btn--ghost-onDark {
  background: transparent;
  color: var(--color-text-white);
  border-color: rgba(255, 255, 255, 0.32);
}

.btn--ghost-onDark:hover {
  border-color: var(--color-text-white);
  background: rgba(255, 255, 255, 0.1);
  color: var(--color-text-white);
}

/* ---- 31.3 Social proof strip ---- */

.social-proof {
  background: var(--color-bg-light);
  border-block: 1px solid var(--color-border);
  padding-block: clamp(2rem, 4vw, 3rem);
}

.social-proof__eyebrow {
  font-family: var(--font-mono);
  font-size: 0.68rem;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--color-text-secondary);
  display: block;
  margin-bottom: 1.1rem;
}

.social-proof__chips {
  overflow: hidden;
  /* Fades the chips out at both edges instead of a hard clip, so the loop
     point (and the track sliding a chip out of view) reads as a soft edge
     rather than an abrupt cut. */
  -webkit-mask-image: linear-gradient(90deg, transparent 0, #000 4%, #000 96%, transparent 100%);
  mask-image: linear-gradient(90deg, transparent 0, #000 4%, #000 96%, transparent 100%);
}

.social-proof__track {
  display: flex;
  width: max-content;
  animation: social-proof-marquee 26s linear infinite;
  cursor: grab;
}

/* Paused on hover/keyboard focus so a reader who stops to look at the chips
   isn't fighting a moving target. */
.social-proof__chips:hover .social-proof__track,
.social-proof__chips:focus-within .social-proof__track {
  animation-play-state: paused;
}

.social-proof__group {
  flex: none;
  display: flex;
  gap: 0.6rem;
  padding-right: 0.6rem;
}

@keyframes social-proof-marquee {
  from {
    transform: translateX(0);
  }
  to {
    /* Exactly one copy's width, since the track holds two identical copies
       back-to-back -- see the aria-hidden duplicate group in home.ejs. */
    transform: translateX(-50%);
  }
}

/* Static, manually-scrollable fallback -- same behaviour the strip had
   before the marquee was added. */
@media (prefers-reduced-motion: reduce) {
  .social-proof__chips {
    overflow-x: auto;
    -webkit-mask-image: none;
    mask-image: none;
    padding-bottom: 0.5rem;
    scrollbar-width: thin;
    -webkit-overflow-scrolling: touch;
  }

  .social-proof__track {
    animation: none;
  }
}

.social-proof__chip {
  flex: none;
  font-size: 0.88rem;
  font-weight: 600;
  color: var(--color-text-primary);
  background: var(--color-bg-paper);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-lg);
  padding: 0.5rem 1rem;
  white-space: nowrap;
}

.social-proof__markets {
  margin-top: 1.1rem;
  font-size: 0.92rem;
  color: var(--color-text-secondary);
}

/* ---- 31.3b Who we serve — asymmetric split with a blurred accent dot ---- */

.who-serve {
  background: var(--color-bg-paper);
}

.who-serve__grid {
  display: grid;
  grid-template-columns: minmax(0, 1.15fr) minmax(0, 0.85fr);
  gap: clamp(2.5rem, 5vw, 4.5rem);
  align-items: start;
  margin-top: clamp(2rem, 4vw, 3rem);
}

.who-serve__media {
  aspect-ratio: 4 / 3;
  border-radius: var(--radius-lg);
  overflow: hidden;
}

.who-serve__media img {
  width: 100%;
  height: 100%;
  object-fit: cover;
}

.who-serve__aside {
  position: relative;
  padding-left: 1.75rem;
  border-left: 1px solid var(--color-border);
  padding-top: clamp(1rem, 6vw, 4rem);
}

/* Drifts and breathes continuously (autonomous, no pointer/scroll needed so
   it still moves on touch devices), plus a scroll-driven parallax offset
   layered on top via --parallax-y (see [data-parallax] and main.js) —
   translate3d composites both without one fighting the other. */
.who-serve__accent {
  position: absolute;
  top: 0;
  right: 10%;
  width: 120px;
  height: 120px;
  border-radius: 50%;
  background: var(--color-accent);
  filter: blur(38px);
  opacity: 0.5;
  pointer-events: none;
  transform: translate3d(0, var(--parallax-y, 0), 0);
  will-change: transform, opacity;
  animation: blob-drift 11.25s ease-in-out infinite;
}

@keyframes blob-drift {
  0%,
  100% {
    transform: translate3d(0, var(--parallax-y, 0), 0) scale(1);
    opacity: 0.5;
  }
  50% {
    transform: translate3d(-18px, calc(var(--parallax-y, 0) + 14px), 0) scale(1.18);
    opacity: 0.72;
  }
}

.who-serve__body {
  color: var(--color-text-secondary);
  font-size: 1.05rem;
  line-height: 1.65;
  margin-bottom: 1.5rem;
  position: relative;
}

@media (max-width: 860px) {
  .who-serve__grid {
    grid-template-columns: minmax(0, 1fr);
  }

  .who-serve__aside {
    padding-top: 1.5rem;
  }
}

/* ---- 31.3c Full-bleed photo banner with an overlaid headline ---- */

.banner-photo {
  position: relative;
  overflow: hidden;
  min-height: 62vh;
  display: flex;
  align-items: center;
  /* Matches .banner-photo__scrim's darkest value -- see the native-resolution
     cap comment on .banner-photo__img above: below the cap the photo stops
     short of the section's height, and without this the page's white
     background showed through there as a washed-out grey band. */
  background: var(--color-bg-dark);
}

/* Same reasoning as .hero--dark's mobile override: 62vh forces object-fit:
   cover to stretch banner-team.jpg well past its native resolution on a
   tall, high-density phone screen, and the native-resolution cap above then
   leaves a tall empty gap below the photo (the headline sitting in it,
   nothing but background below). Letting the section size to its own
   content instead keeps the photo close to native resolution and removes
   the gap entirely, rather than just recoloring it. */
@media (max-width: 640px) {
  .banner-photo {
    min-height: 0;
  }
}

/* See .hero__photo above for why this isn't inset: 0 + why max-height is
   here -- same native-resolution cap, applied by main.js. */
.banner-photo__img {
  position: absolute;
  top: 0;
  left: 0;
  right: 0;
  width: 100%;
  height: 100%;
  max-height: var(--photo-cap-h, 100%);
  object-fit: cover;
}

.banner-photo__scrim {
  position: absolute;
  inset: 0;
  background: linear-gradient(
    100deg,
    rgba(10, 15, 26, 0.78) 0%,
    rgba(10, 15, 26, 0.4) 55%,
    rgba(10, 15, 26, 0.15) 100%
  );
}

.banner-photo__inner {
  position: relative;
  z-index: 1;
  /* Only load-bearing once .banner-photo's mobile min-height: 0 override can
     apply (see above) -- on desktop 62vh + align-items: center already gives
     the headline generous room, so this clamp reduces to a no-op there. */
  padding-block: clamp(2.5rem, 10vw, 4rem);
}

.banner-photo__headline {
  color: var(--color-text-white);
  font-family: var(--font-display);
  font-weight: 700;
  font-size: clamp(2rem, 4.6vw, 3.4rem);
  line-height: 1.15;
  max-width: 34ch;
}

/* ---- 31.0 Photo placeholder + pill button ----
   Image generation is unavailable in this environment (network policy blocks
   the generator's CDN) and no stock-photo source is reachable either, so
   these sections ship with clearly-labelled placeholder slots instead of
   broken <img> tags. Each shows what photo belongs there and at what path —
   drop a real file at that path and swap the markup for an <img> (or a
   background-image) to replace it; no other CSS needs to change, since the
   slot is already sized and positioned correctly. */
.photo-slot {
  position: relative;
  overflow: hidden;
  background: linear-gradient(145deg, var(--color-bg-mid) 0%, var(--navy) 55%, var(--color-accent-dark) 100%);
}

.photo-slot::before {
  content: '';
  position: absolute;
  inset: 0;
  background-image: radial-gradient(rgba(255, 255, 255, 0.07) 1px, transparent 1px);
  background-size: 16px 16px;
}

/* A small corner caption rather than centered overlay text — .hero--dark and
   .banner-photo both combine a photo-slot with real foreground copy, and
   centering the hint across the whole box collided with that copy. Pinned
   to a corner it stays out of the way in every usage, including the plain
   standalone slots (who-serve/service-row/about) that have no other content
   to collide with. */
.photo-slot::after {
  content: attr(data-photo-hint);
  position: absolute;
  left: 0.75rem;
  right: 0.75rem;
  bottom: 0.75rem;
  z-index: 2;
  font-family: var(--font-mono);
  font-size: 0.62rem;
  line-height: 1.5;
  letter-spacing: 0.01em;
  color: rgba(255, 255, 255, 0.7);
}

.btn--pill {
  border-radius: 999px;
}

/* ---- 31.4 Services — alternating full-photo + bullet-list rows ---- */

.service-rows {
  margin-top: clamp(2.5rem, 5vw, 4rem);
  display: flex;
  flex-direction: column;
  gap: clamp(3.5rem, 7vw, 6rem);
}

.service-row {
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
  gap: clamp(2.5rem, 5vw, 4.5rem);
  align-items: center;
}

.service-row--reverse .service-row__media {
  order: 2;
}

.service-row__media {
  aspect-ratio: 4 / 3;
  border-radius: var(--radius-lg);
  overflow: hidden;
}

.service-row__media img {
  width: 100%;
  height: 100%;
  object-fit: cover;
}

/* Used on What We Do's two-division section — a fixed-width image column
   sized to the photo itself, paired with a copy column capped to its own
   readable measure (matching .home-section__body's 56ch) instead of an
   `1fr` that stretches to the shell's full, uncapped width on wide
   screens and leaves the short copy stranded far from the photo. The row
   itself shrinks to fit those two columns (width: fit-content) rather
   than stretching, and the reversed row is pushed to the right edge with
   margin-left: auto so image and copy stay adjacent either way. */
.service-row--compact {
  grid-template-columns: 476px minmax(0, 38rem);
  gap: clamp(2rem, 4vw, 3rem);
  align-items: start;
  width: fit-content;
  max-width: 100%;
}

.service-row--compact.service-row--reverse {
  grid-template-columns: minmax(0, 38rem) 476px;
  margin-left: auto;
}

.service-row--compact .service-row__media {
  width: 476px;
  max-width: 476px;
}

@media (max-width: 860px) {
  .service-row--compact,
  .service-row--compact.service-row--reverse {
    grid-template-columns: minmax(0, 1fr);
    width: 100%;
    margin-left: 0;
  }

  .service-row--compact .service-row__media {
    width: 100%;
    max-width: 100%;
  }
}

.service-row__name {
  font-family: var(--font-display);
  font-weight: 700;
  font-size: clamp(1.8rem, 3.4vw, 2.5rem);
  color: var(--color-text-primary);
  margin-bottom: 1rem;
}

.service-row__list {
  list-style: none;
  margin: 0 0 1.5rem;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: 0.6rem;
}

.service-row__list li {
  color: var(--color-text-secondary);
  font-size: 1.02rem;
  line-height: 1.5;
}

/* ---- 31.4a Homepage expertise list */

.expertise-layout {
  display: grid;
  grid-template-columns: minmax(15rem, 0.78fr) minmax(0, 1.22fr);
  gap: clamp(2rem, 5vw, 5rem);
  align-items: start;
  max-width: 1180px;
  margin-inline: auto;
}

.expertise-intro {
  position: sticky;
  top: calc(var(--header-h) + 2rem);
  padding-inline: 0.2rem;
}

.expertise-intro__heading {
  font-family: var(--font-display);
  font-size: clamp(2.35rem, 4.7vw, 4.35rem);
  font-weight: 800;
  line-height: 0.98;
  color: var(--color-text-primary);
  letter-spacing: -0.045em;
  text-align: left;
  max-width: 14ch;
}

.expertise-intro__heading span:first-child {
  color: var(--navy);
}

.expertise-intro__accent {
  color: #2E7BFF;
}

.expertise-intro__body {
  max-width: 24.5rem;
  margin-top: clamp(1.5rem, 3vw, 2.5rem);
  color: var(--color-text-secondary);
  font-size: clamp(1rem, 1.35vw, 1.18rem);
  line-height: 1.65;
  text-align: left;
}

.expertise-list {
  max-height: min(42rem, 72vh);
  overflow-y: auto;
  padding: 0;
  scrollbar-width: none;
  cursor: ns-resize;
}

.expertise-list:focus-visible {
  outline: 3px solid var(--color-accent);
  outline-offset: 6px;
}

.expertise-list::-webkit-scrollbar {
  display: none;
}

.expertise-card {
  min-height: 7.65rem;
  padding: 1.35rem 0.5rem 1.35rem 0;
  display: grid;
  grid-template-columns: 3.5rem minmax(0, 1fr) 1.5rem;
  align-items: center;
  gap: 1.2rem;
  border-top: 1px solid var(--color-border);
  transition: color 0.25s var(--ease), padding-left 0.25s var(--ease);
}

.expertise-card:hover {
  padding-left: 0.75rem;
  color: var(--color-accent);
}

.expertise-card:last-child {
  border-bottom: 1px solid var(--color-border);
}

.expertise-card__icon {
  width: 3.25rem;
  height: 3.25rem;
  color: #002d5c;
}

.expertise-card__icon svg {
  width: 100%;
  height: 100%;
}

.expertise-card__name {
  font-size: clamp(1.1rem, 1.8vw, 1.5rem);
  font-weight: 700;
  color: var(--color-text-primary);
}

.expertise-card__name a {
  color: inherit;
  text-decoration: none;
}

.expertise-card__arrow {
  font-size: 1.45rem;
  color: var(--color-text-secondary);
  transition: transform 0.25s var(--ease), color 0.25s var(--ease);
}

.expertise-card:hover .expertise-card__arrow {
  color: var(--color-accent);
  transform: translate(2px, -2px);
}

@media (max-width: 640px) {
  .expertise-layout {
    grid-template-columns: 1fr;
    gap: 2.75rem;
  }

  .expertise-intro {
    position: static;
  }

  .expertise-list {
    max-height: 32rem;
  }

  .expertise-card {
    grid-template-columns: 3rem minmax(0, 1fr) 1.25rem;
    gap: 0.8rem;
    min-height: 6.25rem;
  }

  .expertise-card__icon {
    width: 2.75rem;
    height: 2.75rem;
  }
}

@media (max-width: 860px) {
  .service-row,
  .service-row--reverse .service-row__media {
    grid-template-columns: minmax(0, 1fr);
    order: -1;
  }
}

/* ---- 31.5 Tiers — numbered 01/02/03 entry-point cards ---- */

.tier-cards {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: clamp(1.5rem, 3vw, 2rem);
  margin-top: clamp(2rem, 4vw, 3rem);
}

.tier-card {
  border: 1px solid var(--color-border);
  border-radius: var(--radius-lg);
  padding: clamp(1.75rem, 3vw, 2.25rem);
  display: flex;
  flex-direction: column;
  transition: border-color var(--transition-base), transform var(--transition-base);
}

.tier-card:hover {
  border-color: var(--color-accent);
  transform: translateY(-3px);
}

.tier-card__label {
  font-family: var(--font-mono);
  font-size: 1.02rem;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--color-text-secondary);
}

.tier-card__name {
  font-family: var(--font-display);
  font-weight: 700;
  font-size: 1.5rem;
  color: var(--color-text-primary);
  margin-top: 0.7rem;
}

.tier-card__body {
  color: var(--color-text-secondary);
  font-size: 0.98rem;
  line-height: 1.6;
  margin-top: 0.9rem;
  flex: 1;
}

.tier-card__link {
  display: inline-block;
  margin-top: 1.4rem;
  font-weight: 700;
  color: var(--color-accent);
}

.tier-card__note {
  margin-top: 0.8rem;
  font-size: 0.8rem;
  color: var(--color-text-secondary);
}

@media (max-width: 860px) {
  .tier-cards {
    grid-template-columns: minmax(0, 1fr);
  }
}

/* ---- 31.6 How it works — dark section, three columns ---- */

.section--home-dark {
  background: var(--color-bg-mid);
  padding: var(--section-padding);
}

.section--home-dark .eyebrow {
  color: var(--color-accent);
}

.section--home-dark .home-section__heading {
  color: var(--color-text-white);
}

/* Only used by the "Three steps" headline (see how.headlineAccent in
   content/home.js) -- colours just the first sentence, the rest stays the
   section's plain white. */
.heading-accent {
  color: var(--color-accent);
}

/* Vertical connected-spine timeline: a single line down the left with one
   icon-ring marker per step. Built mobile-first (already a stacked list, no
   separate breakpoint needed to avoid overlap) and kept as a single centered
   column on wide screens too -- a narrative flow reads better here than a
   3-up grid would, since these are ordered/sequential steps. */
.how-spine {
  max-width: 34rem;
  margin: clamp(2.5rem, 5vw, 4rem) auto 0;
  position: relative;
  padding-left: 3.1rem;
}

.how-spine::before {
  content: '';
  position: absolute;
  left: 17px;
  top: 17px;
  bottom: 17px;
  width: 1.5px;
  background: var(--color-border-dark);
}

.how-spine__step {
  position: relative;
  padding-bottom: clamp(1.75rem, 3vw, 2.4rem);
}

.how-spine__step:last-child {
  padding-bottom: 0;
}

.how-spine__marker {
  position: absolute;
  left: -3.1rem;
  top: -1px;
  width: 35px;
  height: 35px;
}

/* Continuous soft ping behind each icon -- reads as "always running," not a
   one-off entrance effect. Staggered per step so the three don't beat in
   sync. Off entirely under reduced motion; the icon itself never moves. */
.how-spine__marker::before {
  content: '';
  position: absolute;
  inset: 0;
  border-radius: 50%;
  background: var(--color-accent);
  opacity: 0;
  animation: how-spine-pulse 2.6s ease-out infinite;
}

.how-spine__step:nth-child(1) .how-spine__marker::before {
  animation-delay: 0s;
}

.how-spine__step:nth-child(2) .how-spine__marker::before {
  animation-delay: 0.85s;
}

.how-spine__step:nth-child(3) .how-spine__marker::before {
  animation-delay: 1.7s;
}

.how-spine__ring {
  position: relative;
  width: 100%;
  height: 100%;
  border-radius: 50%;
  background: var(--color-bg-mid);
  border: 1.5px solid var(--color-accent);
  display: flex;
  align-items: center;
  justify-content: center;
}

.how-spine__ring svg {
  width: 16px;
  height: 16px;
  stroke: var(--color-accent);
  fill: none;
  stroke-width: 1.8;
  stroke-linecap: round;
  stroke-linejoin: round;
}

@keyframes how-spine-pulse {
  0% {
    transform: scale(0.8);
    opacity: 0.55;
  }
  100% {
    transform: scale(2.1);
    opacity: 0;
  }
}

@media (prefers-reduced-motion: reduce) {
  .how-spine__marker::before {
    animation: none;
    display: none;
  }
}

.how-step__title {
  font-family: var(--font-display);
  font-weight: 700;
  font-size: 1.3rem;
  color: var(--color-text-white);
  margin: 0;
}

.how-step__body {
  color: var(--color-text-dim);
  font-size: 0.98rem;
  line-height: 1.6;
  margin-top: 0.6rem;
}

/* ---- 31.7 Stats row ---- */

.stats-row {
  display: grid;
  grid-template-columns: repeat(4, minmax(0, 1fr));
  gap: clamp(1.5rem, 3vw, 2rem);
  margin-top: clamp(1.5rem, 3vw, 2rem);
}

.stat {
  text-align: center;
  padding-block: 1rem;
}

.stat__value {
  font-family: var(--font-display);
  font-weight: 700;
  font-size: clamp(1.8rem, 3.5vw, 2.4rem);
  color: var(--navy);
}

.stat__label {
  font-family: var(--font-mono);
  font-size: 0.72rem;
  letter-spacing: 0.06em;
  color: var(--color-text-secondary);
  margin-top: 0.6rem;
  line-height: 1.4;
}

@media (max-width: 768px) {
  .stats-row {
    grid-template-columns: repeat(2, minmax(0, 1fr));
  }
}

@media (max-width: 480px) {
  .stats-row {
    grid-template-columns: minmax(0, 1fr);
  }
}

/* ---- 31.8 Quote / editorial block — dark, full-width ---- */

.quote-block {
  background: var(--color-bg-dark);
  padding: var(--section-padding);
  text-align: center;
}

.quote-block__inner {
  max-width: 46rem;
  margin-inline: auto;
}

.quote-block__headline {
  font-family: var(--font-display);
  font-weight: 700;
  font-size: clamp(1.6rem, 3.4vw, 2.6rem);
  color: var(--color-text-white);
  line-height: 1.3;
}

.quote-block__body {
  margin-top: 1.3rem;
  color: var(--color-text-dim);
  font-size: 1.05rem;
}

/* ---- 31.9 About ---- */

.about-grid {
  display: grid;
  grid-template-columns: minmax(0, 3fr) minmax(0, 2fr);
  gap: clamp(2.5rem, 5vw, 4rem);
  align-items: center;
  margin-top: clamp(2rem, 4vw, 3rem);
}

.about-grid__body p {
  color: var(--color-text-secondary);
  font-size: 1.05rem;
  line-height: 1.7;
  margin: 0 0 1.3rem;
}

.about-grid__visual {
  aspect-ratio: 4 / 3;
  border-radius: var(--radius-lg);
  overflow: hidden;
}

.about-grid__visual img {
  width: 100%;
  height: 100%;
  object-fit: cover;
}

@media (max-width: 860px) {
  .about-grid {
    grid-template-columns: minmax(0, 1fr);
  }

  .about-grid__visual {
    order: -1;
  }
}

/* ---- 31.10 FAQ accordion ---- */

.faq-list {
  margin-top: clamp(2rem, 4vw, 3rem);
  border-top: 1px solid var(--color-border);
}

.faq-item {
  border-bottom: 1px solid var(--color-border);
}

.faq-item__trigger {
  width: 100%;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 1rem;
  background: none;
  border: 0;
  text-align: left;
  padding: 1.4rem 0.2rem;
  font-family: var(--font-display);
  font-weight: 600;
  font-size: 1.05rem;
  color: var(--color-text-primary);
  cursor: pointer;
}

.faq-item__icon {
  flex: none;
  width: 20px;
  height: 20px;
  position: relative;
}

.faq-item__icon::before,
.faq-item__icon::after {
  content: '';
  position: absolute;
  top: 50%;
  left: 50%;
  background: var(--color-accent);
  transform: translate(-50%, -50%);
  transition: transform var(--transition-fast);
}

.faq-item__icon::before {
  width: 14px;
  height: 2px;
}

.faq-item__icon::after {
  width: 2px;
  height: 14px;
}

.faq-item__trigger[aria-expanded='true'] .faq-item__icon::after {
  transform: translate(-50%, -50%) rotate(90deg) scale(0);
}

.faq-item__panel {
  max-height: 0;
  overflow: hidden;
  transition: max-height var(--transition-base);
}

.faq-item__panel-inner {
  padding: 0 0.2rem 1.4rem;
  color: var(--color-text-secondary);
  font-size: 0.98rem;
  line-height: 1.65;
  max-width: 68ch;
}

/* ---- 31.11 Contact CTA ---- */

.contact-cta {
  text-align: center;
}

.contact-cta__body {
  max-width: 46ch;
  margin: 1rem auto 0;
  color: var(--color-text-secondary);
}

.contact-cta__actions {
  margin-top: clamp(1.75rem, 3vw, 2.25rem);
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 1.1rem;
}

.contact-cta__secondary {
  color: var(--color-text-secondary);
  font-weight: 600;
}

.contact-cta__secondary:hover {
  color: var(--color-accent);
}

/* ---- 31.12 Shared homepage section scaffolding ---- */

.home-section {
  padding: var(--section-padding);
}

.home-section__inner {
  max-width: var(--max-width);
  margin-inline: auto;
}

.home-section__heading {
  font-family: var(--font-display);
  font-weight: 700;
  font-size: clamp(1.9rem, 3.6vw, 2.8rem);
  color: var(--color-text-primary);
  margin-top: 0.6rem;
}

.home-section__body {
  color: var(--color-text-secondary);
  font-size: 1.05rem;
  margin-top: 0.9rem;
  max-width: 56ch;
}

@media (prefers-reduced-motion: reduce) {
  .route-line__pulse {
    animation: none;
    left: 50%;
    opacity: 1;
  }

  .tier-card,
  .faq-item__panel {
    transition: none;
  }
}

/* ---- 31.13 Who We Are — collage hero + photo/statement split ---- */

.hero--collage {
  background: var(--color-bg-dark);
  /* .header--transparent is position:fixed (removed from flow) on this
     page, so the hero needs its own top clearance instead of relying on
     the header pushing it down — same reasoning as .hero--dark. */
  padding-top: calc(var(--header-h) + clamp(2.5rem, 5vw, 4rem));
  padding-bottom: clamp(3.5rem, 7vw, 6rem);
}

.hero-collage__inner {
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(0, 0.9fr);
  gap: clamp(2.5rem, 5vw, 4rem);
  align-items: center;
}

.hero-collage__copy .eyebrow {
  color: var(--color-accent);
}

.hero-collage__headline {
  color: var(--color-text-white);
}

.hero-collage__body {
  color: var(--color-text-dim);
  margin-top: 1.4rem;
}

/* Two overlapping photo tiles instead of stock imagery or an illustration —
   the offset stagger reads as an editorial collage without putting text
   behind a photo, which would risk contrast. */
.hero-collage__media {
  position: relative;
  aspect-ratio: 1;
}

.hero-collage__tile {
  position: absolute;
  border-radius: var(--radius-lg);
  overflow: hidden;
  box-shadow: 0 30px 60px -20px rgba(0, 0, 0, 0.5);
}

.hero-collage__tile img {
  width: 100%;
  height: 100%;
  object-fit: cover;
}

.hero-collage__tile--a {
  top: 0;
  left: 0;
  width: 62%;
  height: 62%;
  z-index: 1;
}

.hero-collage__tile--b {
  bottom: 0;
  right: 0;
  width: 62%;
  height: 62%;
}

@media (max-width: 860px) {
  .hero-collage__inner {
    grid-template-columns: minmax(0, 1fr);
  }

  .hero-collage__media {
    order: -1;
    aspect-ratio: 4 / 3;
    max-width: 22rem;
    margin-inline: auto;
  }
}

/* A bold photo bleeding off one edge, paired with a statement — an
   alternative split pattern to the card-based ones elsewhere on the site. */
.statement-split {
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
  align-items: stretch;
}

.statement-split__media {
  min-height: 22rem;
  overflow: hidden;
}

.statement-split__media img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
}

.statement-split__copy {
  display: flex;
  align-items: center;
  padding: clamp(2.5rem, 5vw, 4.5rem);
  background: var(--color-bg-paper);
}

.statement-split__text {
  font-family: var(--font-display);
  font-weight: 700;
  font-size: clamp(1.5rem, 2.6vw, 2.1rem);
  line-height: 1.35;
  color: var(--color-text-primary);
  max-width: 34ch;
}

@media (max-width: 768px) {
  .statement-split {
    grid-template-columns: minmax(0, 1fr);
  }

  .statement-split__media {
    min-height: 16rem;
  }
}

/* ---- 31.14 Contact — textured photo backdrop with a floating card ---- */

.contact-photo-hero {
  position: relative;
  overflow: hidden;
  /* .header--transparent is position:fixed (removed from flow) on this
     page now, so the section needs its own top clearance instead of
     relying on the header pushing it down — same reasoning as the other
     dark-hero pages. */
  padding-top: calc(var(--header-h) + clamp(0.75rem, 1.5vw, 1.25rem));
  padding-bottom: clamp(1rem, 2vw, 1.5rem);
}

/* See .hero__photo above for why this isn't inset: 0 + why max-height is
   here -- same native-resolution cap, applied by main.js. */
.contact-photo-hero__img {
  position: absolute;
  top: 0;
  left: 0;
  right: 0;
  width: 100%;
  height: 100%;
  max-height: var(--photo-cap-h, 100%);
  object-fit: cover;
}

.contact-photo-hero__scrim {
  position: absolute;
  inset: 0;
  background: linear-gradient(160deg, rgba(0, 45, 92, 0.82) 0%, rgba(10, 15, 26, 0.7) 100%);
}

.contact-photo-hero__inner {
  position: relative;
  z-index: 1;
}

.contact-card {
  background: var(--color-bg-light);
  border-radius: var(--radius-lg);
  padding: clamp(1.1rem, 1.8vw, 1.4rem);
  /* Capped width + auto margins instead of the old side-margin clamp —
     the card used to just be the full shell width minus a margin, which
     on wide screens meant a card over 1400px across. Below the width
     itself, .shell's own gutter padding keeps it off the edges on
     narrow screens. */
  max-width: 1000px;
  margin-inline: auto;
  /* A tighter, less diffuse shadow than before — the old 80px blur radius
     read as a soft halo against the busy photo backdrop; this keeps just
     enough lift to separate the card from the photo, with no blur. */
  box-shadow: 0 10px 28px -10px rgba(0, 0, 0, 0.4);
}

.contact-card .eyebrow {
  color: var(--cobalt);
}

/* Compact vertical rhythm throughout the card -- the goal is the whole
   thing (headline through the submit button) fitting a normal laptop
   viewport without scrolling, not just a narrower box. Scoped to this
   card specifically; .display-1/.lede/.field/.aside-card keep their
   normal, more generous sizing everywhere else on the site. */
.contact-card .hero__copy {
  margin-bottom: 0.3rem;
}

.contact-card h1.display-1 {
  font-size: clamp(1.5rem, 2.6vw, 2.1rem);
  line-height: 1.05;
}

.contact-card .lede {
  font-size: 0.95rem;
  line-height: 1.45;
  margin-top: 0.5rem;
}

.contact-card .field {
  margin-bottom: 0.42rem;
}

.contact-card .field__label {
  margin-bottom: 0.25rem;
}

.contact-card .field input,
.contact-card .field textarea {
  padding: 0.4rem 0.65rem;
}

.contact-card .field textarea {
  min-height: 44px;
}

.contact-card .contact-grid {
  gap: clamp(1.5rem, 3vw, 2.5rem);
}

.contact-card .aside-card {
  padding: clamp(1rem, 1.4vw, 1.25rem);
  margin-bottom: 0;
}

.contact-card .aside-card dt {
  margin-top: 0.7rem;
}

/* ---- 31.14b What We Do — faint backdrop behind the division rows ---- */

.division-section {
  position: relative;
  overflow: hidden;
}

/* See .hero__photo above for why this isn't inset: 0 + why max-height is
   here -- same native-resolution cap, applied by main.js. */
.division-section__bg {
  position: absolute;
  top: 0;
  left: 0;
  right: 0;
  width: 100%;
  height: 100%;
  max-height: var(--photo-cap-h, 100%);
  object-fit: cover;
  opacity: 0.15;
  pointer-events: none;
}


/* ---- 31.16 How We Work — full-bleed photo hero ---- */

.work-hero {
  position: relative;
  overflow: hidden;
  min-height: 56vh;
  display: flex;
  align-items: flex-end;
}

/* See .hero__photo above for why this isn't inset: 0 + why max-height is
   here -- same native-resolution cap, applied by main.js. */
.work-hero__img {
  position: absolute;
  top: 0;
  left: 0;
  right: 0;
  width: 100%;
  height: 100%;
  max-height: var(--photo-cap-h, 100%);
  object-fit: cover;
}

.work-hero__scrim {
  position: absolute;
  inset: 0;
  background: linear-gradient(180deg, rgba(10, 15, 26, 0.25) 0%, rgba(10, 15, 26, 0.92) 100%);
}

.work-hero__inner {
  position: relative;
  z-index: 1;
  padding-block: clamp(3rem, 7vw, 5rem);
}

.work-hero .eyebrow {
  color: var(--color-accent);
}

.work-hero__headline {
  color: var(--color-text-white);
}

.work-hero__body {
  color: var(--color-text-dim);
  margin-top: 1.2rem;
  max-width: 60ch;
}

/* ---- 31.17 What We Do — same full-bleed hero, but with a transparent
   header overlaid (like the homepage), so it needs its own top padding to
   clear the now-fixed header, and a stronger, uniform scrim so the nav
   stays legible against the photo from the very first pixel rather than
   the 25%-opacity top edge that's fine when the header is solid. ---- */
.work-hero--dark-nav {
  min-height: 72vh;
  padding-top: var(--header-h);
}

.work-hero--dark-nav .work-hero__scrim {
  background: linear-gradient(180deg, rgba(10, 15, 26, 0.6) 0%, rgba(10, 15, 26, 0.92) 100%);
}

/* ---- 31.18 Expandable nav items (Operations/Analytics/Risk Management) --
   desktop flyout. The mobile accordion variant of this same markup lives in
   views/layout.ejs's inline @media block, not here, for the same reason the
   rest of the hamburger-mode nav does (see that file's comment) -- it needs
   navBreakpoint, an admin-configurable value CSS can't reference from a
   static file. ---- */

.header__item {
  position: relative;
  display: flex;
  align-items: center;
}

.header__caret {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 1.5rem;
  height: 1.5rem;
  margin-left: 0.1rem;
  padding: 0;
  background: none;
  border: 0;
  color: inherit;
  cursor: pointer;
  flex: none;
}

.header__caret span {
  width: 7px;
  height: 7px;
  border-right: 1.5px solid currentColor;
  border-bottom: 1.5px solid currentColor;
  transform: rotate(45deg) translateY(-2px);
  transition: transform 0.2s var(--ease);
}

.header__item.is-open .header__caret span {
  transform: rotate(225deg) translateY(2px);
}

.header__dropdown {
  display: none;
  position: absolute;
  top: 100%;
  left: 0;
  min-width: 16rem;
  margin-top: 0.7rem;
  padding: 0.5rem;
  background: #fff;
  border: 1px solid var(--rule);
  border-radius: var(--radius-lg);
  box-shadow: 0 24px 40px -28px rgba(10, 27, 60, 0.4);
  z-index: 10;
}

.header__item.is-open .header__dropdown {
  display: block;
}

.header__dropdown-link {
  display: block;
  padding: 0.65rem 0.85rem;
  border-radius: 6px;
  font-size: 0.92rem;
  font-weight: 500;
  color: var(--ink);
  transition: background-color 0.16s var(--ease), color 0.16s var(--ease);
}

.header__dropdown-link:hover,
.header__dropdown-link.is-current {
  background: var(--ice);
  color: var(--cobalt);
}

/* Caret needs the same transparent-header color swaps .header__link already
   gets (styles.css:547-566) -- it isn't nested inside .header__link, so it
   doesn't inherit those rules for free. Same white-on-white trap the mobile
   panel's own .header__link rule exists to avoid: the panel always renders
   solid white regardless of .header--transparent, so the last rule here
   matters just as much as the first two. */
.header--transparent .header__caret {
  color: var(--color-text-white);
}

.header--transparent.nav--scrolled .header__caret {
  color: var(--ink);
}

.header--transparent .header__nav.is-open .header__caret {
  color: var(--ink);
}
