/* ============================================================
   flyExclusive — homepage positioning statement + programs
   (index.html only)

   The programs module: one wide cinematic frame carrying the
   selected program's copy over its lower edge, and a strip of five
   thumbnails beneath it that ARE the tablist. Replaced the
   .lux-program-tabs block (a text rail over a 16/9 panel) on
   2026-08-05. Design source: "Program Tabs 1E".

   Loaded last in <head>, after luxury.css and home-hero.css, so it
   wins on equal specificity without needing !important. Every
   selector here is prefixed .hp-, and index.html is the only page
   that links it, so nothing in this file can reach the other ~100
   pages.

   Scoped to its own file rather than folded into luxury.css for the
   same reason css/home-hero.css is: it is homepage-only furniture,
   not a component the design library offers. If a second page ever
   wants this module, move it into luxury.css then and not before.

   ------------------------------------------------------------
   THE TRANSITION: hero -> statement -> programs

   The module leads with a 21:9 cinematic image, and the section
   above it is a full-bleed video hero. Two frames that size in the
   first two screens read as "the hero, and then a second hero",
   with the positioning statement stranded between them as a
   caption. Three moves stop that, and they are most of what this
   file does structurally:

   1. The hero/statement join stays a CRISP edge and gets
      articulated with one centre-bright accent hairline. A white
      feather was built here first and rejected on sight; the note
      recording why moved with the rule, because it is the obvious
      thing to try. Dark-to-white is not the same problem as
      dark-to-navy and does not take the same solution.

   2. The statement keeps the white paper and the module lands on
      --fe-canvas (#F5F7FA) instead. One quiet step of ground, so
      the frame has a surface to sit ON rather than floating in the
      same white as the paragraph above it. That is what makes it
      read as a section and not a second hero.

   3. A 1px accent hairline descending out of the statement into
      the module's eyebrow carries the eye across the ground
      change. It replaces the .lux-divider that sat here, which
      separates where this needs to connect.

   So the transition work happens BELOW the statement, not above
   it: moves 2 and 3 are the join into the module, which is where
   the composition actually needed help.

   That composition is kept described above because it is what
   this module was designed inside, but it is no longer what the
   page does. It went through two revisions:

   - .hp-lead (the statement, its hairline, its stepped-down prose,
     its chevron link) and .hp-thread were generalised into
     .lux-statement in luxury.css, because jet-club, fractional,
     membership and seaplane had the same problem.
   - Then, on 2026-08-05, the homepage stopped opening with a
     statement band at all: the turbine medallion, the chevron link
     and the thread were dropped and the two paragraphs moved onto
     this module's own ground as .hp-statement, at the top of the
     grey. The other four pages still use .lux-statement.

   So there is no white band between the hero and this module any
   more. Move 2 (the ground step) still stands - it is now the step
   from the dark hero straight to --canvas - and move 1's hairline
   moved onto .hp-programs::before, this section's top edge.
   Move 3's descending thread is gone with the band: there is no
   ground change left for it to carry the eye across.

   The module is then bottom-heavy by design: the wide frame, then
   five thumbnails that show every program at once. That was the
   point of choosing it - the rail it replaced hid four of five
   programs behind text labels.
   ============================================================ */

/* ---- local tokens, stated rather than inherited so this layer
   stands alone. The navy/blue values are luxury.css's, restated. ---- */
.hp-programs {
  --hp-navy: #003A70;
  --hp-navy-deep: #00274D;
  /* The ornament blue: correct for a 2px rule or a hairline, and below AA for
     text. Measured 2.81:1 on --hp-canvas (the 3.0:1 this note used to claim is
     its ratio on pure WHITE, which is not this module's ground). So it is for
     lines and marks only, and anything it touches must not be the sole carrier
     of meaning - the selected tab is identified first by having no photograph
     at all, which is structural and survives any colour-vision deficiency. */
  --hp-accent: #299FCF;
  /* Same hue at 5.12:1, for anything made of letters. luxury.css keeps this
     exact pair for this exact reason; .hp-programs__eyebrow uses it too. */
  --hp-accent-ink: #17709A;
  --hp-slate: #5B6B78;
  --hp-canvas: #F5F7FA;
  --hp-line: #E1E7EE;
  --hp-ease: cubic-bezier(.65, 0, .35, 1);
}

.hp-programs * {
  box-sizing: border-box;
}

/* The Webflow export caps the BARE element: `p{max-width:900px}` (its line
   248). That is invisible on a left-aligned paragraph and actively wrong on a
   centred one - the box is 900px pinned to the container's left edge and the
   text centres inside the BOX, not inside the section, so a centred eyebrow
   lands ~700px left of the heading it belongs to.

   Reset here and let the rules below state the measure each paragraph actually
   wants (they all do, and all of them are more specific than this). Margins are
   not reset: the export's `margin-bottom:10px` is overridden explicitly by
   every one of this file's own p rules. */
.hp-programs p {
  max-width: none;
}

/* ============================================================
   2 - THE MODULE
   ============================================================ */
.hp-programs {
  padding: clamp(44px, 6vh, 76px) clamp(20px, 5vw, 40px) clamp(56px, 7vw, 104px);
  /* the quiet step of ground - see (2) in the header note */
  background: var(--hp-canvas);
  position: relative;
  /* Tree order is not enough: the Webflow export carries .home-hero{z-index:9}
     (its line 6845), so the hero is a positive-z-index stacking context and
     paints over every z-index:auto sibling after it - which is what the
     hairline below is. 10 is one step over the hero and far below the fixed
     header (1300). Same reason .lux-statement takes it in luxury.css. */
  z-index: 10;
}

/* The hairline on the hero join. Move (1) of the header note used to happen on
   the white statement band that sat here; that band is gone (the statement is
   inside this module now), so the hero meets this ground directly and the
   articulation moves onto this edge. Verbatim the device
   .home-header--hq::after and .lux-statement::before use: brightest at the
   centre, falling to nothing at both gutters. */
.hp-programs::before {
  content: "";
  position: absolute;
  top: 0;
  left: 0;
  right: 0;
  height: 1px;
  pointer-events: none;
  background: linear-gradient(90deg,
    rgba(41, 159, 207, 0) 0%, rgba(41, 159, 207, .45) 22%,
    rgba(41, 159, 207, .9) 50%, rgba(41, 159, 207, .45) 78%,
    rgba(41, 159, 207, 0) 100%);
}

/* ---- the opening statement, on this ground ---- */
.hp-statement {
  max-width: 880px;
  margin: 0 auto clamp(40px, 6vh, 76px);
  text-align: center;
}

/* Stepped down from .lux-statement__quote's clamp(26px,3.7vw,44px) for the
   reason recorded at the markup: the module's h2 is ~40px and starts two
   elements below this one. Light 300 at 36px still reads as the page's opening
   voice without competing with it. */
.hp-statement .hp-statement__quote {
  margin: 0;
  font-weight: 300;
  font-size: clamp(24px, 3vw, 36px);
  line-height: 1.2;
  letter-spacing: .005em;
  color: var(--hp-navy);
  text-wrap: balance;
}

/* max-width is stated, not inherited: `.hp-programs p{max-width:none}` above
   resets the export's bare `p{max-width:900px}`, which would otherwise pin a
   900px box to the container's left edge and centre this text inside the BOX. */
.hp-statement .hp-statement__prose {
  margin: clamp(16px, 2.2vh, 24px) auto 0;
  max-width: 62ch;
  font-size: 16px;
  line-height: 1.75;
  color: var(--hp-slate);
}

.hp-programs__inner {
  max-width: 1380px;
  margin: 0 auto;
}

.hp-programs__head {
  margin: 0 0 clamp(24px, 3.5vh, 40px);
  text-align: center;
}

.hp-programs__eyebrow {
  margin: 0 0 clamp(12px, 1.8vh, 18px);
  font-size: 12px;
  font-weight: 600;
  letter-spacing: .32em;
  text-transform: uppercase;
  /* --fe-blue-600, not --fe-blue: 3.0:1 fails AA at this size on a light
     ground, and luxury.css keeps this exact value for this exact reason */
  color: #17709A;
}

/* Two classes, and that is load-bearing on a LIGHT ground too, just in
   the other direction: luxury.css's `h2[class]{color:var(--fe-navy)}` is
   the rule that would win here, and it happens to be the colour this
   heading wants - but relying on that would leave the heading's colour
   owned by a global rule written for Webflow husks. Stated locally, at
   (0,2,0), so it is this file's decision. */
.hp-programs .hp-programs__h2 {
  margin: 0 auto;
  /* 58 characters, and luxury.css puts text-wrap:balance on every heading, so
     this measure is what decides the line count: 30ch balances to two lines. */
  max-width: 30ch;
  font-weight: 300;
  font-size: clamp(26px, 3.4vw, 44px);
  line-height: 1.14;
  letter-spacing: .01em;
  color: var(--hp-navy);
}

/* ---- the wide frame ---- */
.hp-frame {
  position: relative;
  overflow: hidden;
  background: var(--hp-navy-deep);
  /* the frame's own aspect, held by the element rather than by the
     images, so five photographs of three different aspect ratios all
     crop into one stable box and switching tabs cannot resize it */
  aspect-ratio: 21 / 9;
}

.hp-frame__media {
  position: absolute;
  inset: 0;
  pointer-events: none;
}

.hp-ph {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  opacity: 0;
  transition: opacity .6s ease;
}

.hp-ph.is-on {
  opacity: 1;
}

/* Two axes, the same idea .lux-hero and .home-hero__scrim use, and the
   same reason any copy-over-photograph block needs it: the copy is left-aligned in
   the frame, so weighting the copy's SIDE buys legibility far more
   cheaply than driving the whole lower half to the same depth.

   The design's own single-axis shape was .34 at 62% climbing to .80 only at the
   very bottom. Measured against lux-arrival-stairs, that put the h3 at
   65% of the frame under .38 darkening, over a sunlit yellow top and a
   white uniform shirt - readable but soft, and it would have failed
   outright on lux-cj3plus-dusk-mirror's bright sky. The vertical ramp now
   starts at 26% and reaches .72 by 82%. The top quarter is still
   completely clear, so the photograph keeps its subject. */
.hp-frame__scrim {
  position: absolute;
  inset: 0;
  pointer-events: none;
  background:
    linear-gradient(100deg,
      rgba(0, 25, 50, .42) 0%,
      rgba(0, 25, 50, .22) 32%,
      rgba(0, 25, 50, .06) 48%,
      rgba(0, 25, 50, 0) 60%),
    linear-gradient(180deg,
      rgba(0, 25, 50, 0) 26%,
      rgba(0, 25, 50, .16) 42%,
      rgba(0, 25, 50, .36) 55%,
      rgba(0, 25, 50, .56) 68%,
      rgba(0, 25, 50, .72) 82%,
      rgba(0, 25, 50, .82) 100%);
}

/* Positioning only. The row inside it is .hp-panel, because the CTA label
   changes per program and therefore has to swap with the panel rather than
   sit outside it as a fixed sibling. */
.hp-frame__overlay {
  position: absolute;
  left: clamp(20px, 4vw, 56px);
  right: clamp(20px, 4vw, 56px);
  bottom: clamp(20px, 3.5vw, 48px);
}

.hp-panel[hidden] {
  display: none;
}

/* Copy on the left, CTA baseline-aligned to it on the
   far right. flex-end rather than baseline so the link sits on the last
   line of the paragraph whatever height the copy runs to. */
.hp-panel {
  display: flex;
  align-items: flex-end;
  justify-content: space-between;
  gap: clamp(20px, 3.5vw, 48px);
}

/* The accent rule down the left of the copy. Same device as
   .new-home-hero__content-container's 2px --fe-blue rule elsewhere on
   the site, so it is the house style rather than a new one. */
.hp-panel__copy {
  flex: 1 1 auto;
  min-width: 0;
  max-width: 640px;
  border-left: 3px solid var(--hp-accent);
  padding-left: clamp(16px, 1.8vw, 26px);
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: clamp(8px, 1.2vh, 14px);
}

/* h3, not the design source's h1: the hero holds the page's h1 and
   .hp-programs__h2 heads this section. Two classes to beat
   `h3[class]{color:var(--fe-navy)}`, which on this dark scrim would be
   #003A70 - the invisible-heading trap. */
.hp-frame .hp-panel__title {
  margin: 0;
  font-weight: 300;
  font-size: clamp(1.5rem, 2.9vw, 2.5rem);
  line-height: 1.1;
  letter-spacing: .01em;
  color: #fff;
}

.hp-panel .hp-panel__desc {
  margin: 0;
  max-width: 56ch;
  font-size: 15px;
  line-height: 1.7;
  color: rgba(255, 255, 255, .88);
}

/* An underlined text link rather than a ghost button: this one sits
   inside a photograph beside the copy, and a bordered button that size
   fights the frame's own edge. */
.hp-panel .hp-panel__cta {
  flex: none;
  display: inline-flex;
  align-items: center;
  gap: .6em;
  padding-bottom: 4px;
  border-bottom: 1px solid rgba(255, 255, 255, .5);
  font-size: 11px;
  font-weight: 600;
  letter-spacing: .28em;
  text-transform: uppercase;
  color: #fff;
  text-decoration: none;
  transition: border-color .25s ease;
}

.hp-panel .hp-panel__cta:hover,
.hp-panel .hp-panel__cta:focus-visible {
  border-bottom-color: #fff;
}

.hp-panel .hp-panel__cta:focus-visible {
  outline: 1px solid rgba(255, 255, 255, .8);
  outline-offset: 4px;
}

.hp-panel__cta svg {
  flex: none;
  width: 1.05em;
  height: 1.05em;
  fill: none;
  stroke: currentColor;
  stroke-width: 2;
  stroke-linecap: round;
  stroke-linejoin: round;
  transition: transform .3s var(--hp-ease);
}

.hp-panel__cta:hover svg,
.hp-panel__cta:focus-visible svg {
  transform: translateX(4px);
}

@keyframes p3PanelIn {
  from { opacity: 0; transform: translateY(10px); }
  to   { opacity: 1; transform: translateY(0); }
}

.hp-panel.is-entering {
  animation: p3PanelIn .5s cubic-bezier(.22, 1, .36, 1) both;
}

/* ---- the thumbnail strip: these ARE the tabs ----

   The selected tab does not show a thumbnail. Its photograph is already
   on screen at full width in the frame directly above, so the thumbnail
   was the same image twice in one view - measured identical src attributes,
   and the duplication is most of what made the strip feel busy. Selected
   collapses to its title alone under a light blue rule; the other four keep
   their photographs, at full colour.

   Two shared rows - photographs in the first, titles in the second - is what
   makes that safe, via subgrid. Every tab spans both tracks, so the titles
   line up because they are literally in the same grid row, and dropping the
   selected tab's frame just leaves its cell in row 1 empty. The row is still
   sized by the four tabs that keep an image, so the strip's height never
   changes and nothing reflows when the selection moves. (The old marker was
   an inset shadow for the same reason: never change a tab's height.)

   Flex was tried first and is not sufficient. align-items:flex-end lines the
   titles up only while they all wrap to the same number of lines: measured at
   700px, "On-Demand Charters" and "Owned MRO Facilities" take three lines
   where the others take two, and the photograph tops went 16px ragged. 744px
   - iPad mini portrait - sits inside that band. Grid rows are shared across
   all five columns, so line counts stop mattering.

   Subgrid is load-bearing, NOT progressive enhancement, and there is an
   @supports fallback at the end of this section because of it. A browser that
   drops the declaration keeps `grid-row: span 2`, so the tab still spans both
   tracks and still stretches to their full height - but its own rows become
   auto, row 1 is empty because the selected frame is display:none, and the
   slack splits evenly between the two. Measured: the selected title lands 71px
   above the other four, exactly half a thumbnail. That is a broken layout, not
   a ragged one. Safari < 16 and Chrome < 117, so realistically iPadOS 15. */
.hp-strip {
  display: grid;
  grid-template-columns: repeat(5, minmax(0, 1fr));
  grid-template-rows: auto auto;
  column-gap: clamp(10px, 1.2vw, 16px);
  /* 0, deliberately - .hp-tab__meta's own padding-top is the photo-to-title
     gap, so the selected rule can sit at the top edge of that space */
  row-gap: 0;
  margin-top: clamp(12px, 1.6vw, 16px);
}

.hp-strip .hp-tab {
  appearance: none;
  -webkit-appearance: none;
  background: none;
  border: 0;
  padding: 0;
  cursor: pointer;
  min-width: 0;
  /* spans both of the strip's rows and adopts them as its own, so the frame
     lands in the shared photo row and the meta in the shared title row */
  grid-row: span 2;
  display: grid;
  grid-template-rows: subgrid;
  font-family: inherit;
  text-align: left;
}

/* Both rows are stated explicitly rather than left to auto-placement. The
   selected tab's frame is display:none, and an auto-placed meta then becomes
   the tab's first in-flow child and lands in row 1 - which put the selected
   title 143px above the other four instead of level with them. */
.hp-tab__frame {
  grid-row: 1;
  display: block;
  width: 100%;
  overflow: hidden;
}

.hp-tab__frame img {
  display: block;
  width: 100%;
  aspect-ratio: 16 / 10;
  object-fit: cover;
  /* Full colour. These used to desaturate so the selected frame was the
     only colour in the row; the selected tab now has no frame at all, so
     the greying had nothing left to contrast with and only made four real
     photographs look switched off. */
  transition: transform .5s var(--hp-ease);
}

/* Every tab reserves the selected rule's 2px and the space under it, and only
   the selected one paints it. Colouring a border that is already there keeps
   all five titles on exactly the same line - giving the border to the selected
   tab alone pushed its own title down by the width of the rule plus its
   padding, which the shared title row then showed up immediately.

   That padding IS the gap between photograph and title, which is why the
   strip's row-gap is 0: the space belongs to this box so the rule can sit at
   the top of it, on the line where the photographs end. */
.hp-tab__meta {
  grid-row: 2;
  align-self: start;
  display: flex;
  align-items: baseline;
  gap: 10px;
  width: 100%;
  min-width: 0;
  border-top: 2px solid transparent;
  padding-top: clamp(9px, 1.1vw, 13px);
  /* matches .hp-tab__num / .hp-tab__label's .3s, so the rule and the two text
     colours arrive together. The old inset-shadow marker had this and losing
     it left the rule snapping while the letters eased. */
  transition: border-top-color .3s ease;
}

.hp-tab__num {
  flex: none;
  font-size: 10.5px;
  font-weight: 600;
  letter-spacing: .2em;
  color: var(--hp-slate);
  font-variant-numeric: tabular-nums;
  transition: color .3s ease;
}

.hp-tab__label {
  font-size: 12px;
  font-weight: 600;
  letter-spacing: .16em;
  text-transform: uppercase;
  line-height: 1.35;
  color: var(--hp-slate);
  transition: color .3s ease;
}

/* Selected: keeps its photograph, and takes a blue outline around the frame.

   Review 2026-08-07 · 19. This used to be `display:none` on the frame — the
   selected tab was the only one WITHOUT a picture, and the absence was the
   selection cue. In review that read as the image being taken away rather than
   as "you are here", and it made the strip look thinner the moment you engaged
   with it. Showing the thumbnail and drawing a border around it is the cue that
   was actually asked for.

   outline, not border: it paints outside the box, so the 16/10 frame keeps its
   measured size and the five cells stay on one grid. outline-offset holds it
   clear of the photograph's own edge so it reads as a frame around the image
   rather than a stroke on it. The blue top rule on .hp-tab__meta below stays —
   the two marks are the same colour and read as one selection. */
.hp-strip .hp-tab[aria-selected="true"] .hp-tab__frame {
  outline: 2px solid var(--hp-accent);
  outline-offset: 3px;
}

/* Colour only - the geometry is already reserved on every .hp-tab__meta.
   The rule spans the full cell so it reads as an underline belonging to the
   whole tab rather than a stroke on the text, and it lands on the line where
   the other four photographs end, so the eye finds the selection on a line it
   is already scanning. */
.hp-strip .hp-tab[aria-selected="true"] .hp-tab__meta {
  border-top-color: var(--hp-accent);
}

/* --hp-accent-ink, not --hp-accent: this is 10.5px text, so it needs 4.5:1 and
   the ornament blue gives 2.81:1 on this canvas. The rule above it can stay the
   lighter blue - it is a mark, not letters. */
.hp-strip .hp-tab[aria-selected="true"] .hp-tab__num {
  color: var(--hp-accent-ink);
}

.hp-strip .hp-tab[aria-selected="true"] .hp-tab__label {
  color: var(--hp-navy);
}

/* Hover is now the only thing the image can respond to, so it carries the
   whole affordance: a small push in, nothing else. */
.hp-strip .hp-tab:hover .hp-tab__frame img {
  transform: scale(1.03);
}

.hp-strip .hp-tab:hover .hp-tab__label {
  color: var(--hp-navy);
}

/* 2px and the ink blue, matching luxury.css's global ring rather than halving
   it. js/lux-tabs.js uses a roving tabindex, so the selected tab is the strip's
   only tab stop and this is the ring every keyboard user gets - and the
   selected tab is also the one with no photograph behind it, so a 1px ornament
   line at 2.81:1 was the whole indicator. */
.hp-strip .hp-tab:focus-visible {
  outline: 2px solid var(--hp-accent-ink);
  outline-offset: 3px;
}

/* Without subgrid the selected tab's empty first row takes half the tab's
   slack and floats its title 71px up (see the note at the top of this
   section). Keeping the frame in flow but invisible fills that row, so the
   titles line up and the cell still reads as blank canvas - the designed
   result, reached without the feature. */
@supports not (grid-template-rows: subgrid) {
  .hp-strip .hp-tab[aria-selected="true"] .hp-tab__frame {
    display: block;
    visibility: hidden;
  }
  /* and pack the tab's own rows at the top rather than letting the default
     stretch share the leftover height between them, which otherwise re-opens
     the 8px raggedness in the 681-780px band where the titles wrap to
     different line counts. With both declarations the fallback measures
     pixel-identical to the subgrid path at every width tested. */
  .hp-strip .hp-tab {
    align-content: start;
  }
}

/* ============================================================
   3 - NARROW VIEWPORTS
   ============================================================ */

/* 21:9 is a 161px-tall sliver at 375px, which cannot hold a heading, a
   paragraph and a link over it. Going to 4:3 is not enough on its own
   either: measured at 390px, the overlay ran 280px of content inside a
   251px frame, so it overflowed upward out of a bottom-anchored absolute
   box and .hp-frame's own overflow:hidden clipped the copy away entirely.
   The heading above it was overlapped too.

   So on a phone the copy stops being an overlay and becomes a panel
   BENEATH the image, inside the same frame:

     .hp-frame gets padding-top:75%. Percentage padding resolves against
     the element's WIDTH, so that reserves an exactly-4:3 band at the top
     whatever the frame measures.

     .hp-frame__media / __scrim keep position:absolute (their containing
     block is the frame's padding box, so top:0 is the frame's true top
     edge) and take aspect-ratio:4/3 - left:0/right:0 already fix their
     width, so that sizes them to exactly the reserved band.

     .hp-frame__overlay drops to position:static and flows in the content
     box, which starts below the band. The frame then GROWS to fit it,
     which is why nothing can clip any more.

   The copy stays white because it is still inside .hp-frame, whose
   background is --hp-navy-deep - so it reads as a navy caption panel
   under the photograph rather than as text fighting an image. That is
   the whole reason to do it this way instead of moving the copy out onto
   the canvas and flipping it to ink: no colour switching, no DOM change,
   and the module still reads as one object. */
@media (max-width: 900px) {
  .hp-frame {
    aspect-ratio: auto;
    padding-top: 75%;
  }
  .hp-frame__media,
  .hp-frame__scrim {
    bottom: auto;
    aspect-ratio: 4 / 3;
  }
  .hp-frame__overlay {
    position: static;
    padding: clamp(18px, 4.5vw, 26px) clamp(18px, 4.5vw, 26px) clamp(20px, 5vw, 28px);
  }
  /* Nothing sits over the photograph any more, so the scrim stops being a
     legibility device and becomes a join: a soft close at the bottom of
     the image band so the picture meets the navy copy panel below it
     without a hard line. Much lighter than the desktop scrim, on purpose -
     the image keeps its full range where it is doing the most work. */
  .hp-frame__scrim {
    background: linear-gradient(180deg,
      rgba(0, 25, 50, 0) 58%,
      rgba(0, 25, 50, .18) 78%,
      rgba(0, 25, 50, .48) 94%,
      rgba(0, 25, 50, .62) 100%);
  }
  /* the CTA drops under the copy rather than sitting beside it - at this
     width there is no room for a .28em-tracked label in a second column */
  .hp-panel {
    flex-direction: column;
    align-items: flex-start;
    gap: clamp(12px, 2.5vw, 16px);
  }
  .hp-panel__copy {
    max-width: none;
  }
}

/* ---- .hp-list: the phone presentation, replacing the tablist outright ----

   Below 680px the whole [data-lux-tabs] block goes away and five links take
   its place. The rationale for that is at the markup in index.html; what
   matters here is that this is a REPLACEMENT, not a reflow, so none of the
   tab rules below 680px exist any more.

   Two shapes were tried in this slot before it and both failed for the same
   reason - they kept the tab metaphor on a device that cannot hold it:

   - A snap scroller at 60% per tab. Measured at 390px it showed 35% of
     itself (390px of a 1130px scroll width), so three of the five programs
     sat off-screen behind a deliberately hidden scrollbar and the second
     label was clipped mid-word by the viewport edge.
   - Then a two-column grid of titles with the photographs dropped. That fit
     all five, but it put the chooser BENEATH a ~600px block (the 4:3 frame
     plus the navy copy panel), so the labels were off-screen from the panel
     they controlled: a tap changed content the thumb had already scrolled
     past, and the module looked like it had stopped responding. Four of five
     programs were 11px uppercase captions in a bordered table.

   The second one is the useful failure. A tablist needs its tabs and its
   panel in one view, and at 390px this module's panel alone is taller than
   the viewport. So the answer is not a better strip - it is no strip. Each
   program becomes its own row and its own destination, which is what a phone
   reader wanted from a program index in the first place.

   The register is .lux-program-list's, on purpose (hairline-divided rows, a
   tracked number, the house chevron), so this reads as the same family as the
   program indexes on /partners and /who-we-are/safety-certification rather
   than as a fifth pattern. It is written locally rather than as a variant of
   that component for the reason recorded at the markup.

   The breakpoint switch itself is at the END of this section, not here, and
   that is load-bearing: a media query adds no specificity, so
   `@media{.hp-list{display:block}}` written above the base rule loses to the
   base `.hp-list{display:none}` on source order alone and the list never
   appears at any width. MEASURED that way first - display computed `none` at
   390px and 680px both, while the tabs block it replaces was correctly hidden,
   so the module simply ended after the statement. Same failure as
   .lux-prose--sm in luxury.css. If these rules are ever reordered, the switch
   block has to stay last. */

/* Hidden by default rather than shown-then-hidden, so this block is inert at
   every width the switch below does not claim - and, more usefully, its five
   images are inside a display:none subtree on desktop, which is what keeps a
   lazy image from being fetched at all. Desktop pays nothing for it. */
.hp-list {
  display: none;
  list-style: none;
  margin: clamp(18px, 5vw, 26px) 0 0;
  padding: 0;
  /* the grid of hairlines: this draws the top, each row draws its own bottom */
  border-top: 1px solid var(--hp-line);
}

/* margin:0 is not tidying - the export puts `margin-left:16px` on the BARE li,
   which list-style:none on the ul does nothing about. MEASURED: every row came
   in 319px inside a 335px list, so the five hairlines sat indented from the
   statement and the h2 above them and the section lost its left edge. Same
   family of trap as the export's bare `p{max-width:900px}` handled at the top
   of this file. */
.hp-list > li {
  margin: 0;
  border-bottom: 1px solid var(--hp-line);
}

/* Three columns: thumbnail, copy, chevron. The chevron gets a column of its
   own rather than riding at the end of the title, because the title wraps to
   two lines on the longer names and an inline arrow then lands under the
   first word instead of at the row's edge. */
.hp-list .hp-list__item {
  display: grid;
  /* 25vw is the thumbnail on a phone (~98px at 390). The 132px cap only comes
     into play above ~528px, which is the 600-680px band where this block still
     runs: pinned at 108px there the row was 597px of text beside a stamp. */
  grid-template-columns: clamp(84px, 25vw, 132px) minmax(0, 1fr) 14px;
  align-items: center;
  column-gap: clamp(13px, 3.5vw, 16px);
  padding: clamp(13px, 3.4vw, 16px) 0;
  text-decoration: none;
  color: inherit;
  /* the whole row is the tap target, and at this padding the thumbnail alone
     already clears 44px, so nothing here is smaller than the guideline */
  -webkit-tap-highlight-color: transparent;
}

/* 4/3 rather than the strip's 16/10: at 108px a 16/10 crop is 68px tall
   against ~90px of copy beside it, which leaves the row looking top-light.
   4/3 gives 81px and the two sides balance. The navy ground shows only while
   the image is in flight. */
.hp-list__media {
  display: block;
  overflow: hidden;
  aspect-ratio: 4 / 3;
  background: var(--hp-navy-deep);
}

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

.hp-list__body {
  display: block;
  min-width: 0;
}

/* The module's own 01-05 device, carried over from the strip - it is the one
   thing worth keeping from the tab shape, because it tells a phone reader how
   many programs there are before they have scrolled all five. */
.hp-list__num {
  display: block;
  font-size: 10px;
  font-weight: 600;
  letter-spacing: .2em;
  color: var(--hp-slate);
  font-variant-numeric: tabular-nums;
}

/* Sentence case at 17px, NOT the strip's 12px uppercase. On the strip these
   are tab labels; here they are the row's heading with a sentence beneath
   them, and a .13em-tracked label over 13px prose puts the smaller type on
   top - the inversion .lux-program-list--reading exists to call out. Two
   classes for the colour, matching this file's rule for itself: navy is what
   luxury.css's `h3[class]` would give anyway, and this file states its own. */
/* Fluid, and the narrow end of the clamp is measured rather than chosen: at a
   flat 17px, four of the five names wrapped to two lines at 360px ("Fractional
   Ownership" and "Owned MRO Facilities" are 20 characters in a ~200px column)
   and only Seaplane Charter stayed on one, so four rows ran 22px taller than
   the fifth. 4.6vw holds 17px on a 390px phone and steps down to 15.5px by
   320px, which keeps every name on one line to 360px. */
.hp-list .hp-list__title {
  margin: 3px 0 0;
  font-family: var(--lux-font);
  font-weight: 400;
  font-size: clamp(15.5px, 4.6vw, 17px);
  line-height: 1.3;
  letter-spacing: -.005em;
  color: var(--hp-navy);
}

/* The clamp is a guard against a longer line being added later, and it has to
   sit ABOVE what the copy actually needs or it stops being a guard and starts
   being a truncation. MEASURED at 4: these run two lines at 390px and three at
   320px. At 3 the first attempt's longer copy was cut mid-sentence on four of
   the five rows at 360px - an ellipsis in the middle of a program's one-line
   description, which is worse than an uneven row. */
.hp-list__desc {
  display: -webkit-box;
  -webkit-box-orient: vertical;
  -webkit-line-clamp: 4;
  overflow: hidden;
  margin: 5px 0 0;
  font-size: 13px;
  line-height: 1.55;
  color: var(--hp-slate);
}

/* --hp-accent-ink, not --hp-accent: this is a 14px mark carrying the row's
   affordance on its own, so it takes the readable blue rather than the
   ornament one. */
.hp-list__go {
  width: 14px;
  height: 14px;
  fill: none;
  stroke: var(--hp-accent-ink);
  stroke-width: 2;
  stroke-linecap: round;
  stroke-linejoin: round;
  transition: transform .3s var(--hp-ease);
}

/* Touch gets a pressed ground - there is no hover on the device this block is
   written for, and a row this size needs to acknowledge the tap before the
   next page paints. :active on the anchor, so it fires on touch and mouse
   alike. */
.hp-list .hp-list__item:active {
  background: rgba(0, 58, 112, .04);
}

.hp-list .hp-list__item:hover .hp-list__go,
.hp-list .hp-list__item:focus-visible .hp-list__go {
  transform: translateX(3px);
}

.hp-list .hp-list__item:hover .hp-list__title {
  color: var(--hp-navy-deep);
}

.hp-list .hp-list__item:focus-visible {
  outline: 2px solid var(--hp-accent-ink);
  outline-offset: -2px;
}

/* aspect-ratio has wide support, but keep a floor so an unsupporting browser
   cannot collapse the thumbnail to nothing - the same guard luxury.css keeps
   on .lux-program-list .pl-media. */
@supports not (aspect-ratio: 4 / 3) {
  .hp-list__media {
    min-height: 78px;
  }
}

/* ---- the switch, and it must stay the last thing in this section ----
   See the note at the top of .hp-list: a media query carries no specificity,
   so this block only beats the base `display:none` above by sitting after it.
   680px rather than the frame's 900px on purpose - 681-900px keeps the tablist
   with its stacked frame, which is a layout that works on a tablet. This
   replaces it only where it stops working. */
@media (max-width: 680px) {
  .hp-programs [data-lux-tabs] {
    display: none;
  }
  .hp-list {
    display: block;
  }
}

/* ============================================================
   3b - FORCED COLORS
   ============================================================ */

/* Both selection marks are invisible here if left alone: forced-colors drops
   box-shadow outright, and the desktop rule is a border the unselected tabs
   already carry as `transparent`, so all five would end up identical. Highlight
   is the system's own selection colour, which is the honest mapping for a
   tablist. Written from the spec - I have not been able to check it in real
   Windows High Contrast, so treat it as a floor, not as verified. */
@media (forced-colors: active) {
  .hp-strip .hp-tab[aria-selected="true"] .hp-tab__meta {
    border-top-color: Highlight;
  }
  .hp-strip .hp-tab[aria-selected="true"] {
    box-shadow: none;
    border-left: 3px solid Highlight;
  }
}

/* ============================================================
   4 - REDUCED MOTION
   ============================================================ */
@media (prefers-reduced-motion: reduce) {
  .hp-ph,
  .hp-tab__meta,
  .hp-tab__frame img,
  .hp-tab__num,
  .hp-tab__label,
  .hp-panel .hp-panel__cta,
  .hp-panel__cta svg,
  .hp-list__go {
    transition-duration: .01ms;
  }
  .hp-strip .hp-tab:hover .hp-tab__frame img {
    transform: none;
  }
  .hp-panel.is-entering {
    animation: none;
  }
}
