/* ==========================================================================
   Legacy Aviation Group — components & layout
   ========================================================================== */

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

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

body {
  margin: 0;
  background: var(--white);
  color: var(--black);
  font-family: var(--font-body);
  font-size: var(--text-rg);
  font-weight: var(--w-light);
  line-height: var(--leading-body);
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  overflow-x: hidden;
}

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

button { font: inherit; color: inherit; background: none; border: 0; cursor: pointer; }
a { color: inherit; text-decoration: none; }
ul { margin: 0; padding: 0; list-style: none; }

h1, h2, h3, h4, h5, h6 { margin: 0; font-family: var(--font-heading); font-weight: var(--w-display); }

/* The UA stylesheet gives blockquote a 40px side margin, which knocks the pull
   quote out of alignment with everything above it. */
blockquote, figure, dl, dd { margin: 0; }

::selection { background: var(--brand-light); color: var(--white); }

:focus-visible {
  outline: 1px solid var(--brand-light);
  outline-offset: 4px;
}

/* --- Layout -------------------------------------------------------------- */
.container {
  width: 100%;
  max-width: var(--container);
  margin-inline: auto;
  padding-inline: var(--gutter);
}

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

.section { position: relative; }
.section--lg { padding-block: var(--section-lg); }
.section--md { padding-block: var(--section-md); }
.section--sm { padding-block: var(--section-sm); }

.section--dark { background: var(--neutral-dark); color: var(--white); }
.section--bone { background: var(--neutral-lightest); }
.section--brand { background: var(--brand-dark); color: var(--white); }

/* --- Type ---------------------------------------------------------------- */
.h1, .h2, .h3, .h4, .h5 {
  font-family: var(--font-heading);
  font-weight: var(--w-display);
}

.h1 {
  font-size: var(--h1);
  line-height: var(--leading-display);
  letter-spacing: var(--track-display);
}

.h2 {
  font-size: var(--h2);
  line-height: var(--leading-heading);
  letter-spacing: var(--track-h2);
}

.h3 {
  font-size: var(--h3);
  line-height: 1.18;
  letter-spacing: var(--track-h2);
}

.h4 {
  font-size: var(--h4);
  line-height: 1.25;
  letter-spacing: -0.015em;
}

.h5 {
  font-size: var(--h5);
  font-weight: var(--w-light);
  line-height: 1.35;
  letter-spacing: -0.012em;
}

/* LUX — the italic serif accent. On the live site it only appears on aircraft
   names; here it also carries one word per headline, which is the single
   biggest lift in perceived luxury and stays inside the brand's own type. */
.accent {
  font-family: var(--font-accent);
  font-style: italic;
  font-weight: 300;
  letter-spacing: -0.01em;
}

.accent--brand { color: var(--brand-light); }

.eyebrow {
  font-size: var(--text-rg);
  font-weight: var(--w-regular);
  letter-spacing: var(--track-eyebrow);
  text-transform: uppercase;
  color: var(--dark-70);
}

.eyebrow--light { color: var(--white-50); }
/* --brand-light on white measures 3.08:1 at this size — below the 4.5:1
   floor for text this small. --brand-medium clears 5.8:1 and reads as the
   same brand blue, just deep enough to stay legible. */
.eyebrow--brand { color: var(--brand-medium); }
.eyebrow--sm { font-size: var(--text-sm); }

.lead {
  font-size: var(--text-lg);
  font-weight: var(--w-light);
  line-height: 1.45;
}

.body-md { font-size: var(--text-md); font-weight: var(--w-light); }
.text-white { color: var(--white); }
.text-muted { color: var(--dark-70); }
.text-muted-light { color: var(--white-70); }
.max-w-lead { max-width: 44rem; }
.max-w-xl { max-width: 60rem; }
.center { text-align: center; margin-inline: auto; }

/* --- Rules --------------------------------------------------------------- */
/* LUX — hairline dividers instead of hard borders. */
.rule {
  height: var(--hairline);
  background: var(--dark-10);
  border: 0;
  margin: 0;
}

.rule--light { background: var(--white-10); }

/* --- Buttons ------------------------------------------------------------- */
.btn {
  --btn-fill: var(--brand-light);
  --btn-ink: var(--white);
  position: relative;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--space-2xs);
  padding: 1rem 2.25rem;
  border: var(--hairline) solid var(--btn-fill);
  border-radius: var(--radius-pill);
  background: var(--btn-fill);
  color: var(--btn-ink);
  font-size: var(--text-md);
  font-weight: var(--w-light);
  line-height: 1.2;
  text-align: center;
  overflow: hidden;
  isolation: isolate;
  transition: color var(--dur-md) var(--ease-lux),
              border-color var(--dur-md) var(--ease-lux);
}

/* LUX — a fill that sweeps up from the baseline, replacing the live site's
   transform: scale(1.05) hover. Scaling reads cheap; a wipe reads tailored. */
.btn::after {
  content: "";
  position: absolute;
  inset: 0;
  z-index: -1;
  background: var(--brand-medium);
  transform: translateY(101%);
  transition: transform var(--dur-md) var(--ease-lux);
}

.btn:hover::after,
.btn:focus-visible::after { transform: translateY(0); }
.btn:hover { border-color: var(--brand-medium); }

.btn--secondary {
  --btn-fill: transparent;
  --btn-ink: var(--black);
  border-color: var(--dark-30);
}

.btn--secondary:hover { --btn-ink: var(--white); border-color: var(--brand-medium); }

.btn--alt {
  --btn-fill: transparent;
  --btn-ink: var(--white);
  border-color: var(--white-40);
}

.btn--alt:hover { border-color: var(--white); }
.btn--alt::after { background: var(--white); }
.btn--alt:hover { --btn-ink: var(--black); }

.btn--sm { padding: 0.75rem 1.625rem; font-size: var(--text-rg); }

.btn-group {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--space-lg) var(--space-2xl);
}

.btn-group--center { justify-content: center; }

/* Text link with a rule that draws in on hover. */
.link {
  display: inline-flex;
  align-items: center;
  gap: var(--space-2xs);
  padding-block: var(--space-3xs);
  font-size: var(--text-rg);
  letter-spacing: 0.02em;
}

.link::after {
  content: "";
  width: 0;
  height: var(--hairline);
  background: currentColor;
  transition: width var(--dur-md) var(--ease-lux);
}

.link:hover::after { width: 1.5rem; }

.link-arrow {
  display: inline-block;
  transition: transform var(--dur-md) var(--ease-lux);
}

.link:hover .link-arrow,
.list-link:hover .link-arrow { transform: translateX(0.375rem); }

/* NAV COLLAPSE POINT — 1150px, not 991px.
   Six links plus a button and a phone number do not fit comfortably below this:
   at 1024px only 16px separated "Client Experience" from the logo. Everything
   tied to the desktop bar collapsing has to move together — the menu, the
   toggle, the drawer, the mega panels and the mobile action bar — or the site
   ends up in a state with no navigation at all. */

/* --- Nav ----------------------------------------------------------------- */
.nav {
  position: fixed;
  inset: 0 0 auto;
  z-index: var(--z-nav);
  min-height: var(--nav-h);
  display: flex;
  align-items: center;
  background: var(--neutral-dark);
  color: var(--white);
  /* LUX — the bar itself (logo, hamburger, phone/CTA) stays put and visible
     at every scroll position, including the very top. Only the solid/blurred
     backdrop behind it is scroll-triggered (.is-solid below) — at the top,
     the ::before gradient scrim carries contrast against the hero instead. */
  background: transparent;
  transition: background var(--dur-md) var(--ease-lux),
              min-height var(--dur-md) var(--ease-lux),
              backdrop-filter var(--dur-md) var(--ease-lux);
}

.nav::before {
  content: "";
  position: absolute;
  inset: 0;
  background: linear-gradient(to bottom, rgb(0 0 0 / 0.55), transparent);
  opacity: 1;
  transition: opacity var(--dur-md) var(--ease-lux);
  pointer-events: none;
}

.nav.is-solid {
  background: rgb(61 60 68 / 0.92);
  backdrop-filter: blur(14px) saturate(140%);
  min-height: 4.75rem;
}

.nav.is-solid::before { opacity: 0; }

/* With the drawer open the bar has to seat against it, or the hero film shows
   through the gap between the two. No backdrop-filter here — that property
   (like filter/transform/contain) makes its element a containing block for
   fixed-position descendants, and .nav__drawer is fixed + inset:0 expecting
   the viewport as that block. With it here, `bottom:0` resolved against the
   bar's own ~76px box instead, collapsing the drawer to the bar's height. */
.nav.is-open {
  background: rgb(61 60 68 / 0.98);
}

.nav.is-open::before { opacity: 0; }

.nav__inner {
  position: relative;
  width: 100%;
  max-width: var(--container);
  margin-inline: auto;
  padding-inline: var(--gutter);
  display: grid;
  /* Hamburger-only at every breakpoint — no left/right link menus. The two
     outer tracks stay equal-width so the logo centres on the bar regardless
     of how wide the toggle or the phone/CTA group happens to be. */
  grid-template-columns: 1fr max-content 1fr;
  align-items: center;
  gap: var(--space-sm);
}

.nav__toggle { justify-self: start; }

.nav__actions {
  display: flex;
  align-items: center;
  justify-content: flex-end;
  gap: var(--space-md);
  justify-self: end;
}

.nav__logo img { height: 3rem; width: auto; }

.nav__toggle {
  display: grid;
  width: 2.5rem;
  height: 2.5rem;
  place-items: center;
}

.nav__toggle span,
.nav__toggle span::before,
.nav__toggle span::after {
  display: block;
  width: 1.375rem;
  height: 1px;
  background: var(--white);
  transition: transform var(--dur-md) var(--ease-lux),
              opacity var(--dur-fast) linear;
}

.nav__toggle span { position: relative; }
.nav__toggle span::before,
.nav__toggle span::after { content: ""; position: absolute; left: 0; }
.nav__toggle span::before { top: -6px; }
.nav__toggle span::after { top: 6px; }

.nav.is-open .nav__toggle span { background: transparent; }
.nav.is-open .nav__toggle span::before { transform: translateY(6px) rotate(45deg); }
.nav.is-open .nav__toggle span::after { transform: translateY(-6px) rotate(-45deg); }

/* --- Hero ---------------------------------------------------------------- */
.hero {
  position: relative;
  min-height: 100svh;
  display: flex;
  align-items: center;
  background: var(--neutral-dark);
  color: var(--white);
  overflow: hidden;
}

.hero__media {
  position: absolute;
  inset: 0;
  z-index: 0;
}

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

/* The slow push-in belongs to stills only. It used to apply to <video> as well,
   on the reasoning that motionless footage reads like a screenshot — but every
   beat in the hero rotation now carries its own movement, either a real camera
   move in the footage or a baked-in zoom for the photo beats, so on video this
   was a second zoom compounding on the first.

   It was also the source of the residual shake. Animating `transform: scale()`
   on a video layer makes the compositor re-rasterise the frame at discrete
   scale steps, which reads as jitter on fine detail — aircraft panel lines,
   tarmac, grass. Worse, it was scaling past native: measured mid-animation at
   scale(1.103), a 1920x1080 clip was being rendered at 2204x1423, so the
   browser was upscaling every frame by 15% and softening it. Encoding was ruled
   out first — re-encoding the same frames at 6x the bitrate changed the
   frame-to-frame error by 0.196 -> 0.205, i.e. not at all. */
.hero__media img {
  animation: kenburns 32s var(--ease-inout) infinite alternate;
}

@keyframes kenburns {
  from { transform: scale(1.02); }
  to { transform: scale(1.12); }
}

/* LUX — cinematic grade over the footage. The hero film cuts between dark
   hangar frames and bright cabin frames, so the scrim has to hold contrast for
   ultralight white type on the worst frame, not the average one: a flat base
   darkening, a heavy left column where the copy sits, and a bottom lift. */
.hero__scrim {
  position: absolute;
  inset: 0;
  z-index: 1;
  background:
    linear-gradient(to right, rgb(0 0 0 / 0.85) 0%, rgb(0 0 0 / 0.6) 38%, rgb(0 0 0 / 0.15) 72%, transparent 100%),
    linear-gradient(to top, rgb(0 0 0 / 0.65) 0%, transparent 60%),
    rgb(0 0 0 / 0.28);
}

.hero__vignette {
  position: absolute;
  inset: 0;
  z-index: 2;
  background: radial-gradient(120% 90% at 50% 45%, transparent 45%, rgb(0 0 0 / 0.45) 100%);
  pointer-events: none;
}

.hero__inner {
  position: relative;
  z-index: 3;
  width: 100%;
  padding-block: calc(var(--nav-h) + var(--space-2xl)) var(--section-md);
}

.hero__content {
  max-width: 60rem;
  /* Half the dissolve, so the headline's fade out and fade back in together
     fill exactly the window the picture takes to blend (main.js derives its
     text timing the same way). --ease-inout rather than --ease-lux: lux is a
     strong ease-out built for things that move, and on opacity it dumps most
     of the change into the first fraction of the duration — measured, the
     headline was already down to 0.31 after 112ms of a 480ms fade, which reads
     as a snap followed by a long tail rather than a fade. */
  transition: opacity calc(var(--dur-hero-dissolve) / 2) var(--ease-inout);
}

/* The drawer sits over the hero's left edge — same side the headline reads
   from. Rather than shifting or resizing the headline to dodge it (both
   read as the page reacting oddly to a menu opening), the headline itself
   recedes, so there is nothing left half-covered to look like a mistake. */
body.nav-open .hero__content { opacity: 0.12; }

@media (prefers-reduced-motion: reduce) {
  .hero__content { transition: none; }
}

/* Wherever the caption rail is present, the headline has to stop short of it.
   A breakpoint cannot do this reliably — the headline rewraps at every width, so
   it cleared the caption at 1440 and collided again at 1200. Reserving the rail's
   width is viewport-independent: the headline simply never reaches it. */
@media (min-width: 1151px) {
  .hero__content { max-width: min(60rem, calc(100% - 18rem)); }
}

.hero .h1 { margin-bottom: var(--space-xl); }

.hero__lead {
  max-width: 34rem;
  margin-bottom: var(--space-2xl);
  color: var(--white-70);
}

/* Vertical slide indicators pinned to the right edge, as on the live site. */
.hero__dots {
  display: flex;
  flex-direction: column;
  gap: var(--space-sm);
}

.hero__dot {
  width: 1px;
  height: 2.5rem;
  background: var(--white-30);
  transition: background var(--dur-md) var(--ease-lux),
              height var(--dur-md) var(--ease-lux);
}

.hero__dot.is-active { background: var(--brand-light); height: 4rem; }

/* LUX — a hairline scroll cue anchored to the baseline of the hero. */
.hero__cue {
  position: absolute;
  bottom: var(--space-xl);
  left: 50%;
  z-index: 4;
  transform: translateX(-50%);
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: var(--space-xs);
  font-size: var(--text-xs);
  letter-spacing: var(--track-eyebrow);
  text-transform: uppercase;
  color: var(--white-50);
}

.hero__cue-line {
  width: 1px;
  height: 3.5rem;
  background: linear-gradient(to bottom, var(--white-40), transparent);
}

.hero__cue-line::after {
  content: "";
  display: block;
  width: 1px;
  height: 1rem;
  background: var(--white);
  animation: cue 2.8s var(--ease-inout) infinite;
}

@keyframes cue {
  0%   { transform: translateY(-1rem); opacity: 0; }
  40%  { opacity: 1; }
  100% { transform: translateY(3.5rem); opacity: 0; }
}

/* --- Logo strip ---------------------------------------------------------- */
.logo-strip { text-align: center; }
.logo-strip .eyebrow { margin-bottom: var(--space-xl); }

/* --- Logo marquee ------------------------------------------------------- */
/* The press and partner marks used to sit in a static centred grid. They now
   drift continuously, which reads current and lets the eight press marks share
   one line instead of wrapping to two. */
.marquee {
  overflow: hidden;
  /* Fade both ends so marks enter and leave rather than being chopped off. */
  mask-image: linear-gradient(to right, transparent, #000 8%, #000 92%, transparent);
  -webkit-mask-image: linear-gradient(to right, transparent, #000 8%, #000 92%, transparent);
}

.logo-strip__row {
  display: flex;
  flex-wrap: nowrap;
  align-items: center;
  justify-content: flex-start;
  gap: var(--space-2xl) var(--space-3xl);
  width: max-content;
  /* Shift by exactly one copy of the set, so the wrap is seamless. Duration is
     set per strip so every marquee drifts at an identical speed regardless of
     how many marks it holds. */
  animation: marquee var(--marquee-dur, 80s) linear infinite;
}

@keyframes marquee {
  to { transform: translateX(calc(-100% / var(--copies))); }
}

/* Hold still so a mark can be read, and never run it for anyone who asked for
   less motion — there it falls back to the original centred, wrapping row. */
.marquee:hover .logo-strip__row { animation-play-state: paused; }

@media (prefers-reduced-motion: reduce) {
  .marquee { mask-image: none; -webkit-mask-image: none; }
  .logo-strip__row {
    animation: none;
    flex-wrap: wrap;
    justify-content: center;
    width: auto;
    padding-inline: var(--gutter);
  }
  /* The duplicate copies are decoration; without motion they are just repeats. */
  .logo-strip__row img[aria-hidden="true"] { display: none; }
}

/* The four partner marks range from 1:1 to 6.6:1, so a fixed height makes the
   wide ones dwarf the square ones. Constrain both axes and let each mark find
   its own size inside a shared optical box. */
.logo-strip__row img {
  width: auto;
  height: auto;
  max-height: 3rem;
  max-width: 8.5rem;
  object-fit: contain;
  /* LUX — desaturated at rest, colour on hover: the editorial convention for
     a press / partner row. */
  filter: grayscale(1);
  opacity: 0.55;
  transition: opacity var(--dur-md) var(--ease-lux),
              filter var(--dur-md) var(--ease-lux);
}

.logo-strip__row img:hover { opacity: 1; filter: grayscale(0); }

/* --- Split content (The Legacy Aviation Difference) --------------------- */
.split {
  display: grid;
  grid-template-columns: 1fr 1fr;
  align-items: center;
  gap: var(--space-4xl);
}

.split__copy > * + * { margin-top: var(--space-md); }
.split__copy .eyebrow + .h2 { margin-top: var(--space-md); }
.split__copy .btn-group { margin-top: var(--space-xl); }

/* Two overlapping frames, matching the live site's staggered photo pair. */
.frames {
  position: relative;
  padding-bottom: 22%;
  padding-right: 12%;
}

.frames__lg {
  position: relative;
  z-index: 1;
  width: 88%;
  aspect-ratio: 627 / 485;
  object-fit: cover;
}

.frames__sm {
  position: absolute;
  right: 0;
  bottom: 0;
  z-index: 2;
  width: 52%;
  aspect-ratio: 1;
  object-fit: cover;
  /* LUX — a hairline mat around the overlapping frame so the two photos read
     as deliberately layered rather than accidentally colliding. */
  border: 8px solid var(--white);
}

.section--bone .frames__sm { border-color: var(--neutral-lightest); }

/* --- Journey tiles ------------------------------------------------------- */
.journey__head {
  max-width: 52rem;
  margin-bottom: var(--section-sm);
}

.journey__head .h2 { margin-bottom: var(--space-md); }

.tiles {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: var(--space-2xs);
}

.tile {
  position: relative;
  display: flex;
  align-items: flex-end;
  min-height: 420px;
  padding: var(--space-xl);
  background: var(--neutral-dark);
  color: var(--white);
  overflow: hidden;
}

.tile__img {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  opacity: 0.4;
  transition: opacity var(--dur-slow) var(--ease-lux),
              transform var(--dur-slow) var(--ease-lux);
}

.tile:hover .tile__img { opacity: 0.55; transform: scale(1.06); }

/* LUX — a bottom-weighted scrim under the label. The culinary and golf frames
   are bright enough to swallow white type on their own. */
.tile::after {
  content: "";
  position: absolute;
  inset: 0;
  background: linear-gradient(to top, rgb(0 0 0 / 0.75) 0%, rgb(0 0 0 / 0.25) 42%, transparent 70%);
  pointer-events: none;
}

.tile__body {
  position: relative;
  z-index: 2;
  width: 100%;
}

.tile__title { margin-bottom: var(--space-sm); }

/* LUX — the label sits low and a hairline + link reveal on hover, so the
   tile has a resting state and an invited state. */
.tile__reveal {
  display: flex;
  align-items: center;
  gap: var(--space-2xs);
  font-size: var(--text-sm);
  letter-spacing: var(--track-eyebrow);
  text-transform: uppercase;
  color: var(--white-70);
  opacity: 0;
  transform: translateY(0.5rem);
  transition: opacity var(--dur-md) var(--ease-lux),
              transform var(--dur-md) var(--ease-lux);
}

.tile:hover .tile__reveal,
.tile:focus-visible .tile__reveal { opacity: 1; transform: translateY(0); }

/* --- Programs ------------------------------------------------------------ */
.programs {
  display: grid;
  grid-template-columns: 1fr 1fr;
  align-items: center;
  gap: var(--space-4xl);
}

.programs__copy .eyebrow { margin-bottom: var(--space-md); }
.programs__copy .h2 { margin-bottom: var(--space-md); }
.programs__copy .lead { margin-bottom: var(--space-xl); }

.program-list { border-top: var(--hairline) solid var(--white-10); }

.list-link {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-md);
  padding-block: var(--space-md);
  border-bottom: var(--hairline) solid var(--white-10);
  font-size: var(--text-md);
  font-weight: var(--w-light);
  transition: padding-left var(--dur-md) var(--ease-lux),
              color var(--dur-md) var(--ease-lux);
}

.list-link:hover { padding-left: var(--space-sm); color: var(--brand-light); }

.programs__media img {
  width: 100%;
  aspect-ratio: 4 / 3;
  object-fit: cover;
}

/* --- Aircraft slider ---------------------------------------------------- */
.fleet__head {
  display: flex;
  align-items: flex-end;
  justify-content: space-between;
  gap: var(--space-lg);
  margin-bottom: var(--section-sm);
}

.fleet__head .eyebrow { margin-bottom: var(--space-md); }

.fleet {
  display: grid;
  grid-template-columns: minmax(0, 1.35fr) minmax(0, 1fr);
  align-items: center;
  gap: var(--space-4xl);
}

.fleet__stage {
  position: relative;
  min-height: 22rem;
  display: grid;
  place-items: center;
}

.fleet__slide {
  grid-area: 1 / 1;
  width: 100%;
  text-align: center;
  opacity: 0;
  visibility: hidden;
  transform: translateX(3rem);
  transition: opacity var(--dur-slow) var(--ease-lux),
              transform var(--dur-slow) var(--ease-lux),
              visibility var(--dur-slow) step-end;
}

.fleet__slide.is-active {
  opacity: 1;
  visibility: visible;
  transform: translateX(0);
  transition: opacity var(--dur-slow) var(--ease-lux),
              transform var(--dur-slow) var(--ease-lux),
              visibility 0s step-start;
}

.fleet__slide img {
  width: 100%;
  max-width: 42rem;
  margin-inline: auto;
  /* LUX — the cutouts are on transparent ground; a soft contact shadow keeps
     them from looking pasted onto the page. */
  filter: drop-shadow(0 24px 40px rgb(0 0 0 / 0.16));
}

.fleet__cat { margin-bottom: var(--space-2xs); }

.fleet__name {
  font-size: var(--accent-size);
  line-height: 1.1;
  margin-bottom: var(--space-lg);
}

/* Round outline arrows, 5.25rem, as on the live build. The pair is split to the
   top and bottom of the stage so it frames the aircraft rather than landing on
   top of the tail. */
.fleet__nav {
  position: absolute;
  right: 0;
  top: 0;
  bottom: 0;
  display: flex;
  flex-direction: column;
  justify-content: space-between;
  pointer-events: none;
}

.fleet__nav .fleet__arrow { pointer-events: auto; }

.fleet__arrow {
  display: grid;
  place-items: center;
  width: 5.25rem;
  height: 5.25rem;
  border: var(--hairline) solid var(--dark-30);
  border-radius: 50%;
  background: var(--white-70);
  backdrop-filter: blur(6px);
  color: var(--black);
  transition: background var(--dur-md) var(--ease-lux),
              border-color var(--dur-md) var(--ease-lux),
              color var(--dur-md) var(--ease-lux);
}

/* LUX — hover shifts the arrow to brand fill rather than scaling it. */
.fleet__arrow:hover {
  background: var(--brand-light);
  border-color: var(--brand-light);
  color: var(--white);
}

.fleet__arrow svg { width: 1.25rem; height: 1.25rem; }

.specs { border-top: var(--hairline) solid var(--dark-15); }

.spec {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: var(--space-md);
  padding-block: var(--space-md);
  border-bottom: var(--hairline) solid var(--dark-15);
}

.spec__label {
  font-size: var(--text-sm);
  letter-spacing: var(--track-eyebrow);
  text-transform: uppercase;
  color: var(--dark-70);
}

.spec__value {
  font-size: var(--text-md);
  font-weight: var(--w-light);
  text-align: right;
  /* LUX — tabular figures so the numbers hold a column as slides change. */
  font-variant-numeric: tabular-nums;
}

.specs__cta { margin-top: var(--space-xl); }

.fleet__counter {
  display: flex;
  align-items: center;
  gap: var(--space-sm);
  margin-bottom: var(--space-lg);
  font-size: var(--text-sm);
  letter-spacing: var(--track-eyebrow);
  color: var(--dark-70);
  font-variant-numeric: tabular-nums;
}

.fleet__counter-rule {
  flex: 1;
  height: var(--hairline);
  background: var(--dark-15);
  position: relative;
  overflow: hidden;
}

.fleet__counter-rule::after {
  content: "";
  position: absolute;
  inset: 0 auto 0 0;
  width: var(--progress, 20%);
  background: var(--brand-light);
  transition: width var(--dur-slow) var(--ease-lux);
}

/* --- Testimonial -------------------------------------------------------- */
.quote { max-width: 62rem; }

/* Ethic New draws its quote glyph small inside the em box, so the mark is
   oversized and its line box collapsed to sit tight above the quote. */
.quote__mark {
  font-family: var(--font-accent);
  font-style: italic;
  font-size: 11rem;
  line-height: 0.42;
  height: 2.5rem;
  color: var(--brand-light);
  margin: 0 0 var(--space-sm);
}

.quote__text { margin-bottom: var(--space-xl); font-weight: var(--w-display); }

.quote__author {
  display: flex;
  align-items: center;
  gap: var(--space-sm);
}

.quote__avatar {
  display: grid;
  place-items: center;
  width: 3.5rem;
  height: 3.5rem;
  border: var(--hairline) solid var(--dark-30);
  border-radius: 50%;
  font-size: var(--text-sm);
  letter-spacing: 0.08em;
  color: var(--dark-70);
}

.quote__name { font-weight: var(--w-medium); }
.quote__role { font-size: var(--text-sm); color: var(--dark-70); }

/* --- CTA ---------------------------------------------------------------- */
.cta {
  position: relative;
  overflow: hidden;
  text-align: center;
}

/* A screen blend at 0.25 lifted the whole panel off #00445a into a flat mid
   teal. Keep the cloud motion as a faint light source only, then re-seat the
   depth with a vignette so the panel reads deep rather than washed. */
.cta__clouds {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  opacity: 0.14;
  mix-blend-mode: screen;
}

.cta::after {
  content: "";
  position: absolute;
  inset: 0;
  background: radial-gradient(120% 100% at 50% 40%, transparent 30%, rgb(0 24 34 / 0.55) 100%);
  pointer-events: none;
}

.cta__inner { position: relative; z-index: 1; }
.cta .h2 { margin-bottom: var(--space-md); }
.cta .lead { margin-bottom: var(--space-xl); }

/* --- FAQ ---------------------------------------------------------------- */
.faq {
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(0, 1.4fr);
  gap: var(--space-4xl);
  align-items: start;
}

.faq__head .h2 { margin-bottom: var(--space-md); }

.faq__list { border-top: var(--hairline) solid var(--dark-15); }

.faq__item { border-bottom: var(--hairline) solid var(--dark-15); }

.faq__trigger {
  width: 100%;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-lg);
  padding-block: var(--space-lg);
  text-align: left;
  transition: color var(--dur-md) var(--ease-lux);
}

.faq__trigger:hover { color: var(--brand-medium); }

/* LUX — a thin plus that rotates to a minus; no chunky chevron. */
.faq__icon {
  position: relative;
  flex: none;
  width: 1rem;
  height: 1rem;
}

.faq__icon::before,
.faq__icon::after {
  content: "";
  position: absolute;
  inset: 50% 0 auto;
  height: 1px;
  background: currentColor;
  transition: transform var(--dur-md) var(--ease-lux),
              opacity var(--dur-md) var(--ease-lux);
}

.faq__icon::after { transform: rotate(90deg); }

.faq__item.is-open .faq__icon::after { transform: rotate(0deg); opacity: 0; }

.faq__panel {
  overflow: hidden;
  height: 0;
  transition: height var(--dur-md) var(--ease-lux);
}

.faq__panel-inner {
  padding-bottom: var(--space-lg);
  padding-right: var(--space-2xl);
  color: var(--dark-70);
  opacity: 0;
  transform: translateY(-0.5rem);
  transition: opacity var(--dur-md) var(--ease-lux),
              transform var(--dur-md) var(--ease-lux);
}

.faq__item.is-open .faq__panel-inner { opacity: 1; transform: translateY(0); }

.faq__still { margin-top: var(--section-sm); }
.faq__still .h3 { margin-bottom: var(--space-2xs); }
.faq__still .lead { margin-bottom: var(--space-md); }

/* --- Footer ------------------------------------------------------------- */
.footer {
  background: var(--neutral-dark);
  color: var(--white);
  padding-block: var(--section-md) var(--space-lg);
}

.footer__newsletter {
  display: flex;
  align-items: center;
  justify-content: space-between;
  flex-wrap: wrap;
  gap: var(--space-lg);
  padding-bottom: var(--section-sm);
  margin-bottom: var(--section-sm);
  border-bottom: var(--hairline) solid var(--white-10);
}

.footer__newsletter .h4 { margin-bottom: var(--space-2xs); }
.footer__newsletter p { margin: 0; font-size: var(--text-sm); color: var(--white-50); max-width: 30rem; }

.subscribe {
  display: flex;
  align-items: center;
  gap: var(--space-sm);
  min-width: min(100%, 28rem);
}

.subscribe input {
  flex: 1;
  height: 3rem;
  padding-inline: var(--space-md);
  background: transparent;
  border: var(--hairline) solid var(--white-30);
  border-radius: var(--radius-pill);
  color: var(--white);
  font: inherit;
  transition: border-color var(--dur-md) var(--ease-lux);
}

.subscribe input::placeholder { color: var(--white-40); }
.subscribe input:focus { outline: none; border-color: var(--brand-light); }

.footer__cols {
  display: grid;
  grid-template-columns: repeat(5, 1fr);
  gap: var(--space-lg);
  align-items: start;
  padding-bottom: var(--section-sm);
}

.footer__col-title {
  display: block;
  margin-bottom: var(--space-md);
  font-size: var(--text-sm);
  font-weight: var(--w-regular);
  letter-spacing: var(--track-eyebrow);
  text-transform: uppercase;
  /* --brand-light on the footer's --neutral-dark only measures 3.54:1 — below
     WCAG AA's 4.5:1 floor for text this size. This tint (30% white mixed in)
     clears 4.9:1 while still reading as the brand blue. */
  color: #4cbbe0;
  transition: padding-left var(--dur-md) var(--ease-lux);
}

.footer__col-title:hover { padding-left: var(--space-2xs); }

.footer__link {
  display: block;
  padding-block: var(--space-2xs);
  font-size: var(--text-sm);
  color: var(--white-70);
  transition: color var(--dur-md) var(--ease-lux),
              padding-left var(--dur-md) var(--ease-lux);
}

.footer__link:hover { color: var(--white); padding-left: var(--space-2xs); }

.footer__bottom {
  display: flex;
  align-items: center;
  justify-content: space-between;
  flex-wrap: wrap;
  gap: var(--space-md);
  padding-top: var(--space-lg);
  border-top: var(--hairline) solid var(--white-10);
}

.footer__credit { font-size: var(--text-sm); color: var(--white-50); }

.footer__legal {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-md);
}

.footer__legal a { font-size: var(--text-sm); color: var(--white-70); }
.footer__legal a:hover { color: var(--white); }

.footer__logo img { height: 2.5rem; width: auto; }

/* --- Scroll reveal ------------------------------------------------------ */
/* LUX — content rises into place on a long curve. Headlines reveal line by
   line via [data-reveal="lines"], which JS splits and staggers.
   Every rule here is scoped to `.js`, which main.js adds to <html> and removes
   again if it throws. Without working JS nothing is ever hidden, so the page
   can never render blank. */
.js [data-reveal] {
  opacity: 0;
  transform: translateY(1.5rem);
  transition: opacity var(--dur-reveal) var(--ease-lux),
              transform var(--dur-reveal) var(--ease-lux);
}

.js [data-reveal].is-in { opacity: 1; transform: none; }

/* The mask that lets each line rise independently also clips whatever falls
   below the line box — so descenders were being cut off: measured 8px lost from
   the "g" in "Premium Long Range" and 14px from the italic "J" in "Heavy Jet".
   The padding gives the mask room for them and the matching negative margin
   takes it straight back out of layout, so line spacing is unchanged and only
   the clipping goes away. In em so it scales with every heading size.

   Bottom only, deliberately. Top padding would let the pre-reveal line (which
   sits translated down by 110%) peek above the mask before it animates in. */
.reveal-line {
  display: block;
  overflow: hidden;
  padding-bottom: 0.18em;
  margin-bottom: -0.18em;
}

.js .reveal-line > span {
  display: block;
  transform: translateY(110%);
  transition: transform var(--dur-reveal) var(--ease-lux);
}

.js [data-reveal="lines"] { opacity: 1; transform: none; }
.js [data-reveal="lines"].is-in .reveal-line > span { transform: translateY(0); }

/* --- Nav drawer ----------------------------------------------------------
   The single primary-nav surface at every breakpoint — there is no separate
   desktop menu to fall back to above 1150px anymore. Full viewport height
   (not just below the bar), the flat item list pinned to the top under the
   bar, and a glassy blur instead of a near-opaque card. */
.nav__drawer {
  position: fixed;
  /* All four sides pinned — `bottom: auto` here would shrink-wrap the panel
     to its content height instead of spanning the viewport, leaving the
     bottom of the screen uncovered. */
  inset: 0;
  /* Below the bar's z-index (900) so its own controls stay on top and
     clickable once the panel is open. */
  z-index: 800;
  display: grid;
  align-content: center;
  text-align: right;
  /* Headroom for a tall flyout on a short viewport, or Contact Us running
     off-screen with no way to reach it. */
  overflow-y: auto;
  /* Full-bleed below 640px — near-opaque, or the hero's own headline shows
     straight through the empty left two-thirds of the panel and collides
     with the menu text sitting on top of it. */
  background: rgb(61 60 68 / 0.94);
  backdrop-filter: blur(16px) saturate(140%);
  padding: var(--nav-h) var(--gutter) var(--space-2xl);
  gap: var(--space-2xs);
  /* A slide, not a clip-path reveal — clip-path animates the mask but the
     content underneath sits still, which read as flatter and gave the
     stagger nothing to play against. Translating the whole panel gives the
     motion an actual direction, and transform is compositor-only work, so
     it stays smooth regardless of what else is going on in the page. */
  transform: translateX(-100%);
  transition: transform var(--dur-slide) var(--ease-slide),
              background var(--dur-md) var(--ease-lux);
}

.nav__drawer.is-open { transform: translateX(0); }

/* Top-level rows only — a plain link or a Programs/Fleet/Lifestyle group's
   row. Sub-items (further down) are excluded on purpose: they're hidden
   until their group expands, independent of the drawer's own open state. */
.nav__drawer > a,
.nav__drawer-row {
  display: flex;
  align-items: center;
  justify-content: flex-end;
  gap: var(--space-sm);
  padding-block: var(--space-sm);
  border-bottom: var(--hairline) solid var(--white-10);
  font-size: var(--text-lg);
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--white-70);
}

.nav__drawer-link { color: inherit; }

.nav__drawer > a,
.nav__drawer > .nav__drawer-group {
  opacity: 0;
  transform: translateX(0.75rem);
  transition: opacity var(--dur-md) var(--ease-lux),
              transform var(--dur-md) var(--ease-lux);
}

.nav__drawer.is-open > a,
.nav__drawer.is-open > .nav__drawer-group {
  opacity: 1;
  transform: none;
}

/* Staggered across the back half of the panel's own 820ms slide, so items
   are still settling in as the panel arrives rather than popping in early
   and sitting idle while the panel finishes. */
.nav__drawer > *:nth-child(1) { transition-delay: 220ms; }
.nav__drawer > *:nth-child(2) { transition-delay: 270ms; }
.nav__drawer > *:nth-child(3) { transition-delay: 320ms; }
.nav__drawer > *:nth-child(4) { transition-delay: 370ms; }
.nav__drawer > *:nth-child(5) { transition-delay: 420ms; }
.nav__drawer > *:nth-child(6) { transition-delay: 470ms; }
.nav__drawer > *:nth-child(7) { transition-delay: 520ms; }
.nav__drawer > *:nth-child(8) { transition-delay: 570ms; }

/* The one drawer row that's a call action, not a page — picked out in brand
   blue so it doesn't read as just another link in the list. Specificity has
   to match `.nav__drawer > a` (a class plus a type selector) or the base
   white-70 wins regardless of source order. */
.nav__drawer > .nav__drawer-phone { color: var(--brand-light); }
.nav__drawer > .nav__drawer-phone:hover { color: var(--white); }

/* --- Drawer sub-menus: Programs, Fleet, Lifestyle -------------------------
   Expand in place below the trigger row. The caret is a separate control
   from the label on purpose — the label still navigates straight to that
   section's landing page, so a plain click isn't hijacked into "expand" the
   way it would be if the whole row were one combined control. */
/* 2.75rem, not 1.75rem. This is the control that opens Programs, Fleet and
   Lifestyle, and at 28x28 it was the smallest target in the primary navigation
   — under every touch guideline and easy to miss with a thumb. The glyph inside
   is unchanged; only the hit area grew, and the negative margin keeps the row's
   height and the label's alignment exactly as they were. */
.nav__drawer-caret {
  display: inline-flex;
  width: 2.75rem;
  height: 2.75rem;
  margin: -0.5rem -0.5rem -0.5rem 0;
  flex: none;
  align-items: center;
  justify-content: center;
  background: none;
  border: 0;
  color: inherit;
  cursor: pointer;
}

.nav__drawer-caret span {
  width: 0.5rem;
  height: 0.5rem;
  border-right: 1.5px solid currentColor;
  border-bottom: 1.5px solid currentColor;
  transform: rotate(45deg);
  /* Matched to .nav__drawer-sub below. Slower still, and switched off
     ease-glide (a fast-out curve — most of its motion happens in the first
     third) to a true ease-in-out: a gradual start reads as deliberate,
     where a fast start reads as a snap no matter how long the tail is. */
  transition: transform 1400ms var(--ease-inout);
}

.nav__drawer-group.is-expanded .nav__drawer-caret span { transform: rotate(225deg); }

.nav__drawer-sub {
  max-height: 0;
  opacity: 0;
  overflow: hidden;
  text-align: right;
  transition: max-height 1400ms var(--ease-inout),
              opacity 1400ms var(--ease-inout);
}

.nav__drawer-group.is-expanded .nav__drawer-sub {
  /* Generous on purpose — Fleet has 7 categories, some (e.g. "Premium Long
     Range Heavy Jet") wrapping to two lines in the narrower panel, and
     20rem was clipping the bottom of that list with overflow:hidden. This
     needs to clear the tallest real group, not look proportional to it. */
  max-height: 32rem;
  opacity: 1;
}

.nav__drawer-sub a {
  display: block;
  padding-block: var(--space-xs);
  font-size: var(--text-sm);
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--white-50);
  transition: color var(--dur-fast) var(--ease-lux);
}

.nav__drawer-sub a:hover { color: var(--white); }

/* Above 640px a single edge-to-edge column of links looks sparse rather than
   editorial, so the panel narrows to a fixed-width sidebar like Flexjet's
   rather than stretching the full viewport width. Right-aligned text against
   the panel's own right edge — that inner edge, not the screen edge — is
   Flexjet's actual layout, not an accident of a full-bleed drawer. */
@media (min-width: 640px) {
  .nav__drawer {
    /* "Client Experience" needs ~225px and was landing right on a subpixel
       rounding boundary against the available width at both 20rem and
       21rem — scrollWidth rounds up, getBoundingClientRect doesn't, and the
       two kept disagreeing by a fraction of a pixel right at the edge.
       22rem clears it with real margin instead of chasing that boundary. */
    width: min(22rem, 85vw);
    inset: 0 auto 0 0;
    /* Contained to a strip rather than spanning the screen, so it can afford
       to be genuinely glassy without the see-through gap reading as a bug.
       Fading to fully transparent over the last 10% — rather than ending in
       a hard vertical edge — means whatever hero text happens to sit behind
       that edge blends off instead of looking sliced. Masking the element
       (not just its background-color) fades the blur along with the tint,
       so the effect reads as depth, not a layered cutout. */
    background: rgb(61 60 68 / 0.82);
    backdrop-filter: blur(24px) saturate(140%);
    box-shadow: 30px 0 60px -20px rgb(0 0 0 / 0.35);
    -webkit-mask-image: linear-gradient(to right, black 0%, black 90%, transparent 100%);
            mask-image: linear-gradient(to right, black 0%, black 90%, transparent 100%);
    /* The fade zone above eats into the last ~10% of the panel — extra
       right padding keeps the right-aligned text's own edge clear of it, or
       the panel's real menu labels would fade out along with the hero text
       behind them. */
    padding-right: 3rem;
  }
}

@media (prefers-reduced-motion: reduce) {
  .nav__drawer > a,
  .nav__drawer > .nav__drawer-group {
    transition-delay: 0ms !important;
    transform: none;
  }
}

/* --- Responsive -------------------------------------------------------- */
@media (max-width: 1150px) {
  .split,
  .programs,
  .fleet,
  .faq { grid-template-columns: 1fr; gap: var(--space-2xl); }

  .fleet__head { flex-direction: column; align-items: flex-start; }
  .fleet__stage { min-height: 20rem; }

  .fleet__nav {
    position: static;
    transform: none;
    flex-direction: row;
    justify-content: center;
    margin-top: var(--space-md);
  }

  .fleet__arrow { width: 4rem; height: 4rem; }

  .tiles { grid-template-columns: 1fr; }
  .tile { min-height: 300px; }

  .footer__cols { grid-template-columns: repeat(2, 1fr); gap: var(--space-xl) var(--space-lg); }
}

@media (max-width: 767px) {
  .frames { padding-right: 0; padding-bottom: 28%; }
  .frames__lg { width: 82%; }
  .frames__sm { width: 58%; border-width: 6px; }

  .btn-group { gap: var(--space-sm); }
  .btn { width: 100%; }
  .hero__dots { display: none; }
  /* Full-width buttons reach the centre line, so the cue would draw straight
     through them. */
  .hero__cue { display: none; }
  .footer__cols { grid-template-columns: 1fr; }
  .quote__text { font-size: var(--h4); }
}

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

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

  .js [data-reveal] { opacity: 1; transform: none; }
  .js .reveal-line > span { transform: none; }
}

/* build.mjs wraps images in <picture> to serve AVIF. `display: contents` keeps
   the wrapper out of layout entirely, so the <img> stays the flex/grid item and
   every existing selector and layout rule continues to apply unchanged. */
picture { display: contents; }

/* The poster is a real <img> so it is a discoverable, high-priority preload
   target (a <video poster> is not). It cross-fades out once the film is playing. */
.hero__poster {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  animation: kenburns 32s var(--ease-inout) infinite alternate;
}

.hero__media.is-playing .hero__poster {
  opacity: 0;
  transition: opacity var(--dur-slow) var(--ease-lux);
}

/* Absolute, not relative. main.js runs two stacked <video> layers and
   cross-fades between them, which only works if both occupy the same box. An
   earlier attempt at this kept `position: relative` and concluded the second
   layer "never reliably composited above the base video regardless of
   z-index/opacity" — but relatively-positioned siblings sit in normal flow, so
   the second video was never on top of the first at all; it was a full viewport
   height below it, off-screen. Nothing about z-index or opacity could have
   fixed that. .hero__media is itself `position: absolute; inset: 0`, so
   absolute children fill it exactly. */
.hero__media video { position: absolute; inset: 0; z-index: 1; }
.hero__media.is-playing video { z-index: 2; }

/* Beats dissolve directly into one another: main.js fades the incoming layer's
   opacity up while the outgoing layer's goes down, so one shot melts into the
   next with no black in between.

   This replaced a brightness dip to near-black and back. Two things were wrong
   with that. The rule driving it selected `video[data-finale-src]`, an
   attribute that appears nowhere in the markup — the element carries `data-src`
   — so `filter` had no transition and the "dip" was an instant cut to black.
   And even working, a dip through black is a harder edit than a dissolve.

   --dur-slow, not --dur-md: a dissolve wants to be unhurried, and at 480ms it
   read as a stumble rather than a blend. main.js holds the same 900ms before it
   swaps the layer roles, and fades the headline out and back inside that same
   window, so keep them in step if you change this.

   --ease-inout, matching .hero__content, so type and picture blend on one
   curve. --ease-lux front-loads opacity badly enough that the two layers hit
   their halfway point almost immediately and the dissolve spends most of its
   length barely changing. */
.hero__media video { transition: opacity var(--dur-hero-dissolve) var(--ease-inout); }

@media (prefers-reduced-motion: reduce) {
  .hero__media video { transition: none; }
}

/* ========================================================================== */
/* Polish pass                                                                */
/* Site-wide interaction consistency. The individual components already had    */
/* their own hovers; what was missing was a shared vocabulary — everything     */
/* liftable lifts by the same amount, on the same curve, and every focus ring  */
/* looks the same. That evenness is most of what reads as "finished".          */
/* ========================================================================== */

/* A 1px ring is easy to miss against photography; 2px stays visible on both the
   bone and dark sections. No border-radius here — this rule has the same
   specificity as .btn and would square off every pill button on focus. Outlines
   follow the element's own radius anyway. */
:focus-visible {
  outline: 2px solid var(--brand-light);
  outline-offset: 3px;
}

/* Buttons gain a small physical lift. Paired with the existing fill sweep this
   is the difference between a button that changes colour and one that responds. */
.btn {
  transition: color var(--dur-md) var(--ease-lux),
              border-color var(--dur-md) var(--ease-lux),
              transform var(--dur-fast) var(--ease-spring),
              box-shadow var(--dur-md) var(--ease-lux);
}

.btn:hover,
.btn:focus-visible { transform: translateY(-2px); }
.btn:active { transform: translateY(0); transition-duration: 80ms; }

/* Only the filled variant gets a shadow — a shadow under a transparent outline
   button reads as a rendering mistake. */
.btn:not(.btn--secondary):not(.btn--alt):hover { box-shadow: 0 10px 24px -8px rgb(0 158 210 / 0.5); }

/* The two cards that were missing the lift every other card has. */
.prog-card,
.article--link {
  transition: transform var(--dur-md) var(--ease-lux),
              border-color var(--dur-md) var(--ease-lux);
}

.prog-card:hover,
.article--link:hover { transform: translateY(-4px); }

/* --- Scroll progress ---------------------------------------------------- */
.nav__progress {
  position: absolute;
  inset: auto 0 0;
  height: 2px;
  opacity: 0;
  transition: opacity var(--dur-md) var(--ease-lux);
  pointer-events: none;
}

/* Hidden over the hero, where there is no bar for it to sit on. */
.nav.is-solid .nav__progress { opacity: 1; }

.nav__progress > span {
  display: block;
  height: 100%;
  width: 100%;
  background: linear-gradient(to right, var(--brand-medium), var(--brand-light));
  transform: scaleX(var(--p, 0));
  transform-origin: 0 50%;
  /* No transition: this tracks the scrollbar, and easing it would make it lag
     behind the content it is reporting on. */
}

@media (prefers-reduced-motion: reduce) {
  .btn:hover,
  .btn:focus-visible,
  .btn:active,
  .prog-card:hover,
  .article--link:hover { transform: none; }
}

/* --- Footer charter desk ------------------------------------------------- */
.footer__desk {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-lg);
  padding-block: var(--space-xl);
  border-bottom: var(--hairline) solid var(--white-15);
}

.footer__desk .eyebrow { margin-bottom: var(--space-2xs); }

.footer__phone {
  display: inline-block;
  font-size: clamp(1.75rem, 3.4vw, 2.75rem);
  line-height: 1;
  letter-spacing: -0.02em;
  color: var(--white);
  text-decoration: none;
  transition: color var(--dur-fast) var(--ease-inout);
}

.footer__phone:hover { color: var(--brand-light); }

.footer__phone-alt {
  margin: var(--space-2xs) 0 0;
  font-size: var(--text-sm);
  font-variant-numeric: tabular-nums;
  color: var(--white-50);
}

/* --- Hero slides -------------------------------------------------------- */
/* Photographs stack above the film (video sits at z-index 2 once playing) and
   cross-fade in. Only opacity animates, so switching slides costs nothing. */
.hero__alt {
  position: absolute;
  inset: 0;
  z-index: 3;
  opacity: 0;
  transition: opacity 1100ms var(--ease-lux);
  pointer-events: none;
}

.hero__alt.is-on { opacity: 1; }

/* Caption and indicators share the right-hand rail, vertically centred. The
   caption cannot live at the bottom of the hero: the quote bar floats ~96px up
   over it and swallowed it entirely. */
.hero__rail {
  position: absolute;
  top: 50%;
  right: var(--gutter);
  z-index: 4;
  transform: translateY(-50%);
  display: flex;
  align-items: center;
  gap: var(--space-lg);
}

.hero__caps { display: grid; }

.hero__cap {
  grid-area: 1 / 1;
  display: flex;
  flex-direction: column;
  align-items: flex-end;
  text-align: right;
  gap: var(--space-3xs);
  opacity: 0;
  transform: translateY(0.5rem);
  transition: opacity var(--dur-md) var(--ease-lux), transform var(--dur-md) var(--ease-lux);
  pointer-events: none;
  text-decoration: none;
}

.hero__cap.is-on { opacity: 1; transform: none; pointer-events: auto; }

.hero__cap-name {
  font-size: var(--text-sm);
  letter-spacing: var(--track-eyebrow);
  text-transform: uppercase;
  color: var(--white);
}

.hero__cap-cue {
  font-size: var(--text-xs);
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--white-50);
  transition: color var(--dur-fast) var(--ease-inout);
}

.hero__cap:hover .hero__cap-cue { color: var(--brand-light); }
.hero__cap:hover .link-arrow { transform: translateX(0.35rem); }

/* The indicators are real buttons now. Padding gives a usable hit area around
   the 1px mark without changing how it looks. */
.hero__dots button.hero__dot {
  padding: 0;
  border: 0;
  cursor: pointer;
  background: var(--white-30);
}

.hero__dots button.hero__dot.is-active { background: var(--brand-light); }
.hero__dots button.hero__dot:hover { background: var(--white-70); }

/* The caption sits on the right rail, vertically centred — exactly where the
   headline is widest. Below 1150px the headline wraps far enough right to run
   into it (measured: caption at x738, headline glyphs to x983 at 1024px), so it
   is dropped for the whole tablet band. The indicators still work and the
   aircraft is one tap away. Same breakpoint as the nav collapse, deliberately. */
@media (max-width: 1150px) {
  .hero__caps { display: none; }
}

/* --- Credibility band --------------------------------------------------- */
.stats__row {
  display: grid;
  grid-template-columns: repeat(4, minmax(0, 1fr));
  gap: var(--space-lg);
  margin: 0;
  padding-block: var(--space-md);
  border-top: var(--hairline) solid var(--dark-15);
  border-bottom: var(--hairline) solid var(--dark-15);
}

.stat { text-align: center; }

.stat__num {
  font-family: var(--font-heading);
  font-weight: var(--w-display);
  font-size: clamp(2rem, 3.4vw, 3rem);
  line-height: 1;
  letter-spacing: -0.02em;
  color: var(--black);
  font-variant-numeric: tabular-nums;
}

.stat__label {
  margin: var(--space-2xs) 0 0;
  font-size: var(--text-sm);
  letter-spacing: var(--track-eyebrow);
  text-transform: uppercase;
  color: var(--dark-70);
}

@media (max-width: 640px) {
  .stats__row { grid-template-columns: repeat(2, minmax(0, 1fr)); gap: var(--space-xl) var(--space-lg); }
}

/* Caption is capped so it can never grow back into the headline. */
.hero__cap { max-width: 14rem; }

.hero__cap-class {
  font-size: var(--text-xs);
  line-height: 1.35;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--white-50);
}

.hero__cap-cue { margin-top: var(--space-2xs); }

/* --- Film grain ---------------------------------------------------------- */
/* A fine noise layer over the dark sections. Large flat fields of #3D3C44 look
   like a filled rectangle; a little grain reads as printed matter and stops the
   photography above and below from feeling pasted on. The texture is an inline
   SVG turbulence, so it costs no request. */
.section--dark::before,
.hero__vignette::after {
  content: "";
  position: absolute;
  inset: 0;
  z-index: 0;
  pointer-events: none;
  opacity: 0.055;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='140' height='140'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.85' numOctaves='3' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='140' height='140' filter='url(%23n)'/%3E%3C/svg%3E");
}

/* The grain must sit under the content, not over it. */
.section--dark > * { position: relative; z-index: 1; }

/* Slightly stronger over the hero film, where the gradient is smoothest. */
.hero__vignette::after { opacity: 0.07; }


/* --- Marquee layout overrides ------------------------------------------- */
/* .logo-strip__row-partners sets display:grid for the static/reduced-motion
   fallback. Inside a moving marquee everything is one row regardless, so
   that grid is both unnecessary and wrong there. These selectors carry two
   classes so they win outright. */
.marquee .logo-strip__row {
  display: flex;
  flex-wrap: nowrap;
  align-items: center;
  justify-content: flex-start;
  width: max-content;
  /* Was 0, relying entirely on each logo's own leftover space inside its
     fixed 11rem box (from object-fit:contain) for spacing. A wide lockup
     like NBC Sports fills that box edge to edge and reads as crowding its
     neighbor, while a square mark like ABC has room to spare either side —
     the row looked unevenly spaced because it effectively was. A real gap
     guarantees the same minimum distance between every pair regardless of
     each logo's own aspect ratio; it's a fixed value like the box widths
     themselves, so the seamless-loop math above is unaffected. */
  gap: 2.5rem;
}

/* Each mark gets a fixed optical box. Without it the track width depends on
   which logos have finished loading, and the animation shifts by a percentage
   OF that width — so a half-loaded strip would loop to the wrong place. */
.marquee .logo-strip__row img {
  flex: 0 0 auto;
  width: 11rem;
  height: 2.25rem;
  max-width: none;
  max-height: none;
  object-fit: contain;
}

/* --- Header phone -------------------------------------------------------- */
.nav__phone {
  display: inline-flex;
  align-items: center;
  gap: var(--space-2xs);
  font-size: var(--text-sm);
  letter-spacing: 0.06em;
  color: var(--white-70);
  white-space: nowrap;
  transition: color var(--dur-fast) var(--ease-inout);
}

.nav__phone:hover { color: var(--white); }

/* Handset drawn in CSS — one more request for a 12px glyph is not worth it. */
.nav__phone-icon {
  width: 0.5rem;
  height: 0.5rem;
  border: 1.5px solid currentColor;
  border-radius: 2px 2px 6px 2px;
  border-top-color: transparent;
  border-right-color: transparent;
  transform: rotate(-45deg);
}

/* With no left/right link menu left to crowd it, the phone number now only
   needs to give way once the hamburger + logo + phone + button genuinely
   can't fit — the mobile action bar carries the same two CTAs below this
   width anyway. */
@media (max-width: 480px) {
  .nav__phone { display: none; }
}

/* The button goes next — at 375px, logo + "Contact Us" alone already ran the
   bar past the viewport edge. Below 640px the sticky action bar (Call the
   desk / Request a Quote) is the CTA; the top bar only needs the hamburger
   and logo. */
@media (max-width: 640px) {
  .nav__actions .btn { display: none; }
}

/* Below ~380px the logo's own fixed height (3rem, ~257px wide at this
   wordmark's aspect ratio) plus the hamburger and grid gaps no longer fit
   even with the CTA button already hidden above -- the logo was overflowing
   the bar and getting silently clipped by the sitewide overflow-x: hidden
   safety net instead of shrinking. Scaling the logo down here keeps it
   fully visible on the narrowest real phones (iPhone SE and similar). */
@media (max-width: 380px) {
  .nav__logo img { height: 2.25rem; }
}

/* --- Mobile action bar --------------------------------------------------- */
.actionbar { display: none; }

@media (max-width: 1150px) {
  .actionbar {
    position: fixed;
    inset: auto 0 0;
    z-index: var(--z-nav);
    display: grid;
    grid-template-columns: auto 1fr;
    align-items: stretch;
    gap: var(--hairline);
    background: var(--neutral-dark);
    /* Clears the iOS home indicator. */
    padding-bottom: env(safe-area-inset-bottom, 0);
    transform: translateY(100%);
    transition: transform var(--dur-md) var(--ease-lux);
  }

  /* Held back until the hero is behind you — it should not cover the first
     impression, only follow it. */
  .actionbar.is-up { transform: translateY(0); }

  .actionbar__call,
  .actionbar__quote {
    display: flex;
    align-items: center;
    justify-content: center;
    gap: var(--space-2xs);
    /* Tight enough that both labels hold one line at 375px — "Request a Quote"
       was wrapping and making the bar two rows tall. */
    padding: 1.125rem var(--space-sm);
    font-size: var(--text-xs);
    letter-spacing: 0.08em;
    text-transform: uppercase;
    white-space: nowrap;
  }

  .actionbar__call { color: var(--white); }
  .actionbar__quote { background: var(--brand-light); color: var(--white); }

  /* So the footer's last row is never permanently sat on by the bar. The bar
     measures ~81px, so this clears it with a little room to spare. */
  .footer { padding-bottom: 6.5rem; }
}

@media (prefers-reduced-motion: reduce) {
  .actionbar { transition: none; }
}


/* --- Nav crowding ------------------------------------------------------- */
/* Nothing in the bar wraps. Without this the two labels containing a space were
   the first to give when the flex row ran short of room. */
.nav__actions .btn,
.nav__phone { white-space: nowrap; }

/* The button was sized for body copy; in a 76px bar it only needs to read as a
   button, and the extra padding was part of what overflowed the row. */
.nav__actions .btn--sm { padding: 0.625rem 1.25rem; font-size: var(--text-sm); }

/* The four partner marks run from 1.07:1 to 6.64:1. Constraining both axes
   leaves them with very different box widths (~8.5 / 8.5 / 6.7 / 3.2rem), so a
   constant flex gap still reads as uneven — the square mark ends up with far
   more apparent air around it than the wordmarks. Giving every mark an
   identical column makes the centres evenly spaced, which is what the eye
   actually reads as even.
   NOTE: one class, same specificity as .logo-strip__row — must stay after it. */
.logo-strip__row--partners {
  display: grid;
  grid-auto-flow: column;
  grid-auto-columns: 10rem;
  place-items: center;
  gap: 0;
}

/* With a shared column, the tall square mark no longer needs to be the widest
   thing on the row; trimming it evens the optical weight as well as the gaps. */
.logo-strip__row--partners img { max-height: 2.5rem; max-width: 7.25rem; }
.logo-strip__row--partners img[data-mark="simply"] { max-height: 2.75rem; max-width: 2.75rem; }

@media (prefers-reduced-motion: reduce) {
  /* The static fallback wraps, so the column flow has to go back to rows. */
  .logo-strip__row--partners {
    grid-auto-flow: row;
    grid-template-columns: repeat(auto-fit, 10rem);
    justify-content: center;
    gap: var(--space-xl) 0;
  }
}

/* Partner marks: even INK spacing, not even boxes.
   `.marquee .logo-strip__row img` pins every mark to a uniform 11rem box with
   object-fit: contain. That makes the centres evenly spaced but not the visible
   gaps — the wordmarks (up to 6.64:1) fill their box while the square Simply
   mark renders ~38px of ink inside 176px, leaving a hole either side of it.
   Letting each mark take its natural width under a shared height cap puts a
   constant gap between the marks themselves, which is what reads as even.
   Specificity 0,3,1 so it beats the 0,2,1 rule above regardless of order. */
.marquee .logo-strip__row.logo-strip__row--partners img {
  width: auto;
  height: auto;
  /* Wordmarks are width-limited and squat, the square mark is height-limited.
     A wider cap with a lower height cap brings the rendered heights within a
     few pixels of each other (~26-32px) instead of 22 vs 46. */
  max-height: 2rem;
  max-width: 11rem;
  object-fit: contain;
}

/* Simply is the only square lockup in the set; at a shared max-height it reads
   smaller than the wordmarks, so it is allowed to run a little taller — the
   same allowance the press row already makes for ABC. */
.marquee .logo-strip__row.logo-strip__row--partners img[data-mark="simply"] {
  max-height: 2.4rem;
}

/* ==========================================================================
   Full-bleed media
   --------------------------------------------------------------------------
   Photographs run edge to edge instead of stopping at the container's gutter.

   How the maths works, since it looks like a magic number and isn't: for a
   block-level child of .container, a percentage margin resolves against the
   container's *content* width C. Setting width:100vw and pulling each side out
   by (50% - 50vw) widens the element by 2 x (50vw - C/2), i.e. exactly to
   100vw, and keeps it centred on the container — which is itself centred in
   the viewport. So the element lands flush with both screen edges at every
   width, with no breakpoints and no per-section numbers.

   body already carries overflow-x: hidden, so on a desktop with a visible
   scrollbar the half-scrollbar overhang on each side is clipped symmetrically
   rather than producing a horizontal scrollbar.

   This only holds for direct block children of .container. Grid items resolve
   percentages against their own grid area, which is why .split's media column
   is handled separately below. */
.bleed,
.tiles,
.offices,
.articles,
.ig__grid {
  width: 100vw;
  max-width: 100vw;
  margin-inline: calc(50% - 50vw);
}

/* The grids lose the container's gutter when they bleed, and that gutter still
   has to exist for text — but on the text only, or the photographs stop short
   of the edge and the whole exercise is pointless.

   .tile and .office already pad their own content (var(--space-xl) and the
   figcaption's own padding), so they need nothing. .article and .ig-reel do
   not, so their non-media children get it here: the picture runs to the screen
   edge, the words below it stay inset. */
.articles .article > :not(picture):not(img),
.ig__grid .ig-reel > :not(.ig-reel__media) {
  padding-inline: var(--gutter);
}

/* --- The split layout's media column -------------------------------------
   .frames and .split__media are grid items, so the calc above does not apply
   to them. Instead the column is pulled out to whichever screen edge it sits
   against, leaving the copy column inside the container: photo to the edge,
   text still aligned to the page. The distance from the viewport edge to the
   container's content edge is the container's own centring offset plus its
   gutter, which is what the max() reproduces — max() rather than a bare
   subtraction because below --container the offset is zero and only the gutter
   applies. */
@media (min-width: 48rem) {
  .split .frames,
  .split .split__media {
    margin-inline-end: calc(-1 * (max(0px, (100vw - var(--container)) / 2) + var(--gutter)));
  }

  .split--flip .frames,
  .split--flip .split__media {
    margin-inline-end: 0;
    margin-inline-start: calc(-1 * (max(0px, (100vw - var(--container)) / 2) + var(--gutter)));
  }

  /* The staggered pair reserved room on its right for the small frame to
     overlap into. Against the screen edge there is nowhere to overlap to, so
     the inset comes off and the small frame lands flush. */
  .split:not(.split--flip) .frames { padding-right: 0; }
  .split--flip .frames { padding-left: 0; }
}

/* Single column below 48rem: .frames is then a full-width grid item, so its
   containing block is the container's content box and the standard bleed
   applies again. */
@media (max-width: 47.99rem) {
  .split .frames,
  .split .split__media {
    width: 100vw;
    max-width: 100vw;
    margin-inline: calc(50% - 50vw);
  }
  .split .frames { padding-right: 0; }
}
