/* WYLD demo — page-specific: home */

/* ---------- hero carousel ----------
   A stack of full-bleed frames. The incoming frame wipes in from the right
   edge, which is the same direction the peek strip sits on, so the gesture the
   strip suggests is the gesture the transition performs. */
.hero { position: relative; height: 100vh; height: 100svh; min-height: 520px; overflow: hidden; touch-action: pan-y; }
.hero__stack { position: absolute; inset: -6% 0; }
.hero__slide { position: absolute; inset: 0; opacity: 0; pointer-events: none; }
.hero__slide img { width: 100%; height: 116%; object-fit: cover; will-change: transform; }
.hero__slide.is-on { opacity: 1; }
/* The push: the arriving frame travels in from the right as a whole print and
   the one it replaces is carried out ahead of it, slower than it arrives. A
   clip wipe cut the image; this moves it, which is what the print at the edge
   promised. */
.js .hero__slide.is-arriving { animation: heroPush 1.65s cubic-bezier(.22,.9,.24,1) forwards; z-index: 2; }
@keyframes heroPush {
  from { transform: translateX(100%); }
  to   { transform: translateX(0); }
}
.js .hero__slide.is-leaving { animation: heroExit 1.65s cubic-bezier(.22,.9,.24,1) forwards; }
@keyframes heroExit {
  from { transform: translateX(0); }
  to   { transform: translateX(-16%); }
}
/* a hairline rides the leading edge of the arriving frame, the way the print's
   own edge does */
.js .hero__slide.is-arriving::before {
  content: ''; position: absolute; top: 0; bottom: 0; left: 0; width: 1px; z-index: 3;
  background: rgba(241,237,228,.5);
  animation: heroEdge 1.65s cubic-bezier(.22,.9,.24,1) forwards;
}
@keyframes heroEdge { 0% { opacity: 1; } 72% { opacity: .6; } 100% { opacity: 0; } }
.js .hero__slide.is-on img { animation: ken 20s ease-out infinite alternate; }
@keyframes ken { from { transform: scale(1); } to { transform: scale(1.12); } }

/* ---------- shade ----------
   A light tint, and no blur. The five-layer progressive blur that used to sit here
   defocused the bottom third of the frame — on a wildlife hero that is where the
   animal is, and it read as a photograph out of focus. Removed 2026-09-24 on client
   feedback. The tint is the whole treatment now: a band at the top for the header,
   the middle of the frame left clean, and a soft floor under the headline. The
   headline's own text-shadow finishes the contrast, so the ink can stay this light. */
.hero__veil { position: absolute; inset: 0; pointer-events: none; z-index: 2; background:
  linear-gradient(to bottom, rgba(10,11,10,.45) 0%, rgba(10,11,10,0) 22%, rgba(10,11,10,0) 52%, rgba(10,11,10,.55) 100%); }

/* ---------- rotation pause (WCAG 2.2.2) ---------- */
.hero__pause {
  display: inline-grid; place-items: center; width: 26px; height: 26px;
  margin-left: 12px; padding: 0; border: 1px solid var(--line-d);
  background: transparent; color: inherit; font-size: 9px; cursor: pointer;
  transition: border-color var(--mo-fast) var(--ease), color var(--mo-fast) var(--ease);
}
.hero__pause:hover, .hero__pause:focus-visible { border-color: currentColor; color: var(--bone); }

/* ---------- the next print ----------
   The next photograph sits as a print pulled part-way out of a stack: a real
   frame with a hairline and a caption, held against the right edge and clipped
   by it. It reads as an object you can pull across rather than as a UI arrow,
   and it says plainly which frame is coming. */
/* ---------- the next frame, on the edge ----------
   Flush to the right margin, not hanging off it. The old version sat at
   translateX(46%) on a five-second nudge loop: half the print was outside the
   viewport, it was vertically centred against nothing, and the loop read as a
   layout bug rather than an invitation.

   Three jobs in one object now — it shows WHICH frame is next, says its NAME, and
   counts down HOW LONG until it arrives. The countdown is the part that actually
   communicates "another photograph is coming": a nudge animation only says "this
   thing moves". */
.hero__peek {
  position: absolute; z-index: 4; right: 0; top: 50%;
  /* One column of type running down it, and since 2026-09-20 nothing else: the width
     is the dwell line, the label's margin and the label's own column, and no more. It
     was 60-92px when it also had to hold a thumbnail. The constraint that matters is
     the strip's HEIGHT — see .hero__peek-lbl. */
  width: var(--peek-w, clamp(30px, 2.6vw, 42px));
  height: clamp(190px, 40vh, 400px);
  transform: translateY(-50%);
  cursor: pointer; text-align: left; padding: 0;
  transition: width .7s var(--ease);
}

/* The next frame, held at zero until the strip is hovered. Keeping it in the DOM at
   rest rather than building it on hover is what lets it fade rather than pop — the
   image is already decoded by the time anyone reaches for it. */
.hero__peek-fig {
  position: absolute; inset: 0; overflow: hidden;
  opacity: 0; transition: opacity .55s ease;
}
.hero__peek-img {
  position: absolute; inset: 0; background-size: cover; background-position: center;
  /* The scale is what makes the reveal read as the photograph stepping forward rather
     than a filter being switched off. */
  transform: scale(1.12);
  filter: saturate(.55) brightness(.62);
  transition: transform 1.1s var(--ease), filter .7s ease;
}
.hero__peek-fig::after {
  content: ''; position: absolute; inset: 0;
  background: linear-gradient(to left, rgba(10,11,10,.42), rgba(10,11,10,.72));
}
/* The dwell line. It runs down the leading hairline over the seven seconds before
   the slide changes, so the strip is the timer as well as the preview — and when the
   rotation is paused it stops with it, which is the honest thing for a countdown to
   do. Driven from ui.js. With JavaScript off the whole strip is hidden; under reduced
   motion the track stays and only the fill goes. */
.hero__peek-dwell {
  /* Widened from 2px: at that weight the countdown was a hairline on a hairline and
     most people never saw it run. One token, so the strip and the fill inside it can
     never disagree about how wide the line is. */
  --dwell-w: 4px;
  position: absolute; top: 0; bottom: 0;
  width: var(--dwell-w);
  /* Flush left now that the fig it used to straddle is gone. The dwell line IS the
     strip's leading edge — there is no longer a separate hairline for it to sit on. */
  left: 0;
  z-index: 3; overflow: hidden; pointer-events: none;
  /* The strip carries its own track now. At 2px the fill sat on the 1px hairline and
     the difference did not show; at 4px it would have read as the line getting wider
     as it ran rather than filling. The track is the unlit part of the same line. */
  background: rgba(241, 237, 228, .22);
}
.hero__peek-dwell > i {
  display: block; width: 100%; height: 100%;
  background: var(--ember);
  transform: scaleY(0); transform-origin: top;
}

/* The name reads upright, a word to a line, down from the top of the strip.

   It used to run bottom-up in vertical writing-mode, and the note here said a
   horizontal name needed clear space to the LEFT and collided with the headline under
   1500px. That was true of the name set as one long line across the hero. A word to a
   line does not have that shape: the stack stays inside the strip's own width, so
   there is nothing to collide with. The cost is that the strip has to be wide enough
   for the longest single word — 'Bandhavgarh' — which is what widened it. */
.hero__peek-lbl {
  position: absolute; left: 0; top: 0; z-index: 2;
  /* vertical-lr, not vertical-rl. Both set the type on its side; the difference is
     which way the columns stack. rl would have put the first word on the RIGHT and
     the reader would meet 'Leopard' before 'Ladakh'. lr stacks them away from the
     dwell line, so the first word sits against it and the name reads outward.
     No rotate(180deg) either — that is what used to run the letters bottom-up. */
  writing-mode: vertical-lr;
  padding: clamp(13px, 1.9vh, 24px) 0 clamp(12px, 1.6vh, 20px) 0;
  margin-left: clamp(9px, .9vw, 15px);
  display: flex; align-items: flex-start;
  gap: clamp(9px, 1vw, 14px);
  max-width: 100%; max-height: 100%; overflow: hidden;
  /* A step up from the 9px meta scale, and the tracking comes in to pay for it. */
  font-size: clamp(10px, .74vw, 12px); letter-spacing: .07em;
  color: var(--bone); opacity: .62;
  transition: opacity .5s ease;
}
.hero__peek-kicker { color: rgba(var(--bone-rgb), .58); }
/* The rule between "Next" and the frame's name. It turns with the label: across the
   stack rather than down beside it. */
/* The rule sits below "Next" in the reading direction, which is now downward, so it
   draws across the column rather than along it. */
.hero__peek-kicker::after {
  content: ''; display: block; width: 1px; height: 13px;
  background: currentColor; opacity: .5; margin: 9px auto 0;
}
/* One column, one word to a block, each turned a quarter turn right.

   flex-direction: row, not column. Inside vertical-lr the ROW axis is the inline one,
   which runs downward — so the words queue one after another down the same column.
   column would follow the block axis instead and set each word in a column of its own,
   side by side across the strip. The gap is the point of splitting them: a plain space
   between words in one long run cannot be opened up without also tracking out the
   letters inside them. */
.hero__peek-name { display: flex; flex-direction: row; gap: clamp(9px, 1.1vh, 15px); }
.hero__peek-word { display: block; white-space: nowrap; }

/* Hovering opens the strip and brings the frame up inside it. */
.hero__peek:hover, .hero__peek:focus-visible { --peek-w: clamp(140px, 12vw, 216px); }
.hero__peek:hover .hero__peek-fig,
.hero__peek:focus-visible .hero__peek-fig { opacity: 1; }
.hero__peek:hover .hero__peek-img,
.hero__peek:focus-visible .hero__peek-img { transform: scale(1); filter: saturate(1) brightness(.92); }
.hero__peek:hover .hero__peek-lbl,
.hero__peek:focus-visible .hero__peek-lbl { opacity: 1; }
.hero__peek:focus-visible { outline: 2px solid var(--ember); outline-offset: 3px; }

/* Narrow enough on a phone to be a margin rather than a column, and the name comes
   off — a vertical label in 62px of height is two words and an ellipsis. */
@media (max-width: 640px) {
  .hero__peek { width: 22px; height: clamp(120px, 26vh, 210px); }
  /* The strip is 22px wide here — 4px of it would be nearly a fifth of it. */
  .hero__peek-dwell { --dwell-w: 3px; }
  .hero__peek-lbl { display: none; }
  /* No hover on a phone, so the reveal would only ever fire on tap, one frame before
     the tap advances past it. The strip stays a countdown here. */
  .hero__peek:hover, .hero__peek:focus-visible { --peek-w: 22px; }
  .hero__peek-fig { display: none; }
}
/* Under reduced motion nothing rotates, so there is no countdown to run. The track
   stays: it used to be able to go because the thumbnail was still there holding the
   strip's edge, and without it hiding the line would leave the name floating against
   the photograph with nothing to anchor it. Only the fill goes. */
@media (prefers-reduced-motion: reduce) {
  .hero__peek, .hero__peek-lbl, .hero__peek-img, .hero__peek-fig { transition: none; }
  .hero__peek-dwell > i { display: none; }
}

.hero__in { position: relative; z-index: 3; height: 100%; display: grid; align-content: end;
  padding-top: var(--header-h);
  padding-bottom: clamp(28px, 6vh, 62px); }
.hero__eyebrow { padding-bottom: clamp(18px, 4vh, 40px); }
/* The hero headline enters sideways rather than rising, so it reads as a frame
   being pushed into place. Same guard as every other reveal: the hidden state
   must not exist unless motion is both possible and wanted, or a script that
   throws between splitting the headline and revealing it would leave the one
   line of copy that matters most parked off the left edge. */
@media (scripting: enabled) and (prefers-reduced-motion: no-preference) {
  .hero__h .ln > i {
    transform: translateX(-104%);
    transition: transform var(--mo-slow) var(--ease);
    transition-delay: calc(var(--l, 0) * 70ms);
  }
  .hero__h.is-in .ln > i { transform: none; }
}

.hero__h {
  /* Home only, and smaller than the inner pages' --fs-hero: the photograph is what
     the visitor should see first, and the line should caption it rather than cover
     it. Cut again on 2026-09-24 after the two token cuts on 09-20. */
  font-size: clamp(2rem, min(5vw, 8.4vh), 5rem);
  text-shadow: 0 1px 24px rgba(10,11,10,.35);
  will-change: transform; max-width: 15ch;
  padding-right: calc(clamp(80px, 10vw, 150px) + 116px);
}
.hero__foot { display: flex; align-items: flex-end; justify-content: space-between; gap: var(--colgap); padding-top: clamp(26px, 5vh, 56px); }
.hero__strip { display: flex; flex-wrap: wrap; gap: 6px clamp(20px, 3vw, 52px); }
.hero__strip div { display: grid; gap: 5px; }
.hero__strip span:first-child { transition: opacity .3s ease; }
.hero__strip.is-swapping { opacity: 0; transform: translateX(-26px); }
.hero__strip { transition: opacity .35s var(--ease), transform .55s cubic-bezier(.22,.9,.24,1); }

/* counter and rail: which frame of the set, and how far through */
/* The counter never breaks and never shrinks; the rail absorbs whatever room
   is left when a long meta strip wraps and squeezes the row. */
/* The slide counter carried .m--9, the 9px step, and read as unreadably small next
   to the headline. Overridden here rather than changing .m--9, which is a shared
   utility several other components sit on. pages.css loads last, so this wins. */
.hero__nav { display: flex; align-items: center; gap: 16px; flex: 0 1 auto; min-width: 0;
  font-size: 12px; letter-spacing: .14em; }
.hero__nav [data-hero-count] { white-space: nowrap; flex: 0 0 auto; }
.hero__rail { flex: 1 1 auto; min-width: 24px; width: clamp(44px, 9vw, 150px);
  height: 1px; background: rgba(241,237,228,.28); position: relative; }
.hero__rail i { position: absolute; inset: 0 auto 0 0; background: var(--bone);
  transform-origin: left; transform: scaleX(.25); transition: transform .6s var(--ease); }
.hero__cue { display: grid; justify-items: center; gap: 10px; }
.hero__cue i { display: block; font-size: clamp(15px, 2.4vh, 20px); color: rgba(241,237,228,.62); animation: cue 2.4s var(--ease) infinite; }
@keyframes cue {
  0%, 100% { transform: translateY(-3px); opacity: .45; }
  50%      { transform: translateY(4px);  opacity: 1; }
}
@media (max-width: 640px) { .hero__cue { display: none; } }
@media (prefers-reduced-motion: reduce) {
  .hero__slide img { animation: none !important; transform: none !important; }
  .hero__slide.is-arriving { animation: none !important; }
  .hero__cue i { animation: none; }
}

/* ---------- statement ---------- */
.stmt { padding-block: clamp(110px, 20vh, 260px); }
.stmt__col { grid-column: 3 / span 8; }
.stmt__p { font-size: var(--fs-state); line-height: 1.24; font-family: var(--f-display); font-weight: 300; letter-spacing: -.02em; max-width: 34ch; }
.stmt__sig { padding-top: clamp(30px, 5vh, 60px); }
@media (max-width: 900px) { .stmt__col { grid-column: 1 / -1; } }

/* ---------- dark to light transition ----------
   The image bleeds out of the dark ground; the ground changes exactly at its
   lower edge; the display line straddles the boundary and reads on both
   grounds by blend mode. */
.shift {
  position: relative; background: var(--ink); overflow: hidden;

  /* A full-screen photographic ground with the enquiry panel centred in it. From 1200px
     wide the section is held to one screen: the panel's vertical rhythm is budgeted in
     vh (see the budget block after .cform__done), so it fits with photograph above and
     below. Narrower layouts stack the groups and are allowed to grow.

     min-height, not height: a short viewport or a longer form grows the section rather
     than clipping the panel. svh excludes the mobile browser chrome that collapses on
     scroll, which is what makes a plain 100vh section jump on a phone; the vh line above
     it is the fallback for anything that does not know svh. */
  min-height: 100vh;
  min-height: 100svh;
  display: grid; align-content: center;

  /* The top band is the deeper one: it has to clear the fixed header first. */
  padding-block: clamp(96px, 13vh, 150px) clamp(64px, 10vh, 120px);
}
.shift__fig { position: absolute; inset: 0; overflow: hidden; }
.shift__fig img { width: 100%; height: 100%; object-fit: cover; }
/* The line sits inside the frame over a scrim, which is how every other
   caption on a photograph is handled here — the pinned panels use the same
   gradient. No plate, no blend mode, no element crossing the boundary. */
.shift__fig::before {
  content: ''; position: absolute; inset: 0;
  background-color: rgba(10,11,10,.14);
  pointer-events: none; z-index: 2;
}
.shift__cap {
  position: relative; z-index: 4;
  max-width: 15ch; margin: 0 0 clamp(22px, 3.4vh, 44px);
  color: var(--bone);
  text-shadow: 0 1px 18px rgba(10,11,10,.5);
}
.shift__meta {
  position: absolute; right: var(--gutter); top: clamp(40px, 7vh, 96px); z-index: 5;
  color: var(--bone); text-align: right;
  text-shadow: 0 1px 14px rgba(10,11,10,.55);
}
@media (max-width: 640px) { .shift__meta { display: none; } }

/* ---------- section head ---------- */
.head {
  display: flex; flex-wrap: wrap; align-items: flex-end; justify-content: space-between;
  gap: 18px clamp(24px, 4vw, 70px);
  /* padding is the gap from the heading down to the rule */
  padding-bottom: clamp(24px, 4vh, 48px);
  /* margin is the clearance below the rule */
  margin-bottom: clamp(22px, 3vh, 34px);
}
.head__l { display: grid; gap: clamp(14px, 2.4vh, 26px); }
/* A section link now carries an arrow that travels on hover, in the accent.
   The rule underneath retracts as the arrow moves, so the two read as one
   gesture rather than two competing signals. */
.link-u { position: relative; display: inline-flex; align-items: center; gap: .7em; }
.link-u i {
  display: inline-block; font-size: .95em; color: var(--ember);
  transition: transform .45s var(--ease);
}

.link-u:hover i { transform: translateX(5px); }
.link-u::after { content: ''; position: absolute; left: 0; right: 1.7em; bottom: -4px; height: 1px; background: currentColor; transform: scaleX(1); transform-origin: left; transition: transform .45s var(--ease); }
.link-u:hover::after { transform: scaleX(0); transform-origin: right; }

/* ---------- enquire ---------- */
.enq { display: grid; grid-template-columns: 1.2fr .8fr; gap: clamp(28px, 5vw, 90px); align-items: end; }
.enq__lines { display: grid; gap: 14px; }
.enq__lines a { position: relative; display: inline-flex; align-items: center; gap: .6em; font-family: var(--f-display); font-size: clamp(1.25rem, 2vw, 1.75rem); letter-spacing: -.02em; }
/* The icon sits at label scale rather than display scale — a 28px envelope
   beside an email address reads as an ad.

   --ember-text, not --ember. At .52em these glyphs are ~13px of non-text content,
   which wants 3:1, and the raw accent is engineered as a GROUND — on paper it
   measures under 2:1. The token is already re-pointed back to --ember on every
   genuinely dark ground, so this is correct on the dark enquiry band, correct on
   /about's paper letter, and correct on the enquiry band once light mode lifts it. */
/* The icon is set at the label's own size and the underline runs under both, so
   icon and text read as one link rather than a bullet beside it. A fixed-width box
   keeps the three labels starting on the same line whatever each glyph's width. */
.enq__lines a i { font-size: 1em; width: 1.15em; text-align: center; flex: 0 0 auto; color: var(--ember-text); }
.enq__lines a::after { content: ''; position: absolute; left: 0; right: 0; bottom: -2px; height: 1px; background: currentColor; transform: scaleX(0); transform-origin: left; transition: transform .45s var(--ease); }
.enq__lines a:hover::after { transform: scaleX(1); }
@media (max-width: 900px) { .enq { grid-template-columns: 1fr; } }

/* ---------- trust ---------- */
.trust__top { display: grid; grid-template-columns: minmax(0,.42fr) minmax(0,1fr); gap: clamp(26px, 5vw, 84px); align-items: end; }
/* Carries the .cv convergence so the figure does not have to. The figure is a
   parallax layer, and [data-parallax] and .cv both write `transform` — one element
   cannot hold both, and the half that lost would simply not move. Split across two
   elements the picture drifts inside a block that is arriving, which is the depth
   the pairing was after in the first place. No box of its own: it is a grid child
   that hands its column straight to the figure. */
/* A step under .d--1: the name is a caption to the portrait, not the section's claim. */
.trust__name { font-size: clamp(2.1rem, 4.6vw, 4.25rem); }
.trust__figwrap { min-width: 0; }
.trust__fig { position: relative; overflow: hidden; aspect-ratio: 4/5; }
.trust__fig img { width: 100%; height: 100%; object-fit: cover; }
.trust__quotes { display: grid; grid-template-columns: repeat(3, 1fr); gap: clamp(22px, 3vw, 56px); padding-top: clamp(44px, 7vh, 92px); }

/* The quotes half of Guest galleries. The rail above it is full-bleed, so this comes
   back into the container and opens on a hairline — the same device the enquiry panel's
   head closes on — so it reads as the second movement of one section rather than as a
   new one. It carries no eyebrow number for the same reason. */
.gsay { padding-top: clamp(40px, 6vh, 80px); }
.gsay__head {
  display: flex; flex-wrap: wrap; gap: 12px clamp(20px, 4vw, 60px);
  align-items: baseline; justify-content: space-between;
  border-top: 1px solid var(--line); padding-top: clamp(24px, 3.4vh, 40px);
}
.gsay__h { margin: 0; max-width: 26ch; }
.gsay__out { flex: 0 0 auto; white-space: nowrap; }
/* The stock padding above was sized to open a section. Here the head already did. */
.gsay .trust__quotes { padding-top: clamp(26px, 4vh, 54px); }
@media (max-width: 640px) { .gsay__head { display: grid; } }
@media (max-width: 900px) {
  .trust__top { grid-template-columns: 1fr; }
  .trust__quotes { grid-template-columns: 1fr; }
  .trust__fig { max-width: 320px; }
  /* One column: there is no left half and no right half to converge from, so the
     pair would cross the same axis one after the other and read as a stutter.
     Retire the sideways travel and let both halves rise instead. `!important`
     because the direction is authored inline — same as .learn above. */
  .trust__top > .cv { --cv-x: 0 !important; --cv-y: 24px !important; }
}

/* ---------- 1680+ ---------- */
@media (min-width: 1680px) {
  :root { --fs-body: 1.125rem; }
  .stmt__col { grid-column: 3 / span 7; }
}

/* ---------- how it works ---------- */
/* The section holds most of a screen so the sequence is seen whole, with the
   content centred in it: 80% of the viewport on desktop, 70% on tablet. svh so
   mobile browser chrome does not change the height as it collapses; vh is the
   fallback. On phones and short landscape screens the stacked list is already
   taller than that, so the content sets the height rather than a forced minimum. */
.how { display: flex; flex-direction: column; justify-content: center; min-height: 80vh; min-height: 80svh; }
.how > .container-xxl { width: 100%; }
@media (max-width: 1180px) { .how { min-height: 70vh; min-height: 70svh; } }
@media (max-width: 780px), (max-height: 560px) { .how { min-height: 0; } }

/* Five stages along one line instead of five full-width rows. The old layout gave
   each stage the whole container for a paragraph that used a third of it, so the
   section ran to a screen and a half and the fifth stage was never on screen with
   the first — which is the one thing a sequence has to show.

   The drawing is made of the site's own parts and nothing new: a hairline the same
   weight as every rule on the page, a square tick rather than a circle because
   nothing here is round, and the same arrow glyph the links use. */
.flow {
  --flow-gap: clamp(18px, 2.4vw, 44px);
  display: grid; grid-template-columns: repeat(5, minmax(0, 1fr));
  grid-template-rows: auto auto auto auto;
  gap: var(--flow-gap); margin: 0; padding: 0; list-style: none;
  padding-top: clamp(10px, 2vh, 22px);
}
/* Subgrid, so the four rows are the list's and not each step's: a title that wraps
   to two lines pushes every body down, not just its own. Without it the five bodies
   start at five different heights and the row stops reading as one line of the
   diagram. Where subgrid is missing the steps simply stack as they would anyway. */
.flow__s { display: grid; align-content: start; gap: 0; grid-row: span 4; }
@supports (grid-template-rows: subgrid) {
  .flow__s { grid-template-rows: subgrid; }
}

/* The connector. Each step owns its own segment and bridges the gap into the next,
   so the line is continuous without a second absolutely-positioned element that
   would have to be kept in sync with the grid. */
.flow__rail {
  position: relative; display: block; height: 1px;
  width: calc(100% + var(--flow-gap));
  background: var(--line);
  margin-bottom: clamp(16px, 2.2vh, 26px);
}
@media (min-width: 781px) {
  .flow__s--last .flow__rail { width: 100%; }
}
.flow__dot {
  position: absolute; left: 0; top: 50%;
  width: 7px; height: 7px; margin-top: -3px;
  background: var(--ink);
  transition: background-color .35s ease, transform .45s var(--ease);
}
/* Sitting in the gap, on the line, with the ground painted behind it so the rule
   reads as passing under the arrow rather than colliding with it. */
.flow__arw {
  position: absolute; right: calc(var(--flow-gap) / 2); top: 50%;
  transform: translate(50%, -50%);
  font-size: 11px; color: var(--stone);
  background: var(--bone); padding: 0 7px;
  transition: color .35s ease, transform .45s var(--ease);
}
.flow__n { color: var(--stone); transition: color .3s ease; }
.flow__t { padding-top: 10px; text-wrap: pretty; }
.flow__b {
  padding-top: clamp(10px, 1.4vh, 16px);
  font-size: .875rem; line-height: 1.55; color: var(--stone-lit);
  text-wrap: pretty;
}

/* Pointing at a stage marks it on the line. The whole column responds, so the
   target is the stage rather than a seven-pixel square. */
@media (hover: hover) and (pointer: fine) {
  .flow__s:hover .flow__dot { background: var(--ember); transform: scale(1.35); }
  .flow__s:hover .flow__n { color: var(--ember-text); }
  .flow__s:hover .flow__b { color: var(--ink); }
}

/* The line draws itself across, stage by stage, and each column rises behind its own
   segment. Reusing .rv's --i stagger would have moved the whole column including the
   rule, so the rail is scaled from its left edge instead: what you see is a line
   being drawn left to right, which is the diagram explaining itself. */
@media (scripting: enabled) and (prefers-reduced-motion: no-preference) {
  .flow__s .flow__rail { transform: scaleX(0); transform-origin: left; }
  .flow__s .flow__rail { transition: transform var(--mo-slow) var(--ease); }
  .flow__s :is(.flow__n, .flow__t, .flow__b) {
    opacity: 0; transform: translateY(14px);
    transition: opacity var(--mo-base) var(--ease), transform var(--mo-base) var(--ease);
  }
  .flow__s .flow__arw, .flow__s .flow__dot { opacity: 0; transition: opacity var(--mo-base) var(--ease); }
  .flow.is-in .flow__s .flow__rail,
  .flow.is-in .flow__s :is(.flow__n, .flow__t, .flow__b),
  .flow.is-in .flow__s :is(.flow__arw, .flow__dot) {
    transition-delay: calc(var(--i, 0) * 130ms);
  }
  .flow.is-in .flow__s .flow__rail { transform: none; }
  .flow.is-in .flow__s :is(.flow__n, .flow__t, .flow__b) { opacity: 1; transform: none; }
  .flow.is-in .flow__s :is(.flow__arw, .flow__dot) { opacity: 1; }
  .flow.is-in .flow__s :is(.flow__t, .flow__b) { transition-delay: calc(var(--i, 0) * 130ms + 90ms); }
  .flow.is-instant :is(.flow__rail, .flow__n, .flow__t, .flow__b, .flow__arw, .flow__dot) {
    transition: none !important;
  }
}

/* Three across, then the sequence turns a corner: the arrow on the third step would
   point off the end of its row, so it is dropped and the second row reads as a
   continuation of the first. */
@media (max-width: 1180px) and (min-width: 781px) {
  .flow { grid-template-columns: repeat(3, minmax(0, 1fr)); grid-template-rows: repeat(8, auto);
          row-gap: clamp(30px, 4vh, 52px); }
  .flow__s:nth-child(3n) .flow__rail { width: 100%; }
  .flow__s:nth-child(3n) .flow__arw { display: none; }
}
/* Below that the flow runs down the page instead of across, and the connector goes
   with it: the rule becomes a vertical spine on the left and the arrows point down. */
@media (max-width: 780px) {
  .flow { grid-template-columns: 1fr; grid-template-rows: none; row-gap: 0; }
  .flow__s {
    grid-row: auto; grid-template-rows: auto auto auto;
    grid-template-columns: 30px minmax(0, 1fr);
    grid-template-areas: 'rail num' 'rail title' 'rail body';
    column-gap: clamp(14px, 3vw, 22px);
    padding-bottom: clamp(26px, 4vh, 40px);
  }
  .flow__s .flow__rail {
    grid-area: rail; align-self: stretch;
    width: 1px; height: auto; margin: 6px 0 0;
  }
  .flow__s--last .flow__rail { height: 10px; align-self: start; }
  .flow__dot { left: 50%; top: 0; margin: 0 0 0 -3px; }
  .flow__arw {
    right: auto; left: 50%; top: auto; bottom: calc(var(--flow-gap) / -2);
    transform: translate(-50%, 50%) rotate(90deg);
  }
  .flow__n { grid-area: num; }
  .flow__t { grid-area: title; padding-top: 8px; }
  .flow__b { grid-area: body; }
  @media (scripting: enabled) and (prefers-reduced-motion: no-preference) {
    .flow__s .flow__rail { transform: scaleY(0); transform-origin: top; }
  }
}

/* ---------- day in the field ---------- */
/* The photograph is the ground the section stands on rather than a banner above it.
   As a banner it cost 617px before the timeline had said anything, and the section
   ran to 2283px — two and a half screens for a day that fits on one.

   The frame stays put and the picture drifts inside it. That is the honest way to
   parallax a fixed image: translating the figure itself would move the box and open
   gaps at the section's edges, so the image is oversized by the travel distance in
   each direction and clipped back to the frame. */
/* The grain belongs to the section, not to the figure. `.grain` styles ::after, and
   the scrim below needs that same pseudo-element on .day__bg — sharing it meant the
   scrim inherited grain's `opacity: .035` and `mix-blend-mode: overlay` and painted
   at three percent, which is indistinguishable from not painting at all.

   In light mode the site deliberately flips .g-dark bands to a light ground and
   re-points --bone at --ink, which would have put ink type on this section's own dark
   scrim. .day-sec is listed with .pin__vp and .panel--data in tokens.css as one of the
   places that keeps its ground because it carries a photograph — the swap is opted out
   of there rather than fought with overrides here. */
/* One screen, and the whole day inside it.

   min-height rather than height, following .shift above: on a short viewport, or
   the day the copy is translated, the section grows rather than clipping an hour
   off the bottom. Losing content is never the better failure here. svh so the
   phone's collapsing chrome does not make it jump.

   Everything vertical inside is driven by vh rather than px — the section padding,
   the head clearance, the row padding, the gap inside a row. That is what lets
   fourteen hours land on one screen at 720px tall and still breathe at 1080. The
   enquiry panel is budgeted the same way; see .shift__panel. */
.day-sec {
  position: relative; overflow: hidden; isolation: isolate;
  min-height: 100vh;
  min-height: 100svh;
  display: grid; align-content: center;
  /* The header is fixed and never retracts, so a section that is exactly one screen
     tall has to begin below it — otherwise its first line sits behind the bar the
     moment the section fills the viewport. The bottom has no such obligation. */
  padding-block: calc(var(--header-h) + clamp(6px, 1.4vh, 30px)) clamp(34px, 4.6vh, 90px);
}
/* Tighter than the shared .head, which is budgeted for sections that are free to
   run as long as they need to. This one is not. */
.day-sec .head {
  padding-bottom: clamp(10px, 1.7vh, 34px);
  margin-bottom: clamp(8px, 1.2vh, 24px);
}
.day-sec .head__l { gap: clamp(6px, 1vh, 18px); }
.day-sec .body { color: rgba(var(--bone-rgb), .80); }
.day__bg { position: absolute; inset: 0; z-index: 0; margin: 0; pointer-events: none; }
.day__bg img {
  position: absolute; left: 0; top: -80px;
  width: 100%; height: calc(100% + 160px); object-fit: cover;
}
/* Eight paragraphs sit on this, not a headline, so the picture is pushed a long way
   back: a heavy even scrim plus a real defocus. Blurring collapses the local contrast
   range behind the type, which is what lets the scrim stay short of opaque and leaves
   the photograph still legible as a place. The section's own comment says the day
   starts before light — this is that, rather than an illustration of it. */
.day__bg::after {
  content: ''; position: absolute; inset: 0;
  /* Eased back ~10 points so the dawn frame reads as a place rather than a texture.
     What the section can afford was measured against the photograph itself, not
     guessed: blurred and cover-fitted, its brightest 0.1% sits at 216/255, and the
     body copy — bone at .80, the bulk of the section — still clears 5.0:1 on that
     spot at the values below. Re-measure if day.dawn is ever swapped; a brighter
     frame eats this margin. */
  background: linear-gradient(to bottom,
    rgba(10,11,10,.82) 0%, rgba(10,11,10,.68) 26%, rgba(10,11,10,.66) 74%, rgba(10,11,10,.84) 100%);
  -webkit-backdrop-filter: blur(7px) saturate(88%);
          backdrop-filter: blur(7px) saturate(88%);
}
@supports not (backdrop-filter: blur(1px)) {
  .day__bg::after { background: linear-gradient(to bottom,
    rgba(10,11,10,.88) 0%, rgba(10,11,10,.80) 26%, rgba(10,11,10,.78) 74%, rgba(10,11,10,.90) 100%); }
}
/* The muted greys have to come up to pay for the lighter scrim. They were already
   the weakest thing here — .m--stone measured 3.8:1 and .day__light 3.2:1 against
   that same bright spot, both under AA before this change — so this is a fix, not
   compensation. At .75 they land at 4.6:1. They stay visibly quieter than the
   headings, which is all the hierarchy asked for. */
.day-sec .m--stone,
.day-sec .day__light { color: rgba(var(--bone-rgb), .75); }
.day__in { position: relative; z-index: 1; }

/* Both layers are measured against the same section, so the separation is theirs
   alone. The section is now ~1000px rather than 2283, so the same drift reads as a
   far larger fraction of the scroll it happens over — the travel comes down and the
   effect goes up. */
.day__bg img[data-parallax] { --px-start: -62px; --px-end: 62px; }
.day[data-parallax] { --px-start: 22px; --px-end: -22px; }
@media (max-width: 900px) {
  .day__bg img[data-parallax] { --px-start: -34px; --px-end: 34px; }
  .day[data-parallax] { --px-start: 12px; --px-end: -12px; }
}

/* Down the first column, then down the second: morning on the left, afternoon and
   evening on the right. Row-major would have put 05:10 next to 06:15 and asked the
   reader to zigzag through fourteen hours.

   That order used to live only in grid-auto-flow, which the reader cannot see — the
   eye went straight across the gutter from 05:10 to 12:00 because two bare columns of
   hairline rules look exactly like four wide rows. It is drawn now, in three parts:
   each half is labelled with the hours it covers, an arrow sits in the gutter between
   the two heads, and a spine with a mark at every hour runs down each half. The
   vertical line is the instruction; the arrow is the one jump the reader has to make. */
.day {
  --day-gap: clamp(28px, 4vw, 76px);
  --day-spine: clamp(16px, 1.8vw, 26px);
  /* One value for an hour's breathing room, read by both the row and the mark on
     the spine beside it. */
  --day-rowpad: clamp(7px, 1.15vh, 22px);
  position: relative; display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  /* One head plus four hours. The tracks are declared on the parent so both halves
     measure against the same five rows. */
  grid-template-rows: auto repeat(4, auto);
  column-gap: var(--day-gap);
  padding-top: clamp(2px, .7vh, 18px);
}
.day__half { display: grid; align-content: start; min-width: 0; grid-row: span 5; }

/* Subgrid, so 05:10 sits level with 12:00 and every rule crosses the gutter.
   Without it each half sizes its own four rows and the two columns drift apart by
   however much longer one paragraph runs than its opposite number — which is what
   made the section read as two lists that happen to be side by side rather than one
   day in two halves. Nested twice because the rows are a level down from the half.

   Where subgrid is missing the columns simply size independently, as they did
   before: the content is unchanged and only the cross-gutter alignment is lost. */
@supports (grid-template-rows: subgrid) {
  .day__half { grid-template-rows: subgrid; }
  .day__rows { grid-row: span 4; display: grid; grid-template-rows: subgrid; }
}

/* The head is the label the columns never had. Baseline-aligned so the hours read as
   a caption on the name rather than a second heading. */
.day__head {
  position: relative;
  display: flex; align-items: baseline; flex-wrap: wrap;
  gap: 4px clamp(10px, 1.2vw, 18px);
  padding-bottom: clamp(5px, .9vh, 17px);
}
.day__hl { color: var(--bone); }
.day__hs { font-variant-numeric: tabular-nums; }

/* Sitting in the gutter between the two heads, at the same size and colour as the
   arrows on the "how it works" line, so the two sequences on this page are drawn the
   same way. It is the only arrow here: the spines say "down", this says "then". */
.day__harw {
  position: absolute; right: calc(var(--day-gap) / -2); top: .12em;
  transform: translateX(50%);
  font-size: 11px; color: var(--stone);
}

.day__rows { position: relative; padding-left: var(--day-spine); }
.day__rows::before {
  content: ''; position: absolute; left: 0; top: 0; bottom: 0;
  width: 1px; background: var(--line-d);
}
/* Pulled back onto the spine and padded forward again, so each hour's rule starts at
   the vertical line rather than floating a spine's width clear of it. The two rules
   have to meet or the grid reads as a table that happens to have a line beside it. */
.day__row {
  display: grid; grid-template-columns: auto minmax(0, 1fr);
  gap: clamp(14px, 1.8vw, 28px);
  margin-left: calc(var(--day-spine) * -1);
  padding-left: var(--day-spine);
  padding-block: var(--day-rowpad);
  border-bottom: 1px solid var(--line-d);
  position: relative;
}
/* The mark on the spine, set to the cap height of the time beside it rather than to
   the row box, so it lines up with the numeral and not with the padding. */
.day__row::before {
  content: ''; position: absolute;
  left: -3px;
  top: calc(var(--day-rowpad) + .62em);
  width: 7px; height: 7px;
  background: var(--stone);
  transition: background-color .35s ease, transform .45s var(--ease);
}
.day__rows > .day__row:first-child { border-top: 1px solid var(--line-d); }
.day__t { font-variant-numeric: tabular-nums; color: var(--bone); padding-top: .3em; }
.day__c { display: grid; gap: clamp(3px, .55vh, 7px); max-width: 46ch; }
.day__c .body { font-size: clamp(.8125rem, .74rem + .19vh, .875rem); line-height: 1.48; }
.day__light { color: var(--stone); }

/* Pointing at an hour marks it on the spine, the same gesture the flow line uses. */
@media (hover: hover) and (pointer: fine) {
  .day__row:hover::before { background: var(--ember); transform: scale(1.35); }
}

/* One column, so the two halves are already in reading order and the arrow has to
   turn with them: it points down the page instead of across the gutter. */
@media (max-width: 900px) {
  .day { grid-template-columns: 1fr; }
  .day__half + .day__half .day__head { padding-top: clamp(26px, 4vh, 46px); }
  .day__harw {
    right: auto; left: 0; top: auto; bottom: calc(clamp(11px, 1.5vh, 17px) * -1);
    transform: translateY(50%) rotate(90deg);
  }
}
@media (max-width: 560px) {
  .day__row { grid-template-columns: 1fr; gap: 6px; }
  .day__row::before { top: calc(var(--day-rowpad) + .55em); }
}

/* ---------- what you learn ---------- */
.learn { display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, .82fr); gap: clamp(28px, 5vw, 92px); align-items: start; }
.learn__fig { position: relative; overflow: hidden; aspect-ratio: 3/4; }
.learn__fig img { width: 100%; height: 100%; object-fit: cover; }
@media (max-width: 900px) {
  .learn { grid-template-columns: 1fr; }
  .learn__fig { aspect-ratio: 3/2; }
  /* Stacked, there is no left and no right to converge from — both halves are
     full width, so a horizontal slide would just be two things shuffling
     sideways. Retire the device by zeroing its variable and let the pair rise
     instead. No JS branch, no second rule: the same trick Apple uses to switch
     this exact effect off on tall viewports.

     `!important` is doing real work here, not papering over specificity: the
     direction is authored as an inline style so each half can carry its own,
     and an inline custom property beats any stylesheet rule. Without it this
     override silently does nothing. */
  .learn > .cv { --cv-x: 0 !important; --cv-y: 24px !important; }
}

/* ---------- guest gallery ---------- */
.gcap { display: grid; gap: 4px; }



/* =========================================================
   INNER PAGES
   ========================================================= */

/* ---------- year tabs ---------- */
[role="tab"] { color: var(--stone); position: relative; transition: color .3s ease; }
[role="tab"]:hover { color: var(--ink); }
[role="tab"]::after { content: ''; position: absolute; inset: auto 0 -1px 0; height: 1px; background: var(--ink); transform: scaleX(0); transform-origin: left; transition: transform .4s var(--ease); }
[role="tab"][aria-selected="true"] { color: var(--ink); }
[role="tab"][aria-selected="true"]::after { transform: scaleX(1); }

/* ---------- sortable headers ---------- */
[data-sort] { transition: color .3s ease; cursor: pointer; }
[data-sort]:hover { color: var(--ink); }
th[aria-sort="ascending"] [data-sort],
th[aria-sort="descending"] [data-sort] { color: var(--ink); }
th[aria-sort="ascending"] [data-sort]::after { content: ' ↑'; }
th[aria-sort="descending"] [data-sort]::after { content: ' ↓'; }

/* ---------- page hero (shorter than the home hero) ---------- */
.phero { position: relative; height: 74vh; min-height: 440px; overflow: hidden; }
.phero__fig { position: absolute; inset: -5% 0; }
.phero__fig img { width: 100%; height: 112%; object-fit: cover; }
/* Same progressive blur as the home hero, and for the same reason: a flat
   gradient of ink over a photograph destroys the photograph. The page heroes
   carried the old scrim until now, which means they carried the same AA
   contrast shortfall on the eyebrow that this replaces. A .phero is shorter
   than the home hero (74vh vs 100vh) and its copy sits lower in the frame, so
   the bands are tighter than the home set. */
.phero__veil { position: absolute; inset: 0; pointer-events: none; z-index: 2; background:
  linear-gradient(to bottom, rgba(10,11,10,.64) 0%, rgba(10,11,10,.12) 30%, rgba(10,11,10,.44) 56%, rgba(10,11,10,.84) 100%); }
.phero__veil > i { position: absolute; inset: 0; display: block; pointer-events: none; }
/* One knob for the whole ramp; each layer takes a fixed share of it. Halved from
   the original 16px: the band sat over the bottom of the frame, which on a
   wildlife hero is exactly where the animal is. The tint below carries the type,
   so the glass does not have to. */
.phero__veil { --veil-blur: 9px; }
.phero__veil > i:nth-child(1) { -webkit-backdrop-filter: blur(var(--veil-blur)); backdrop-filter: blur(var(--veil-blur));
  -webkit-mask-image: linear-gradient(to top, #000 0%, #000 6%, transparent 15%);
          mask-image: linear-gradient(to top, #000 0%, #000 6%, transparent 15%); }
.phero__veil > i:nth-child(2) { -webkit-backdrop-filter: blur(calc(var(--veil-blur) * .5)); backdrop-filter: blur(calc(var(--veil-blur) * .5));
  -webkit-mask-image: linear-gradient(to top, #000 0%, #000 12%, transparent 25%);
          mask-image: linear-gradient(to top, #000 0%, #000 12%, transparent 25%); }
.phero__veil > i:nth-child(3) { -webkit-backdrop-filter: blur(calc(var(--veil-blur) * .25)); backdrop-filter: blur(calc(var(--veil-blur) * .25));
  -webkit-mask-image: linear-gradient(to top, #000 0%, #000 20%, transparent 36%);
          mask-image: linear-gradient(to top, #000 0%, #000 20%, transparent 36%); }
.phero__veil > i:nth-child(4) { -webkit-backdrop-filter: blur(calc(var(--veil-blur) * .125)); backdrop-filter: blur(calc(var(--veil-blur) * .125));
  -webkit-mask-image: linear-gradient(to top, #000 0%, #000 34%, transparent 54%);
          mask-image: linear-gradient(to top, #000 0%, #000 34%, transparent 54%); }
.phero__veil > i:nth-child(5) { -webkit-backdrop-filter: blur(calc(var(--veil-blur) * .0625)); backdrop-filter: blur(calc(var(--veil-blur) * .0625));
  -webkit-mask-image: linear-gradient(to top, #000 0%, #000 46%, transparent 70%);
          mask-image: linear-gradient(to top, #000 0%, #000 46%, transparent 70%); }
@media (max-width: 734px) {
  .phero__veil > i:nth-child(3), .phero__veil > i:nth-child(4), .phero__veil > i:nth-child(5) { display: none; }
}
.phero__in { position: relative; z-index: 3; height: 100%; display: grid; align-content: end; padding-top: var(--header-h); padding-bottom: clamp(26px, 5vh, 54px); }
.phero__h { max-width: 20ch; padding-top: clamp(16px, 3vh, 34px); }
.phero__strip { display: flex; flex-wrap: wrap; gap: 6px clamp(18px, 3vw, 48px); padding-top: clamp(22px, 4vh, 44px); }

/* ---------- sticky sub-nav ---------- */
.subnav { position: sticky; top: var(--hdr-now); transition: top .45s var(--ease); z-index: 400; background: var(--bone); border-bottom: 1px solid var(--line); }
.subnav__in { display: flex; gap: clamp(14px, 2.4vw, 40px); overflow-x: auto; scrollbar-width: none; }
.subnav__in::-webkit-scrollbar { display: none; }
.subnav a { padding-block: 18px; color: var(--moss); white-space: nowrap; position: relative; transition: color .3s ease; }
.subnav a::after { content: ''; position: absolute; inset: auto 0 0 0; height: 1px; background: var(--ink); transform: scaleX(0); transform-origin: left; transition: transform .4s var(--ease); }
.subnav a.is-on, .subnav a:hover { color: var(--ink); }
.subnav a.is-on::after { transform: scaleX(1); }

/* ---------- editorial two-column ---------- */
.edit { display: grid; grid-template-columns: 1fr 1fr; gap: clamp(26px, 5vw, 96px); }
.pull { font-family: var(--f-display); font-size: clamp(1.4rem, 2.4vw, 2.125rem); line-height: 1.16; letter-spacing: -.022em; font-style: italic; font-weight: 300; max-width: 26ch; }
@media (max-width: 900px) { .edit { grid-template-columns: 1fr; } }

/* ---------- drag gallery ---------- */
.gal { display: flex; gap: clamp(14px, 2vw, 30px); overflow-x: auto; scroll-snap-type: x proximity; scrollbar-width: none; cursor: grab; padding-inline: var(--gutter); }
.gal::-webkit-scrollbar { display: none; }
.gal.is-dragging { scroll-snap-type: none; }
.gal__i { flex: 0 0 auto; width: min(58vw, 700px); scroll-snap-align: start; }
/* The browser starts its own native image drag on pointerdown, which cancels
   the pointer sequence the gallery is listening for — so dragging worked on the
   gutters and died on the frames. Taking the images out of hit-testing hands
   every press to the track itself. */
.gal__i img {
  width: 100%; aspect-ratio: 3/2; object-fit: cover;
  pointer-events: none; user-select: none; -webkit-user-drag: none;
}
.gal { user-select: none; -webkit-user-select: none; }
/* The frame is a link to that guest's full set. The control is always visible
   rather than hover-only — a hover-only affordance is invisible to the person
   deciding whether the section is clickable at all, and invisible on touch. */
.gal__open { display: block; position: relative; overflow: hidden; pointer-events: none; }
.gal__cue { pointer-events: auto; }
.gal__cue {
  position: absolute; top: 14px; right: 14px;
  display: inline-flex; align-items: center; gap: 0;
  height: 34px; padding: 0 10px;
  background: rgba(10,11,10,.62); color: #F1EDE4;
  transition: background-color .35s ease, gap .4s var(--ease), padding .4s var(--ease);
}
.gal__cue i { font-size: 11px; }
/* The label is clipped to nothing until hover, so the resting state is a small
   square mark and the expanded state names what it does. */
.gal__cue > span {
  max-width: 0; overflow: hidden; white-space: nowrap;
  transition: max-width .45s var(--ease);
}
.gal__open:hover .gal__cue { background: var(--ember); color: var(--on-ember); gap: 8px; }
.gal__open:hover .gal__cue > span { max-width: 110px; }
.gal__open:focus-visible .gal__cue { background: var(--ember); color: var(--on-ember); gap: 8px; }
.gal__open:focus-visible .gal__cue > span { max-width: 110px; }
.gal__open img { transition: transform 1.1s var(--ease); }
.gal__open:hover img { transform: scale(1.03); }
@media (pointer: coarse) {
  .gal__cue { gap: 8px; }
  .gal__cue > span { max-width: 110px; }
}

@media (max-width: 734px) {
  /* Same reason as .learn above: inline properties, so !important. */
  .vgal__i.cv, .vgal__row.cv { --cv-x: 0 !important; --cv-y: 28px !important; }
}

/* .gcap is the CARD caption: absolutely positioned on glass over a photograph, and set
   in bone because that glass is always dark. The gallery rail reuses the class for a
   caption that sits UNDER its frame on paper, and so inherited the card's positioning
   along with its colour. .sec is position:relative, so every caption in the rail was
   pinned to the bottom of the SECTION rather than to its own figure — all of them in
   the same place, on top of each other, in cream on paper.

   It went unnoticed while the section ended just below the rail and the pile happened
   to land about where captions belong. Adding the quotes block moved that bottom edge
   and dropped the whole stack underneath them.

   Reset explicitly. The rule below this used to override `display` alone, which only
   ever masked half of what the card caption was imposing. */
.gal__i figcaption {
  position: static; inset: auto; z-index: auto;
  color: inherit;
  border-top: 1px solid var(--line);
  margin-top: 12px; padding: 12px 0 0;
  display: flex; flex-wrap: wrap; gap: 6px clamp(14px, 2vw, 30px);
}
/* ---------- rail scrub ----------
   A frame resolves as it is dragged into the rail and lets go again as it
   leaves: it scales up out of the edge and comes to full strength once it is
   properly in view. --rail-p is written by motion.js as a 0-1 progress value,
   and it defaults to 1 so a frame is simply finished when nothing is driving
   it — no script, reduced motion, or a rail that has not been measured yet.

   The scale sits on .gal__open and the fade on .gal__i, deliberately on two
   different elements: the hover lift already owns the transform on .gal__i
   and the hover zoom owns the transform on the image, so a third transform
   needed a third element rather than a fight. No transition on either — this
   is scrubbed to the drag, so the finger is the easing. */
@media (scripting: enabled) and (prefers-reduced-motion: no-preference) {
  .gal__open { transform: scale(calc(.9 + .1 * var(--rail-p, 1))); transform-origin: 50% 50%; }
  .gal__i { opacity: calc(.35 + .65 * var(--rail-p, 1)); }
}
.gal-hint { transition: opacity .5s ease; display: inline-flex; align-items: center; gap: 9px; }
.gal-hint.is-gone { opacity: 0; }
/* The arrow drifts on a loop: a static "drag" label reads as a caption, a
   moving one reads as an instruction. */
.gal-hint i { display: block; font-size: 12px; }

@media (prefers-reduced-motion: no-preference) {
  .gal-hint i { animation: hintNudge 1.9s var(--ease) infinite; }
}
@keyframes hintNudge {
  0%, 100% { transform: translateX(0); opacity: .55; }
  45%      { transform: translateX(7px); opacity: 1; }
}

/* A rail under the track: how far along the roll you are, and that there is
   more of it. The only always-visible affordance that survives the hint
   fading on first interaction. */
.gal-rail { height: 1px; background: var(--line); position: relative; margin-top: clamp(18px, 2.6vh, 30px); }
.gal-rail i { position: absolute; inset: 0 auto 0 0; background: var(--ink); transform-origin: left; transform: scaleX(0); }
.g-dark .gal-rail { background: var(--line-d); }
.g-dark .gal-rail i { background: var(--bone); }

/* ---------- seasonality matrix ---------- */
.seas { display: grid; grid-template-columns: minmax(120px, 1.4fr) repeat(12, minmax(0, 1fr)); align-items: center; column-gap: 3px; }
.seas__h { padding-bottom: 12px; color: var(--stone); text-align: center; }
.seas__lbl { padding-block: 9px; padding-right: 14px; white-space: nowrap; }
.seas__cell { height: 8px; background: rgba(10,11,10,.07); }
.seas__cell.is-peak { background: var(--ink); }
.seas__cell.is-ok { background: rgba(10,11,10,.3); }
.seas__row { display: contents; }
.seas__row + .seas__row .seas__cell { margin-top: 0; }
@media (max-width: 640px) { .seas { grid-template-columns: minmax(88px, 1fr) repeat(12, minmax(0, 1fr)); font-size: 9px; } }

/* ---------- expedition: sticky rail ---------- */
.exp { display: grid; grid-template-columns: minmax(0, 1fr) minmax(280px, .42fr); gap: clamp(28px, 5vw, 90px); align-items: start; }
.rail { position: sticky; top: calc(var(--hdr-now) + 26px); transition: top .45s var(--ease); border: 1px solid var(--line); padding: clamp(20px, 2.4vw, 32px); display: grid; gap: 20px; background: var(--paper); }
.rail__price { display: flex; align-items: baseline; gap: 10px; }
.btn-line { display: block; text-align: center; padding: 15px 20px; background: var(--ink); color: var(--bone); transition: opacity .3s ease; }
.btn-line:hover { opacity: .84; }
@media (max-width: 900px) {
  .exp { grid-template-columns: 1fr; }
  .rail { position: fixed; inset: auto 0 0 0; z-index: 420; border: 0; border-top: 1px solid var(--line); grid-template-columns: 1fr auto; align-items: center; gap: 14px; padding: 12px var(--gutter); }
  .rail__hide { display: none; }
}

/* ---------- accordion ---------- */
.acc__item { border-bottom: 1px solid var(--line); }
.acc__btn { display: grid; grid-template-columns: auto 1fr auto; gap: clamp(14px, 2.4vw, 40px); align-items: baseline; width: 100%; padding-block: clamp(16px, 2.2vh, 24px); text-align: left; }
.acc__btn .d { transition: opacity .3s ease; }
.acc__btn:hover .d { opacity: .58; }
/* A plus turned 45 degrees is a cross, so open and closed are one glyph and
   one rotation rather than two icons. */
.acc__sign { font-size: 12px; transition: transform .4s var(--ease), color .3s ease; }
.acc__btn:hover .acc__sign { color: var(--ember); }
.acc__btn[aria-expanded="true"] .acc__sign { transform: rotate(45deg); color: var(--ember); }
/* Bootstrap's Collapse owns the open/close animation. */

/* ---------- lists ---------- */
.hlist { border-top: 1px solid var(--line); }
.hlist li { display: grid; grid-template-columns: auto 1fr; gap: clamp(12px, 2vw, 28px); padding-block: 13px; border-bottom: 1px solid var(--line); }
.hlist li > .m { color: var(--stone); }

/* ---------- article ---------- */
.artbar { position: fixed; top: var(--hdr-now); transition: top .45s var(--ease); left: 0; right: 0; height: 1px; z-index: 450; background: var(--line); }
.artbar i { display: block; height: 1px; background: var(--ink); transform: scaleX(0); transform-origin: left; }
.art { max-width: 68ch; margin-inline: auto; }
.art p { margin-bottom: 1.35em; }
.art > p:first-of-type::first-letter {
  float: left; font-family: var(--f-display); font-weight: 300;
  font-size: 4.6em; line-height: .8; padding: .06em .12em 0 0;
}
.art h2 { font-family: var(--f-display); font-size: clamp(1.4rem, 2.1vw, 1.875rem); line-height: 1.1; letter-spacing: -.02em; margin: 2.2em 0 .7em; }
/*
 * tokens.css resets every heading and block element to margin:0 with inherited font, and
 * .art re-styled only p, h2 and blockquote. So an h3 was indistinguishable from body
 * text, a list had no indent, and a table had no borders or padding — the pricing
 * article's price table rendered as a run of unspaced text. Element selectors, because
 * the body is editor HTML and carries no classes of its own.
 */
.art h3 { font-family: var(--f-display); font-size: clamp(1.05rem, 1.4vw, 1.25rem); line-height: 1.25; margin: 1.8em 0 .5em; }
.art ul,
.art ol { margin: 0 0 1.35em; padding-left: 1.25em; }
.art li { margin-bottom: .4em; }
.art ul { list-style: disc; }
.art ol { list-style: decimal; }
.art table { width: 100%; border-collapse: collapse; margin: 2em 0; font-size: .95em; }
.art th,
.art td { border-bottom: 1px solid var(--line); padding: .6em .8em; text-align: left; vertical-align: top; }
.art thead th { border-bottom-width: 2px; font-weight: 500; }

.art blockquote { margin: 2em 0; padding-left: 0; }
.art blockquote p { font-family: var(--f-display); font-style: italic; font-weight: 300; font-size: clamp(1.35rem, 2.3vw, 2rem); line-height: 1.18; letter-spacing: -.022em; }
.artfig { margin-block: clamp(36px, 6vh, 76px); }
.artfig img { width: 100%; }
.artfig figcaption { border-top: 1px solid var(--line); margin-top: 12px; padding-top: 12px; display: flex; flex-wrap: wrap; gap: 6px clamp(14px, 2vw, 32px); }
/* A full-bleed breakout has to be measured in viewport units, and every
   viewport unit includes the scrollbar gutter — so the figure is always the
   scrollbar's width wider than the visible page. That is clipped at the root
   rather than corrected here (see tokens.css): there is no scrollbar-aware
   length in CSS, and the alternatives all involve measuring one in JS. */
.artfig--bleed { max-width: none; margin-inline: calc(50% - 50vw); padding-inline: var(--gutter); }
.credit { border-top: 1px solid var(--line); padding-top: 16px; margin-top: clamp(36px, 5vh, 64px); display: grid; gap: 6px; }
/* =========================================================
   MOBILE — the layouts that need real attention below 900px
   ========================================================= */
@media (max-width: 900px) {
  :root { --header-h: 81px; --header-h-stuck: 60px; } /* 62px, scaled 1.3x with the mark 2026-09-20 */
  .hero__foot { flex-direction: column; align-items: flex-start; gap: 18px; }
  .hero__h { max-width: none; }
  /* The eyebrow shares the column with the edge strip and runs long on two lines
     here. Clearance is the strip's own width plus a gutter — it used to add another
     116px for the horizontal label the strip no longer has. */
  .hero__eyebrow { padding-right: calc(clamp(58px, 5.2vw, 88px) + 24px); }
  .phero { height: 66vh; height: 66svh; min-height: 400px; }
  .stmt { padding-block: clamp(70px, 12vh, 120px); }
  .subnav { top: var(--hdr-now); }
  .trust__quotes { gap: 26px; }
  .enq__lines a { font-size: clamp(1.15rem, 5vw, 1.5rem); }
}

@media (max-width: 640px) {
  .hero__eyebrow { padding-right: 46px; }
}
@media (max-width: 640px) {
  .gal__i { width: min(82vw, 460px); }
  .gal { padding-inline: var(--gutter); }
  .panel--img { width: 88vw; }
  .panel--data { width: 92vw; grid-template-columns: 1fr; }
  .panel--data .panel__fig { display: none; }
  .panel--type { width: 88vw; }
  .pin__pad--start { width: 88vw; }
  .pin__pad--end { width: 60vw; }
  .dl-m { grid-template-columns: 1fr auto; }
  .art > p:first-of-type::first-letter { font-size: 3.6em; }
  .jrow { grid-template-columns: 1fr; }
  .jrow img { width: 100%; }
  .learn__fig { aspect-ratio: 4/3; }
  .seas { column-gap: 2px; }
  .rail { grid-template-columns: 1fr; }
  .rail .btn-line { width: 100%; }
}

/* The departures table is the one place a phone genuinely needs a different
   shape: rows become stacked records with their column names as labels. */
@media (max-width: 620px) {
  .tbl thead { position: absolute; width: 1px; height: 1px; overflow: hidden; clip: rect(0 0 0 0); }
  .tbl tbody tr { display: block; padding-block: 18px; }
  .tbl tbody td {
    display: grid; grid-template-columns: 84px minmax(0, 1fr);
    gap: 12px; align-items: baseline;
    height: auto; padding: 3px 0; text-align: left;
  }
  .tbl tbody td::before {
    content: attr(data-label);
    font-family: var(--f-mono); font-size: 10px; letter-spacing: .14em;
    text-transform: uppercase; color: var(--stone);
  }
  .tbl tbody td:last-child { text-align: left; padding-top: 8px; }
  .tbl tbody td.c-exp { font-size: 1.2rem; padding-bottom: 6px; }
  .tbl .c-sm { display: grid; }
  .tbl tbody td[colspan] { display: block; }
  .tbl tbody td[colspan]::before { content: none; }
}

/* ---------- touch targets ----------
   Anything tapped by a thumb gets 44px, which is larger than the visual weight
   the design wants. The box grows, the ink does not: padding on the control,
   not a bigger type size. */
@media (pointer: coarse), (max-width: 940px) {
  .burger { width: 44px; height: 44px; margin-right: -7px; }
  .burger i { left: 11px; right: 11px; }
  .burger i:first-child { top: 20px; }
  .burger i:last-child { top: 26px; }
}
@media (pointer: coarse) {
  .chip { padding: 13px 15px; }
  [role="tab"] { padding-block: 15px; }
  .soc a { width: 44px; height: 44px; }
  .subnav a { padding-block: 15px; }
  .spx__row { padding-block: 16px; }
  .pbar__toggle { padding: 14px; }
  .pbar__opt { padding: 12px 8px; }
}

/* The sticky rail becomes a fixed bottom bar below 900px. Two consequences:
   the last section needs clearance so the bar never covers it, and the palette
   switcher has to sit above the bar rather than on top of it. */
@media (max-width: 900px) {
  body.has-rail { padding-bottom: 84px; }
  body.has-rail .pbar { bottom: calc(84px + clamp(10px, 2vw, 18px)); }
  body.has-rail .tbar { bottom: calc(84px + clamp(10px, 2vw, 18px) + 40px); }
  body.has-rail .pbar { bottom: calc(84px + clamp(10px, 2vw, 18px)); }
  body.has-rail .wafab { bottom: calc(84px + clamp(10px, 2vw, 18px)); }
}

/* Below the stacked-table breakpoint the column heads are gone, so the sort
   controls inside them must leave the tab order too rather than sit invisible
   and focusable. Sorting is a desktop affordance here; the filter chips are
   the mobile equivalent and stay fully usable. */
@media (max-width: 620px) {
  .tbl thead { display: none; }
}

/* ---------- video gallery ----------
   No footage exists yet, so each frame is a poster with a play affordance and
   an honest label. The slot is real; the film is not. When Tarun's footage
   arrives these become <video poster> with the same geometry. */
/* Feature plus playlist: roughly 70/30, which is the split that lets the
   feature stay cinematic while the queue beside it stays readable rather than
   becoming three postage stamps.

   --mo-stagger is raised from the site default here on purpose. At 90ms four
   rows land inside a third of a second and read as one event; at 170ms the eye
   follows them down the column, which is the point of a playlist arriving. */
.vgal { display: grid; grid-template-columns: minmax(0, 2.33fr) minmax(0, 1fr);
  gap: clamp(16px, 2vw, 34px); align-items: start;
  --mo-stagger: 170ms;
  /* The entry offsets travel outside the grid box. `clip` rather than
     `hidden` because hidden would make this a scroll container and break any
     sticky ancestor. */
  overflow-x: clip;
}
.vgal__i { position: relative; overflow: hidden; aspect-ratio: 16/10; display: block; }
.vgal__i--feat { aspect-ratio: 16/9; min-height: clamp(240px, 44vh, 520px); }

/* ---------- the playlist ---------- */
 /* Rows stay tight at the top rather than spreading to the feature's full
    height. Dividing the column between three rows left each one floating in
    its own band of empty space, which reads as a layout accident; a queue is
    supposed to look like a queue. The feature is shortened and the thumbs
    enlarged instead, so the two columns end up close in height on their own. */
.vgal__list { display: grid; gap: clamp(10px, 1.2vh, 16px); align-content: start; }
.vgal__row {
  display: grid; grid-template-columns: clamp(92px, 8.5vw, 124px) minmax(0, 1fr);
  gap: clamp(12px, 1.1vw, 18px); align-items: center;
  padding: clamp(7px, .9vh, 11px); position: relative;
  /* No transition declared here on purpose. A `transition` shorthand on this
     element would replace the one .cv needs to slide the row in, and a row
     that never slides is a worse trade than a hover tint that arrives
     instantly. */
}
 /* --line-d is the token that flips with the mode, so one value serves the
    dark ground and the light one it becomes in light mode. A literal bone
    tint here vanished completely once the band lifted onto paper. */
.vgal__row:hover, .vgal__row:focus-visible { background: var(--line-d); }
.vgal__thumb { position: relative; overflow: hidden; aspect-ratio: 16/10; display: block; }
.vgal__thumb img { width: 100%; height: 100%; object-fit: cover; transition: transform 1.1s var(--ease); }
.vgal__row:hover .vgal__thumb img { transform: scale(1.06); }
.vgal__rowtxt { display: grid; gap: 5px; min-width: 0; }
.vgal__rowt { font-size: clamp(.95rem, 1.3vw, 1.15rem); line-height: 1.12; }
.vgal__play--sm {
  width: clamp(26px, 2.4vw, 34px); height: clamp(26px, 2.4vw, 34px);
  border-width: 1px;
}
.vgal__play--sm i { font-size: 10px; }

@media (max-width: 900px) {
  /* Stacked, the feature sits above its own queue. */
  .vgal { grid-template-columns: minmax(0, 1fr); }
}
.vgal__i img { width: 100%; height: 100%; object-fit: cover; transition: transform 1.1s var(--ease); }
.vgal__i:hover img { transform: scale(1.035); }
.vgal__i::after {
  content: ''; position: absolute; inset: 0;
  background: linear-gradient(to top, rgba(10,11,10,.72), rgba(10,11,10,.05) 62%);
}
.vgal__play {
  position: absolute; top: 50%; left: 50%; z-index: 2;
  width: clamp(44px, 4vw, 60px); height: clamp(44px, 4vw, 60px);
  transform: translate(-50%, -50%); border: 1.5px solid rgba(241,237,228,.8);
  display: grid; place-items: center;
  transition: background-color .35s ease, border-color .35s ease;
}
.vgal__i:hover .vgal__play { background: var(--ember); border-color: var(--ember); }
.vgal__i:hover .vgal__play i { transform: scale(1.15); color: var(--on-ember); }
.vgal__play i {
  font-size: 15px; margin-left: 2px; color: #F1EDE4;
  transition: transform .35s var(--ease);
  border-left: 0 solid transparent;
  border-top: 6px solid transparent; border-bottom: 6px solid transparent;
}
.vgal__meta { position: absolute; inset: auto 0 0 0; z-index: 2; padding: clamp(14px, 2vh, 24px); display: grid; gap: 7px; color: #F1EDE4; }
.vgal__meta .m--stone { color: rgba(241,237,228,.6); }
@media (max-width: 900px) {
  .vgal { grid-template-columns: 1fr 1fr; }
  .vgal__i:first-child { grid-column: span 2; grid-row: auto; min-height: 0; aspect-ratio: 16/10; }
}
@media (max-width: 560px) { .vgal { grid-template-columns: 1fr; } }

/* ---------- region tabs over a rail ---------- */
/* .nav supplies display:flex; the fluid gap and the hairline are ours. */
.rtab { gap: clamp(18px, 2.4vw, 38px); border-bottom: 1px solid var(--line); }
.rtab button {
  position: relative; padding: 0 0 15px; color: var(--moss);
  transition: color .3s ease;
}
.rtab button::after {
  content: ''; position: absolute; left: 0; right: 0; bottom: -1px; height: 1px;
  background: var(--ember); transform: scaleX(0); transform-origin: left;
  transition: transform .45s var(--ease);
}
.rtab button:hover { color: var(--ink); }
.rtab button[aria-selected="true"] { color: var(--ember-text); }
.rtab button[aria-selected="true"]::after { transform: scaleX(1); height: 2px; }
.rtab button[aria-selected="true"] .rtab__n { color: var(--ember-text); opacity: .7; }
.rtab__n { color: var(--stone); margin-left: .55em; transition: color .3s ease; }

.rpanel[hidden] { display: none; }
.rrail { display: flex; gap: clamp(14px, 2vw, 28px); overflow-x: auto; scroll-snap-type: x proximity; scrollbar-width: none; cursor: grab; }
.rrail::-webkit-scrollbar { display: none; }
/* Native dragging off, so a press on a card scrolls the rail instead of
   picking the link up. */
.rrail__i, .rrail__i img { -webkit-user-drag: none; user-select: none; }
.rrail__i { flex: 0 0 auto; width: clamp(230px, 26vw, 340px); scroll-snap-align: start; display: block; text-decoration: none; color: inherit; }
.rrail__fig { position: relative; overflow: hidden; aspect-ratio: 4/5; }
.rrail__fig img { width: 100%; height: 100%; object-fit: cover; transition: transform 1.1s var(--ease); }
.rrail__i:hover .rrail__fig img { transform: scale(1.04); }

/* The cards arrive from the right, one after another, so the rail reads as a rail —
   a line of frames being dealt out rather than a grid switching on.

   An animation rather than a transition, deliberately: .rrail__i already carries a
   transform transition for its two-pixel hover lift, and driving the entry through
   the same property would either slow that lift to the entry's duration or be
   overridden by it. `backwards` fill holds the first frame through the stagger delay
   and then hands the element back to its own base styles, so once a card has landed
   the hover lift is untouched. */
@media (scripting: enabled) and (prefers-reduced-motion: no-preference) {
  .rrail:not(.is-in) .rrail__i { opacity: 0; }
  .rrail.is-in .rrail__i {
    animation: rrail-in var(--mo-slow) var(--ease) backwards;
    /* Capped: a region with eleven destinations should not take a second and a half
       to finish dealing them out. Only the cards that will actually be on screen when
       the rail reveals need a delay of their own; past that the rest can share one. */
    animation-delay: calc(min(var(--i, 0), 5) * 120ms);
  }
  .rrail.is-instant .rrail__i { animation: none; }
}
/* The travel is the whole point of the device — a card that moves 40px over 1.2s is a
   fade with extra steps. Far enough to read as arriving from off to the right. */
@keyframes rrail-in {
  from { opacity: 0; transform: translate3d(clamp(70px, 9vw, 150px), 0, 0); }
  to { opacity: 1; transform: none; }
}

/* The region card's caption-on-glass moved to components.css as the shared
   .veil / .gcap / .gcard device — the destination and expedition cards use it too. */

/* ---------- comparison table ---------- */
/* ---- one screen ----
   A comparison of four is only a comparison while all four columns are in the eye at
   once. Scrolled, it stops being one reading and becomes four; so the section is
   budgeted to a screen the same way the day timeline is, min-height rather than
   height, growing instead of clipping when the budget will not close.

   The header is fixed and never retracts, so the top padding starts below it. */
.cmp-sec {
  min-height: 100vh;
  min-height: 100svh;
  display: grid; align-content: center;
  padding-block: calc(var(--header-h) + clamp(6px, 1.2vh, 26px)) clamp(26px, 3.4vh, 70px);
}
/* Centring the section makes the container a grid item, and a grid item's automatic
   minimum size is its content's — which here includes the table's 720px min-width.
   Without this the container refused to shrink below it and took the whole page
   sideways on a phone: 760px of body in a 390px viewport, every section on the page
   scrolling horizontally, not just this one. The table still scrolls inside .cmp. */
.cmp-sec > .container-xxl { min-width: 0; }
/* .d--1 wrapped this heading onto two lines and spent 200px of the budget doing it —
   a quarter of the screen on eight words. A step down fits it on one line to 1280 and
   leaves the room for the table, which is the thing the section is actually for. */
.cmp-sec__h { font-size: clamp(1.6rem, 3.15vw, 3.125rem); }
.cmp-sec .head {
  padding-bottom: clamp(9px, 1.5vh, 26px);
  margin-bottom: clamp(9px, 1.4vh, 22px);
  align-items: flex-end;
}
.cmp-sec .head__l { gap: clamp(5px, .9vh, 16px); }
.cmp-sec__note {
  display: grid; gap: clamp(5px, .8vh, 10px);
  justify-items: end; text-align: right; max-width: 32ch;
}


/* Columns are expeditions, rows are attributes. Bootstrap's .table and the site's
   generic .tbl are both off this one: .tbl right-aligns its last column, which on a
   four-expedition grid silently knocked the fourth expedition out of line with the
   other three. Everything below is owned by .cmp__t so nothing can reach in again. */
.cmp { overflow-x: auto; overflow-y: hidden; scrollbar-width: none; cursor: grab; }
.cmp::-webkit-scrollbar { display: none; }
.cmp.is-dragging { cursor: grabbing; }
.cmp__t {
  width: 100%; min-width: 860px; border-collapse: separate; border-spacing: 0;
  table-layout: fixed; margin: 0;
}
.cmp__t :is(th, td) {
  text-align: left; vertical-align: middle;
  padding: 0 clamp(12px, 1.5vw, 26px);
}

/* ---- the label rail ---- */
/* Sticky, because the table can be dragged sideways and a value whose row name has
   scrolled off is just a number. The rail carries the section's own ground colour so
   the columns pass behind it rather than through it. */
.cmp__lbl, .cmp__corner {
  position: sticky; left: 0; z-index: 3;
  width: clamp(126px, 15vw, 190px);
  padding-left: 0; padding-right: clamp(14px, 1.6vw, 24px);
  background: var(--paper);
  color: var(--stone); font-weight: 400; text-wrap: balance;
  transition: color .3s ease;
}
.cmp__corner { z-index: 4; }
/* The rail's own edge. A border on a sticky cell separates from the cell during the
   sub-pixel of a drag; a pseudo-element pinned to the inside does not. */
.cmp__lbl::after {
  content: ''; position: absolute; top: 0; bottom: 0; right: 0; width: 1px;
  background: var(--line);
}

/* ---- column heads ---- */
.cmp__head { padding-bottom: clamp(11px, 1.8vh, 26px); vertical-align: bottom; }
/* The head sits at the very top of a scroll container, so a cell with no top padding
   clips the display face's ascenders against the container's edge. This is the padding
   that was missing. */
.cmp thead th { padding-top: 4px; }
.cmp__card {
  display: grid; gap: clamp(5px, .8vh, 10px); align-content: start;
  justify-items: start;
  color: inherit; text-decoration: none;
}
/* The roman numeral ties the column back to the pinned signature section above, which
   is where the visitor first met these four in this order. It sat on the photograph;
   with the photograph gone it becomes the column's own small marker, and it is the
   thing that carries the accent when the column is live. */
.cmp__num {
  display: inline-block; padding: 3px 7px;
  background: rgba(var(--ink-rgb), .075); color: var(--stone);
  transition: background-color .4s ease, color .4s ease;
}
/* Two lines' worth of room whether the name needs it or not. "South Luangwa Elephant
   & Light" wraps where the other three do not, and without this it pushed its own
   column's name half a line below the rest — four headings on four different
   baselines, in the one place on the page whose whole job is alignment. */
.cmp__nm {
  font-family: var(--f-display); font-weight: 400;
  font-size: clamp(.9375rem, 1.2vw, 1.25rem);
  line-height: 1.16; letter-spacing: -.015em;
  min-height: 2.32em;   /* two lines, so the four names share a baseline */
  text-transform: none; text-wrap: pretty; hyphens: none;
  color: var(--ink);
}
.cmp__sep { color: var(--stone); }

/* ---- rows ---- */
/* Reclaimed from the four head photographs. The rows were squeezed to 4.5vh to make
   the budget close with them in; without them the same budget buys real room. */
.cmp__t tbody :is(th, td) { height: clamp(33px, 5.05vh, 72px); border-top: 1px solid var(--line); }
.cmp__t tbody tr:first-child :is(th, td) { border-top-width: 1px; }
.cmp__t tbody tr:last-child :is(th, td) { border-bottom: 1px solid var(--line); }
/* Alternating ground rather than a rule between every pair: ten hairlines across five
   columns reads as graph paper. The tint is on the value cells only, so the label rail
   stays one continuous surface. */
.cmp__t tbody tr:nth-child(even) td { background: rgba(var(--ink-rgb), .028); }
.cmp__r--num td { font-family: var(--f-mono); font-variant-numeric: tabular-nums; }
/* Price is the axis an international buyer is actually weighing, so it gets the display
   face and the weight of a conclusion — and it is the last row before the links. */
.cmp__r--price td {
  font-family: var(--f-display); text-transform: none; letter-spacing: -.01em;
  font-size: clamp(1rem, 1.3vw, 1.375rem);
}
/* The two rows that are conclusions rather than attributes keep more room than the
   rest, at every size — the ratio between them is the hierarchy. */
.cmp__t tbody .cmp__r--price :is(th, td) { height: clamp(44px, 6.5vh, 92px); }
.cmp__t tbody .cmp__r--cta :is(th, td) { height: clamp(36px, 5.3vh, 76px); }
/* Price is the row the eye is looking for, so it gets a real edge above it rather
   than the same hairline as everything else. */
.cmp__t tbody .cmp__r--price :is(th, td) { border-top: 1px solid rgba(var(--ink-rgb), .30); }

/* ---- meters ---- */
/* The segments were a fixed pixel width, which made every gauge a different length
   and left the four cells to be compared by counting rather than by looking. They
   share one track width now and the segments divide it, so 2/5 next to 5/5 is a
   difference you see before you read it. */
.cmp__meter { display: flex; align-items: center; gap: clamp(8px, .9vw, 13px); }
.cmp__bar {
  display: flex; gap: 3px; align-items: center;
  flex: 1 1 auto; min-width: 0; max-width: clamp(76px, 8.5vw, 140px);
}
.cmp__bar i {
  flex: 1 1 0; min-width: 0; height: clamp(5px, .7vh, 9px);
  background: rgba(var(--ink-rgb), .14);
  transition: background-color .35s ease;
}
.cmp__bar i.is-on { background: var(--ink); }
/* The reading, not a caption on it: the numeral carries the same weight as the bar
   so the cell works with the bars ignored entirely. */
.cmp__lvl { color: var(--ink); font-variant-numeric: tabular-nums; flex: none; }
.cmp__lvl-of { color: var(--stone); }

/* The step buttons are injected after any [data-drag] rail, and they disable
   themselves when there is nothing left to scroll. On a desktop the table fits, so
   both arrive disabled — sixty pixels of dead control under a section budgeted to
   the pixel. If neither button can do anything, the pair has nothing to offer.
   It comes back on its own on a narrow screen, where the table does scroll.

   !important because the nav is injected carrying Bootstrap's .d-flex, which is
   itself !important — without it this rule matches and does nothing. */
.cmp-sec .rail-nav:not(:has(.rail-btn:not(:disabled))) { display: none !important; }

/* ---- column focus ---- */
/* Hovering anywhere in a column brings it forward and drops the other three back. It is
   the one thing a comparison of four needs and a static table cannot do: read one
   expedition down its whole length without losing the row labels. Pointer-only — there
   is no hover to leave on a touch screen, and on one the dimmed state would stick. */
/* Every :has() test below reads `> tr > :nth-child(N):hover` — child combinators, not
   descendants. As a descendant test it matched ANY hovered element that happened to be
   its parent's Nth child, and the head card's name span is its parent's 2nd child. So
   pointing at the name of the fourth expedition satisfied "column 2 is hovered" via the
   span and "column 5 is hovered" via the cell it sits in, both at once — dim everything
   but 2, dim everything but 5, and all four columns went back together. The child
   combinator restricts the test to the cells themselves, which is what it always meant. */
@media (hover: hover) and (pointer: fine) {
  .cmp__t :is(th, td) { transition: opacity .35s var(--ease), background-color .35s ease; }
  .cmp__t:has(:is(thead, tbody) > tr > :nth-child(2):hover) tbody td:not(:nth-child(2)),
  .cmp__t:has(:is(thead, tbody) > tr > :nth-child(2):hover) thead th:not(:nth-child(2)):not(:first-child),
  .cmp__t:has(:is(thead, tbody) > tr > :nth-child(3):hover) tbody td:not(:nth-child(3)),
  .cmp__t:has(:is(thead, tbody) > tr > :nth-child(3):hover) thead th:not(:nth-child(3)):not(:first-child),
  .cmp__t:has(:is(thead, tbody) > tr > :nth-child(4):hover) tbody td:not(:nth-child(4)),
  .cmp__t:has(:is(thead, tbody) > tr > :nth-child(4):hover) thead th:not(:nth-child(4)):not(:first-child),
  .cmp__t:has(:is(thead, tbody) > tr > :nth-child(5):hover) tbody td:not(:nth-child(5)),
  .cmp__t:has(:is(thead, tbody) > tr > :nth-child(5):hover) thead th:not(:nth-child(5)):not(:first-child) {
    opacity: .38;
  }
  .cmp__t:has(:is(thead, tbody) > tr > :nth-child(2):hover) :is(td, th):nth-child(2),
  .cmp__t:has(:is(thead, tbody) > tr > :nth-child(3):hover) :is(td, th):nth-child(3),
  .cmp__t:has(:is(thead, tbody) > tr > :nth-child(4):hover) :is(td, th):nth-child(4),
  .cmp__t:has(:is(thead, tbody) > tr > :nth-child(5):hover) :is(td, th):nth-child(5) {
    background: rgba(var(--ink-rgb), .06);
  }
  /* The head card answers a hover anywhere in its column, and the column answers a
     hover of the head card — either end of it lights the whole thing. */
  .cmp__t:has(:is(thead, tbody) > tr > :nth-child(2):hover) th:nth-child(2) .cmp__num,
  .cmp__t:has(:is(thead, tbody) > tr > :nth-child(3):hover) th:nth-child(3) .cmp__num,
  .cmp__t:has(:is(thead, tbody) > tr > :nth-child(4):hover) th:nth-child(4) .cmp__num,
  .cmp__t:has(:is(thead, tbody) > tr > :nth-child(5):hover) th:nth-child(5) .cmp__num,
  .cmp__card:hover .cmp__num { background: var(--ember); color: var(--on-ember); }
}
.cmp__card:focus-visible { outline: 2px solid var(--ember); outline-offset: 4px; }

/* The other axis. Pointing at a value necessarily points at its column, so a row can
   only be asked for from the label rail — which is exactly where a reader's eye goes to
   ask 'what do all four do about light?'. Sibling selector rather than :has(), because
   the cells being lit are the label's own siblings. */
.cmp__lbl:hover { color: var(--ink); }
.cmp__t tbody .cmp__lbl:hover ~ td { background: rgba(var(--ink-rgb), .07); }

@media (max-width: 900px) {
  /* Four columns cannot honestly fit a phone, so below this width the table keeps its
     own width and scrolls sideways behind the sticky label rail — which is what the
     rail was built for. The step buttons come back with it, because here they have
     something to do. */
  .cmp__t { min-width: 720px; }
  .cmp__lbl, .cmp__corner { width: 118px; }
  /* Still budgeted against the viewport, just to a tighter floor than the desktop
     scale — these were flat pixels and overrode the vh sizing above. */
  .cmp__t tbody :is(th, td) { height: clamp(46px, 5.4vh, 64px); }
  .cmp__t tbody .cmp__r--price :is(th, td) { height: clamp(56px, 6.6vh, 78px); }
  .cmp__t tbody .cmp__r--cta :is(th, td) { height: clamp(46px, 5.4vh, 66px); }
}

/* A species name that shows its animal. The dotted rule is the only signal
   that the name is worth pointing at; it fills in on hover. */
.tgt { position: relative; cursor: help; }
.tgt::after {
  content: ''; position: absolute; left: 0; right: 0; bottom: -3px; height: 1px;
  background: currentColor; opacity: .28;
  transition: opacity .3s ease, transform .4s var(--ease);
  transform: scaleX(1); transform-origin: left;
}
.tgt:hover::after { opacity: 1; }

/* ---------- affiliation marquee ----------
   A registry rather than a row of empty boxes. Dashed placeholder rectangles
   read as a wireframe; the site's own vocabulary is hairline rules, mono
   labels and numbered items, so each slot is a numbered entry separated by a
   vertical hairline, with a short rule standing in for the mark that will sit
   there. Still visibly unfilled — that is the point — but unfilled in the same
   language as the rest of the page.

   The track is duplicated in the markup and translated by half its own width,
   so the loop has no seam. */
.mq { position: relative; overflow: hidden; }
.mq::before, .mq::after {
  content: ''; position: absolute; top: 0; bottom: 0; width: clamp(40px, 8vw, 120px); z-index: 2;
  pointer-events: none;
}
.mq::before { left: 0; background: linear-gradient(to right, var(--bone), transparent); }
.mq::after { right: 0; background: linear-gradient(to left, var(--bone), transparent); }
.g-paper .mq::before { background: linear-gradient(to right, var(--paper), transparent); }
.g-paper .mq::after { background: linear-gradient(to left, var(--paper), transparent); }

.mq__track { display: flex; width: max-content; animation: mqRun 46s linear infinite; }
.mq:hover .mq__track { animation-play-state: paused; }
@keyframes mqRun { to { transform: translateX(-50%); } }

.mq__i {
  flex: 0 0 auto; width: clamp(178px, 17vw, 240px);
  display: grid; align-content: start; gap: 9px;
  padding: 4px clamp(20px, 2.4vw, 40px) 4px 0;
  margin-right: clamp(20px, 2.4vw, 40px);
  border-left: 1px solid var(--line);
  padding-left: clamp(16px, 1.8vw, 26px);
}
.mq__n { color: var(--stone); }
.mq__lbl {
  color: var(--ink); line-height: 1.45; max-width: 15ch;
  transition: color .3s ease;
}
/* The rule stands where the mark will go: a real element, so it can be a
   different weight from the hairline that separates the slots. */
.mq__rule {
  display: block; width: 34px; height: 2px;
  background: var(--ink); opacity: .22;
  transition: width .5s var(--ease), opacity .3s ease;
}
.mq__st { color: var(--stone); }
.mq__i:hover .mq__rule { width: 52px; opacity: .5; }

@media (max-width: 640px) { .mq__i { width: 158px; } }

@media (prefers-reduced-motion: reduce) {
  .mq__track { animation: none; }
  .mq { overflow-x: auto; }
}

/* ---------- hero height budget ----------
   The headline's own size now carries the height term — see --fs-hero in tokens.css,
   which takes min(vw, vh) so it falls off smoothly rather than stepping. What is left
   here is the spacing around it, which genuinely does want to close up on a short
   viewport and does not benefit from being continuous. */
@media (max-height: 820px) {
  .hero__foot { padding-top: clamp(18px, 3vh, 34px); }
}
@media (max-height: 680px) {
  .hero__eyebrow { padding-bottom: clamp(10px, 2vh, 20px); }
  .hero__foot { padding-top: 14px; }
}

/* ---------- expedition index ----------
   One set of markup, two layouts. The grid and list views are the same cards
   re-flowed by a class on the container rather than two blocks of duplicated
   content — so the DOM is written once, nothing is hidden from a screen reader,
   and the toggle cannot let the two drift apart. Bootstrap has no equivalent
   for a card that reshapes like this, so the layout is custom and the
   utilities inside it are Bootstrap's. */
.explist { display: grid; gap: clamp(20px, 2.4vw, 38px); }
.explist--grid {
  grid-template-columns: repeat(auto-fill, minmax(min(100%, 300px), 1fr));
  align-items: stretch;
  /* Row gap runs far wider than the column gap: a card's foot has to read as
     closer to its own photograph than to the next card's, and Gestalt
     grouping needs roughly double, not a few pixels. The vh term is a floor
     rather than the driver — 5vh resolves to about 27px on an ordinary
     viewport, which is why an earlier clamp floor of 38px was doing all the
     work and still lost. */
  row-gap: clamp(72px, 9vh, 104px);
}
.explist--list { grid-template-columns: 1fr; gap: 0; border-top: 1px solid var(--line); }

/* The card is a frame with its own caption plus a data foot. .expc__frame is the
   positioning context for the glass in grid view and dissolves with display:contents in
   list view, where the figure and the body go back to being siblings in the row grid. */
.expc { min-width: 0; display: flex; }
.expc__link {
  display: grid; grid-template-rows: auto auto;
  gap: clamp(14px, 1.6vh, 20px);
  color: var(--ink); width: 100%; height: 100%;
  align-content: start;
}
.explist--grid .expc__link { row-gap: 12px; }
.expc__frame { position: relative; min-width: 0; --veil-h: 72%; }
.expc__fig { position: relative; overflow: hidden; aspect-ratio: 4/5; background: var(--paper); }
.expc__fig img { width: 100%; height: 100%; object-fit: cover; transition: transform 1.1s var(--ease); }
.expc__link:hover .expc__fig img { transform: scale(1.035); }
/* The numeral rides the frame, as it does on the pinned panels. */
.expc__n {
  position: absolute; top: 0; left: 0; z-index: 2;
  padding: 10px 12px; color: var(--bone);
  background: linear-gradient(135deg, rgba(var(--ink-rgb), .62), transparent 78%);
}
.expc__body { display: grid; gap: 9px; align-content: start; min-width: 0; }
/* Two lines, always. A longer excerpt is truncated rather than allowed to
   change the card's height — the detail page carries the full text. */
.expc__line {
  font-size: .9375rem; line-height: 1.5; color: var(--moss);
  display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical;
  overflow: hidden;
}
/* In grid view the caption is on the photograph and the excerpt is not: the eyebrow
   carries the facts a browser sorts on, the foot carries the price, and a third block of
   prose on glass would need half the frame's height in veil to stay legible. */
.explist--grid .expc__line { display: none; }
.explist--grid .gcard--flat .expc__line { display: block; }

.expc__foot {
  display: flex; align-items: center; justify-content: space-between;
  gap: 12px; padding-top: 10px; margin-top: 0;
  border-top: 1px solid var(--line);
}
/* The foot belongs to the card above it, so the accent rule that appears on
   hover makes that ownership explicit. */
.explist--grid .expc__foot { position: relative; }
.explist--grid .expc__foot::after {
  content: ''; position: absolute; left: 0; top: -1px; height: 2px; width: 34px;
  background: var(--ember); transition: width .5s var(--ease);
}
.explist--grid .expc__link:hover .expc__foot::after { width: 100%; }
.expc__price { color: var(--ink); }
.expc__cta { color: var(--ember-text); }
.expc__cta i { transition: transform .4s var(--ease); }
.expc__link:hover .expc__cta i { transform: translateX(4px); }

/* ---- list view ----
   The frame becomes a fixed-width thumbnail, the body runs beside it, and the
   data block turns into a single mono row. Same elements throughout: .gcap is
   unwound back to static flow rather than a second set of nodes being rendered. */
.explist--list .expc { border-bottom: 1px solid var(--line); }
.explist--list .expc__frame { display: contents; }
.explist--list .veil { display: none; }
.explist--list .expc__body {
  position: static; padding: 0; color: inherit; transform: none;
  gap: 9px;
}
.explist--list .gcap__eyebrow { color: var(--stone); }
.explist--list .gcap__name > span::after { display: none; }
.explist--list .gcap__arw { display: none; }
.explist--list .gcap__qual { color: var(--stone); }
.explist--list .expc__link {
  grid-template-columns: clamp(120px, 15vw, 210px) 1fr auto;
  grid-template-rows: auto;
  gap: clamp(18px, 2.4vw, 40px);
  align-items: center;
  padding-block: clamp(16px, 2.2vh, 26px);
  row-gap: 0;
}
.explist--list .expc__fig { aspect-ratio: 3/2; }
.explist--list .expc__n { padding: 7px 9px; }
.explist--list .expc__line { -webkit-line-clamp: 1; max-width: 62ch; }
.explist--list .expc__foot {
  border-top: 0; padding-top: 0; margin-top: 0;
  justify-content: flex-end; gap: clamp(14px, 2vw, 30px);
}
@media (max-width: 760px) {
  .explist--list .expc__link { grid-template-columns: clamp(96px, 22vw, 150px) 1fr; }
  .explist--list .expc__foot { grid-column: 2; justify-content: flex-start; padding-top: 6px; }
}
@media (max-width: 480px) {
  .explist--list .expc__link { grid-template-columns: 1fr; }
  .explist--list .expc__fig { aspect-ratio: 16/9; }
  .explist--list .expc__foot { grid-column: 1; }
}

/* ---- view toggle ----
   Two buttons, hairline, accent only on the active one — the same rule the
   filter chips follow: colour marks the state you set, not the control. */
.vtog { display: flex; align-items: center; gap: 8px; }
.vtog__count { margin-right: clamp(8px, 1.4vw, 18px); }
.vtog__b {
  --bs-btn-border-color: var(--line);
  --bs-btn-color: var(--moss);
  --bs-btn-bg: transparent;
  --bs-btn-hover-bg: transparent;
  --bs-btn-hover-border-color: rgba(var(--ink-rgb), .34);
  --bs-btn-hover-color: var(--ink);
  --bs-btn-padding-y: 8px; --bs-btn-padding-x: 12px;
  --bs-btn-border-radius: 0;
  display: inline-flex; align-items: center; gap: 8px;
}
.vtog__b i { font-size: 12px; }
.vtog__b[aria-pressed="true"] {
  background-color: var(--ember) !important;
  border-color: var(--ember) !important;
  color: var(--on-ember) !important;
}
@media (max-width: 560px) { .vtog__b span { display: none; } .vtog__b { --bs-btn-padding-x: 11px; } }
@media (pointer: coarse) { .vtog__b { --bs-btn-padding-y: 13px; --bs-btn-padding-x: 15px; } }

/* ---- the two routes out ---- */
.nxt {
  display: grid; gap: 12px; align-content: start; height: 100%;
  padding: clamp(20px, 2.6vh, 32px) 0 clamp(18px, 2.4vh, 28px);
  border-top: 1px solid var(--line); color: var(--ink);
}
.nxt .d { max-width: 22ch; transition: opacity .3s ease; }
.nxt:hover .d { opacity: .62; }
.nxt .cta-cell { margin-top: 4px; }

/* The index hero is shorter than a detail hero — there is no single subject to
   hold the frame, so the photograph does less work here. */
.phero--index { min-height: 62vh; }

/* A back link is the section link reversed: the arrow leads and travels left. */
.link-u i.fa-arrow-left { order: -1; }
.link-u:hover i.fa-arrow-left { transform: translateX(-5px); }

/* ---------- destination index ----------
   The same two-view card as the expedition index, so the toggle behaves
   identically and .explist carries the layout. What differs is what a
   destination card has to say: coordinates and an expedition count instead of
   a price, and the state or country set beside the place name. */
.dstc { min-width: 0; display: flex; }
.dstc__link {
  display: grid; grid-template-rows: auto auto;
  gap: clamp(14px, 1.6vh, 20px);
  color: var(--ink); width: 100%; height: 100%;
  align-content: start;
}
.explist--grid .dstc__link { row-gap: 12px; }
.dstc__frame { position: relative; min-width: 0; --veil-h: 72%; }
.dstc__fig { position: relative; overflow: hidden; aspect-ratio: 4/5; background: var(--paper); }
.dstc__fig img { width: 100%; height: 100%; object-fit: cover; transition: transform 1.1s var(--ease); }
.dstc__link:hover .dstc__fig img { transform: scale(1.035); }
.dstc__n {
  position: absolute; top: 0; left: 0; z-index: 2;
  padding: 10px 12px; color: var(--bone);
  background: linear-gradient(135deg, rgba(var(--ink-rgb), .62), transparent 78%);
}
.dstc__body { display: grid; gap: 9px; align-content: start; min-width: 0; }
/* The state sits beside the name rather than under it: at display size a
   second line reads as a subtitle, and this is a qualifier. */
.dstc__line {
  font-size: .9375rem; line-height: 1.5; color: var(--moss);
  display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden;
}
.explist--grid .dstc__line { display: none; }
/* The frame with no photograph in it. Ink so the caption's bone type keeps its
   contrast, the mark held small and quiet so it reads as a held place rather than as
   a logo someone decided to put on a card, and the same grain the photographic
   frames carry so it sits in the set instead of beside it. */
.dstc__fig--ph, .card-fig--ph { background: var(--ink); }
.dstc__fig--ph::before, .card-fig--ph::before {
  content: ''; position: absolute; inset: 0; z-index: 0;
  background: url("../images/wyld-mark-on-dark.png") no-repeat center center;
  background-size: clamp(56px, 24%, 104px);
  opacity: .24;
  transition: opacity .5s var(--ease);
}
/* Nothing to scale on hover, so the frame answers with the mark instead — the card
   still responds to the pointer rather than going dead in one cell of the grid. */
.dstc__link:hover .dstc__fig--ph::before,
.expc__link:hover .card-fig--ph::before { opacity: .34; }
.explist--grid .gcard--flat .dstc__line { display: block; }
.dstc__foot {
  display: flex; align-items: center; flex-wrap: wrap; gap: 6px clamp(12px, 1.6vw, 22px);
  padding-top: 10px; border-top: 1px solid var(--line); position: relative;
}
.dstc__coord { color: var(--ink); }
.dstc__meta { color: var(--stone); margin-right: auto; }
.dstc__cta { color: var(--ember-text); }
.dstc__cta i { transition: transform .4s var(--ease); }
.dstc__link:hover .dstc__cta i { transform: translateX(4px); }
.explist--grid .dstc__foot::after {
  content: ''; position: absolute; left: 0; top: -1px; height: 2px; width: 34px;
  background: var(--ember); transition: width .5s var(--ease);
}
.explist--grid .dstc__link:hover .dstc__foot::after { width: 100%; }

.explist--list .dstc { border-bottom: 1px solid var(--line); }
.explist--list .dstc__frame { display: contents; }
.explist--list .dstc__body {
  position: static; padding: 0; color: inherit; transform: none; gap: 9px;
}
.explist--list .dstc__link {
  grid-template-columns: clamp(120px, 15vw, 210px) 1fr auto;
  grid-template-rows: auto; row-gap: 0;
  gap: clamp(18px, 2.4vw, 40px);
  align-items: center;
  padding-block: clamp(16px, 2.2vh, 26px);
}
.explist--list .dstc__fig { aspect-ratio: 3/2; }
.explist--list .dstc__n { padding: 7px 9px; }
.explist--list .dstc__line { -webkit-line-clamp: 1; max-width: 62ch; }
.explist--list .dstc__foot {
  border-top: 0; padding-top: 0; justify-content: flex-end;
  flex-direction: column; align-items: flex-end; gap: 6px;
}
.explist--list .dstc__meta { margin-right: 0; }
@media (max-width: 760px) {
  .explist--list .dstc__link { grid-template-columns: clamp(96px, 22vw, 150px) 1fr; }
  .explist--list .dstc__foot { grid-column: 2; align-items: flex-start; padding-top: 6px; }
}
@media (max-width: 480px) {
  .explist--list .dstc__link { grid-template-columns: 1fr; }
  .explist--list .dstc__fig { aspect-ratio: 16/9; }
  .explist--list .dstc__foot { grid-column: 1; }
}

/* Region filter above the list. The count rides the label the way the region
   tabs on the home page carry theirs. */
.dstfilter {
  display: flex; flex-wrap: wrap; align-items: center; gap: 10px clamp(14px, 2vw, 26px);
  padding-bottom: clamp(26px, 3.8vh, 42px);
}
.chip__n { opacity: .55; margin-left: .55em; }

/* ---------- expedition calendar: season timeline ----------
   A departure is plotted across the year rather than listed in a table, so the
   question the page answers is "when in the season is this" rather than "what
   row is it on". The twelve-month scale is the same vocabulary as the
   seasonality matrices on the destination and wildlife pages.

   No sort controls: a timeline is chronological by construction, which is what
   the table needed buttons for. */
.calfilter { display: grid; gap: clamp(12px, 1.6vh, 18px); padding-bottom: clamp(24px, 3.4vh, 40px); }
/* The palette and typeface bars are fixed in the bottom-left corner, which is
   where this block's label column lands on a short viewport. Reserved only
   while those bars exist: stripping them for launch drops the class and this
   rule with it. */
@media (max-height: 700px) {
  body.has-demobars .calfilter { padding-left: 104px; }
}
@media (max-height: 700px) and (max-width: 560px) {
  /* Stacked rows put the labels at the container's left edge, so the indent
     applies to the rows rather than the grid's first column. */
  body.has-demobars .calfilter__row { padding-left: 0; }
}
.calfilter__row { display: grid; grid-template-columns: 84px 1fr; gap: clamp(12px, 1.6vw, 22px); align-items: start; }
.calfilter__lbl { padding-top: 11px; }
@media (max-width: 560px) {
  .calfilter__row { grid-template-columns: 1fr; gap: 8px; }
  .calfilter__lbl { padding-top: 0; }
}

.caltabs { display: flex; flex-wrap: wrap; gap: clamp(14px, 2vw, 28px); }
.caltabs__b { position: relative; padding: 10px 0; color: var(--stone); transition: color .3s ease; }
.caltabs__b::after {
  content: ''; position: absolute; left: 0; right: 0; bottom: 4px; height: 2px;
  background: var(--ember); transform: scaleX(0); transform-origin: left;
  transition: transform .45s var(--ease);
}
.caltabs__b:hover { color: var(--ink); }
.caltabs__b[aria-selected="true"] { color: var(--ink); }
.caltabs__b[aria-selected="true"]::after { transform: scaleX(1); }

.calbar {
  display: flex; flex-wrap: wrap; align-items: baseline; justify-content: space-between;
  gap: 10px clamp(18px, 3vw, 40px);
  padding-bottom: 16px;
}
.calkey { display: flex; flex-wrap: wrap; gap: 8px clamp(14px, 2vw, 26px); }
.calkey__i { display: inline-flex; align-items: center; gap: 8px; }
.calkey__i--today { display: none; }
.has-today .calkey__i--today { display: inline-flex; }
.calkey__sw { display: inline-block; width: 18px; height: 8px; background: var(--ink); }
.calkey__sw--low { background: var(--ember); }
.calkey__sw--full { background: repeating-linear-gradient(45deg, var(--stone) 0 2px, transparent 2px 5px); box-shadow: inset 0 0 0 1px var(--stone); }
.calkey__sw--today { width: 2px; height: 12px; background: var(--ember); }

/* ---- the board ----
   One grid for the scale and every row, so the twelve month columns run unbroken
   from the scale to the last departure. Month columns are painted as a background
   on each row's track (a hairline at each month start, every other month faintly
   toned) rather than as twelve nodes per row: they line up because every track has
   the same width. */
.gantt {
  --g-cols: minmax(210px, 1fr) minmax(0, 3.1fr) minmax(128px, .6fr);
  --g-month: calc(100% / 12);
  border-top: 1px solid var(--ink);
}
.depscale, .dep {
  display: grid;
  grid-template-columns: var(--g-cols);
  align-items: stretch;
}
.depscale {
  position: sticky; top: var(--hdr-now); z-index: 3; transition: top .45s var(--ease);
  background: var(--bone);
  border-bottom: 1px solid var(--line);
}
.depscale__lbl { align-self: center; padding: 14px 20px 14px 0; font-size: .6875rem; }
.depscale__lbl--end { text-align: right; padding: 14px 0 14px 18px; }
.depscale__months { display: grid; grid-template-columns: repeat(12, minmax(0, 1fr)); border-right: 1px solid var(--line); }
.depscale__months span {
  padding: 14px 0 14px 8px;
  border-left: 1px solid var(--line);
  color: var(--stone); font-size: .6875rem;
  transition: color .3s ease, background-color .3s ease;
}
.depscale__months span:nth-child(even) { background: rgba(var(--ink-rgb), .028); }
.depscale__months span.is-on { color: var(--ember-text); background: rgba(var(--ember-rgb), .12); }
.depscale__months span.is-now { color: var(--ink); }

.deplist { display: grid; }
.depmo { display: none; }
/* The empty state is a supporting line like any other on this page, so it
   takes the mono idiom rather than the body face. */
.deplist__empty {
  padding-block: clamp(30px, 5vh, 58px);
  border-bottom: 1px solid var(--line);
  max-width: 52ch;
}

.dep {
  border-bottom: 1px solid var(--line);
  transition: background-color .3s ease, opacity .4s var(--ease), transform .4s var(--ease);
}
.dep:hover { background: rgba(var(--ink-rgb), .035); }
.dep.is-out { opacity: 0; transform: translateY(8px); }

.dep__info { display: grid; align-content: center; gap: 6px; padding: clamp(18px, 2.4vh, 26px) 22px clamp(18px, 2.4vh, 26px) 0; min-width: 0; }
.dep__dates { color: var(--ink); font-size: .6875rem; }
.dep__name { font-size: clamp(1.05rem, 1.25vw, 1.3rem); line-height: 1.15; letter-spacing: -.01em; transition: color .3s ease; }
.dep__dest { font-size: .625rem; }

.dep__track {
  position: relative; min-height: 64px;
  border-right: 1px solid var(--line);
  background-image:
    linear-gradient(to right, var(--line) 0 1px, transparent 1px),
    linear-gradient(to right, transparent 0 50%, rgba(var(--ink-rgb), .028) 50%);
  background-size: var(--g-month) 100%, calc(var(--g-month) * 2) 100%;
}
/* The filtered month, as a toned column down the whole board. */
.dep__track::before {
  content: ''; position: absolute; top: 0; bottom: 0;
  left: calc(var(--g-month) * var(--hl, 0)); width: var(--g-month);
  background: rgba(var(--ember-rgb), .1);
  opacity: 0; transition: opacity .3s ease, left .4s var(--ease);
}
.has-hl .dep__track::before { opacity: 1; }
/* Today, when the board is showing this year. */
.dep__track::after {
  content: ''; position: absolute; top: 0; bottom: -1px; width: 2px; margin-left: -1px;
  left: var(--today, 0); background: var(--ember);
  display: none;
}
.has-today .dep__track::after { display: block; }

.dep__bar {
  position: absolute; z-index: 1; top: 50%; height: 14px; margin-top: -7px; min-width: 6px;
  background: var(--ink);
  transition: background-color .3s ease, transform .3s var(--ease);
}
.dep--low .dep__bar { background: var(--ember); }
.dep--full .dep__bar { background: repeating-linear-gradient(45deg, var(--stone) 0 2px, transparent 2px 5px); box-shadow: inset 0 0 0 1px var(--stone); }
.dep:hover .dep__bar { transform: scaleY(1.35); }
.dep:hover:not(.dep--full) .dep__bar { background: var(--ember); }
.dep__lbl {
  position: absolute; top: 50%; left: calc(100% + 10px); transform: translateY(-50%);
  white-space: nowrap; font-size: .625rem; color: var(--ink);
}
.dep--full .dep__lbl { color: var(--stone); }
.dep__bar--flip .dep__lbl { left: auto; right: calc(100% + 10px); }

.dep__end { display: grid; align-content: center; justify-items: end; gap: 5px; text-align: right; padding: 18px 0 18px 18px; }
.dep__price { color: var(--ink); }
.dep__avail { color: var(--stone); font-size: .625rem; display: inline-flex; align-items: center; }
.dep--low .dep__avail { color: var(--ember-text); }
.dep__cta { font-size: .6875rem; white-space: nowrap; }

@media (max-width: 1180px) and (min-width: 901px) {
  .gantt { --g-cols: minmax(180px, 1fr) minmax(0, 2.6fr) minmax(112px, .6fr); }
  .depscale__months span { padding-left: 5px; font-size: .5625rem; }
}

/* ---- narrow: the board becomes month-grouped cards ----
   Below 900px twelve columns are thirty pixels each and a nine-day bar is three, so
   the timeline stops being readable. The month headings, which the board does not
   need, take over the job of saying when. */
@media (max-width: 900px) {
  .gantt { border-top: 0; }
  .depscale { display: none; }
  .depmo:not([hidden]) {
    display: block;
    padding: clamp(22px, 4vh, 34px) 0 10px;
    border-bottom: 1px solid var(--ink);
    margin-bottom: 12px;
    color: var(--ink);
  }
  .deplist > .depmo:first-child { padding-top: 0; }
  .dep {
    grid-template-columns: minmax(0, 1fr) auto;
    column-gap: 16px;
    margin-bottom: 10px;
    padding: 16px 18px;
    background: var(--paper);
    border: 1px solid var(--line);
    border-left: 3px solid var(--ink);
  }
  .dep--low { border-left-color: var(--ember); }
  .dep--full { border-left-color: var(--stone); }
  .dep__info { padding: 0; }
  .dep__end { padding: 0; align-content: start; }
  .dep__track { display: none; }
  .dep__name { font-size: 1.125rem; }
}
@media (max-width: 480px) {
  .dep { grid-template-columns: 1fr; row-gap: 14px; }
  .dep__end {
    justify-items: start; text-align: left;
    grid-template-columns: auto auto 1fr; align-items: center; column-gap: 14px;
    padding-top: 12px; border-top: 1px solid var(--line);
  }
  .dep__cta { justify-self: end; }
}

/* ---------- customise your expedition ----------
   A dark panel over the transition photograph. Enquiry only: no payment, no
   checkout, no card marks — the brief forbids them and the demo form sends
   nothing at all. The panel is a flat ink plate with hairline dividers rather
   than a frosted or blurred one, and its inputs are underlines rather than
   boxes, which is how every other field on the site is drawn. */
.cform__wrap { position: relative; z-index: 4; }
.cform {
  /* Anchors .cform__sent, which covers the panel after a successful post. */
  position: relative;
  background: rgba(var(--bone-rgb), .52);
  /* An 8px blur, not 30px. Thirty flattened the photograph into an even wash —
     safe for type, but it threw away the one thing the panel sits on. Eight
     softens the backdrop's local contrast enough for the type to hold while the
     elephants behind stay legible as elephants.

     The brightness lift is what pays for the extra transparency: it raises the
     backdrop's dark end, which is the only part that ever threatens the text.
     Measured against the darkest pixel of the real composite, the labels come
     out at 4.7:1 and the input values at 5.7:1 — both clear AA. Re-measure if
     the backdrop photograph is ever swapped for a darker frame. */
  backdrop-filter: blur(8px) saturate(1.08) brightness(1.3);
  -webkit-backdrop-filter: blur(8px) saturate(1.08) brightness(1.3);
  border: 1px solid rgba(var(--ink-rgb), .14);
  /* The panel fits one screen on desktop (see the budget block after .cform__done).
     It used to get there by packing every field into one grid with rows 8px apart,
     which the client read as congested; it now gets there by grouping the fields into
     columns and spending the height it saves on space between them. */
  /* Kept lean: every pixel of padding is a pixel the fields do not get. */
  padding: clamp(20px, 3vh, 36px) clamp(18px, 2.2vw, 34px);
  /* Centred in the container. Without the auto margin the panel sat flush to
     the left gutter — 63px from the left and 277px from the right at 1440px,
     and 72 against 388 at 1920px. */
  max-width: 1320px;
  margin-inline: auto;
  display: grid; gap: clamp(28px, 4vh, 48px);
  color: var(--ink);
  /* A light ground inside a section marked dark, so the grey has to be
     restated: :root holds its dark-ground value and only .g-light / .g-paper
     swap it. --ember-text needs nothing — .shift is not in the dark-ground
     override list, so it already inherits the darkened light-ground value. */
  --stone: var(--stone-lit);
}
/* Without the blur the wash is doing all the work alone, so it goes back up —
   0.52 over an unblurred photograph would leave the type competing with the
   grass. The photograph is a backdrop here, not the point of the section. */
@supports not ((backdrop-filter: blur(1px)) or (-webkit-backdrop-filter: blur(1px))) {
  .cform { background: rgba(var(--bone-rgb), .94); }
}
/* The head closes on a hairline, so the form below starts as its own block rather
   than running on from the intro. */
.cform__head {
  display: grid; gap: clamp(16px, 2.2vh, 24px);
  padding-bottom: clamp(24px, 3.4vh, 40px);
  border-bottom: 1px solid rgba(var(--ink-rgb), .14);
}
.cform__title { display: grid; gap: clamp(10px, 1.4vh, 16px); }
/* The head used to split at 1200px, title left and intro right on its baseline. The
   intro was cut on 2026-09-20, and a 1fr + 46ch grid with nothing in the second track
   just squeezed the title into half the panel, so the head is a single column now. */

/* While the groups stack, the gap between them has to clearly outrun the gap between
   fields inside one, or the group titles read as just another row. */
.cform__form {
  display: grid; gap: clamp(48px, 6.4vh, 68px) clamp(32px, 3.6vw, 64px);
  grid-template-columns: minmax(0, 1fr);
}
.cfield { display: grid; gap: 4px; align-content: start; min-width: 0; }
.cfield--wide { grid-column: 1 / -1; }
.cfield--chips { grid-column: 1 / -1; }

/* A group is a titled cluster of fields: two columns while the groups stack, one
   column once they stand side by side. */
.cgroup {
  display: grid; grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: clamp(22px, 3vh, 32px) clamp(24px, 3vw, 40px);
  align-content: start; min-width: 0;
}
.cgroup__l {
  grid-column: 1 / -1; margin: 0;
  padding-bottom: 12px; border-bottom: 1px solid rgba(var(--ink-rgb), .14);
  color: var(--ink);
}
.cform .cfield { gap: 10px; }

/* Three columns from 1200px up, one per group. Below that a column is too narrow for
   the chips to hold their lines, and the groups stack instead. */
@media (min-width: 1200px) {
  .cform__form { grid-template-columns: repeat(3, minmax(0, 1fr)); }
  .cgroup { grid-template-columns: minmax(0, 1fr); }
}
/* Solved against the panel's worst-case composite — the photograph's darkest
   region seen through the 0.52 wash and the brightness lift. Stated as an
   alpha colour rather than element opacity: opacity would compound with the
   nested "optional" hint below and halve it again. */
.cfield label, .cfield__legend { color: rgba(var(--ink-rgb), .84); }
/* Its own alpha rather than a fraction of the label's, for the same reason. */
.cfield__opt { text-transform: none; letter-spacing: .04em; color: rgba(var(--ink-rgb), .86); }

/* Underlines, not boxes. Bootstrap's .form-control brings a border, a radius
   and a light ground, so all three are unset here rather than fought. */
.cinput {
  --bs-body-bg: transparent;
  background: transparent; color: var(--ink);
  border: 0; border-bottom: 1px solid rgba(var(--ink-rgb), .62); border-radius: 0;
  padding: 4px 0 5px; font-family: var(--f-ui); font-size: .9375rem;
  transition: border-color .3s ease;
}
.cinput:hover { border-bottom-color: rgba(var(--ink-rgb), .78); }
.cinput:focus {
  background: transparent; color: var(--ink);
  border-bottom-color: rgba(var(--ink-rgb), .62);
  box-shadow: none; outline: none;
}
/* Only a keyboard focus is marked, and by the underline rather than a ring. */
.cinput:focus-visible {
  border-bottom-color: var(--ember);
  outline: none;
}
.cinput::placeholder { color: rgba(var(--ink-rgb), .64); }
.cinput--area { resize: vertical; min-height: 40px; line-height: 1.45; }
/* Taller hit areas in the home panel only; the tailor-made form keeps its own rhythm.
   Block padding alone, so a select's right padding for its caret survives. */
.cform .cinput { padding-block: 8px 10px; font-size: 1rem; }
.cform .cinput--area { min-height: 84px; }
@media (pointer: coarse) { .cform .cinput { padding-block: 12px; } }
/* The native date and select controls are light-on-light by default. */
.cinput option { background: var(--paper); color: var(--ink); }
select.cinput {
  appearance: none;
  background-image: none;
  padding-right: 22px;
}
.cfield { position: relative; }
input[type="month"].cinput::-webkit-calendar-picker-indicator { opacity: .5; cursor: pointer; }
input[type="month"].cinput::-webkit-calendar-picker-indicator:hover { opacity: .9; }

/* The chips are the same control as the calendar's filters, on a dark ground. */
.cform .chip {
  --bs-btn-border-color: rgba(var(--ink-rgb), .2);
  --bs-btn-color: var(--moss);
  --bs-btn-hover-border-color: rgba(var(--ink-rgb), .45);
  --bs-btn-hover-color: var(--ink);
  --bs-btn-padding-y: 10px; --bs-btn-padding-x: 15px;
}
.cform .chips { gap: 10px; }

.cform__foot {
  display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between;
  gap: 16px clamp(20px, 3vw, 44px);
  border-top: 1px solid rgba(var(--ink-rgb), .14); padding-top: clamp(24px, 3.2vh, 36px);
}
.cform__note { max-width: 44ch; }
/* --stone-lit is solved against opaque paper, which this panel is not. The
   demo disclosure carries the brief's placeholder disclaimer, so it takes an
   alpha colour solved against the panel's worst-case composite instead. */
.cform .m--stone { color: rgba(var(--ink-rgb), .86); }
.cform .ph { color: rgba(var(--ink-rgb), .8); }
.cform__submit {
  --bs-btn-bg: var(--ember);
  --bs-btn-border-color: var(--ember);
  --bs-btn-color: var(--on-ember);
  --bs-btn-hover-bg: transparent;
  --bs-btn-hover-border-color: var(--ember);
  /* --ember-text, not --ember. Hover drops the fill, so the label stops being ink on a
     ground and becomes the accent on the panel — and the raw accent is engineered as a
     GROUND. #F2A20C on the bone panel measures 1.82:1, which is why the label all but
     vanished on hover. --ember-text is the same hue taken down to 4.62:1. It is also
     already re-pointed back to the raw accent on every genuinely dark ground
     (tokens.css: .hdr:not(.is-light), .drawer), so this stays correct everywhere. */
  --bs-btn-hover-color: var(--ember-text);
  --bs-btn-padding-y: 13px; --bs-btn-padding-x: 22px;
  --bs-btn-font-size: inherit; --bs-btn-font-family: inherit;
  display: inline-flex; align-items: center; gap: 11px; white-space: nowrap;
}
.cform__submit i { transition: transform .4s var(--ease); }
.cform__submit:hover i { transform: translateX(4px); }

.cform__done {
  grid-column: 1 / -1;
  display: flex; align-items: flex-start; gap: 12px;
  border-top: 1px solid rgba(var(--ink-rgb), .14); padding-top: clamp(18px, 2.4vh, 26px);
  color: var(--ink); text-transform: none; letter-spacing: .02em;
  font-size: 12px; line-height: 1.6; max-width: 62ch;
}
.cform__done i { color: var(--ember-text); margin-top: 3px; }
/* Both of these set display in the author sheet, which beats the UA stylesheet's
   [hidden] { display: none } no matter the specificity. Without this they render while
   still carrying the attribute — the thank-you would sit over the form on first paint. */
.cform__done[hidden], .cform__sent[hidden] { display: none; }
.cinput.is-bad { border-bottom-color: var(--ember); }

/* The thank-you, once the enquiry is in. It covers the whole panel at inset 0 rather
   than replacing the form in flow: the form stays where it is, so the panel keeps its
   height, the section keeps its one-screen budget and the photograph behind is never
   reflowed. The ground is opaque bone — the panel below is only 52% and the form would
   read straight through a second translucent layer.

   It is built from the panel's own furniture rather than as a notification: the same
   eyebrow, the same d--2 as 'Design your expedition', the same hairline the head closes
   on, and the same padding, so the text lands on the exact left edge the form's did. The
   panel reads as having changed what it says, not as a dialog dropped over it. There is
   no tick — the accent appears as a rule, which is how ember is used everywhere else
   that is not the one filled button. */
.cform__sent {
  position: absolute; inset: 0; z-index: 2;
  display: grid; align-content: center;
  padding: clamp(20px, 3vh, 36px) clamp(18px, 2.2vw, 34px);
  background: var(--bone);
  color: var(--ink);
  animation: cformSent .55s var(--ease) both;
}
@keyframes cformSent { from { opacity: 0; transform: translateY(12px); } to { opacity: 1; transform: none; } }
@media (prefers-reduced-motion: reduce) { .cform__sent { animation: none; } }

.cform__sent-in { display: grid; justify-items: start; gap: clamp(10px, 1.5vh, 18px); max-width: 52ch; }
/* The one piece of accent, and it is a line. The nav underline, the flow dot and the
   scroll rail all spend ember this way; a filled badge would make this the loudest
   thing on the page for a message that is simply confirming a quiet action. */
.cform__sent-rule { width: clamp(28px, 3vw, 44px); height: 1px; background: var(--ember); }
.cform__sent-eb { color: var(--ink); }
.cform__sent-t { margin: 0; }
.cform__sent-b { margin: 0; max-width: 46ch; }
/* Closes on the same hairline the head opens the form with, so the block is bounded
   top and bottom by the panel's own rules rather than by a box. */
.cform__sent-sig {
  margin: 0; margin-top: clamp(6px, 1.2vh, 14px);
  padding-top: clamp(14px, 2vh, 22px);
  border-top: 1px solid rgba(var(--ink-rgb), .14);
  width: 100%; max-width: 46ch;
}

/* One-screen budget, 1200px and up. The section is capped at 100vh, so every vertical
   value in the panel is driven by vh with a floor for short screens and a ceiling for
   tall ones. It sits after the rules it overrides so it wins at equal specificity.
   About-you holds only short text inputs, so it gives up width to the two columns
   carrying chips, and the chips pad tighter: the four regions hold to one line down
   to 1200px, where a second line was what pushed the section past the screen.
   Measured in browser viewports (screen minus tabs and address bar) from 1920x1080
   down to 1280x633. */
@media (min-width: 1200px) {
  /* Exactly one screen, with the panel dead centre in it. The stuck header is opaque
     over the top of the section, so the band it takes is mirrored at the bottom: equal
     padding at both ends keeps the panel clear of the header and centred in the box. */
  .shift { height: 100vh; height: 100svh; padding-block: calc(var(--header-h) + clamp(8px, 1.6vh, 32px)); }
  .cform { padding-block: clamp(16px, 3vh, 36px); gap: clamp(12px, 2.4vh, 36px); }
  .cform__head { padding-bottom: clamp(12px, 2.6vh, 32px); }
  .cform__title { gap: clamp(6px, 1vh, 14px); }
  .cform__form {
    grid-template-columns: minmax(0, .9fr) minmax(0, 1.05fr) minmax(0, 1.05fr);
    row-gap: clamp(12px, 2.6vh, 36px); column-gap: clamp(28px, 2.8vw, 48px);
  }
  .cgroup { row-gap: clamp(8px, 2vh, 26px); }
  .cgroup__l { padding-bottom: clamp(6px, 1.2vh, 12px); }
  .cform .cfield { gap: clamp(4px, 1.1vh, 12px); }
  .cform .cinput { padding-block: clamp(3px, .8vh, 9px) clamp(4px, 1vh, 11px); }
  .cform .cinput--area { min-height: clamp(40px, 6.4vh, 72px); }
  .cform .chips { gap: 6px; }
  .cform .chip { --bs-btn-padding-y: clamp(5px, 1vh, 10px); --bs-btn-padding-x: 10px; }
  /* The last row: the note across two columns, the send action stacked in the third.
     Both open on the same hairline so the row reads as one band. */
  .cform__notefield { grid-column: span 2; border-top: 1px solid rgba(var(--ink-rgb), .14); }
  .cform__foot {
    grid-column: auto;
    flex-direction: column; align-items: stretch; justify-content: flex-end; flex-wrap: nowrap;
  }
  .cform__note { max-width: none; }
  .cform__submit { justify-content: center; }
  .cform__foot, .cform__notefield { padding-top: clamp(10px, 2.4vh, 30px); }
}
/* Short laptop screens. The vh values above scale down, but not far enough before
   their floors: at 768px and under the panel still ran 20-40px past the screen. This
   step trades a little of the breathing room for fit, and takes the display heading
   down a size, which is the single tallest line in the panel. */
@media (min-width: 1200px) and (max-height: 820px) {
  .shift { padding-block: calc(var(--header-h) + 10px); }
  .cform { padding-block: clamp(14px, 2.4vh, 20px); gap: clamp(12px, 2.2vh, 18px); }
  .cform__head { padding-bottom: clamp(10px, 1.8vh, 16px); }
  .cform__title .d--2, .cform__sent-t { font-size: clamp(34px, 5.6vh, 46px); }
  .cform__form { row-gap: clamp(12px, 2.2vh, 18px); }
  .cgroup { row-gap: clamp(10px, 1.9vh, 16px); }
  .cform .cfield { gap: 6px; }
  .cform .cinput { padding-block: 4px 5px; }
  .cform .cinput--area { min-height: 62px; }
  .cform .chip { --bs-btn-padding-y: 6px; }
  .cform__foot, .cform__notefield { padding-top: clamp(10px, 1.8vh, 16px); }
}
/* Laptop browser windows once the tabs and address bar are taken off: 1366x657,
   1280x720, 1280x633. With a header-sized band mirrored at the bottom of the section
   the panel has well under 500px here, so everything tightens: the intro takes a wider
   measure to hold to two lines, the inputs and chips slim down, and the send button
   loses a little height. */
@media (min-width: 1200px) and (max-height: 760px) {
  .shift { padding-block: calc(var(--header-h) + 8px); }
  .cform { padding-block: 12px; gap: 8px; }
  .cform__head { padding-bottom: 8px; }
  .cform__title { gap: 4px; }
  .cform__title .d--2, .cform__sent-t { font-size: 32px; }
  .cform__form { row-gap: 8px; }
  .cgroup { row-gap: 6px; }
  .cform .cinput { padding-block: 2px 3px; }
  .cform .cinput--area { min-height: 48px; }
  .cform .chip { --bs-btn-padding-y: 4px; }
  .cform__foot { gap: 8px; }
  .cform__submit { --bs-btn-padding-y: 9px; }
  .cform__foot, .cform__notefield { padding-top: 8px; }
}

@media (max-width: 560px) {
  .cgroup { grid-template-columns: minmax(0, 1fr); }
  .cform { padding: clamp(16px, 4.6vw, 22px); }
  .cform__submit { width: 100%; justify-content: center; }
}
@media (pointer: coarse) { .cinput { padding: 12px 0; } }

/* ---------- enhanced select ----------
   A native <select>'s open list is OS-drawn and unstyleable, so it is replaced
   by a listbox in the site's own vocabulary: square, hairline, mono. The
   <select> itself stays in the form as the value holder and the no-JS
   fallback. */
.sel { position: relative; }
.sel__native { position: absolute; opacity: 0; pointer-events: none; width: 1px; height: 1px; }
html:not(.js) .sel__native { position: static; opacity: 1; pointer-events: auto; width: auto; height: auto; }

.sel__btn {
  width: 100%; text-align: left; display: flex; align-items: center; gap: 10px;
  padding-right: 22px; position: relative;
}
.sel__btn.is-empty { color: rgba(var(--ink-rgb), .46); }
.sel__btn::after {
  content: ''; position: absolute; right: 3px; top: 50%;
  width: 6px; height: 6px; pointer-events: none;
  border-right: 1.5px solid rgba(var(--ink-rgb), .55);
  border-bottom: 1.5px solid rgba(var(--ink-rgb), .55);
  transform: translateY(-70%) rotate(45deg);
  transition: transform .3s var(--ease);
}
.sel.is-open .sel__btn::after { transform: translateY(-30%) rotate(-135deg); }
.sel.is-open .sel__btn { border-bottom-color: var(--ember); }

.sel__list {
  position: absolute; left: 0; right: 0; top: calc(100% + 6px); z-index: 20;
  background: var(--paper);
  border: 1px solid rgba(var(--ink-rgb), .16);
  display: grid; max-height: 260px; overflow-y: auto;
  /* The panel behind is translucent, so an opaque list needs its own edge to
     read as sitting above rather than punched through. */
  box-shadow: 0 12px 30px rgba(var(--ink-rgb), .16);
}
.sel__opt {
  text-align: left; padding: 11px 14px;
  font-family: var(--f-mono); font-size: 11px; letter-spacing: .1em;
  text-transform: uppercase; color: var(--moss);
  border-bottom: 1px solid rgba(var(--ink-rgb), .08);
  transition: background-color .2s ease, color .2s ease;
}
.sel__opt:last-child { border-bottom: 0; }
.sel__opt:hover { background: rgba(var(--ink-rgb), .05); color: var(--ink); }
.sel__opt.is-on { color: var(--ink); }
/* The selected row is marked by a rule in the accent rather than a tick, which
   is how the active state reads everywhere else on the site. */
.sel__opt.is-on { box-shadow: inset 2px 0 0 var(--ember); }
.sel__opt:focus-visible { outline: none; background: rgba(var(--ink-rgb), .07); color: var(--ink); }
@media (pointer: coarse) { .sel__opt { padding: 14px; } }

/* ---------- tailor-made: the multi-step enquiry ----------
   The page was written against .tm-form / .field / .input / .b-primary, none of which
   existed in the stylesheet — the whole four-panel form rendered as browser defaults on
   a full-bleed column. The fields now use .cfield / .cinput, the same controls the
   customise panel on the home page ships, so there is one form vocabulary on the site.
   Only the stepper itself is new. */
.tm-form { display: grid; gap: clamp(22px, 3.4vh, 40px); max-width: 940px; }

/* The step list is a state display, not a control: a reader cannot jump to panel four
   before filling panel one, so it is an <ol> that marks position rather than a row of
   buttons that lie about being clickable. */
.tm-steps {
  display: flex; flex-wrap: wrap; gap: 0; margin: 0; padding: 0; list-style: none;
  border-top: 1px solid var(--line); border-bottom: 1px solid var(--line);
  counter-reset: tmstep;
}
.tm-steps li {
  flex: 1 1 0; min-width: 108px; position: relative;
  padding: 14px 0 13px; color: var(--stone);
  font-family: var(--f-mono); font-size: 11px; letter-spacing: .14em; text-transform: uppercase;
  transition: color .3s ease;
  counter-increment: tmstep;
}
.tm-steps li::before { content: counter(tmstep, decimal-leading-zero) '  '; opacity: .55; }
/* The accent rule marks the panel you are on, the same way the calendar's year tabs and
   the destination chips mark theirs. */
.tm-steps li::after {
  content: ''; position: absolute; left: 0; right: 0; bottom: -1px; height: 2px;
  background: var(--ember); transform: scaleX(0); transform-origin: left;
  transition: transform .45s var(--ease);
}
.tm-steps li[aria-current="step"] { color: var(--ink); }
.tm-steps li[aria-current="step"]::after { transform: scaleX(1); }
@media (max-width: 560px) { .tm-steps li { min-width: 50%; flex: 0 0 50%; } }

.tm-panel { border: 0; padding: 0; margin: 0; display: grid; gap: clamp(14px, 2vh, 22px); }
.tm-panel > legend { padding: 0 0 clamp(6px, 1vh, 12px); float: left; width: 100%; }
.tm-panel > .body { max-width: 56ch; }

/* Two columns once there is room: eight short fields down a single column is four
   screens of scrolling for a form that fits on one. */
@media (min-width: 780px) {
  .tm-panel { grid-template-columns: repeat(2, minmax(0, 1fr)); align-items: start; }
  .tm-panel > legend, .tm-panel > .body,
  .tm-panel > .cfield:has(textarea),
  .tm-panel > .cfield:has(select[multiple]),
  .tm-panel > .cfield:has(.tm-chips),
  .tm-panel > .m--stone { grid-column: 1 / -1; }
}

.cfield__help { display: block; padding-top: 6px; color: var(--stone); }
.cfield__err { display: block; padding-top: 6px; color: var(--ember-text); }
.cinput.is-bad, .cfield__err ~ .cinput { border-bottom-color: var(--ember); }
select[multiple].cinput { border: 1px solid rgba(var(--ink-rgb), .28); padding: 8px; }
select[multiple].cinput option { padding: 5px 6px; }

/* The species picker is the site's chip, wrapping a real checkbox: the control stays
   keyboard-reachable and works with JavaScript off, and the box itself is hidden only
   once the label can show the state. */
.tm-chips { display: flex; flex-wrap: wrap; gap: 8px; padding-top: 4px; }
.tm-chip {
  display: inline-flex; align-items: center; gap: 8px;
  padding: 9px 14px; cursor: pointer;
  border: 1px solid var(--line); color: var(--moss);
  font-family: var(--f-mono); font-size: 11px; letter-spacing: .1em; text-transform: uppercase;
  transition: border-color .3s ease, color .3s ease, background-color .3s ease;
}
.tm-chip:hover { border-color: rgba(var(--ink-rgb), .34); color: var(--ink); }
.tm-chip input { accent-color: var(--ember); margin: 0; }
.tm-chip:has(input:checked) { border-color: var(--ember); color: var(--ink); }
.tm-chip:has(input:focus-visible) { outline: 2px solid var(--ember); outline-offset: 2px; }

.tm-actions {
  display: flex; flex-wrap: wrap; gap: 12px;
  border-top: 1px solid var(--line); padding-top: clamp(16px, 2.4vh, 26px);
}
.tm-btn {
  --bs-btn-padding-y: 13px; --bs-btn-padding-x: 24px;
  --bs-btn-font-size: inherit; --bs-btn-font-family: inherit;
  --bs-btn-border-radius: 0;
  font-family: var(--f-mono); font-size: 11px; letter-spacing: .14em; text-transform: uppercase;
}
.tm-btn--go {
  --bs-btn-bg: var(--ember); --bs-btn-border-color: var(--ember); --bs-btn-color: var(--on-ember);
  --bs-btn-hover-bg: transparent; --bs-btn-hover-border-color: var(--ember); --bs-btn-hover-color: var(--ember-text);
}
.tm-btn--ghost {
  --bs-btn-bg: transparent; --bs-btn-border-color: var(--line); --bs-btn-color: var(--moss);
  --bs-btn-hover-bg: transparent; --bs-btn-hover-border-color: rgba(var(--ink-rgb), .34); --bs-btn-hover-color: var(--ink);
}
.tm-btn[hidden] { display: none; }

/* ---------- document pages: terms, privacy, sitemap ----------
   A legal document is not an editorial page and should not pretend to be one. No
   photographic hero, no parallax, no reveal — a reader who has arrived here wants to
   find a clause, so the page is a contents rail and a measured column, and the only
   motion is the rail marking where they are.

   The rail is sticky rather than fixed, so it scrolls out with its own section on a
   short viewport instead of overlapping the footer. */
.doc-head {
  padding-top: calc(var(--header-h) + clamp(42px, 8vh, 104px));
  padding-bottom: clamp(28px, 4.5vh, 56px);
  border-bottom: 1px solid var(--line);
}
.doc-head__meta {
  display: flex; flex-wrap: wrap; gap: 8px clamp(18px, 3vw, 40px);
  padding-top: clamp(20px, 3vh, 34px);
}

.doc {
  display: grid; grid-template-columns: minmax(0, 250px) minmax(0, 1fr);
  gap: clamp(28px, 5vw, 88px);
  align-items: start;
  padding-block: clamp(38px, 6vh, 78px);
}
@media (max-width: 900px) { .doc { grid-template-columns: 1fr; } }

.doc__rail { position: sticky; top: calc(var(--hdr-now) + 26px); transition: top .45s var(--ease); display: grid; gap: 10px; }
@media (max-width: 900px) {
  .doc__rail { position: static; padding-bottom: clamp(20px, 3vh, 34px); border-bottom: 1px solid var(--line); }
}
.doc__railh { color: var(--stone); padding-bottom: 4px; }
.doc__toc { display: grid; gap: 0; margin: 0; padding: 0; list-style: none; counter-reset: doctoc; }
.doc__toc a {
  display: block; padding: 9px 0 9px 0; color: var(--moss);
  border-top: 1px solid var(--line);
  font-family: var(--f-mono); font-size: 11px; letter-spacing: .1em; text-transform: uppercase;
  transition: color .3s ease, padding-left .35s var(--ease);
}
.doc__toc li:last-child a { border-bottom: 1px solid var(--line); }
.doc__toc a::before { content: counter(doctoc, decimal-leading-zero) '  '; color: var(--stone); }
.doc__toc li { counter-increment: doctoc; }
.doc__toc a:hover { color: var(--ink); padding-left: 6px; }
.doc__toc a.is-here { color: var(--ink); box-shadow: inset 2px 0 0 var(--ember); padding-left: 10px; }

.doc__body { max-width: 72ch; display: grid; gap: clamp(30px, 5vh, 58px); }
/* Every section is addressable: a clause you can send someone a link to is the whole
   point of numbering them. scroll-margin clears the fixed header on a jump. */
.doc__s { scroll-margin-top: calc(var(--header-h-stuck) + 22px); display: grid; gap: clamp(10px, 1.6vh, 16px); }
.doc__n { color: var(--stone); }
/* A clause title is a label, not a statement: at display-2 the fifteen headings were
   most of the page's height and the prose read as captions under them. */
.doc__s h2 { padding-bottom: 2px; font-size: clamp(1.25rem, 1.6vw, 1.6rem); line-height: 1.18; }
.doc__s p, .doc__s li { color: var(--moss); font-size: .9375rem; line-height: 1.62; }
.doc__s ul, .doc__s ol { margin: 0; padding-left: 1.15em; display: grid; gap: 8px; }
.doc__s ul { list-style: none; padding-left: 0; }
.doc__s ul > li { position: relative; padding-left: 1.15em; }
.doc__s ul > li::before {
  content: ''; position: absolute; left: 0; top: .62em;
  width: 5px; height: 5px; background: var(--stone);
}
.doc__s dl { display: grid; gap: 10px; margin: 0; }
.doc__s dt { color: var(--ink); }

/* The scaffold notice. It is a content-integrity mark like the placeholder chip, not a
   design flourish, so it is stated plainly at the top of the column and repeated in the
   page's own words rather than being a dismissible banner someone can close and forget. */
.doc-note {
  border: 1px solid var(--ember);
  padding: clamp(14px, 2vh, 20px) clamp(16px, 2vw, 24px);
  display: grid; gap: 8px; max-width: 72ch;
}
.doc-note p { color: var(--ink); font-size: .9375rem; line-height: 1.55; }

/* ---------- human sitemap ---------- */
.smap { display: grid; gap: clamp(34px, 5.5vh, 70px); padding-block: clamp(34px, 5vh, 68px); }
.smap__g { display: grid; grid-template-columns: minmax(0, 250px) minmax(0, 1fr); gap: clamp(20px, 4vw, 70px); align-items: start; }
@media (max-width: 760px) { .smap__g { grid-template-columns: 1fr; gap: 14px; } }
.smap__h { display: grid; gap: 6px; }
.smap__list {
  margin: 0; padding: 0; list-style: none;
  display: grid; grid-template-columns: repeat(auto-fill, minmax(min(100%, 240px), 1fr));
  gap: 0 clamp(20px, 3vw, 44px);
}
.smap__list li { border-top: 1px solid var(--line); }
.smap__list a { display: block; padding: 11px 0; color: var(--moss); transition: color .3s ease, padding-left .35s var(--ease); }
.smap__list a:hover { color: var(--ink); padding-left: 6px; }
/* A child sits under its parent rather than in its own list: the indent is the tree. */
.smap__list--tree { grid-template-columns: minmax(0, 1fr); max-width: 62ch; }
.smap__list li.is-child a { padding-left: clamp(14px, 1.6vw, 22px); color: var(--stone); }
.smap__list li.is-child a:hover { color: var(--ink); padding-left: clamp(20px, 2.2vw, 28px); }

/* ---------- about: the founder's page ----------

   Three devices, all of them on /about only. The page is a letter with two
   product cells in the middle of it, and each device exists because the copy
   it carries stops working in the shapes the site already had.
   ------------------------------------------------------------------------ */

/* The opening three beats. A setup at reading weight and a punchline at display
   scale — .stmt was the obvious home for them, but .stmt is one paragraph in one
   size and the whole point here is the change of size between the second line and
   the third. Sized off --fs-d2 so it steps with the rest of the scale. */
.beats-sec { padding-block: clamp(92px, 16vh, 230px); }
.beats { display: grid; gap: clamp(16px, 2.4vh, 30px); }
.beats__s {
  font-size: clamp(1.1rem, 1.9vw, 1.6rem); line-height: 1.32;
  font-weight: 300; letter-spacing: -.012em; max-width: 40ch;
}
.beats__p {
  font-size: var(--fs-d2); line-height: 1.04; font-weight: var(--d-weight, 400);
  max-width: 17ch; padding-top: clamp(12px, 2.2vh, 26px);
}

/* Two audiences, side by side, with a rule between them. The copy argues that one
   discipline answers two briefs, and that only reads when both briefs are in the
   eye at once — stacked, the section becomes a list of features.

   The borders take --line and are re-pointed on a dark ground, the same way .rule
   does it, rather than being authored against one ground. */
.twin {
  display: grid; grid-template-columns: 1fr 1fr;
  border-top: 1px solid var(--line);
}
.twin__c {
  display: grid; gap: clamp(13px, 2vh, 22px); align-content: start;
  padding-top: clamp(26px, 4.2vh, 50px); min-width: 0;
}
.twin__c + .twin__c { border-left: 1px solid var(--line); padding-left: clamp(26px, 4.4vw, 84px); }
.twin__c:first-child { padding-right: clamp(26px, 4.4vw, 84px); }
.g-dark .twin, .g-dark .twin__c + .twin__c { border-color: var(--line-d); }

.twin__lbl { display: flex; align-items: baseline; gap: .9em; }
.twin__h { max-width: 20ch; }
.twin__b { font-size: .9375rem; max-width: 42ch; }
/* Each cell closes on the same sentence said two ways, so it is set apart from the
   prose above it rather than running on as a fourth line of it. */
.twin__f {
  font-family: var(--f-display);
  font-size: clamp(1.0625rem, 1.5vw, 1.3125rem); line-height: 1.3; font-weight: 300;
  letter-spacing: -.015em; padding-top: clamp(6px, 1.4vh, 14px);
}

@media (max-width: 900px) {
  .twin { grid-template-columns: 1fr; }
  .twin__c:first-child { padding-right: 0; padding-bottom: clamp(26px, 4vh, 44px); }
  .twin__c + .twin__c { border-left: 0; border-top: 1px solid var(--line); padding-left: 0; }
  .g-dark .twin__c + .twin__c { border-top-color: var(--line-d); }
}

/* The two things a visitor can actually buy. This is the expedition card's own
   device — glass caption on the photograph, prose on paper beneath — at two-up
   scale, so the cells read as products rather than as an essay with pictures. */
.jrny { display: grid; grid-template-columns: 1fr 1fr; gap: clamp(26px, 4vw, 72px); align-items: stretch; }
.jrny__c { min-width: 0; display: grid; }
.jrny__link { display: grid; grid-template-rows: auto 1fr; gap: clamp(18px, 2.6vh, 30px); width: 100%; }
.jrny__fig { position: relative; overflow: hidden; aspect-ratio: 5/4; --veil-h: 70%; }
.jrny__fig img { width: 100%; height: 100%; object-fit: cover; }
/* The numeral sits on the photograph, as it does on an expedition card: on paper it
   would collide with the section's own 03. */
.jrny__n {
  position: absolute; top: 0; left: 0; z-index: 2;
  padding: 9px 11px; color: rgba(var(--bone-rgb), .8);
}
.jrny__cap { position: absolute; left: 0; right: 0; bottom: 0; z-index: 2; padding: clamp(16px, 2.4vw, 30px); }
.jrny__body { display: flex; flex-direction: column; align-items: flex-start; gap: clamp(12px, 1.8vh, 18px); }
/* auto beats the gap, so the link drops to the foot of the taller card and its twin
   follows it rather than sitting wherever its own paragraph happened to end. */
.jrny__body .cta-cell { margin-top: auto; }
.jrny__body .body { max-width: 46ch; font-size: .9375rem; }
/* The line the paragraph was building to, lifted out of it. */
.jrny__k { font-family: var(--f-display); font-size: clamp(1.0625rem, 1.5vw, 1.3125rem);
  line-height: 1.3; letter-spacing: -.015em; font-weight: 300; color: var(--ink); }
.jrny__link:hover .cta-cell::after { transform: scaleX(1); }

@media (max-width: 900px) {
  .jrny { grid-template-columns: 1fr; }
  .jrny__fig { aspect-ratio: 16/10; }
}

/* The close, set as a letter: the body in one narrow measure with the contact
   details beside it, and a signature under it. Display type at reading length
   rather than .body, because this is the only place on the site where the words
   are addressed to one person by one person. */
.letter {
  display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, .5fr);
  gap: clamp(34px, 5vw, 96px); align-items: start;
  /* Narrower than the container on purpose. The body is set to a 30ch measure, so at
     full width the contact block ends up most of a screen away from the signature it
     belongs to and the pair stops reading as one letter. */
  max-width: min(100%, 1120px);
}
.letter__body { display: grid; gap: clamp(18px, 2.8vh, 34px); }
.letter__l {
  font-size: clamp(1.25rem, 2.1vw, 1.875rem); line-height: 1.26;
  font-weight: 300; letter-spacing: -.018em; max-width: 30ch;
}
.letter__sig {
  display: grid; gap: 10px; justify-items: start;
  padding-top: clamp(20px, 3.4vh, 40px); margin-top: clamp(8px, 1.6vh, 18px);
  border-top: 1px solid var(--line);
}
.letter__name { font-weight: 300; }
.letter__connect { display: grid; gap: clamp(14px, 2.2vh, 22px); justify-items: start; }
.letter__clbl { padding-bottom: 2px; }

@media (max-width: 900px) {
  .letter { grid-template-columns: 1fr; }
  .letter__connect { padding-top: clamp(10px, 2vh, 20px); }
}
