/*
Theme Name:   Blue Horizon Conferencing Child
Description:  Child theme for Astra
Author:       Francois Van Der Elst
Template:     astra
Version:      1.0.0
Text Domain:  blue-horizon-conferencing-child
*/

/* ═══════════════════════════════════════════════════════════════
   TOKENS
   ═══════════════════════════════════════════════════════════════ */
:root {
  /* Palette */
  --navy:    #1b2a41;
  --ink:     #0b1017;
  --bone:    #f4f1ea;
  --paper:   #ffffff;
  --glacier: #8fb8c4;

  /* Antarctic ice blue. Two tints of one hue: the light one for accents
     on dark grounds and for fills, the deepened one wherever the accent
     has to work as small text on white or carry white text itself.
     --accent on white measures about 2:1, which is fine for a shape and
     unreadable for type; --accent-ink measures 4.7:1 and passes AA. */
  --accent:     #7fc7e3;
  --accent-ink: #2c7c9b;

  --rule:    rgba(27, 42, 65, 0.14);
  --rule-on-dark: rgba(255, 255, 255, 0.16);

  /* Type */
  --display: "Cormorant Garamond", Georgia, "Times New Roman", serif;
  --body:    "Montserrat", ui-sans-serif, system-ui, -apple-system, "Segoe UI", Roboto, Arial, sans-serif;
  --label:   "Montserrat", ui-sans-serif, system-ui, -apple-system, "Segoe UI", Roboto, Arial, sans-serif;

  /* Fluid type scale */
  /* Halved. Constrained by viewport height as well as width so the
     three-line hero cannot overflow and collide with the nav. The lower
     bound is a shade above half, to keep the headline from dropping
     below body copy on very narrow phones. Doubled on phones, see HERO. */
  --t-hero:    clamp(1.5rem, min(6.5vw, 9.5vh), 6.5rem);
  --t-display: clamp(2.5rem, 7vw, 6.5rem);
  --t-lead:    clamp(1.5rem, 3.1vw, 3rem);
  --t-h3:      clamp(1.35rem, 2vw, 2rem);
  --t-body:    clamp(1rem, 1.05vw, 1.125rem);
  --t-label:   clamp(0.7rem, 0.78vw, 0.8rem);

  /* Fluid spacing — the reference runs 320px/160px section padding */
  --space-xl: clamp(6rem, 14vw, 20rem);
  --space-lg: clamp(4rem, 8vw, 10rem);
  --space-md: clamp(2.5rem, 4vw, 5rem);
  --space-sm: clamp(1.25rem, 2vw, 2rem);

  --gutter: clamp(1.25rem, 4vw, 4.5rem);
  --measure: 62ch;
  --ease: cubic-bezier(0.22, 1, 0.36, 1);
}

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

/* Lenis drives scrolling, so the native smooth behaviour is turned off
   to stop the two fighting each other. Required Lenis styles inlined
   rather than loaded as a separate stylesheet. */
html.lenis, html.lenis body { height: auto; }
.lenis.lenis-smooth [data-lenis-prevent] { overscroll-behavior: contain; }
.lenis.lenis-stopped { overflow: clip; }
.lenis.lenis-smooth iframe { pointer-events: none; }

body {
  margin: 0;
  background: var(--paper);
  color: var(--navy);
  font-family: var(--body);
  font-size: var(--t-body);
  line-height: 1.65;
  font-weight: 400;
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
  /* MUST be clip, not hidden. `overflow-x: hidden` forces overflow-y to
     `auto`, which makes the body a scroll container and silently breaks
     `position: sticky` on the hero pane — the pane stops pinning, the
     hero scrolls away like a normal section, and its bottom edge cuts a
     hard horizontal line through the mist transition. `clip` contains
     the same overflow without creating a scroll container. */
  overflow-x: clip;
}

/* The body's overflow does NOT clip on its own: while <html> is left at
   visible, the body's value is handed up to the viewport instead, and
   phone browsers ignore that for zoom. Anything poking past the right
   edge — every .sr--right item waits 70px off to the right before it
   slides in — then widened the layout viewport to 425px on a 375px
   phone and the whole page rendered zoomed out. Clipping on <main>
   applies to <main> itself. Still clip, never hidden, for the sticky
   heroes inside it; fixed layers are unaffected because their
   containing block is the viewport, not <main>. */
main { overflow-x: clip; }

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

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

h1, h2, h3, h4 {
  font-family: var(--display);
  /* Cormorant Garamond is delicate at 400. 500 gives it presence at
     display sizes without tipping into bold. */
  font-weight: 500;
  line-height: 1;
  letter-spacing: -0.005em;
  margin: 0;
  text-wrap: balance;
}

p { margin: 0 0 1.1em; }
p:last-child { margin-bottom: 0; }

::selection { background: var(--accent-ink); color: #fff; }

/* ═══════════════════════════════════════════════════════════════
   LAYOUT
   ═══════════════════════════════════════════════════════════════ */
.wrap {
  width: min(100% - (var(--gutter) * 2), 1440px);
  margin-inline: auto;
}

.wrap--narrow { width: min(100% - (var(--gutter) * 2), 900px); }

/* Top padding was --space-xl (up to 320px, the reference's rhythm), which
   left every section sitting low on the page. --space-lg is the same
   figure the sections already use underneath. */
.section { padding-block: var(--space-lg); }

/* The section straight after the hero needs far less headroom. The hero
   already ends on a full screen of white, so a further 320px of top
   padding reads as a large empty gap rather than as breathing room. */
.hero + .section { padding-top: var(--space-md); }

/* NOTE: the section after the image grid deliberately has NO override.
   It used to take --space-xl, which made its heading sit 90px lower than
   every other section's and broke the page's vertical rhythm. It uses
   the same --space-lg as the rest. */
.section--tight { padding-block: var(--space-lg); }
.section--flush { padding-block: 0; }

.section--dark {
  background: var(--ink);
  color: var(--bone);
}
.section--bone { background: var(--bone); }

/* A dark section over a photograph: the section's own ink laid over it
   at 40%, plus a further 20% fading out from the left, behind the copy. */
.section--photo { position: relative; overflow: hidden; isolation: isolate; }
.section-photo { position: absolute; inset: 0; z-index: -1; }
.section-photo img { width: 100%; height: 100%; object-fit: cover; object-position: var(--pos, 50% 50%); }
.section-photo::after {
  content: "";
  position: absolute;
  inset: 0;
  background:
    linear-gradient(to right, rgba(11, 16, 23, 0.2) 0, rgba(11, 16, 23, 0) 60%),
    rgba(11, 16, 23, 0.4);
}

.skip-link { position: absolute; left: -9999px; }
.skip-link:focus {
  left: 1rem; top: 1rem; z-index: 999;
  padding: 0.75rem 1rem; background: var(--paper); color: var(--navy);
}

/* ═══════════════════════════════════════════════════════════════
   TYPOGRAPHY HELPERS
   ═══════════════════════════════════════════════════════════════ */
.eyebrow {
  font-family: var(--label);
  font-size: var(--t-label);
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.22em;
  line-height: 1;
  margin: 0 0 var(--space-sm);
  color: var(--accent-ink);
}
.section--dark .eyebrow { color: var(--glacier); }

.lead {
  font-family: var(--display);
  font-size: var(--t-lead);
  line-height: 1.12;
  letter-spacing: -0.015em;
  max-width: 22ch;
}

.prose { max-width: var(--measure); }
.prose--wide { max-width: 78ch; }
.prose--narrow { max-width: calc(var(--measure) * 0.9); }

/* ═══════════════════════════════════════════════════════════════
   BUTTONS
   ═══════════════════════════════════════════════════════════════ */
/* Squared off to match the reference: 2px radius, hairline border,
   sentence case at 16px regular. No pills, no uppercase tracking. */
.btn {
  display: inline-flex;
  align-items: center;
  gap: 0.6em;
  font-family: var(--body);
  font-size: 1rem;
  font-weight: 400;
  letter-spacing: normal;
  line-height: 1;
  padding: 1.05rem 1.5rem;
  border: 1px solid rgba(27, 42, 65, 0.25);
  border-radius: 2px;
  background: transparent;
  color: inherit;
  cursor: pointer;
  transition: background 0.35s var(--ease), color 0.35s var(--ease), border-color 0.35s var(--ease);
}
.btn:hover { background: var(--navy); color: var(--paper); border-color: var(--navy); }

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

.btn--light { color: var(--paper); border-color: rgba(255, 255, 255, 0.28); }
.btn--light:hover { background: var(--paper); color: var(--ink); border-color: var(--paper); }

/* ═══════════════════════════════════════════════════════════════
   NAV
   ═══════════════════════════════════════════════════════════════ */
.nav {
  position: fixed;
  inset: 0 0 auto;
  z-index: 100;
  padding-block: 1.5rem;
  color: var(--paper);
  transition:
    color 0.45s var(--ease),
    transform 0.32s var(--ease);
}

/* Away from the top: no bar at all. The links sit in a floating white
   pill (desktop, below) and the logo swaps to the black version. It used
   to be a dark translucent bar across the full width. */
.nav.is-stuck { color: var(--ink); }

/* Retreats on scroll down, returns on scroll up. Used only once past
   the hero. */
.nav.is-hidden { transform: translateY(-100%); }

/* While riding with the hero copy the bar is positioned every frame, so
   transform must NOT be transitioned — a transition here is exactly the
   lag that made the bar trail behind the headline. */
.nav.is-linked { transition: color 0.45s var(--ease); }

.nav-inner {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-sm);
}

.nav-logo {
  position: relative;
  display: block;
  flex: none;
}
.nav-logo img {
  /* Doubled on request. The files are 300x138, so up to 69px tall
     they are sharp on high-density screens and at the 76px maximum only
     marginally scaled. */
  height: clamp(52px, 6vw, 76px);
  width: auto;
  transition: opacity 0.45s var(--ease);
}
/* The black logo sits exactly on top of the white one and fades in. */
.nav-logo-img--dark { position: absolute; top: 0; left: 0; opacity: 0; }
.nav.is-stuck .nav-logo-img--light { opacity: 0; }
.nav.is-stuck .nav-logo-img--dark  { opacity: 1; }

.nav-links {
  display: flex;
  align-items: center;
  gap: clamp(1rem, 2.2vw, 2.5rem);
  font-family: var(--label);
  font-size: var(--t-label);
  font-weight: 500;
  text-transform: uppercase;
  letter-spacing: 0.16em;
}
.nav-links a { position: relative; padding-block: 0.4em; }

/* Hover roll, in place of the underline. The label sits in a mask one
   line tall. On hover the visible copy rises up out of the top while the
   second copy rises in from below. The second starts 0.07s behind the
   first, so the two overlap rather than one waiting for the other to
   clear. Leaving the link runs it backwards, with the stagger swapped so
   it still overlaps the same way. Block display keeps the mask exactly
   one line box tall, so the link and the pill are the same height as
   before. */
.nav-roll {
  position: relative;
  display: block;
  overflow: hidden;
}
.nav-roll-out,
.nav-roll-in {
  display: block;
  transition: transform 0.6s var(--ease);
}
.nav-roll-out { transition-delay: 0.07s; }
.nav-roll-in {
  position: absolute;
  inset: 0 auto auto 0;
  transform: translateY(100%);
}
.nav-links a:hover .nav-roll-out,
.nav-links a:focus-visible .nav-roll-out {
  transform: translateY(-100%);
  transition-delay: 0s;
}
.nav-links a:hover .nav-roll-in,
.nav-links a:focus-visible .nav-roll-in {
  transform: translateY(0);
  transition-delay: 0.07s;
}

/* Floating white panel, desktop only; on phones the links are the
   full-screen menu instead. Square corners, on request; it was a fully
   rounded pill. Padding animates in with the white, so the links ease
   inward rather than jumping when the pill appears. The shadow is kept
   faint: most of the page is white, and without it a white pill over a
   white section has no visible edge. */
@media (min-width: 861px) {
  .nav-links {
    border-radius: 0;
    transition:
      background-color 0.45s var(--ease),
      padding 0.45s var(--ease),
      box-shadow 0.45s var(--ease);
  }
  .nav.is-stuck .nav-links {
    background-color: var(--paper);
    /* 2.75rem either side, up from 2rem. */
    padding: 0.75rem 2.75rem;
    box-shadow: 0 4px 24px rgba(11, 16, 23, 0.08);
  }
}

.nav-toggle {
  display: none;
  flex-direction: column;
  gap: 5px;
  background: none;
  /* Buttons do not inherit colour; without this the bars were drawn in
     the UA's black and vanished over dark header images. */
  color: inherit;
  border: 0;
  padding: 0.5rem;
  cursor: pointer;
}
.nav-toggle span {
  display: block;
  width: 26px; height: 1px;
  background: currentColor;
  transition: transform 0.4s var(--ease), opacity 0.3s;
}

/* ═══════════════════════════════════════════════════════════════
   HERO
   ═══════════════════════════════════════════════════════════════ */
.hero {
  position: relative;
  /* Above the grid's fixed layer, which is z-index 0. */
  z-index: 1;
  /* Nearly three viewports tall. Everything past the first is runway
     for the pinned pane, so a taller hero means the whole sequence
     unfolds over more scrolling and reads slower. */
  /* 220svh. The clouds travel a fixed distance — two screens — so the
     only way to slow them is more scroll to cover it in. This pins for
     120svh, which is enough for the same 1.67x drag as 300svh gave,
     without the tail that 300svh left sticking out below the section
     that climbs over it. */
  height: 220svh;
  /* White, not ink. Any sliver of hero that ever shows past the pinned
     pane then matches the section below instead of cutting a dark edge. */
  background: var(--paper);
}
/* Phones only: the headline at double size, and the line above it at
   35% of the headline, on two lines. The height term still stops it
   running up under the logo on a short phone. */
@media (max-width: 720px) {
  :root { --t-hero: clamp(3rem, min(13vw, calc(24vh - 98px)), 13rem); }
  /* Two classes: the base .hero-kicker rule comes later in the file. */
  .hero-inner .hero-kicker { font-size: calc(var(--t-hero) * 0.35); max-width: 36ch; }
}

/* Shorter on phones, so the clouds clear and the section below arrives
   about a third of a screen sooner. The section climbing over it
   (--climb, 74svh) is still well under its phone height of about 816px,
   so the hero's foot stays covered. */
@media (max-width: 720px) {
  .hero { height: 185svh; }
}

.hero-pin {
  position: sticky;
  top: 0;
  height: 100svh;
  overflow: hidden;
  display: flex;
  flex-direction: column;
  justify-content: flex-end;
  color: var(--paper);
}

/* ─── WordPress admin bar ───────────────────────────────────────────
   Logged-in users get a 32px bar (46px on small screens) and WordPress
   pushes the whole document down by that much. Anything pinned to the
   top of the viewport then sits below it and has to travel that gap
   before it engages — which showed as the hero video moving for the
   first 32px of scroll. Visitors never see this; it only matters while
   working on the site logged in. The pane is pinned just under the bar
   and shortened to match, so pin release lands at exactly the same
   scroll position as with no bar at all. The scroll handler reads the
   pane's `top` so its maths follows suit. */
.admin-bar .hero-pin { top: 32px; height: calc(100svh - 32px); }
.admin-bar .nav { top: 32px; }
.admin-bar .reveal-float { top: 32px; }
@media (max-width: 782px) {
  .admin-bar .hero-pin { top: 46px; height: calc(100svh - 46px); }
  .admin-bar .nav { top: 46px; }
  .admin-bar .reveal-float { top: 46px; }
}

/* Deliberately no parallax. The footage holds still while the copy,
   and the clouds move over it. */
.hero-media { position: absolute; inset: 0; z-index: 0; }
.hero-media img,
.hero-media video { width: 100%; height: 100%; object-fit: cover; }

/* Scrim for the hero copy. Weighted for high-key footage: the Antarctic
   clip is bright snow and sky, so white type needs real cover through
   the middle band where the headline sits, not just at the edges. */
.hero-pin::after {
  content: "";
  position: absolute;
  inset: 0;
  z-index: 1;
  background:
    linear-gradient(180deg,
      rgba(11,16,23,0.58) 0%,
      rgba(11,16,23,0.34) 32%,
      rgba(11,16,23,0.42) 58%,
      rgba(11,16,23,0.72) 100%);
}

.hero-inner {
  position: relative;
  z-index: 2;
  /* Clears the fixed nav so hero copy can never sit under the logo. */
  padding-top: clamp(5rem, 12vh, 9rem);
  /* The block is bottom-anchored, so extra bottom padding lifts it. The
     10svh raises it by a tenth of the screen. */
  padding-bottom: calc(var(--space-md) + 10svh);
  will-change: transform, filter, opacity;
}


.hero-kicker {
  font-family: var(--display);
  font-style: italic;
  font-size: clamp(0.95rem, 1.3vw, 1.25rem);
  max-width: 20ch;
  line-height: 1.4;
  margin-bottom: clamp(1.35rem, 5.4vh, 4.5rem);
  opacity: 0.9;
}

/* True italics, not synthesised — the 500 and 600 italic weights are
   loaded alongside the uprights so the browser never has to slant the
   roman face itself. */
.hero-title em {
  font-style: italic;
  font-weight: 500;
}

.hero-title {
  font-size: var(--t-hero);
  font-weight: 600;
  line-height: 0.88;
  letter-spacing: -0.01em;
  /* 10% tighter than --space-md. */
  margin-bottom: clamp(2.25rem, 3.6vw, 4.5rem);
}

.hero-actions { display: flex; flex-wrap: wrap; gap: 1rem; align-items: center; }

.hero-scroll {
  position: absolute;
  /* Decorative only. It carries .wrap, which makes it 1380px wide, and
     at z-index 2 later in the DOM than .hero-inner it was sitting on top
     of both hero buttons and swallowing every click. */
  pointer-events: none;
  right: 0; bottom: var(--space-md);
  z-index: 2;
  font-family: var(--label);
  font-size: var(--t-label);
  text-transform: uppercase;
  letter-spacing: 0.2em;
  writing-mode: vertical-rl;
  opacity: 0.75;
}

/* ═══════════════════════════════════════════════════════════════
   MIST TRANSITION
   Sits between the hero and the white section, pulled up over the
   seam by a negative margin. The hero clips its own overflow, so
   this has to be a sibling rather than a child.
   ═══════════════════════════════════════════════════════════════ */
.mist {
  position: absolute;
  inset: 0;
  z-index: 3;
  pointer-events: none;
  overflow: hidden;
}
/* Each bank is twice the pane: the cloud image fills the top half, and
   white trails beneath it. Parked one full bank-height down, it rises
   two screens — the first brings the cloud into view, the second pushes
   the cloud out past the top with its white following, until only white
   is left. That trailing white IS the whiteout; there is no separate
   fade. This is the reference's model. */
.mist-bank {
  position: absolute;
  left: 0;
  right: 0;
  bottom: 0;
  height: 200%;
  will-change: transform;
  transform: translate3d(0, 100%, 0);
}

/* Stretched to cover exactly one pane, anchored at the top — the
   reference's object-position 50% 0%. */
.mist-bank img {
  width: 100%;
  height: 50%;
  object-fit: cover;
  object-position: 50% 0%;
  display: block;
}

/* The trailing white. It begins at the cloud image's OWN foot colour and
   eases to pure white over 40% of the pane, so there is never a step at
   the join. Measured: cloud-2-full's lowest rows are rgb(255,255,255),
   but cloud-1-cropped's are rgb(249,249,248) — six units off white, dead
   flat across the whole width. Butted straight against #fff that is a
   visible horizontal line, and it is exactly the line that disappeared
   whenever cloud-1-cropped was hidden. */
.mist-bank::after {
  content: "";
  position: absolute;
  left: 0;
  right: 0;
  top: 50%;
  bottom: 0;
  background: linear-gradient(to bottom, var(--foot, #fff) 0, var(--paper) 40%);
}
.mist-bank--wisp { --foot: rgb(249, 249, 248); }
.mist-bank--mass { --foot: rgb(255, 255, 255); }

/* NOTE: no opacity on the banks. Depth comes from travel speed only. */

.mist-gradient {
  position: absolute;
  inset: 0;
  opacity: 0;
  transform: scaleY(-1);
  background-repeat: no-repeat;
  background-position: center;
  background-size: 100% 100%;
  will-change: opacity;
}

@media (prefers-reduced-motion: reduce) {
  .mist-bank { transform: none !important; }
}

/* ═══════════════════════════════════════════════════════════════
   SCALING IMAGE GRID
   Built on the reference's model (thetailor.com), after inspecting it:
   - the grid lives in a position:fixed layer filling the viewport, and
     is switched on only while its section is in range;
   - the section itself is an empty scroll runway at z-index 0;
   - the sections before and after carry z-index 9 and simply scroll
     over the fixed layer — no sticky pane, no negative margins;
   - every cell is a viewport percentage, with the centre cell 46% wide
     and 46% tall, so scaling the whole grid by 1/0.46 makes that cell
     exactly full-screen. Nothing is measured and no target is computed.
   ═══════════════════════════════════════════════════════════════ */
.reveal {
  position: relative;
  z-index: 0;
  /* Scroll runway. At 150svh the scale finished BEFORE the section
     above had even cleared, so the centre was already full-screen by the
     time you could see the grid. 300svh gives the scale room to still be
     running well after the uncover, which is what keeps the side columns
     in view. */
  height: 300svh;
  background: transparent;
}

.reveal-float {
  position: fixed;
  inset: 0;
  z-index: 0;
  display: none;
  /* The grid is built larger than the screen (see below), so clip it to
     the layer rather than rely on it never mattering. */
  overflow: hidden;
  /* Warm off-white ground, so the gaps between cells read as
     deliberate rather than as page showing through. */
  background: #f8f8f4;
}
.reveal-float.is-on { display: block; }

.reveal-change {
  /* The reference uses a flat 40px. Tightened to 30px on request. The
     gap sits inside the scaled element, so on screen it grows and shrinks
     with the grid. */
  --gap: 30px;
  /* Scale the grid starts at, read by js/main.js. The reference starts
     at 1; because the scale is already climbing while the section above
     uncovers the grid, the side images were three-quarters off screen by
     the time the whole grid was in view. Starting at 0.8 leaves about
     half of each side image showing at that moment. */
  --start: 0.8;
  position: absolute;
  top: 50%;
  left: 50%;
  /* Larger than the screen by exactly 1 / --start (125% at 0.8), so that
     at the starting scale the grid fills the screen edge to edge. When
     it was screen-sized, starting at 0.8 left an off-white margin round
     the outside with the images stopping short of every edge. */
  width: calc(100% / var(--start));
  height: calc(100% / var(--start));
  transform: translate(-50%, -50%) scale(var(--start));
  transform-origin: center center;
  display: flex;
  will-change: transform;
}

.reveal-col {
  display: flex;
  flex-direction: column;
  justify-content: space-between;
  height: 100%;
}

/* The centre column is 46% of the screen at scale 1, as in the
   reference, which is what lets js/main.js scale it to full screen with
   a fixed 1 / 0.46. The grid box is 1 / --start of the screen, so as a
   share of the box that is 46% × --start. The side columns split what is
   left, and now reach out past the screen edges, which is where the
   extra width goes. */
.reveal-col--left,
.reveal-col--right { width: calc((100% - 46% * var(--start)) / 2); }
.reveal-col--mid   { width: calc(46% * var(--start)); }
.reveal-col--right { align-items: flex-end; }

.reveal-cell { overflow: hidden; }
.reveal-cell img,
.reveal-cell video { width: 100%; height: 100%; object-fit: cover; display: block; }

/* Side columns: two cells each, running out to the edge of the grid and
   leaving a double gap on the inside, next to the centre column. */
.reveal-col--left  .reveal-cell,
.reveal-col--right .reveal-cell {
  width: calc(100% - var(--gap) * 2);
  height: calc(50% - var(--gap));
}

/* Centre column: a strip above and below the main cell, running to the
   top and bottom edges of the grid. The same split as across, applied
   to the height, and space-between leaves the double gap either side of
   the main cell. */
.reveal-col--mid .reveal-cell--edge { width: 100%; height: calc((100% - 46% * var(--start)) / 2 - var(--gap) * 2); }
.reveal-col--mid .reveal-cell--hero { width: 100%; height: calc(46% * var(--start)); }

/* Sections either side of the grid, and the one after that, need an
   opaque background and a higher stacking order than the fixed layer,
   or they would not pass over it. */
.reveal-neighbour {
  position: relative;
  z-index: 9;
  background: var(--paper);
}

/* A neighbour that already has its own ground keeps it — reveal-neighbour
   is only here for the stacking order and the opacity. */
.reveal-neighbour.section--bone { background: var(--bone); }

/* Phones run the same scaling grid as desktop, as the reference does.
   At 30px the gaps would leave the side cells about 40px wide on a
   375px screen, so they close up there. */
@media (max-width: 720px) {
  .reveal-change { --gap: 12px; }
}

/* Static fallback for reduced motion only: the layer becomes an ordinary
   block, unscaled. It used to apply below 720px as well, which is why
   the grid did not scale on phones. */
@media (prefers-reduced-motion: reduce) {
  .reveal { height: auto; }
  .reveal-float { position: relative; display: block; inset: auto; height: 100svh; }
  /* Held at the starting scale, where the oversized grid exactly fills
     the frame. */
  .reveal-change { transform: translate(-50%, -50%) scale(var(--start)) !important; }
}

/* ═══════════════════════════════════════════════════════════════
   PARALLAX BANNER
   ═══════════════════════════════════════════════════════════════ */
.banner {
  position: relative;
  height: clamp(28rem, 88vh, 52rem);
  overflow: hidden;
}
/* The parallax moves the image by up to 18% of the banner's height
   (data-parallax="0.18"), so it needs at least that much spare above and
   below; at 12% a band of empty space showed along the top as the banner
   came into view. */
.banner-media { position: absolute; inset: -20% 0; will-change: transform; }
.banner-media img { width: 100%; height: 100%; object-fit: cover; }

/* Bottom scrim under the caption, which sits bottom-left. */
.banner::after {
  content: "";
  position: absolute;
  inset: 0;
  z-index: 1;
  background: linear-gradient(to top, rgba(11, 16, 23, 0.6) 0, rgba(11, 16, 23, 0.2) 40%, rgba(11, 16, 23, 0) 65%);
  pointer-events: none;
}

.banner-caption {
  position: relative;
  z-index: 2;
  height: 100%;
  display: flex;
  align-items: flex-end;
  padding-bottom: var(--space-lg);
  color: var(--paper);
}
.banner-caption h2 { font-size: var(--t-display); max-width: 16ch; }

/* ═══════════════════════════════════════════════════════════════
   STATEMENT
   ═══════════════════════════════════════════════════════════════ */
.statement-title { font-size: var(--t-display); max-width: 14ch; }

/* ═══════════════════════════════════════════════════════════════
   STATEMENT STAGE
   Centred sentence with three images scattered around it. The images
   are absolutely placed so they can bleed off the edges and overlap
   the text, and each drifts at its own rate as the section passes.
   ═══════════════════════════════════════════════════════════════ */
/* Full-bleed section, so the images reach the viewport edges. */
.statement-section {
  position: relative;
  width: 100%;
  background: var(--paper);

  /* Climbs over the tail of the hero — by --climb, and it comes in
     while the clouds are still rising. That is allowed because this
     section's top is SOFT: a fade from transparent to white over the
     same distance (::before below). A hard white top edge sliced across
     the clouds as a straight line, which is why earlier attempts had to
     wait for the pane to go flat white. A soft one simply whitens what
     is beneath it, so there is nothing to slice.
     The fade must not be longer than the climb: the climb is exactly the
     overlap with the hero, so a fade of that length always has the hero
     behind it and never the image grid's fixed layer further down. */
  /* MUST NOT EXCEED THIS SECTION'S OWN HEIGHT. The section starts
     --climb above the hero's foot, so if it is shorter than that it
     cannot reach the foot and the hero's tail shows below it as blank
     white. At 160svh over a 300svh hero it fell 790px short, which read
     as a huge gap under the sentence. The section is ~730px tall here,
     so this stays under it. */
  --climb: 74svh;
  margin-top: calc(-1 * var(--climb));

  /* The white is painted by ::before so its top can fade. */
  background: transparent;

  /* No top padding: the climb already provides the separation.
     The bottom gives roughly 200px under the sentence, together with the
     stage's own 30px. Extra height here is free — it only makes the
     section taller than --climb, which is the direction that keeps the
     hero's tail covered. */
  padding-block: 0 clamp(4.5rem, 11.3vw, 12rem);
}

.statement-section::before {
  content: "";
  position: absolute;
  inset: 0;
  z-index: -1;
  background: linear-gradient(to bottom, rgba(255, 255, 255, 0) 0, var(--paper) var(--climb));
}

.statement-stage {
  position: relative;
  width: 100%;
  /* Asymmetric. The top padding is the room the images need above the
     sentence, but nothing hangs below it — the lowest image bottoms out
     above the sentence's own last line — so the bottom only needs enough
     to keep the text off the section edge. */
  padding-block: clamp(3.5rem, 8vw, 8rem) clamp(1rem, 2vw, 2rem);
  display: grid;
  place-items: center;
}

.statement-lead {
  position: relative;
  z-index: 2;
  margin: 0;
  text-align: center;
  font-family: var(--display);
  font-weight: 500;
  /* Tops out at 64px. */
  font-size: clamp(1.9rem, 4.4vw, 4rem);
  line-height: 1.16;
  letter-spacing: -0.005em;
  /* Matches the reference's measure — roughly seven lines. */
  max-width: 18ch;
  text-wrap: balance;
  will-change: transform;
}

/* Word-by-word colour reveal, on the supreme-luxury.com model: each word
   starts pale and darkens to full colour as the block crosses the screen.
   The words are wrapped by script, so with JS off the sentence is simply
   its normal colour and nothing is hidden. */

.statement-img {
  position: absolute;
  z-index: 1;
  object-fit: cover;
  will-change: transform;
}

/* Positions as percentages of the full-bleed stage. Widths are 20%
   larger than the previous pass. */

/* Each box takes its image's own shape, so nothing is cropped. The
   widths are the ones already signed off; only the heights follow the
   pictures. */

/* float-image1.png — top left, moved in from the left edge. 20% wide,
   up from 11% and then 15%. Its right edge, at 27%, still stops short
   of the sentence, which starts at about 31%. */
.statement-img--a {
  left: 7%;
  top: 8%;
  width: 20%;
  aspect-ratio: 666 / 406;
}

/* float-image3.png — right. */
.statement-img--b {
  /* The sentence ends at about 69% of the viewport. At 15% this image
     lapped over the end of the first two lines; at 11% its left edge
     (100 − 11 − 19.2 = 69.8%) sits just clear of them. The higher
     stacking order stays so it still wins if a narrower screen
     rewraps the sentence wider. */
  right: 11%;
  top: 26%;
  width: 19.2%;
  aspect-ratio: 788 / 498;
  z-index: 3;
}

/* float-image2.png — the portrait one, closest to the text. It laps
   under the start of the sentence's lower lines: z-index 1 puts it
   BEHIND the sentence, which sits at 2. */
.statement-img--c {
  /* 24%: moved left from 26% to 22%, then back right. */
  left: 24%;
  top: 58%;
  width: 12%;
  aspect-ratio: 418 / 630;
  z-index: 1;
}

/* Absolute placement cannot survive a narrow screen, so below this the
   stage becomes a column. It was a stack of three full-width photos,
   which pushed the sentence almost a thousand pixels down the page.
   Now it keeps the desktop idea at phone scale: the landscape photo
   runs off the left edge, the portrait one sits lower on the right, the
   sentence has room above and below, and the last photo runs off the
   right edge. Each keeps its own shape (set above). Everything drifts
   as on desktop, at 35% of the rate (js/main.js), so the photos never
   close the space around the sentence. */
@media (max-width: 720px) {
  .statement-stage {
    display: flex;
    flex-direction: column;
    padding: clamp(2rem, 10vw, 3rem) var(--gutter) 0;
    min-height: 0;
  }
  .statement-img {
    position: relative;
    inset: auto;
  }
  .statement-img--a {
    order: 1;
    align-self: flex-start;
    width: 60%;
    margin-left: calc(-1 * var(--gutter));
  }
  .statement-img--c {
    order: 2;
    align-self: flex-end;
    width: 34%;
    /* Rises alongside the first photo, so the pair reads as one group. */
    margin: -24% 6% 0 0;
  }
  /* Two classes, to outrank p:last-child's zero bottom margin. */
  .statement-stage .statement-lead {
    order: 3;
    max-width: 20ch;
    align-self: center;
    margin: 4rem 0;
  }
  .statement-img--b {
    order: 4;
    align-self: flex-end;
    width: 64%;
    margin-right: calc(-1 * var(--gutter));
  }
}

/* ═══════════════════════════════════════════════════════════════
   STATS
   ═══════════════════════════════════════════════════════════════ */
.stats {
  display: grid;
  grid-template-columns: repeat(2, 1fr);
  gap: var(--space-md) var(--space-sm);
  border-top: 1px solid var(--rule);
  padding-top: var(--space-md);
}
@media (min-width: 800px) { .stats { grid-template-columns: repeat(4, 1fr); } }

.stat-num {
  font-family: var(--display);
  font-size: clamp(3rem, 6vw, 5.5rem);
  line-height: 1;
  display: block;
}
.stat-label {
  font-family: var(--label);
  font-size: var(--t-label);
  font-weight: 500;
  text-transform: uppercase;
  letter-spacing: 0.18em;
  opacity: 0.65;
  margin-top: 0.7em;
  display: block;
}

/* ═══════════════════════════════════════════════════════════════
   EVENT CARDS
   ═══════════════════════════════════════════════════════════════ */
.events {
  display: grid;
  grid-template-columns: 1fr;
  gap: var(--space-md);
  margin-top: var(--space-lg);
}
@media (min-width: 700px)  { .events { grid-template-columns: repeat(2, 1fr); } }
@media (min-width: 1100px) { .events { grid-template-columns: repeat(3, 1fr); } }

.event { display: block; }
.event-media {
  position: relative;
  /* The thumbnails are all 800x588, so the card takes their own shape
     and nothing is cropped. */
  aspect-ratio: 800 / 588;
  overflow: hidden;
  margin-bottom: var(--space-sm);
  background: var(--bone);
}
.event-media img {
  width: 100%; height: 100%; object-fit: cover;
  transition: transform 1.2s var(--ease);
}
.event:hover .event-media img { transform: scale(1.06); }

.event-date {
  font-family: var(--label);
  font-size: var(--t-label);
  text-transform: uppercase;
  letter-spacing: 0.18em;
  color: var(--accent-ink);
  display: block;
  margin-bottom: 0.6em;
}
.event-place { font-size: var(--t-h3); margin-bottom: 0.35em; }
.event-topic { font-size: 0.95rem; opacity: 0.72; line-height: 1.5; }

/* ═══════════════════════════════════════════════════════════════
   VALUE GRID (dark section)
   ═══════════════════════════════════════════════════════════════ */
.values {
  display: grid;
  grid-template-columns: 1fr;
  gap: 0;
  margin-top: var(--space-lg);
  border-top: 1px solid var(--rule-on-dark);
}
@media (min-width: 700px)  { .values { grid-template-columns: repeat(2, 1fr); } }
@media (min-width: 1100px) { .values { grid-template-columns: repeat(4, 1fr); } }

.value {
  padding: var(--space-md) var(--space-sm) var(--space-md) 0;
  border-bottom: 1px solid var(--rule-on-dark);
}
@media (min-width: 700px) {
  .value { border-right: 1px solid var(--rule-on-dark); padding-left: var(--space-sm); }
  /* Each tile draws its own RIGHT edge, which leaves the first one with
     nothing down its left. It gets that edge explicitly. */
  .value:first-child { border-left: 1px solid var(--rule-on-dark); padding-left: var(--space-sm); }
}
.value-num {
  font-family: var(--label);
  font-size: var(--t-label);
  font-weight: 600;
  letter-spacing: 0.2em;
  color: var(--glacier);
  display: block;
  margin-bottom: var(--space-sm);
}
.value h3 { font-size: var(--t-h3); }

/* ═══════════════════════════════════════════════════════════════
   TESTIMONIALS
   ═══════════════════════════════════════════════════════════════ */
.quotes { margin-top: var(--space-lg); display: grid; gap: var(--space-md); }
@media (min-width: 900px) { .quotes { grid-template-columns: repeat(2, 1fr); gap: var(--space-lg); } }

.quote { border-top: 1px solid var(--rule); padding-top: var(--space-sm); }
.quote blockquote {
  margin: 0 0 var(--space-sm);
  font-family: var(--display);
  font-size: var(--t-h3);
  line-height: 1.3;
}
.quote cite {
  font-family: var(--label);
  font-style: normal;
  font-size: var(--t-label);
  text-transform: uppercase;
  letter-spacing: 0.18em;
  opacity: 0.6;
}

/* ═══════════════════════════════════════════════════════════════
   VIDEO TESTIMONIALS
   Click-to-play facades. The Vimeo iframe is only injected on press,
   so the page loads no third-party code until a visitor asks for it.
   ═══════════════════════════════════════════════════════════════ */
.vids {
  display: grid;
  grid-template-columns: 1fr;
  gap: var(--space-md);
  margin-top: var(--space-md);
}
@media (min-width: 700px)  { .vids { grid-template-columns: repeat(2, 1fr); } }
@media (min-width: 1000px) { .vids { grid-template-columns: repeat(3, 1fr); } }

.vid { margin: 0; }

.vid-play {
  position: relative;
  display: block;
  width: 100%;
  padding: 0;
  border: 0;
  background: var(--navy);
  aspect-ratio: 16 / 9;
  overflow: hidden;
  cursor: pointer;
}
.vid-play img {
  width: 100%; height: 100%;
  object-fit: cover;
  transition: transform 1.2s var(--ease), opacity 0.5s var(--ease);
}
.vid-play:hover img { transform: scale(1.05); opacity: 0.85; }

.vid-play::after {
  content: "";
  position: absolute;
  inset: 0;
  background: linear-gradient(180deg, rgba(11,16,23,0) 45%, rgba(11,16,23,0.55) 100%);
}

.vid-icon {
  position: absolute;
  left: 50%; top: 50%;
  transform: translate(-50%, -50%);
  z-index: 2;
  width: clamp(3.25rem, 5vw, 4.5rem);
  aspect-ratio: 1;
  border: 1px solid rgba(255, 255, 255, 0.85);
  border-radius: 50%;
  background: rgba(11, 16, 23, 0.25);
  backdrop-filter: blur(2px);
  transition: background 0.4s var(--ease), transform 0.4s var(--ease);
}
/* Play triangle */
.vid-icon::before {
  content: "";
  position: absolute;
  left: 54%; top: 50%;
  transform: translate(-50%, -50%);
  border-style: solid;
  border-width: 0.46em 0 0.46em 0.75em;
  border-color: transparent transparent transparent #fff;
  font-size: clamp(0.9rem, 1.4vw, 1.2rem);
}
.vid-play:hover .vid-icon {
  background: var(--accent-ink);
  transform: translate(-50%, -50%) scale(1.08);
}
.vid-play:focus-visible .vid-icon { outline: 2px solid var(--accent-ink); outline-offset: 4px; }

.vid-time {
  position: absolute;
  right: 0.9rem; bottom: 0.8rem;
  z-index: 2;
  font-family: var(--label);
  font-size: 0.7rem;
  font-weight: 600;
  letter-spacing: 0.1em;
  color: #fff;
}

.vid figcaption {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: 0.6rem;
  padding-top: var(--space-sm);
}
.vid-name { font-family: var(--display); font-size: var(--t-h3); font-weight: 500; }
.vid-place {
  font-family: var(--label);
  font-size: var(--t-label);
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.18em;
  color: var(--accent-ink);
}

/* Injected player keeps the slot's shape so nothing reflows on play. */
.vid-frame { position: relative; aspect-ratio: 16 / 9; background: #000; }
.vid-frame iframe { position: absolute; inset: 0; width: 100%; height: 100%; border: 0; }

/* ═══════════════════════════════════════════════════════════════
   FAQ
   ═══════════════════════════════════════════════════════════════ */
.faq { margin-top: var(--space-lg); border-top: 1px solid var(--rule); }
.faq details { border-bottom: 1px solid var(--rule); }
.faq summary {
  list-style: none;
  cursor: pointer;
  padding: var(--space-sm) 3rem var(--space-sm) 0;
  position: relative;
  font-family: var(--display);
  font-size: var(--t-h3);
  line-height: 1.25;
}
.faq summary::-webkit-details-marker { display: none; }
.faq summary::after {
  content: "";
  position: absolute;
  right: 0.5rem; top: 50%;
  width: 12px; height: 12px;
  border-right: 1px solid currentColor;
  border-bottom: 1px solid currentColor;
  transform: translateY(-70%) rotate(45deg);
  transition: transform 0.4s var(--ease);
}
.faq details[open] summary::after { transform: translateY(-30%) rotate(-135deg); }
.faq-answer { padding-bottom: var(--space-sm); max-width: var(--measure); opacity: 0.78; }

/* ═══════════════════════════════════════════════════════════════
   ENQUIRY / FORM SECTION
   ═══════════════════════════════════════════════════════════════ */
.enquire-grid { display: grid; gap: var(--space-lg); }
@media (min-width: 1000px) { .enquire-grid { grid-template-columns: 1fr 1fr; align-items: start; } }

.form-shell {
  background: var(--paper);
  color: var(--navy);
  padding: var(--space-md);
  border: 1px solid var(--rule);
}
.form-embed { width: 100%; }
.form-embed iframe { width: 100%; border: 0; display: block; }
/* GoHighLevel's resizer sets the height to whatever the form reports
   plus 5px. A form that has not been drawn yet reports 0, which left a
   5px strip in testing. This floor stops that; the real height (about
   670px) is always above it. */
#bhc-form-embed iframe { min-height: 30rem; }

.form-consent {
  margin-top: var(--space-sm);
  font-size: 12px;
  line-height: 1.55;
  opacity: 0.7;
}
.form-consent a { text-decoration: underline; text-underline-offset: 0.2em; }
/* The GoHighLevel form leaves about 64px of empty space below its own fine
   print, inside the iframe where it cannot be styled. Pulling the consent
   line up into that space keeps it reading as part of the form. It still
   clears the form's last line by a comfortable margin. */
#bhc-form-embed + .form-consent { position: relative; margin-top: -1.25rem; }

/* ═══════════════════════════════════════════════════════════════
   FOOTER — modelled on supreme-luxury.com
   Two stacked parts: the footer and wordmark, which scroll normally, and
   a still photograph that they uncover as they scroll away.
   ═══════════════════════════════════════════════════════════════ */
.ftr { background: var(--paper); position: relative; z-index: 9; }





.ftr-reveal-heading {
  color: var(--paper);
  font-family: var(--display);
  font-weight: 500;
  letter-spacing: -0.01em;
  line-height: 1.06;
  margin: 0;
}

.ftr-reveal-heading { font-size: clamp(2.25rem, 5.5vw, 5rem); }
.ftr-reveal-heading em { font-style: italic; }

/* Underline grows from the left on hover, as theirs does. */
.ftr-cta-link {
  position: relative;
  display: inline-flex;
  flex-direction: column;
  gap: 0.4rem;
  color: var(--paper);
  font-family: var(--body);
  font-size: 1rem;
}
.ftr-cta-rule {
  display: block;
  height: 1px;
  background: currentColor;
  opacity: 0.5;
  transition: opacity 0.4s var(--ease);
}
.ftr-cta-link:hover .ftr-cta-rule { opacity: 1; }

/* ── 1. Footer and wordmark ──────────────────────────────────── */
.ftr-body {
  position: relative;
  z-index: 2;
  background: var(--paper);
}

.ftr-main { padding-top: var(--space-lg); }

.ftr-content {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-md);
  justify-content: space-between;
  align-items: flex-end;
  margin-bottom: var(--space-md);
}
.ftr-left { display: flex; flex-direction: column; gap: var(--space-sm); }

.ftr-tagline {
  margin: 0;
  font-family: var(--display);
  font-size: clamp(1.25rem, 2vw, 1.75rem);
  line-height: 1.3;
}

.ftr-subscribe {
  display: flex;
  align-items: stretch;
  width: min(100%, 23rem);
  border-bottom: 1px solid var(--navy);
  padding-bottom: 0.5rem;
}
.ftr-subscribe-input {
  flex: 1;
  border: 0;
  outline: none;
  background: none;
  font: inherit;
  font-size: 0.875rem;
  color: var(--navy);
}
.ftr-subscribe-input::placeholder { color: rgba(27, 42, 65, 0.5); }
.ftr-subscribe-btn {
  flex-shrink: 0;
  border: 0;
  cursor: pointer;
  background: var(--navy);
  color: var(--paper);
  font-family: var(--label);
  font-size: 0.75rem;
  font-weight: 600;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  padding: 0.5rem 1rem;
  transition: opacity 0.3s var(--ease);
}
.ftr-subscribe-btn:hover { opacity: 0.85; }

.ftr-right { display: flex; flex-direction: column; align-items: flex-start; gap: 1.1rem; }
.ftr-contact {
  display: inline-flex;
  flex-direction: column;
  gap: 2px;
  font-size: clamp(1rem, 1.3vw, 1.25rem);
  white-space: nowrap;
}
.ftr-contact-line { position: relative; display: block; height: 1px; background: currentColor; }
.ftr-contact-line::after {
  content: "";
  position: absolute;
  left: 0; bottom: 0;
  width: 100%; height: 2px;
  background: currentColor;
  transform: scaleX(0);
  transform-origin: 100% center;
  transition: transform 0.4s var(--ease);
}
.ftr-contact:hover .ftr-contact-line::after { transform: scaleX(1); transform-origin: 0 center; }

.ftr-nav { display: flex; flex-wrap: wrap; gap: var(--space-md); margin-bottom: var(--space-md); }
.ftr-nav-list { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; }
.ftr-nav-link { font-size: 1rem; font-weight: 500; line-height: 2; }
.ftr-nav-link:hover { opacity: 0.6; }
.ftr-nav-addr { line-height: 1.7; opacity: 0.7; }

.ftr-meta {
  display: flex;
  flex-wrap: wrap;
  justify-content: space-between;
  gap: 1.25rem;
  padding-bottom: var(--space-sm);
}
.ftr-meta-copy { margin: 0; font-size: 0.875rem; color: rgba(27, 42, 65, 0.7); }
.ftr-meta-copy a:hover { text-decoration: underline; }
.ftr-meta-link { margin-left: 1.25rem; }
.ftr-credit { font-size: 11px; letter-spacing: 0.02em; }

/* Oversized wordmark. Sized in vw so it always spans the screen; the
   tight line-height plus overflow crops the line box to the letters. */
.ftr-wordmark {
  background: var(--paper);
  line-height: 0.85;
  overflow: hidden;
}
.ftr-wordmark-text {
  display: block;
  text-align: center;
  white-space: nowrap;
  text-transform: uppercase;
  font-family: var(--display);
  font-weight: 600;
  font-size: 13.5vw;
  letter-spacing: -0.04em;
  color: var(--navy);
}

/* ── 2. Still photograph ─────────────────────────────────────── */
/* The wrapper is an ordinary block in the flow, one screen tall, so the
   photograph fills the screen exactly when the page reaches its end. The
   panel is fixed to the screen and the wrapper's clip-path shows it only
   where the wrapper is, so the photograph never moves: the footer above
   simply scrolls off it. */
.ftr-reveal-wrap {
  position: relative;
  z-index: 1;
  width: 100%;
  height: 100svh;
  clip-path: inset(0);
}

.ftr-reveal {
  position: fixed;
  inset: 0;
  z-index: 3;
  display: grid;
  place-items: center;
  background-position: 50% center;
  background-size: cover;
  background-repeat: no-repeat;
}
.ftr-reveal::after {
  content: "";
  position: absolute;
  inset: 0;
  background: rgba(11, 16, 23, 0.3);
}
.ftr-reveal-inner {
  position: relative;
  z-index: 2;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: var(--space-md);
  width: min(100% - 2.5rem, 44rem);
  text-align: center;
}

@media (max-width: 769px) {
  .ftr-content { flex-direction: column; align-items: flex-start; }
}
@media (max-width: 600px) {
  /* The copyright wraps here, so Privacy takes its own line rather than
     starting one indented. */
  .ftr-meta-link { display: table; margin: 0.5rem 0 0; }
}

@media (prefers-reduced-motion: reduce) {
  .ftr-reveal-wrap { height: auto; }
  .ftr-reveal { position: relative; min-height: 70svh; }
}

/* ═══════════════════════════════════════════════════════════════
   POLICY MODAL
   ═══════════════════════════════════════════════════════════════ */
.modal {
  border: 0;
  padding: 0;
  width: min(100% - 2rem, 46rem);
  max-height: 82vh;
  background: var(--paper);
  color: var(--navy);
}
.modal::backdrop { background: rgba(11, 16, 23, 0.65); }
.modal-inner { padding: var(--space-md); overflow-y: auto; max-height: 82vh; }
/* Section headings inside a policy, in the popup and on the page itself. */
#bhc-policy-body h3,
.page-body h3 { font-size: var(--t-h3); line-height: 1.15; margin: 1.6em 0 0.5em; }
/* The popup runs a size smaller than the page, and does not scale up with
   the viewport, so a policy reads as fine print rather than a feature. */
#bhc-policy-title { font-size: 1.25rem; margin-bottom: var(--space-sm); }
#bhc-policy-body { font-size: 0.875rem; line-height: 1.6; }
#bhc-policy-body p,
#bhc-policy-body li { font-size: inherit; }
#bhc-policy-body h3 { font-size: 1.05rem; }
#bhc-policy-body a,
.page-body a { text-decoration: underline; text-underline-offset: 0.2em; }
.modal-close {
  position: sticky;
  top: 0;
  margin-left: auto;
  display: block;
  background: none;
  border: 0;
  font-size: 1.6rem;
  line-height: 1;
  cursor: pointer;
  color: inherit;
}

/* ═══════════════════════════════════════════════════════════════
   IMAGE PLACEHOLDERS
   Temporary. Replace each <div class="ph"> with an <img> and the
   layout holds, because every slot already has its own aspect ratio.
   ═══════════════════════════════════════════════════════════════ */
.ph {
  width: 100%;
  height: 100%;
  min-height: 12rem;
  background:
    linear-gradient(140deg, rgba(255,255,255,0.06) 0%, rgba(255,255,255,0) 55%),
    linear-gradient(200deg, #2b4257 0%, #1b2a41 45%, #12202f 100%);
}
.ph--hero   { background: linear-gradient(190deg, #34506a 0%, #1b2a41 55%, #0b1017 100%); }
.ph--banner { background: linear-gradient(160deg, #4a6a7d 0%, #2b4257 50%, #16283a 100%); }

/* ═══════════════════════════════════════════════════════════════
   EVENT CARDS — large variant (events page)
   ═══════════════════════════════════════════════════════════════ */
/* Two across instead of three, so the 800×588 thumbnails are shown at
   roughly their own size rather than scaled down. */
@media (min-width: 700px)  { .events--lg { grid-template-columns: repeat(2, 1fr); } }
@media (min-width: 1100px) {
  .events--lg {
    grid-template-columns: repeat(2, 1fr);
    gap: var(--space-lg) var(--space-md);
  }
}

/* Column flex so the cue can sit on the card's foot. Topics wrap to
   different line counts, and without this the cues in one row landed
   at different heights. Grid items already stretch to the row. */
.events--lg .event { display: flex; flex-direction: column; }
.events--lg .event-topic { margin-bottom: var(--space-sm); }

.event-cue {
  display: inline-flex;
  flex-direction: column;
  align-self: flex-start;
  gap: 0.45rem;
  margin-top: auto;
  font-family: var(--label);
  font-size: var(--t-label);
  text-transform: uppercase;
  letter-spacing: 0.18em;
  color: var(--accent-ink);
}
.event-cue-rule {
  display: block;
  height: 1px;
  width: 100%;
  background: currentColor;
  opacity: 0.35;
  transform-origin: left;
  transition: opacity 0.4s var(--ease);
}
.event:hover .event-cue-rule { opacity: 1; }

/* ═══════════════════════════════════════════════════════════════
   LEGACY LIST — past events, as an index
   ═══════════════════════════════════════════════════════════════ */
.legacy {
  list-style: none;
  margin: var(--space-lg) 0 0;
  padding: 0;
  border-top: 1px solid var(--rule);
}

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

/* The whole row is the link, so the grid lives on it. */
.legacy-link {
  position: relative;
  isolation: isolate;
  display: grid;
  grid-template-columns: 1fr;
  gap: 0.25rem;
  padding: var(--space-sm) 0;
}
@media (min-width: 700px) {
  .legacy-link {
    grid-template-columns: 14rem 12rem 1fr auto;
    gap: var(--space-sm);
    align-items: baseline;
  }
}

/* Hover: a panel a shade deeper than the bone ground fades in behind
   the row, the copy steps in a little and darkens, and the arrow lifts
   toward its corner. The panel runs slightly past the text on both
   sides so the copy never touches its edge. */
.legacy-link::before {
  content: "";
  position: absolute;
  inset: 0 -1.25rem;
  z-index: -1;
  background: #e9e3d7;
  opacity: 0;
  transition: opacity 0.4s var(--ease);
}
.legacy-link:hover::before,
.legacy-link:focus-visible::before { opacity: 1; }

.legacy-date,
.legacy-place,
.legacy-topic { transition: color 0.35s var(--ease), opacity 0.35s var(--ease), transform 0.5s var(--ease); }
.legacy-link:hover .legacy-date,
.legacy-link:hover .legacy-place,
.legacy-link:hover .legacy-topic,
.legacy-link:focus-visible .legacy-date,
.legacy-link:focus-visible .legacy-place,
.legacy-link:focus-visible .legacy-topic { transform: translateX(0.75rem); }
.legacy-link:hover .legacy-place,
.legacy-link:focus-visible .legacy-place { color: var(--accent-ink); }
.legacy-link:hover .legacy-topic,
.legacy-link:focus-visible .legacy-topic { opacity: 1; }

/* Says what opens, since a plain row gives no hint it is a link. Faint
   until the row is hovered or focused. */
.legacy-cue {
  font-family: var(--label);
  font-size: var(--t-label);
  text-transform: uppercase;
  letter-spacing: 0.18em;
  color: var(--accent-ink);
  opacity: 0.55;
  transition: opacity 0.35s var(--ease), color 0.35s var(--ease);
}
.legacy-cue span { display: inline-block; transition: transform 0.4s var(--ease); }
.legacy-link:hover .legacy-cue,
.legacy-link:focus-visible .legacy-cue { opacity: 1; }
.legacy-link:hover .legacy-cue span,
.legacy-link:focus-visible .legacy-cue span { transform: translate(3px, -3px); }

.legacy-date {
  font-family: var(--label);
  font-size: var(--t-label);
  text-transform: uppercase;
  letter-spacing: 0.18em;
  color: var(--accent-ink);
}
.legacy-place { font-family: var(--display); font-size: var(--t-h3); line-height: 1; }
.legacy-topic { font-size: 0.95rem; opacity: 0.72; }

/* ═══════════════════════════════════════════════════════════════
   FILTERS — destination tabs on the testimonials page
   ═══════════════════════════════════════════════════════════════ */
/* Hidden without JavaScript, because nothing would filter. The full set
   is in the markup either way. */
.filters { display: none; gap: 0.5rem; flex-wrap: wrap; margin-bottom: var(--space-md); }
.js .filters { display: flex; }

.filter {
  font-family: var(--label);
  font-size: var(--t-label);
  text-transform: uppercase;
  letter-spacing: 0.18em;
  line-height: 1;
  padding: 0.85rem 1.1rem;
  border: 1px solid var(--rule);
  border-radius: 2px;
  background: transparent;
  color: inherit;
  cursor: pointer;
  transition: background 0.35s var(--ease), color 0.35s var(--ease), border-color 0.35s var(--ease);
}
.filter:hover { border-color: rgba(27, 42, 65, 0.4); }
.filter.is-on { background: var(--navy); border-color: var(--navy); color: var(--paper); }

.filter-count { opacity: 0.55; margin-left: 0.45em; }
.filter.is-on .filter-count { opacity: 0.7; }

/* ═══════════════════════════════════════════════════════════════
   CONTACT
   ═══════════════════════════════════════════════════════════════ */
.contact-grid { display: grid; grid-template-columns: 1fr; gap: var(--space-lg); }
@media (min-width: 1000px) {
  .contact-grid { grid-template-columns: 1.4fr 1fr; gap: var(--space-lg); }
}

.cform { margin-top: var(--space-md); display: grid; gap: var(--space-sm); }
.cform-row { display: grid; grid-template-columns: 1fr; gap: var(--space-sm); }
@media (min-width: 600px) { .cform-row { grid-template-columns: repeat(2, 1fr); } }

.cform-field { display: flex; flex-direction: column; gap: 0.45rem; }
.cform-field label {
  font-family: var(--label);
  font-size: var(--t-label);
  text-transform: uppercase;
  letter-spacing: 0.18em;
  opacity: 0.7;
}
.cform-opt { text-transform: none; letter-spacing: normal; opacity: 0.6; }

.cform-field input,
.cform-field select,
.cform-field textarea {
  font-family: var(--body);
  font-size: 1rem;
  line-height: 1.5;
  color: inherit;
  background: var(--paper);
  border: 1px solid var(--rule);
  border-radius: 2px;
  padding: 0.95rem 1rem;
  width: 100%;
  transition: border-color 0.3s var(--ease);
}
.cform-field textarea { resize: vertical; min-height: 9rem; }
.cform-field input:focus,
.cform-field select:focus,
.cform-field textarea:focus {
  outline: none;
  border-color: var(--accent-ink);
}

/* Honeypot. Off-screen rather than display:none, which some bots skip. */
.cform-trap {
  position: absolute;
  left: -9999px;
  width: 1px;
  height: 1px;
  overflow: hidden;
}

.cform .btn { justify-self: start; margin-top: var(--space-sm); }

.form-note {
  margin: var(--space-sm) 0 0;
  padding: 1rem 1.1rem;
  border-left: 2px solid var(--accent-ink);
  background: var(--bone);
  font-size: 0.95rem;
}
.form-note--bad { border-left-color: #a8452f; }
.form-note a { text-decoration: underline; }

.contact-aside { display: grid; gap: var(--space-md); align-content: start; }

.office { border-top: 1px solid var(--rule); padding-top: var(--space-sm); }
.office-title {
  font-family: var(--label);
  font-size: var(--t-label);
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.22em;
  color: var(--accent-ink);
  margin-bottom: var(--space-sm);
}
.office-body { margin: 0 0 var(--space-sm); line-height: 1.7; }
.office-link {
  display: block;
  font-size: 0.95rem;
  border-bottom: 1px solid var(--rule);
  padding-bottom: 0.5rem;
  margin-bottom: 0.5rem;
  transition: border-color 0.35s var(--ease);
}
.office-link:hover { border-color: var(--accent-ink); }
.office-fine { margin: 0; font-size: 0.82rem; opacity: 0.55; line-height: 1.5; }

/* ═══════════════════════════════════════════════════════════════
   FOUNDERS
   ═══════════════════════════════════════════════════════════════ */
.founders {
  display: grid;
  grid-template-columns: 1fr;
  gap: var(--space-md);
  margin-top: var(--space-lg);
  /* The portraits are only 416px wide. Uncapped, two columns stretched
     them to about 600px on a 1440px screen and they went soft. */
  max-width: 60rem;
}
@media (min-width: 700px) { .founders { grid-template-columns: repeat(2, 1fr); gap: var(--space-lg); } }

.founder { margin: 0; }
.founder img { width: 100%; aspect-ratio: 416 / 326; object-fit: cover; }
.founder figcaption {
  display: flex;
  flex-direction: column;
  gap: 0.3rem;
  margin-top: var(--space-sm);
  padding-top: var(--space-sm);
  border-top: 1px solid var(--rule);
}
.founder-name { font-family: var(--display); font-size: var(--t-h3); line-height: 1; }
.founder-role {
  font-family: var(--label);
  font-size: var(--t-label);
  text-transform: uppercase;
  letter-spacing: 0.18em;
  opacity: 0.6;
}
.founder-mail { font-size: 0.95rem; color: var(--accent-ink); }
.founder-mail:hover { text-decoration: underline; }

/* ═══════════════════════════════════════════════════════════════
   ABOUT (and CREATE YOUR OWN, which borrows its parts)
   After supreme-luxury.com/#/about. Its figures at a 1440px screen are
   the maximums here: 82px side margins (a 1276px column), 180px between
   sections, 628px statements beside 412px portraits, 412px founder
   portraits, a 520px text column beside a 520 × 720 photograph.
   ═══════════════════════════════════════════════════════════════ */
.awrap { width: min(100% - (var(--gutter) * 2), 1276px); margin-inline: auto; }

/* Hero: the navy band holds the title and runs on behind the top of the
   photograph (--ahero-lap), so the photograph laps out of it. */
.ahero { --ahero-lap: clamp(4rem, 8vw, 7rem); background: var(--paper); }
.ahero-head {
  padding: clamp(8.5rem, 14.6vw, 13.2rem) 0 calc(var(--ahero-lap) + clamp(1.5rem, 2.8vw, 2.5rem));
  background: var(--navy);
  color: var(--paper);
  text-align: center;
}
.ahero-eyebrow {
  margin: 0 0 1.25rem;
  font-family: var(--label);
  font-size: 0.875rem;
  font-weight: 500;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--accent);
}
.ahero-title {
  margin: 0 auto;
  font-family: var(--display);
  font-size: clamp(2.75rem, 6vw, 5.5rem);
  font-weight: 400;
  line-height: 1.05;
  letter-spacing: -0.02em;
}
.ahero-title em { font-style: italic; }
.ahero-lead {
  margin: 1.5rem auto 0;
  max-width: 60ch;
  text-wrap: balance;
  font-size: 1.0625rem;
  line-height: 1.6;
  opacity: 0.78;
}
.ahero-cover {
  position: relative;
  width: min(100% - (var(--gutter) * 2), 1276px);
  height: clamp(20rem, 47vw, 42.5rem);
  margin: calc(var(--ahero-lap) * -1) auto 0;
  overflow: hidden;
}
/* Taller than the frame so the parallax never shows an edge. */
.ahero-cover-media { position: absolute; inset: -10% 0; will-change: transform; }
.ahero-cover-media img { width: 100%; height: 100%; object-fit: cover; object-position: var(--ahero-pos, 50% 50%); }
@media (max-width: 600px) {
  .ahero-cover { width: 100%; }
}

/* Title and photograph rise in on load. */
@keyframes ahero-rise { from { opacity: 0; transform: translateY(30px); } }
.ahero-eyebrow,
.ahero-title,
.ahero-lead,
.ahero-cover { animation: ahero-rise 1.1s var(--ease) both; }
.ahero-title { animation-delay: 0.12s; }
.ahero-lead { animation-delay: 0.2s; }
.ahero-cover { animation-delay: 0.3s; }

/* Sections: space above each, and below the last. */
.asec { padding-top: clamp(5rem, 12.5vw, 11.25rem); background: var(--paper); }
.asec--last { padding-bottom: clamp(5rem, 12.5vw, 11.25rem); }
.asec .eyebrow { margin: 0; }

/* The statements, set large in the sans. */
.asec-quote {
  margin: 0;
  font-family: var(--body);
  font-size: clamp(1.5rem, 2.8vw, 2.5rem);
  font-weight: 400;
  line-height: 1.3;
  letter-spacing: -0.025em;
  color: var(--navy);
}
/* With JS off (or reduced motion) the second clause is simply grey. */
.asec-quote .is-muted { color: #8d95a0; }
[data-word-reveal] .word { transition: color 0.18s linear; }

.asec-title {
  margin: 0;
  font-family: var(--display);
  font-size: clamp(2.25rem, 4.2vw, 3.75rem);
  font-weight: 400;
  line-height: 1.1;
  letter-spacing: -0.01em;
  color: var(--navy);
}
.asec-body {
  margin: 0;
  max-width: 517px;
  font-size: 1rem;
  line-height: 1.75;
  color: rgba(27, 42, 65, 0.72);
}

/* Statement beside a portrait photograph, which sits a little lower. */
.asplit { display: grid; gap: 3.75rem; align-items: start; }
.asplit-text { display: flex; flex-direction: column; gap: 2.5rem; }
.asplit-media { margin: 0; }
.asplit-media img { width: 100%; aspect-ratio: 412 / 526; object-fit: cover; }
.asplit--flip .asplit-media img { object-position: 30% 50%; }
@media (min-width: 1025px) {
  .asplit {
    grid-template-columns: minmax(0, 628px) minmax(0, 412px);
    justify-content: space-between;
    column-gap: 4rem;
  }
  .asplit--flip { grid-template-columns: minmax(0, 412px) minmax(0, 628px); }
  .asplit-media { margin-top: 3.75rem; }
}
/* Stacked, the statement always comes first. */
@media (max-width: 1024px) {
  .asplit--flip .asplit-media { order: 2; }
}

/* Founders: two portraits, the left one set lower, over a centred
   introduction. Two across on phones too; the photographs are small
   files, and full width they would soften. */
.afounders {
  display: grid;
  grid-template-columns: minmax(0, 412px) minmax(0, 412px);
  grid-template-areas: "left right" "text text";
  justify-content: space-between;
  align-items: start;
  gap: 2.75rem 1.25rem;
}
.afounder { margin: 0; }
.afounder--left { grid-area: left; margin-top: clamp(3rem, 6.5vw, 5.8rem); }
.afounder--right { grid-area: right; }
.afounder-media img { width: 100%; aspect-ratio: 4 / 5; object-fit: cover; }
.afounder-caption {
  margin-top: 0.625rem;
  font-size: 0.875rem;
  line-height: 1.6;
  color: var(--navy);
}
.afounder-role { white-space: nowrap; }
.afounder-mail { display: block; color: var(--accent-ink); }
.afounder-mail:hover { text-decoration: underline; }
.afounders-text {
  grid-area: text;
  display: flex;
  flex-direction: column;
  align-items: center;
  max-width: 628px;
  margin: clamp(1rem, 3vw, 2.5rem) auto 0;
  text-align: center;
}
.afounders-text .eyebrow { margin-bottom: clamp(1.5rem, 4vw, 3.75rem); }
.afounders-text .asec-title { margin-bottom: clamp(1.25rem, 2.9vw, 2.6rem); }
@media (max-width: 1024px) {
  .afounders { grid-template-columns: 1fr 1fr; }
}
@media (max-width: 600px) {
  .afounder-caption { font-size: 0.75rem; }
}

/* Closing text column beside a tall photograph. */
.aways { display: grid; gap: 3.75rem; align-items: start; }
.aways-text { display: flex; flex-direction: column; gap: 2.5rem; }
.aways-text .asec-body + .asec-body { margin-top: -1rem; }
.aways-links { display: flex; flex-wrap: wrap; gap: 1.25rem 2.5rem; margin: 0; }
.aways-media { margin: 0; }
.aways-media img { width: 100%; aspect-ratio: 520 / 720; object-fit: cover; }
@media (min-width: 1025px) {
  .aways {
    grid-template-columns: minmax(0, 520px) minmax(0, 520px);
    justify-content: space-between;
    column-gap: 4rem;
  }
  .aways-text { padding-top: 3.6rem; }
}
@media (max-width: 1024px) {
  .aways-media img { aspect-ratio: 4 / 5; }
}

/* The photograph holds still beside a longer column while it scrolls.
   With a photograph swap, the list is spaced out so each item holds the
   screen for a while, and the last item is at least as tall as the
   frame: the frame lets go when the column's foot reaches its own, so
   anything shorter and the last heading could never reach its top. */
@media (min-width: 1025px) {
  .asplit--sticky .asplit-media { position: sticky; top: 12rem; }
  .asplit--sticky .alist-item { min-height: 15rem; }
  .asplit--sticky .alist-item:last-child { min-height: 42rem; }
}

/* Photograph swap: one photograph per list item, stacked in the held
   frame. The script moves .is-on as each item's heading reaches the
   frame's top edge; the incoming one fades in while settling from a
   slight zoom. On narrow screens nothing is held, so the frame goes and
   each item shows its own photograph above it instead. */
.aswap { position: relative; aspect-ratio: 412 / 526; overflow: hidden; }
.aswap-img {
  position: absolute;
  inset: 0;
  opacity: 0;
  transform: scale(1.06);
  transition: opacity 0.8s var(--ease), transform 1.2s var(--ease);
}
.aswap-img.is-on { opacity: 1; transform: none; }
.asplit-media .aswap-img img { height: 100%; aspect-ratio: auto; object-position: var(--pos, 50% 50%); }
.alist-img { display: none; }
@media (max-width: 1024px) {
  .aswap { display: none; }
  .alist-img { display: block; grid-column: 1 / -1; margin-bottom: 0.5rem; }
  .alist-img img { width: 100%; aspect-ratio: 4 / 5; object-fit: cover; object-position: var(--pos, 50% 50%); }
}
@media (prefers-reduced-motion: reduce) {
  .aswap-img { transition: none; transform: none; }
}

/* Numbered list: a small number beside a serif heading and its body,
   a hairline above each. */
.alist { list-style: none; margin: 0; padding: 0; }
.alist-item {
  display: grid;
  grid-template-columns: 3rem minmax(0, 1fr);
  gap: 1rem;
  padding: 1.75rem 0;
  border-top: 1px solid var(--rule);
}
.alist-item:last-child { padding-bottom: 0; }
.alist-num {
  padding-top: 0.55rem;
  font-family: var(--label);
  font-size: 0.75rem;
  letter-spacing: 0.04em;
  color: rgba(27, 42, 65, 0.45);
}
.alist-title {
  margin: 0 0 0.5rem;
  font-family: var(--display);
  font-size: clamp(1.5rem, 2.2vw, 2rem);
  font-weight: 400;
  line-height: 1.15;
  color: var(--navy);
}

/* Centred section heading, and a centred link beneath the section. */
.asec-head {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: clamp(1.25rem, 2.5vw, 2rem);
  margin-bottom: clamp(2.5rem, 5vw, 4.5rem);
  text-align: center;
}
.asec-foot { display: flex; justify-content: center; margin: clamp(2.5rem, 5vw, 4rem) 0 0; }

/* A person, small: photograph, name, role and address. */
.acard { display: flex; align-items: center; gap: 1.25rem; }
.acard img { flex-shrink: 0; width: 96px; height: 96px; object-fit: cover; }
.acard-name,
.acard-role,
.acard-mail { display: block; }
.acard-name { font-family: var(--display); font-size: 1.5rem; line-height: 1.1; color: var(--navy); }
.acard-role {
  margin: 0.3rem 0 0.35rem;
  font-family: var(--label);
  font-size: 0.7rem;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: rgba(27, 42, 65, 0.6);
}
.acard-mail { font-size: 0.9rem; color: var(--accent-ink); }
.acard-mail:hover { text-decoration: underline; }

/* Text link with a rule beneath; a heavier rule sweeps in on hover. */
.alink {
  display: inline-flex;
  flex-direction: column;
  gap: 4px;
  width: fit-content;
  font-family: var(--label);
  font-size: 0.875rem;
  font-weight: 600;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--navy);
}
.alink-line { position: relative; display: block; width: 100%; height: 1px; background: currentColor; }
.alink-line::after {
  content: "";
  position: absolute;
  left: 0;
  bottom: 0;
  width: 100%;
  height: 2px;
  background: currentColor;
  transform: scaleX(0);
  transform-origin: 100% 50%;
  transition: transform 0.4s var(--ease);
}
.alink:hover .alink-line::after,
.alink:focus-visible .alink-line::after { transform: scaleX(1); transform-origin: 0 50%; }

/* Banner with its caption centred over an even scrim. */
.banner--centre { height: clamp(28rem, 42vw, 37.5rem); }
.banner--centre::after { background: rgba(11, 16, 23, 0.38); }
.banner--centre .banner-caption {
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 2rem;
  padding-bottom: 0;
  text-align: center;
}
.banner--centre .banner-caption h2 {
  margin: 0;
  max-width: 16ch;
  font-size: clamp(2.5rem, 5vw, 4.75rem);
  font-weight: 400;
  line-height: 1.05;
}
.banner--centre .banner-caption h2 em { font-style: italic; }

@media (prefers-reduced-motion: reduce) {
  .ahero-eyebrow, .ahero-title, .ahero-lead, .ahero-cover { animation: none; }
  .ahero-cover-media { transform: none !important; }
}

/* ═══════════════════════════════════════════════════════════════
   PHOTO STRIP — home page, above the FAQ
   Square tiles, edge to edge, sliding left on a loop. The track holds two
   identical sets and moves by -50%, which is exactly one set, so the loop
   has no seam. Each set is wider than any screen (28 tiles), so no gap can
   open at the right-hand end.

   That CSS loop is the no-script version. With script, .is-live turns it
   off and js/main.js moves the track instead, at the same pace, so it can
   also be dragged with a mouse or swiped. touch-action: pan-y leaves
   vertical swipes to the page, so the strip never traps scrolling.
   ═══════════════════════════════════════════════════════════════ */
.strip {
  --strip-h: clamp(200px, 22vw, 340px);
  position: relative;
  overflow: hidden;
  background: var(--ink);
}
.strip-track {
  display: flex;
  width: max-content;
  /* One set per loop, so the duration scales with the photo count to
     keep the same pace: 90s was 14 photos, this is 28. */
  animation: strip-loop 180s linear infinite;
  will-change: transform;
}
.strip:hover .strip-track { animation-play-state: paused; }
.strip.is-live { cursor: grab; touch-action: pan-y; user-select: none; -webkit-user-select: none; }
.strip.is-live .strip-track { animation: none; }
.strip.is-dragging { cursor: grabbing; }
.strip.is-live img { -webkit-user-drag: none; pointer-events: none; }
.strip-set { display: flex; flex: none; }
.strip-set img {
  flex: none;
  width: var(--strip-h);
  height: var(--strip-h);
  max-width: none;
  object-fit: cover;
  display: block;
}
@keyframes strip-loop {
  from { transform: translate3d(0, 0, 0); }
  to   { transform: translate3d(-50%, 0, 0); }
}
@media (prefers-reduced-motion: reduce) {
  .strip:not(.is-live) { overflow-x: auto; }
  .strip-track { animation: none; }
}

/* ═══════════════════════════════════════════════════════════════
   MENU — full-screen, screens up to 860px wide
   Near-black panel of six rows filling the height below the bar: the
   number small on the left, the page name large on the right, a hairline
   between rows. The logo and the menu button stay on top (the nav sits at
   z-index 100, this at 99), and the button turns into a cross.
   ═══════════════════════════════════════════════════════════════ */
.menu { display: none; }

@media (max-width: 860px) {
  .menu {
    position: fixed;
    inset: 0;
    z-index: 99;
    display: flex;
    flex-direction: column;
    /* Clears the logo and the button: 52px logo plus 1.5rem either side.
       Room at the foot too, clear of a phone's home indicator. */
    padding: 6.5rem var(--gutter) max(1.5rem, env(safe-area-inset-bottom));
    background: #111111;
    color: var(--paper);
    visibility: hidden;
    opacity: 0;
    transition: opacity 0.45s var(--ease), visibility 0s linear 0.45s;
  }
  /* Logged in, the bar sits below the WordPress toolbar. The panel still
     covers the whole screen (on phones the toolbar scrolls away, and a
     top offset left a strip of the page showing), and only its padding
     makes room for the lower bar. */
  .admin-bar .menu { padding-top: calc(6.5rem + 32px); }
  html.menu-open { overflow: hidden; }
  .menu-open .menu {
    visibility: visible;
    opacity: 1;
    transition: opacity 0.45s var(--ease), visibility 0s;
  }

  .menu nav { display: flex; flex: 1; }
  .menu-list { list-style: none; margin: 0; padding: 0; flex: 1; display: flex; flex-direction: column; }
  /* A hairline above every row and below the last, so the list reads as
     one framed block under the logo. */
  .menu-item { flex: 1; display: flex; border-top: 1px solid rgba(255, 255, 255, 0.12); }
  .menu-item:last-child { border-bottom: 1px solid rgba(255, 255, 255, 0.12); }

  .menu-link {
    flex: 1;
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 1rem;
    color: inherit;
  }
  .menu-num {
    font-family: var(--label);
    font-size: 0.75rem;
    letter-spacing: 0.04em;
    color: rgba(255, 255, 255, 0.45);
  }
  .menu-label {
    font-family: var(--display);
    font-size: clamp(2.4rem, 10vw, 5.5rem);
    font-weight: 400;
    letter-spacing: -0.01em;
    text-align: right;
  }
  /* Room for descenders, which the roll's mask would otherwise clip. */
  .menu-label .nav-roll-out,
  .menu-label .nav-roll-in { line-height: 1.2; }

  /* Rows rise in one after another as the menu opens. */
  .menu-link {
    transform: translateY(24px);
    opacity: 0;
    transition: transform 0.6s var(--ease), opacity 0.6s var(--ease);
  }
  .menu-open .menu-link {
    transform: none;
    opacity: 1;
    transition-delay: calc(var(--i) * 60ms + 80ms);
  }

  /* The same hover roll as the bar's links. */
  .menu-link:hover .nav-roll-out,
  .menu-link:focus-visible .nav-roll-out { transform: translateY(-100%); transition-delay: 0s; }
  .menu-link:hover .nav-roll-in,
  .menu-link:focus-visible .nav-roll-in { transform: translateY(0); transition-delay: 0.07s; }

  /* The bar over the open menu: white logo, no white square behind the
     button, and held in place even while it is riding the hero. */
  .menu-open .nav { color: var(--paper); transform: none !important; }
  .menu-open .nav .nav-logo-img--light { opacity: 1; }
  .menu-open .nav .nav-logo-img--dark  { opacity: 0; }
  /* html. outranks the stuck bar's white square, which comes later. */
  html.menu-open .nav .nav-toggle { background-color: transparent; box-shadow: none; }
  /* The button's padding (for the white square) left the cross short of
     the right edge of the page names; it slides over to line up as it
     folds. */
  html.menu-open .nav .nav-toggle { transform: translateX(1rem); }

  /* The three bars fold into a cross. They are 1px tall with a 5px gap, so
     the outer ones sit 6px from the middle. */
  .menu-open .nav-toggle span:nth-child(1) { transform: translateY(6px) rotate(45deg); }
  .menu-open .nav-toggle span:nth-child(2) { opacity: 0; }
  .menu-open .nav-toggle span:nth-child(3) { transform: translateY(-6px) rotate(-45deg); }
}

@media (max-width: 782px) {
  .admin-bar .menu { padding-top: calc(6.5rem + 46px); }
}

@media (prefers-reduced-motion: reduce) {
  .menu, .menu-link { transition: none !important; }
}

/* ═══════════════════════════════════════════════════════════════
   SCROLL REVEAL — matches the .sr convention from the SHH theme
   ═══════════════════════════════════════════════════════════════ */
/* Scoped to .js so the content is never hidden when JavaScript does not
   run. Without the class the page renders fully visible and static. */
.js .sr {
  opacity: 0;
  transform: translateY(40px);
  transition: opacity 0.9s var(--ease), transform 0.9s var(--ease);
}

/* Slides in from the right instead of up. Same specificity as .js .sr,
   so it has to come after it to win; .js .sr.sr--in is more specific
   again and clears both. */
.js .sr--right { transform: translateX(70px); }

.js .sr.sr--in { opacity: 1; transform: none; }
/* A tight run so items arrive one just after another rather than
   together. Enough steps for a six-card grid. */
.sr--delay-0 { transition-delay: 0s; }
.sr--delay-1 { transition-delay: 0.1s; }
.sr--delay-2 { transition-delay: 0.2s; }
.sr--delay-3 { transition-delay: 0.3s; }
.sr--delay-4 { transition-delay: 0.4s; }
.sr--delay-5 { transition-delay: 0.5s; }

/* ═══════════════════════════════════════════════════════════════
   RESPONSIVE
   ═══════════════════════════════════════════════════════════════ */
@media (max-width: 860px) {
  .nav-toggle { display: flex; }
  /* The inline links give way to the full-screen menu (see MENU). */
  .nav-links { display: none; }
  .hero-scroll { display: none; }

  /* The phone equivalent of the white panel: the menu button gets a
     square white ground away from the top, so its now-dark bars stay
     visible. Square to match the desktop panel. */
  .nav-toggle {
    padding: 0.8rem 0.75rem;
    border-radius: 0;
    transition: background-color 0.45s var(--ease), box-shadow 0.45s var(--ease), transform 0.4s var(--ease);
  }
  .nav.is-stuck .nav-toggle {
    background-color: var(--paper);
    box-shadow: 0 4px 24px rgba(11, 16, 23, 0.08);
  }
}

@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
  /* Scoped to .js to outrank the .js .sr hidden state above. */
  .js .sr { opacity: 1; transform: none; transition: none; }
  .banner-media, .hero-media { transform: none !important; }

  /* Drop the runway entirely. With no motion it would just be a
     viewport of dead scroll. */
  .hero { height: 100svh; }
  .hero-pin { position: relative; }
  .mist { display: none; }
  .hero-inner { filter: none !important; transform: none !important; opacity: 1 !important; }

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