/* =========================================================
   DAN TOURS — Feature sections (cinematic redesign)
   Interactive map · Big Five · Migration timeline ·
   Guide profiles · dark chapter-break
   ========================================================= */

/* ---------------------------------------------------------
   DARK CHAPTER-BREAK (reusable dramatic band)
   --------------------------------------------------------- */
.chapter-break {
  position: relative;
  min-height: clamp(340px, 52vh, 560px);
  display: flex;
  align-items: center;
  justify-content: center;
  text-align: center;
  overflow: hidden;
  background: var(--charcoal);
}
.chapter-break .media-clip,
.chapter-break > img {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
}
.chapter-break > img { object-fit: cover; }
.chapter-break::after {
  content: "";
  position: absolute;
  inset: 0;
  background: rgba(20, 16, 10, 0.58);
}
.chapter-break-inner {
  position: relative;
  z-index: 2;
  max-width: 820px;
  padding: 0 var(--container-pad);
  color: var(--warm);
}
.chapter-break-eyebrow {
  display: block;
  font-family: var(--font-label);
  font-size: 0.893rem;
  font-weight: 400;               /* Marcellus ships 400 only — avoid faux-bold */
  letter-spacing: 0.28em;
  text-transform: uppercase;
  color: var(--gold);
  margin-bottom: 22px;
}
.chapter-break-title {
  font-family: var(--font-display);
  font-size: clamp(1.7rem, 4vw, 3rem);
  line-height: 1.1;
  color: var(--warm);
}

/* ---------------------------------------------------------
   INTERACTIVE KENYA MAP
   --------------------------------------------------------- */
.section-map {
  padding: var(--section-pad) 0;
  background: var(--sand);
  overflow: hidden;
}
.section-map .map-header {
  max-width: var(--container-max);
  margin: 0 auto clamp(32px, 5vw, 56px);
  padding: 0 var(--container-pad);
  text-align: center;
}
.map-header .section-heading { color: var(--charcoal); }
.map-sub {
  max-width: 56ch;
  margin: 18px auto 0;
  color: var(--muted);
  line-height: 1.8;
}
.map-wrap {
  max-width: var(--container-max);
  margin: 0 auto;
  padding: 0 var(--container-pad);
  display: grid;
  grid-template-columns: 1.15fr 0.85fr;
  gap: clamp(28px, 5vw, 80px);
  /* Center the detail card vertically against the (now size-capped) map so
     the two columns stay balanced instead of the card floating at the top. */
  align-items: center;
  align-items: center;
}
.map-figure {
  position: relative;
  width: 100%;
  /* Cap the square so a wider --container-max doesn't make the map (and the
     whole section) taller than the viewport. Centered within its column. */
  max-width: 600px;
  margin-inline: auto;
  aspect-ratio: 1 / 1;
}
.kenya-svg {
  width: 100%;
  height: 100%;
  overflow: visible;
  filter: drop-shadow(0 18px 40px rgba(29, 29, 29, 0.18));
}
.kenya-svg path {
  fill: var(--safari);
  transition: fill var(--t-base);
}
.section-map.has-active .kenya-svg path { fill: #3f4b39; }

/* pins */
.map-pin {
  position: absolute;
  transform: translate(-50%, -50%);
  /* Counter-scaled against the global `zoom`, which rendered the old flat 18px
     as a 13x13 real-pixel target. Capped at 16, not the usual 44: these parks
     cluster geographically, and on a phone the tightest pair (Lake Naivasha /
     Aberdare) is only 16.6 real pixels apart on its widest axis. Anything
     larger overlaps its neighbour, and a pin that steals its neighbour's taps
     is a worse bug than a small one. 44 here is not a CSS problem — it needs a
     different mobile affordance, e.g. a tappable destination list. */
  width:  calc(16px / var(--ui-scale, 1));
  height: calc(16px / var(--ui-scale, 1));
  padding: 0;
  border: none;
  background: none;
  cursor: pointer;
  z-index: 3;
}
/* The dot is centred at a fixed 10px rather than inset from the button edge,
   so growing the tap area above leaves the visible marker exactly as it was. */
.map-pin::before {
  content: "";
  position: absolute;
  top: 50%;
  left: 50%;
  width: 10px;
  height: 10px;
  margin: -5px 0 0 -5px;
  border-radius: 50%;
  background: var(--gold);
  box-shadow: 0 0 0 3px rgba(248, 246, 242, 0.85);
  transition: transform var(--t-base);
}
.map-pin::after {
  content: "";
  position: absolute;
  top: 50%;
  left: 50%;
  width: 10px;
  height: 10px;
  margin: -5px 0 0 -5px;
  border-radius: 50%;
  background: var(--gold);
  animation: pinPulse 2.4s ease-out infinite;
}
@keyframes pinPulse {
  0%   { transform: scale(1); opacity: 0.7; }
  70%  { transform: scale(3); opacity: 0; }
  100% { transform: scale(3); opacity: 0; }
}
.map-pin:hover::before,
.map-pin:focus-visible::before,
.map-pin.active::before { transform: scale(1.5); }
.map-pin-label {
  position: absolute;
  left: 50%;
  bottom: calc(100% + 8px);
  transform: translateX(-50%);
  white-space: nowrap;
  font-size: 0.861rem;
  font-weight: 600;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--charcoal);
  background: rgba(248, 246, 242, 0.92);
  padding: 4px 9px;
  border-radius: 2px;
  opacity: 0;
  pointer-events: none;
  transition: opacity var(--t-fast);
}
.map-pin:hover .map-pin-label,
.map-pin:focus-visible .map-pin-label,
.map-pin.active .map-pin-label { opacity: 1; }

/* detail card */
.map-detail {
  position: relative;
  background: var(--page);
  border: 1px solid rgba(154, 131, 82, 0.3);
  box-shadow: 0 20px 50px rgba(29, 29, 29, 0.12);
  overflow: hidden;
  min-height: 340px;
  display: flex;
  flex-direction: column;
}
.map-detail-img {
  aspect-ratio: 16 / 10;
  overflow: hidden;
  background: var(--sand);
}
.map-detail-img img {
  width: 100%; height: 100%; object-fit: cover;
  transition: opacity var(--t-base);
}
.map-detail-body { padding: 26px 28px 30px; }
.map-detail-tag {
  font-family: var(--font-label);
  font-size: 0.872rem;
  font-weight: 400;               /* Marcellus ships 400 only — avoid faux-bold */
  letter-spacing: 0.22em;
  text-transform: uppercase;
  color: var(--gold);
}
.map-detail-name {
  font-family: var(--font-display);
  font-size: clamp(1.4rem, 2.1vw, 1.7rem);
  color: var(--charcoal);
  margin: 8px 0 12px;
}
.map-detail-desc { color: var(--muted); line-height: 1.7; margin-bottom: 20px; }

@media (max-width: 860px) {
  .map-wrap { grid-template-columns: 1fr; gap: 32px; }
  .map-figure { max-width: 460px; margin: 0 auto; }
}

/* ---------------------------------------------------------
   BIG FIVE SHOWCASE
   --------------------------------------------------------- */
.section-bigfive {
  padding: var(--section-pad) 0;
  background: var(--charcoal);
}
.section-bigfive .section-heading { color: var(--warm); }
.bigfive-header {
  max-width: var(--container-max);
  margin: 0 auto clamp(32px, 5vw, 56px);
  padding: 0 var(--container-pad);
  text-align: center;
}
/* No measure cap at all before this, so the lede ran the full 1312px band
   as one ~80-character line — past the point where the eye can find the
   start of the next line reliably. 56ch matches .map-sub, which is the
   established pattern for a centred section lede here. */
.bigfive-header .bigfive-sub {
  color: rgba(248,246,242,0.6);
  margin: 16px auto 0;
  max-width: 56ch;
  text-wrap: pretty;
}
.bigfive-row {
  display: flex;
  gap: 10px;
  max-width: var(--container-max);
  margin: 0 auto;
  padding: 0 var(--container-pad);
  height: clamp(360px, 56vh, 560px);
}
.bigfive-card {
  position: relative;
  flex: 1 1 0;
  overflow: hidden;
  cursor: pointer;
  transition: flex-grow 0.6s var(--ease-out);
  min-width: 0;
}
.bigfive-card img {
  position: absolute;
  inset: 0;
  width: 100%; height: 100%;
  object-fit: cover;
  filter: grayscale(0.35) brightness(0.72);
  transition: filter 0.6s var(--ease-out), transform 0.9s var(--ease-out);
}
.bigfive-card::after {
  content: "";
  position: absolute;
  inset: 0;
  background: linear-gradient(180deg, transparent 40%, rgba(20,16,10,0.75) 100%);
}
.bigfive-card:hover,
.bigfive-card:focus-within { flex-grow: 2.6; }
.bigfive-card:hover img,
.bigfive-card:focus-within img { filter: grayscale(0) brightness(0.92); transform: scale(1.05); }
.bigfive-caption {
  position: absolute;
  z-index: 2;
  left: 0; right: 0; bottom: 0;
  padding: 22px;
  color: var(--warm);
  /* Single-column grid so the description's row can be animated 0fr -> 1fr
     instead of animating its max-height. The first two rows stay `auto`, so
     the number and the (vertical-rl) name lay out exactly as before. */
  display: grid;
  grid-template-rows: auto auto 0fr;
  transition: grid-template-rows 0.5s ease;
}
.bigfive-card:hover .bigfive-caption,
.bigfive-card:focus-within .bigfive-caption { grid-template-rows: auto auto 1fr; }
.bigfive-num {
  font-size: 0.861rem;
  font-weight: 600;
  letter-spacing: 0.2em;
  color: var(--gold);
}
.bigfive-name {
  font-family: var(--font-display);
  font-size: clamp(1.2rem, 2vw, 1.9rem);
  line-height: 1.1;
  margin-top: 4px;
  writing-mode: vertical-rl;
  transform: rotate(180deg);
  transition: writing-mode 0s, transform 0.4s;
}
.bigfive-card:hover .bigfive-name,
.bigfive-card:focus-within .bigfive-name {
  writing-mode: horizontal-tb;
  transform: none;
}
.bigfive-desc {
  color: rgba(248,246,242,0.85);
  font-size: 0.92rem;
  line-height: 1.6;
  max-width: 42ch;
  margin-top: 10px;
  opacity: 0;
  min-height: 0;   /* lets the 0fr row on .bigfive-caption actually collapse */
  overflow: hidden;
  transition: opacity 0.5s ease 0.15s;
}
.bigfive-card:hover .bigfive-desc,
.bigfive-card:focus-within .bigfive-desc { opacity: 1; }

@media (max-width: 720px) {
  .bigfive-row { flex-direction: column; height: auto; }
  /* .bigfive-card's base "flex: 1 1 0" sets flex-basis:0, which overrides
     any height set here in a column flex layout — the card (and the
     absolutely-positioned image inside it) collapsed to 0px tall, hiding
     every photo. flex:none makes the explicit height apply instead. */
  .bigfive-card { flex: none; height: 120px; }
  .bigfive-card:hover, .bigfive-card:focus-within { flex: none; height: 300px; }
  .bigfive-name { writing-mode: horizontal-tb; transform: none; }
}

/* ---------------------------------------------------------
   MIGRATION TIMELINE
   --------------------------------------------------------- */
.section-migration {
  padding: var(--section-pad) 0;
  background: var(--page);
}
.migration-header {
  max-width: var(--container-max);
  margin: 0 auto clamp(28px, 4vw, 48px);
  padding: 0 var(--container-pad);
  text-align: center;
}
.migration-track {
  /* The rail (background + border) paints over the PADDING box, so the old
     `padding: 0 var(--container-pad)` drew a ~64px band of rail down each
     side, outside the first and last card — it read as a stray strip
     hanging off the right-hand end of the timeline. Sizing the element to
     the content column instead means the border hugs the four cards, and
     the gutter is produced by the width calc rather than by padding. */
  width: calc(100% - 2 * var(--container-pad));
  max-width: calc(var(--container-max) - 2 * var(--container-pad));
  margin: 0 auto;
  display: grid;
  /* Four quarters of the migration cycle — same reasoning as .why-grid,
     the count stays 4 → 2 → 1 so a quarter is never orphaned. */
  grid-template-columns: repeat(4, 1fr);
  gap: 1px;
  /* The rail shows THROUGH the 1px gaps and the border, so its colour is
     the hairline colour. rgba(29,29,29,0.1) is neutral grey, which on the
     warm-white band read as a dirty grey slab framing the four cards
     rather than as a hairline grid. Same weight, warm instead of grey. */
  background: rgba(var(--gold-rgb) / 0.28);
  border: 1px solid rgba(var(--gold-rgb) / 0.28);
}
.migration-step {
  background: var(--page);
  padding: 26px 22px;
  position: relative;
  transition: background var(--t-base);
}
.migration-step.peak { background: rgba(154, 131, 82,0.12); }
.migration-step-months {
  font-size: 0.861rem;
  font-weight: 600;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: var(--gold);
  margin-bottom: 10px;
}
.migration-step-title {
  font-family: var(--font-display);
  font-size: 1.25rem;
  color: var(--charcoal);
  margin-bottom: 8px;
  text-wrap: balance;
}
/* `pretty` rather than `balance`: these run 80–90 characters, long enough
   that balancing every line would loosen the whole block. pretty leaves
   the body of the paragraph alone and only prevents the last line from
   ending as a single stranded word. */
.migration-step-desc {
  font-size: 0.9rem;
  color: var(--muted);
  line-height: 1.6;
  text-wrap: pretty;
}
.migration-step-bar {
  position: absolute;
  left: 0; bottom: 0;
  height: 3px;
  /* Full width, revealed by scaling from the left edge — a composited
     transform rather than a per-frame width animation. Visually identical:
     the bar is a plain 3px fill with no radius or text to distort. */
  width: 100%;
  transform: scaleX(0);
  transform-origin: left center;
  background: var(--gold);
  transition: transform 1s var(--ease-out);
}
.migration-track.in .migration-step-bar { transform: scaleX(1); }
@media (max-width: 860px) {
  .migration-track { grid-template-columns: 1fr 1fr; }
}
@media (max-width: 520px) {
  .migration-track { grid-template-columns: 1fr; }
}

/* ---------------------------------------------------------
   GUIDE / TEAM PROFILES
   --------------------------------------------------------- */
.section-team { padding: var(--section-pad) 0; background: var(--page); }
.team-header {
  max-width: var(--container-max);
  margin: 0 auto clamp(32px, 5vw, 56px);
  padding: 0 var(--container-pad);
  text-align: center;
}
.team-sub { color: var(--muted); margin-top: 16px; max-width: 56ch; margin-left: auto; margin-right: auto; }
.team-grid {
  max-width: var(--container-max);
  margin: 0 auto;
  padding: 0 var(--container-pad);
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: clamp(20px, 3vw, 40px);
}
.team-card { position: relative; }
.team-card-img {
  aspect-ratio: 3 / 4;
  overflow: hidden;
  background: var(--sand);
}
.team-card-img img {
  width: 100%; height: 100%; object-fit: cover;
  filter: grayscale(0.25);
  transition: filter 0.5s ease, transform 0.9s var(--ease-out);
}
.team-card:hover .team-card-img img { filter: grayscale(0); transform: scale(1.05); }
.team-card-name {
  font-family: var(--font-display);
  font-size: 1.5rem;
  color: var(--charcoal);
  margin: 18px 0 4px;
}
.team-card-role {
  font-size: 0.883rem;
  font-weight: 600;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--gold);
}
.team-card-bio { color: var(--muted); line-height: 1.7; margin-top: 12px; }
@media (max-width: 860px) { .team-grid { grid-template-columns: 1fr 1fr; } }
@media (max-width: 560px) { .team-grid { grid-template-columns: 1fr; } }

/* ---------------------------------------------------------
   WHY DAN TOURS — four pillars
   --------------------------------------------------------- */
.section-why { padding: var(--section-pad) 0; background: var(--page); }
.why-inner {
  max-width: var(--container-max);
  margin: 0 auto;
  padding: 0 var(--container-pad);
  text-align: center;
}
.why-grid {
  margin-top: clamp(32px, 4vw, 56px);
  display: grid;
  /* Four pillars, so the column count stays 4 → 2 → 1 (see the media
     queries below). auto-fit is deliberately NOT used here: at tablet
     widths it resolves to THREE columns, which leaves the fourth pillar
     stranded on its own row. */
  grid-template-columns: repeat(4, 1fr);
  /* The gap was clamp(20px, 3vw, 40px). 40px × 3 gaps spent 120px of the
     1312px content width on air while each blurb was wrapping at ~21
     characters — the tightest measure on the page. Trimming it returns
     that width to the text, which is where it does more good. */
  gap: clamp(18px, 2vw, 26px);
}
.why-item { text-align: center; }
.why-icon {
  font-size: 1.6rem;
  color: var(--gold);
  margin-bottom: 16px;
  display: block;
}
.why-item-title {
  font-family: var(--font-display);
  font-size: 1.15rem;
  color: var(--charcoal);
  margin-bottom: 8px;
  line-height: 1.3;
  text-wrap: balance;
}
/* A four-across grid inside a 1440px container cannot give these blurbs a
   45–75 character measure — the geometry simply isn't there, and the copy
   is short enough (5–9 words) that it doesn't need one. What it does need
   is an even rag: at ~21 characters a line, German strings like "Wir sind
   rund um die Uhr für Sie da" were breaking to a two-word line plus a
   single stranded "da". `balance` distributes the words evenly across the
   lines instead, which is what makes the row read as organised. */
.why-item-desc {
  color: var(--muted);
  line-height: 1.65;
  font-size: 0.95rem;
  text-wrap: balance;
}
@media (max-width: 860px) { .why-grid { grid-template-columns: 1fr 1fr; } }
@media (max-width: 480px) { .why-grid { grid-template-columns: 1fr; } }

/* ---- Full staff roster (real team, grouped by department) ---- */
.team-roster {
  max-width: var(--container-max);
  margin: 0 auto;
  padding: 0 var(--container-pad);
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: clamp(24px, 3vw, 48px);
  align-items: start;
}
.team-roster-title {
  font-family: var(--font-display);
  font-size: 1.25rem;
  color: var(--charcoal);
  padding-bottom: 12px;
  margin-bottom: 8px;
  border-bottom: 1px solid color-mix(in srgb, var(--gold) 45%, transparent);
}
.team-roster-list { list-style: none; margin: 0; padding: 0; }
.team-roster-person {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 12px;
  padding: 9px 0;
  border-bottom: 1px solid color-mix(in srgb, var(--charcoal) 8%, transparent);
}
.team-roster-person:last-child { border-bottom: 0; }
.team-roster-name { color: var(--charcoal); font-size: 1rem; }
.team-roster-role {
  font-size: 0.861rem;
  font-weight: 600;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--gold);
  white-space: nowrap;
}
.team-roster-closing {
  max-width: var(--container-max);
  margin: clamp(32px, 4vw, 56px) auto 0;
  padding: 0 var(--container-pad);
  text-align: center;
}
.team-roster-closing-title {
  font-family: var(--font-display);
  font-size: clamp(1.1rem, 2vw, 1.4rem);
  color: var(--charcoal);
  /* Uncapped, this centred heading can run the full 1312px band. The German
     string is short enough to stay on one line, but NL and FR are longer —
     capping it here means it wraps to two centred lines instead of stretching
     edge to edge in whichever language runs long. */
  max-width: 40ch;
  margin-inline: auto;
  text-wrap: balance;
}
/* Had no measure cap at all inside a full-width centred block, so this ran
   as a single ~112-character line — the widest measure on the site. */
.team-roster-closing-body {
  color: var(--muted);
  margin: 8px auto 0;
  max-width: 56ch;
  text-wrap: pretty;
}
@media (max-width: 860px) { .team-roster { grid-template-columns: 1fr 1fr; } }
@media (max-width: 560px) { .team-roster { grid-template-columns: 1fr; } }

/* ---------------------------------------------------------
   RICHER TESTIMONIALS (guest photo + trip)
   --------------------------------------------------------- */
.testimonial-guest {
  display: flex;
  align-items: center;
  gap: 14px;
  /* auto, not 20px: .testimonial is a column flex container, so this pushes
     the guest block to the bottom of the card. The three quotes are
     different lengths, and with a fixed margin the avatar rows finished at
     three different heights — the misalignment read as sloppiness rather
     than as variation. The parent's 20px gap still supplies the minimum
     spacing when a quote is long enough to fill the card. */
  margin-top: auto;
}
.testimonial-guest img {
  width: 46px; height: 46px;
  border-radius: 50%;
  object-fit: cover;
  border: 2px solid var(--gold);
}
.testimonial-guest-meta { display: flex; flex-direction: column; }
.testimonial-guest-name { font-weight: 600; color: var(--charcoal); font-size: 0.95rem; }
.testimonial-guest-trip { font-size: 0.927rem; color: var(--gold); }

/* ---- Monogram placeholders (until client supplies photos) ---- */
.team-card-photo--placeholder {
  aspect-ratio: 3 / 4;
  background: linear-gradient(155deg, var(--safari) 0%, var(--charcoal) 100%);
  display: flex;
  align-items: center;
  justify-content: center;
}
.team-card-photo--placeholder span {
  font-family: var(--font-display);
  font-size: clamp(2.6rem, 4.8vw, 3.7rem);
  color: var(--gold);
  letter-spacing: 0.05em;
}
/* testimonial initials avatar (no photo needed) */
.testimonial-guest-avatar {
  width: 46px; height: 46px;
  border-radius: 50%;
  border: 2px solid var(--gold);
  display: flex;
  align-items: center;
  justify-content: center;
  font-family: var(--font-display);
  font-size: 1.1rem;
  color: var(--gold);
  flex: 0 0 auto;
  background: rgba(154, 131, 82,0.08);
}

/* =========================================================
   CINEMATIC POLISH (site-wide)
   ========================================================= */

/* Slow Ken Burns push-in on every inner-page hero image */
.hero-band-bg {
  animation: heroBandZoom 22s ease-in-out infinite alternate;
  transform-origin: center;
  will-change: transform;
}
@keyframes heroBandZoom {
  from { transform: scale(1.02); }
  to   { transform: scale(1.13); }
}

/* Card lift + image warmth on hover (safe: transforms the card, not the
   parallax image, so it never fights the scroll-driven transforms) */
.pkg-card,
.package-row,
.dest-item,
.related-card,
.content-block {
  transition: transform 0.55s var(--ease-out);
}
.related-card:hover { transform: translateY(-6px); }

.package-img img,
.dest-item-img img,
.content-block .media-clip img,
.pkg-card .media-clip img {
  transition: filter 0.6s var(--ease-out);
}
.package-row:hover .package-img img,
.dest-item:hover .dest-item-img img,
.pkg-card:hover .media-clip img,
.content-block:hover .media-clip img {
  filter: brightness(1.05) saturate(1.05);
}

/* Gallery hover zoom (gallery images have no parallax transform) */
.gallery-item img,
.gallery-masonry-item img {
  transition: transform 0.9s var(--ease-out), filter 0.5s ease;
}
.gallery-item:hover img,
.gallery-masonry-item:hover img {
  transform: scale(1.06);
  filter: brightness(1.03);
}

/* Carousel card hover — brighten + name shift */
.pcarousel-item { transition: transform 0.5s var(--ease-out); }
.pcarousel-item:hover .dest-card-name { color: var(--gold); }

@media (prefers-reduced-motion: reduce) {
  .hero-band-bg { animation: none; transform: none; }
}

/* ---------------------------------------------------------
   SAFARI PACKAGES HUB (safaris.html) + category cross-links
   --------------------------------------------------------- */
/* The tiles sit directly under the hero now, so the section keeps its normal
   top padding — it only gave that up to sit flush beneath the intro prose. */
.hub-heading {
  font-family: var(--font-display);
  font-weight: 400;
  font-size: clamp(1.9rem, 3.4vw, 2.9rem);
  line-height: 1.1;
  color: var(--ink);
}
.hub-lead {
  max-width: 62ch;
  margin-top: 14px;
  margin-bottom: clamp(28px, 3.4vw, 44px);
  color: var(--muted);
  font-size: 1.02rem;
  line-height: 1.7;
  text-wrap: pretty;
}
.hub-grid {
  max-width: var(--container-max);
  margin: 0 auto;
  padding: 0 var(--container-pad);
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: clamp(16px, 2.5vw, 32px);
}
.hub-tile {
  position: relative;
  display: block;
  /* Portrait, because the tile carries four rows of content: count, name,
     description and CTA. In 4/3 they had to be stacked into 232px, which is
     why the description was collapsed behind a hover in the first place. */
  aspect-ratio: 3 / 4;
  overflow: hidden;
  color: var(--warm);
}
.hub-tile-media, .hub-tile-media img {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
}
.hub-tile-media { overflow: hidden; }
.hub-tile-media img { object-fit: cover; transition: transform 0.9s var(--ease-out), filter 0.6s ease; }
.hub-tile:hover .hub-tile-media img { transform: scale(1.06); filter: brightness(1.05); }
.hub-tile-overlay {
  position: absolute;
  inset: 0;
  /* Carries more text than it used to, so the scrim starts earlier and ends
     darker — the description has to stay legible over a bright photo. */
  background: linear-gradient(180deg, rgba(20,16,10,0.05) 8%, rgba(20,16,10,0.58) 38%, rgba(20,16,10,0.93) 100%);
  transition: background var(--t-base);
}
.hub-tile-body {
  position: absolute;
  left: 0; right: 0; bottom: 0;
  padding: clamp(20px, 3vw, 36px);
  z-index: 2;
  /* Rows: count · name · description · CTA, all of them open. The description
     used to be a 0fr row that only expanded on :hover, so on every touch
     device — and on any desktop glance that did not involve pointing at the
     tile — the hub read as three captioned images and nothing else. */
  display: grid;
  grid-template-rows: auto auto auto auto;
}
.hub-tile-count {
  display: inline-block;
  font-size: 0.861rem;
  font-weight: 600;
  letter-spacing: 0.2em;
  text-transform: uppercase;
  color: var(--gold);
  margin-bottom: 8px;
}
.hub-tile-name {
  font-family: var(--font-display);
  font-size: clamp(1.5rem, 2.4vw, 2rem);
  line-height: 1.08;
  color: var(--warm);
  margin-bottom: 10px;
}
.hub-tile-desc {
  color: rgba(248, 246, 242, 0.82);
  font-size: 0.92rem;
  line-height: 1.6;
  max-width: 52ch;
  text-wrap: pretty;
}
/* Breathing room above the CTA. */
.hub-tile-desc { margin-bottom: 14px; }
.hub-tile-cta {
  font-size: 0.893rem;
  font-weight: 600;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: var(--gold);
}
@media (max-width: 780px) {
  .hub-grid { grid-template-columns: 1fr; }
  /* One tile per row at full width. A fixed ratio would either letterbox the
     photo or run the tile off a phone screen, so the content sets the height:
     the body comes out of absolute positioning and back into flow, and its top
     padding is what leaves the photo visible above the text. A min-height
     cannot do this job — an absolutely positioned body contributes nothing to
     its parent's height, so the copy would simply be clipped away. */
  .hub-tile { aspect-ratio: auto; }
  /* relative, not static: in flow so it gives the tile its height, but still
     positioned, so its z-index keeps it above the overlay. */
  .hub-tile-body { position: relative; padding-top: clamp(150px, 46vw, 260px); }
}

/* cross-links row on category pages */
.hub-crosslinks { padding-top: 0 !important; }
.hub-crosslinks-row {
  display: flex;
  flex-wrap: wrap;
  gap: clamp(20px, 4vw, 48px);
  margin-top: 20px;
}

/* ---------------------------------------------------------
   SAFARI INDEX (safaris.html)
   Every trip we sell, grouped by type, with its duration and
   its from-price. The hub used to hide all fourteen behind
   three tiles, so the page never showed what was for sale.
   Names, durations and prices are the same i18n keys the
   category pages use — they cannot drift apart.
   --------------------------------------------------------- */
.safari-index-heading {
  font-family: var(--font-display);
  font-weight: 400;
  font-size: clamp(1.9rem, 3.4vw, 2.9rem);
  line-height: 1.1;
  color: var(--ink);
}
.safari-index-sub {
  max-width: 60ch;
  margin-top: 12px;
  color: var(--muted);
  font-size: 1rem;
  line-height: 1.7;
  text-wrap: pretty;
}
.safari-index {
  margin-top: clamp(30px, 4vw, 52px);
  display: grid;
  gap: clamp(34px, 4.5vw, 60px);
}
.si-group-head {
  display: flex;
  align-items: baseline;
  flex-wrap: wrap;
  gap: 8px 16px;
  padding-bottom: 12px;
  border-bottom: 1px solid rgba(var(--ink-rgb) / 0.18);
}
.si-group-name {
  font-family: var(--font-display);
  font-weight: 400;
  font-size: clamp(1.3rem, 2.1vw, 1.7rem);
  line-height: 1.15;
  color: var(--ink);
}
.si-group-count {
  font-size: 0.83rem;
  font-weight: 600;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--gold);
}
.si-group-link {
  margin-left: auto;
  font-size: 0.83rem;
  font-weight: 600;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--gold);
  border-bottom: 1px solid transparent;
  transition: border-color var(--t-fast), color var(--t-fast);
}
.si-group-link:hover { color: var(--gold-deep); border-bottom-color: currentColor; }

.si-row {
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto auto 1.2em;
  align-items: center;
  gap: clamp(12px, 2vw, 32px);
  padding: clamp(14px, 1.7vw, 20px) 0;
  border-bottom: 1px solid rgba(var(--ink-rgb) / 0.10);
  transition: background var(--t-fast), padding var(--t-fast);
}
.si-row:hover,
.si-row:focus-visible {
  background: rgba(var(--sand-rgb) / 0.45);
  padding-left: 14px;
  padding-right: 14px;
}
.si-row:focus-visible { outline: 2px solid var(--gold); outline-offset: 2px; }
.si-name {
  font-family: var(--font-display);
  font-size: clamp(1.05rem, 1.5vw, 1.25rem);
  line-height: 1.25;
  color: var(--ink);
  transition: color var(--t-fast);
}
.si-row:hover .si-name { color: var(--gold); }
.si-badge {
  font-size: 0.78rem;
  font-weight: 600;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--muted);
  white-space: nowrap;
}
.si-price {
  font-size: 0.95rem;
  font-weight: 600;
  color: var(--gold);
  white-space: nowrap;
}
.si-go {
  color: var(--gold);
  text-align: right;
  transition: transform var(--t-fast);
}
.si-row:hover .si-go { transform: translateX(4px); }

@media (max-width: 720px) {
  /* Name and price stay on one line — those are the two a visitor scans.
     The duration drops underneath rather than squeezing the name to a word. */
  .si-row {
    grid-template-columns: minmax(0, 1fr) auto;
    grid-template-areas:
      "name  price"
      "badge go";
    row-gap: 6px;
  }
  .si-name  { grid-area: name; }
  .si-price { grid-area: price; }
  .si-badge { grid-area: badge; }
  .si-go    { grid-area: go; }
}
