/* Jaba landing concept · brand: El Messiri + Helvetica · #705137 / #2D2A26 / #F1DAB2 */

:root {
  --charcoal: #2D2A26;
  --ink: #201D19;
  --brown: #705137;
  --sand: #F1DAB2;
  --cream: #F2EDE3;
  --paper: #F8F4EC;
  --teal: #16332D;
  --line: rgba(45, 42, 38, 0.16);
  --line-light: rgba(241, 218, 178, 0.25);
  --display: "El Messiri", serif;
  --body: "Helvetica Neue", Helvetica, Arial, sans-serif;
  --ease: cubic-bezier(0.22, 1, 0.36, 1);
  --pad: clamp(20px, 4vw, 56px);
  --edge-h: 34px;            /* matches EDGE_H in assets/brand/pattern-wood.gen.py
                                (34 since 2 Aug: smaller dunes). Change BOTH and
                                regenerate: a mask-size that fights the SVG's own
                                ratio letterboxes the wave into square tabs. */
  --r-card: 10px;            /* light card rounding, 29 July: squares reversed */
  --star: #C0913F;           /* review stars only: off-palette like the season
                                ribbon, because stars have to read as stars */
  /* Normalisation tokens (3 Aug audit): one standard section beat and two
     card-grid gaps, so sibling sections and sibling grids stop drifting by
     a few pixels each. Deliberate outliers (the index grids' shorter top,
     the CTA band, page tails) stay written out by hand. */
  --sec-pad: clamp(48px, 6vw, 84px);
  --gap-card: clamp(18px, 2.4vw, 30px);
  --gap-card-lg: clamp(26px, 3vw, 44px);
}

* { margin: 0; padding: 0; box-sizing: border-box; }
html { scroll-behavior: smooth; }
/* The mobile menu sets its own display, which would override the hidden
   attribute and leave an invisible full-screen overlay eating all mouse events
   (breaking every hover on the page). Keep hidden meaning hidden. */
[hidden] { display: none !important; }
body {
  font-family: var(--body);
  font-size: 16px;
  line-height: 1.6;
  color: var(--charcoal);
  background: var(--cream);
  -webkit-font-smoothing: antialiased;
}
img { display: block; width: 100%; height: 100%; object-fit: cover; }
a { color: inherit; text-decoration: none; }
ul { list-style: none; }
button { font: inherit; color: inherit; background: none; border: 0; cursor: pointer; }

.wrap { max-width: 1280px; margin: 0 auto; padding-left: var(--pad); padding-right: var(--pad); }
.wrap-narrow { max-width: 860px; margin: 0 auto; padding-left: var(--pad); padding-right: var(--pad); }

.visually-hidden, .skip {
  position: absolute; width: 1px; height: 1px; overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap;
}
.skip:focus { position: fixed; top: 12px; inset-inline-start: 12px; width: auto; height: auto; clip: auto; background: var(--charcoal); color: var(--sand); padding: 10px 16px; z-index: 200; }

/* ---------- Type ---------- */

h1, h2, h3 { font-family: var(--display); font-weight: 600; line-height: 1.08; }
h1 { font-size: clamp(52px, 8vw, 108px); letter-spacing: -0.01em; }
h2 { font-size: clamp(34px, 4.6vw, 62px); }
h3 { font-size: clamp(19px, 1.6vw, 24px); }

/* Sentence case since 1 Aug, Abdulla: the tracked sand caps read as an AI
   tell. The whole mini-header family (labels, define-say, footer h4, fact
   terms, field labels) is white on dark grounds and charcoal on light ones,
   minimal tracking. */
.label {
  font-family: var(--body);
  font-size: 13px;
  font-weight: 600;
  letter-spacing: 0.04em;
  color: var(--charcoal);
  display: flex;
  align-items: center;
  margin-bottom: 18px;
}
.label.center { justify-content: center; }
.label-light { color: #fff; }

/* Sentence case since 1 Aug (Abdulla: unnecessary caps read as a tell);
   buttons keep theirs, links and labels do not. */
.text-link {
  font-size: 13px;
  font-weight: 600;
  letter-spacing: 0.04em;
  border-bottom: 1px solid currentColor;
  padding-bottom: 4px;
  transition: opacity 0.25s;
  white-space: nowrap;
}
.text-link:hover { opacity: 0.55; }
.text-link span { display: inline-block; transition: transform 0.3s var(--ease); }
.text-link:hover span { transform: translateX(5px); }

/* ---------- Buttons ---------- */

.btn {
  display: inline-flex;
  align-items: center;
  gap: 10px;
  font-size: 13px;
  font-weight: 600;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  padding: 15px 30px;
  border-radius: 4px;
  transition: transform 0.35s var(--ease), background 0.3s, color 0.3s, border-color 0.3s;
}
.btn:hover { transform: translateY(-2px); }
.btn:active { transform: translateY(0); }
.btn-solid { background: var(--sand); color: var(--charcoal); }
.btn-solid:hover { background: #f7e6c4; }
.btn-ghost { border: 1px solid currentColor; color: #fff; }
.btn-ghost:hover { background: rgba(255, 255, 255, 0.12); }
/* Hero pair: outlined at rest, each filling with a different brand colour on hover. */
.btn-fill-sand:hover { background: var(--sand); border-color: var(--sand); color: var(--charcoal); }
.btn-fill-brown:hover { background: var(--brown); border-color: var(--brown); color: var(--cream); }
/* .btn-ghost hard-codes white for the hero, so every ghost button on a light
   ground needs its colour put back. Must stay after .btn-fill-brown:hover so
   the hover colour still wins. */
.finder .btn-ghost,
.cta-band-light .btn-ghost,
.enquiry .btn-ghost { color: var(--charcoal); }
.finder .btn-ghost:hover,
.cta-band-light .btn-ghost:hover,
.enquiry .btn-ghost:hover { background: var(--brown); color: var(--cream); }
/* Parked 28 July: every CTA-band button went clear on the client's ask ("no
   colour fill"), so nothing uses .btn-dark now. Kept in case a filled button
   is ever wanted back. */
.btn-dark { background: var(--charcoal); color: var(--sand); }
.btn-dark:hover { background: var(--ink); }
/* Outlined like the hero pair. Colour is inherited, so it reads white over the hero
   and charcoal once the header goes cream. */
.btn-nav { background: transparent; border: 1px solid currentColor; padding: 11px 22px; }
.btn-nav:hover { background: var(--brown); border-color: var(--brown); color: var(--cream); }

/* ---------- Header ---------- */

.site-header {
  position: fixed;
  inset: 0 0 auto 0;
  z-index: 100;
  transition: background 0.4s, box-shadow 0.4s, transform 0.4s;
}
.header-inner {
  display: flex;
  align-items: center;
  gap: clamp(16px, 3vw, 40px);
  padding: 22px var(--pad);
  transition: padding 0.4s var(--ease);
}
.site-header.scrolled { background: rgba(242, 237, 227, 0.94); backdrop-filter: blur(12px); box-shadow: 0 1px 0 var(--line); }
.site-header.scrolled .header-inner { padding-top: 12px; padding-bottom: 12px; }

.brand { display: inline-flex; align-items: center; transition: opacity 0.3s; }
.brand:hover { opacity: 0.8; }
/* Official lockup (symbol + logotype). Payoff is dropped at header size per the
   brand book's minimum-size rule (below ~2.5cm: symbol and logotype only). */
.brand-logo { height: 46px; aspect-ratio: 299.81 / 146.87; display: block; }

.site-header { color: #fff; }
.site-header.scrolled { color: var(--charcoal); }

.main-nav { display: flex; gap: clamp(14px, 2.2vw, 30px); margin-inline-start: auto; }
.main-nav a {
  font-family: var(--display);
  font-size: 15.5px;
  position: relative;
  padding-bottom: 3px;
}
.main-nav a::after {
  content: ""; position: absolute; left: 0; bottom: 0; height: 1px; width: 100%;
  background: currentColor; transform: scaleX(0); transform-origin: right;
  transition: transform 0.35s var(--ease);
}
.main-nav a:hover::after { transform: scaleX(1); transform-origin: left; }

.header-utils { display: flex; align-items: center; gap: clamp(10px, 1.6vw, 22px); }
/* Nav links carry 3px of padding-bottom for their underline, which drops EN's
   baseline below theirs once the flex row centres both. Lift it back onto the line. */
.lang-switch { position: relative; top: -3.6px; }
.lang { font-size: 12px; letter-spacing: 0.14em; font-weight: 600; }
.lang:hover { opacity: 0.65; }
.lang-caret { opacity: 0.5; margin-inline-start: 6px; font-size: 9px; }
.lang-menu {
  position: absolute; top: calc(100% + 14px); inset-inline-end: 0; min-width: 150px; z-index: 110;
  background: var(--paper); color: var(--charcoal);
  border-radius: 12px; padding: 6px;
  box-shadow: 0 24px 60px rgba(32, 29, 25, 0.35);
  opacity: 0; visibility: hidden; transform: translateY(-6px);
  transition: opacity 0.25s, transform 0.25s var(--ease), visibility 0.25s;
}
.lang-switch.open .lang-menu { opacity: 1; visibility: visible; transform: translateY(0); }
.lang-menu li { padding: 9px 12px; border-radius: 8px; font-size: 14.5px; cursor: pointer; transition: background 0.15s; }
.lang-menu li:hover { background: var(--cream); }
.lang-menu li[aria-selected="true"]::after { content: "✓"; float: right; font-size: 12px; opacity: 0.6; }

.burger { display: none; flex-direction: column; gap: 6px; padding: 6px; }
.burger span { width: 24px; height: 2px; background: currentColor; transition: transform 0.3s var(--ease), opacity 0.3s; }
.burger.open span:nth-child(1) { transform: translateY(4px) rotate(45deg); }
.burger.open span:nth-child(2) { transform: translateY(-4px) rotate(-45deg); }

.mobile-menu {
  position: fixed; inset: 0; z-index: 90;
  background-color: var(--charcoal); color: var(--cream);
  display: flex; align-items: center; justify-content: center;
  opacity: 0; transition: opacity 0.35s;
}
.mobile-menu.show { opacity: 1; }
.mobile-menu nav { display: flex; flex-direction: column; gap: 26px; text-align: center; }
.mobile-menu a { font-family: var(--display); font-size: 32px; }
.mobile-menu .mm-cta { color: var(--sand); }

/* ---------- Hero ---------- */

.hero {
  position: relative;
  min-height: 100svh;
  display: flex;
  align-items: center;
  color: #fff;
  overflow: hidden;
}
.hero-media { position: absolute; inset: 0; }
.hero-poster {
  position: absolute; inset: 0;
  animation: heroZoom 18s var(--ease) forwards;
}
@keyframes heroZoom { from { transform: scale(1.12); } to { transform: scale(1); } }
/* The hero photograph is a night scene, so the scrim only needs to steady the
   top bar and lift the copy off the rock: any heavier and the sky goes muddy. */
.hero-scrim {
  position: absolute; inset: 0;
  background:
    linear-gradient(180deg, rgba(32, 29, 25, 0.5) 0%, rgba(32, 29, 25, 0.1) 30%, rgba(32, 29, 25, 0.05) 60%, rgba(32, 29, 25, 0.45) 100%),
    radial-gradient(85% 65% at 28% 58%, rgba(32, 29, 25, 0.42) 0%, rgba(32, 29, 25, 0) 72%);
}
/* The film sits above the still, so if the video fails the photograph is what
   shows. The ::after fades the last of the frame into the Welcome brown: the
   client asked for the video to blend into the next section, not break on a
   line, so the hero's straight-edge rule does not apply here. */
/* No opacity trick here: the element's own poster paints instantly and the
   film takes over the moment it has frames, so a fast connection sees the
   film at once (the 31 July fade made every load start on the still). */
.hero-film { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; }
.hero::after {
  content: ""; position: absolute; left: 0; right: 0; bottom: 0;
  height: clamp(140px, 16vw, 220px);
  background: linear-gradient(180deg, rgba(112, 81, 55, 0) 0%, var(--brown) 100%);
  z-index: 1; pointer-events: none;
}
@media (prefers-reduced-motion: reduce) { .hero-film { display: none; } }
.hero-content { position: relative; z-index: 2; padding: 120px var(--pad) 140px; max-width: 1280px; margin: 0 auto; width: 100%; }
.hero-sub { max-width: 460px; font-size: clamp(16px, 1.4vw, 19px); line-height: 1.65; margin-top: 22px; text-wrap: pretty; }
.hero-ctas { display: flex; flex-wrap: wrap; gap: 14px; margin-top: 34px; }

.scroll-cue {
  position: absolute; bottom: 28px; left: 50%; transform: translateX(-50%);
  z-index: 2; color: rgba(255, 255, 255, 0.85);
  display: flex; flex-direction: column; align-items: center; gap: 10px;
  font-size: 11px; letter-spacing: 0.18em;
  transition: opacity 0.5s var(--ease), transform 0.5s var(--ease);
}
/* Set once the page first moves, and never cleared: the cue has said its piece. */
.scroll-cue.spent { opacity: 0; visibility: hidden; transform: translate(-50%, 16px); }
.scroll-cue i { width: 1px; height: 46px; background: rgba(255, 255, 255, 0.5); overflow: hidden; position: relative; }
.scroll-cue i::after {
  content: ""; position: absolute; left: 0; top: -50%; width: 100%; height: 50%;
  background: #fff; animation: cueDrop 2.2s ease-in-out infinite;
}
@keyframes cueDrop { 0% { top: -50%; } 70% { top: 100%; } 100% { top: 100%; } }

/* ---------- Textured sections ---------- */

/* Grain texture: topographic contour lines, the old jaba.com.sa treatment
   rebuilt as crisp vector (pattern-topo.gen.py, 3 Aug, the client's ask via
   Abdulla: same as the old site, more elegant, better quality). Nested
   loops and saddles from a periodic scalar field, so every line closes or
   continues across the tile seam; every fourth level runs heavier, a survey
   map's index contour. Brown tile on light backgrounds, sand on dark,
   unlike the old site's single white overlay. The strand-track and mesh
   tiles this replaced stay on disk. The tile is 1920 x 2880; the waves
   stay 640-periodic and 1920 = 3 x 640 with both starting at x=0, so the
   horizontal phases still lock. */
/* --grain-y re-phases each section onto one page-wide 640px grid, so the grain
   runs unbroken through every boundary instead of restarting at each section's
   top edge. main.js sets it; the fallback is the old per-section tiling.
   --grain-drift is the scroll parallax (31 July): main.js drives it from
   scrollY on wide screens, the same value everywhere, so the whole field
   drifts as one plane behind the content and the phase lock survives. */
.texture,
.journal, .journal-page,
.why, .todo, .season, .stays, .finder,
.enquiry, .dest-rail, .factbar, .guest-reviews, .team, .exp-index,
.about-story, .about-backing {
  background-image: url("../assets/brand/pattern-topo.svg");
  background-size: 1920px 2880px;
  background-position: 0 calc(var(--grain-y, 0px) + var(--grain-drift, 0px));
}
.intro, .trips, .cta-band, .site-footer, .page-hero, .mobile-menu,
.dest-index, .way-hero, .about-mission {
  background-image: url("../assets/brand/pattern-topo-dark.svg");
  background-size: 1920px 2880px;
  background-position: 0 calc(var(--grain-y, 0px) + var(--grain-drift, 0px));
}
/* The home rail went cream on 2 Aug (Abdulla: the dark ground fought the
   cards). .trips-light rides on .trips and must outrank the dark tile group
   above, hence the doubled class. The yanbu rail keeps plain .trips and
   stays ink. */
.trips.trips-light {
  background-color: var(--cream); color: var(--charcoal);
  background-image: url("../assets/brand/pattern-topo.svg");
}

/* ---------- Section edges ---------- */

/* The colour break between two sections follows the grain rather than ruling a
   straight line across it. Each section reaches --edge-h up into the one above
   it and masks that overhang with a wave from pattern-wood.gen.py, drawn from
   one of the tile's own strands. The wave is 640px-periodic like the tile, so
   repeat-x holds it in the tile's horizontal phase at every viewport width.
   The overhang carries the section's own colour and grain, both inherited and
   re-phased by --edge-h, so strands cross the boundary without a step. It
   overlaps the section by 1px because a fractional device pixel at the join
   would otherwise show as a hairline.
   The heroes are deliberately not in this list. The landing hero fades into
   the Welcome brown, and since 29 July the two photo page heroes fade into
   their charcoal index sections the same way (Abdulla called the wave there a
   hard cut), which is why .dest-index and .exp-index no longer carry a
   ::before or an --edge. Region-page heroes keep their straight edges. */
.finder, .trips, .journal, .cta-band, .site-footer,
.journal-page, .contact-section,
.dest-index, .exp-index, .why, .todo, .season, .stays,
.enquiry, .dest-rail, .guest-reviews, .team,
.about-story, .about-mission, .about-backing { position: relative; }

.finder::before, .trips::before, .journal::before,
.cta-band::before, .site-footer::before, .journal-page::before,
.contact-section::before,
.why::before, .todo::before, .season::before,
.stays::before, .enquiry::before, .dest-rail::before,
.dest-index::before, .exp-index::before, .guest-reviews::before,
.team::before,
.about-story::before, .about-mission::before, .about-backing::before {
  content: "";
  position: absolute;
  left: 0; right: 0; bottom: calc(100% - 1px);
  height: var(--edge-h);
  background-color: inherit;
  background-image: inherit;
  background-size: 1920px 2880px;
  /* Minus the 1px overlap: the box top is --edge-h - 1px above the section,
     so compensating by the full --edge-h would shift the grain by a pixel and
     put back the seam this exists to remove. */
  background-position: 0 calc(var(--grain-y, 0px) + var(--grain-drift, 0px) + var(--edge-h) - 1px);
  -webkit-mask-image: var(--edge); mask-image: var(--edge);
  -webkit-mask-size: 640px var(--edge-h); mask-size: 640px var(--edge-h);
  -webkit-mask-repeat: repeat-x; mask-repeat: repeat-x;
  -webkit-mask-position: left top; mask-position: left top;
  /* When a section's top lands on a fractional device pixel, Chromium
     paints this box's background-colour across its partial top row at
     partial alpha even though the mask's top rows are fully transparent,
     which draws a faint full-width hairline 34px above the wave. The
     wave art never reaches the top 2px of the mask, so clipping the top
     pixel costs nothing and kills the artifact at any alignment. */
  clip-path: inset(1px 0 0 0);
  pointer-events: none;
}

/* Paired so no two touching sections share a wave. On about.html the run
   since 11 Aug is way-hero (brown, no wave of its own), about-story (1)
   biting into it, about-mission (2), about-backing (4), footer (3): no
   touching pair clashes. The .team pairing stays for the parked roster. */
.why, .stays, .enquiry, .dest-rail, .team, .about-story { --edge: url("../assets/brand/edge-wave-1.svg"); }
.finder, .cta-band, .about-mission { --edge: url("../assets/brand/edge-wave-2.svg"); }
.trips, .site-footer { --edge: url("../assets/brand/edge-wave-3.svg"); }
.journal-page, .todo, .about-backing { --edge: url("../assets/brand/edge-wave-4.svg"); }
/* The index sections' wave bites into the hero photograph itself (31 July,
   Abdulla's third call on this boundary: wave, then fade, then hard cut, now
   the wave again — but riding the photo, no band between). Wave-4 clashes
   with neither neighbour: the footer below destinations is 3, the CTA band
   below the experiences grids is 2. */
.dest-index, .exp-index { --edge: url("../assets/brand/edge-wave-4.svg"); }
.journal, .contact-section, .season { --edge: url("../assets/brand/edge-wave-5.svg"); }
/* Landing foot since 1 Aug: cta-band (2) above it, footer (3) below it. */
.guest-reviews { --edge: url("../assets/brand/edge-wave-5.svg"); }

/* ---------- Chapters ---------- */

/* The opening story (1 Aug; gesture-stepped since 2 Aug, Abdulla's ask): the
   film holds one viewport while the motto and the ja·ba definition sit
   stacked over it. main.js intercepts wheel / key / touch gestures while the
   page is at the very top: one downward gesture SNAPS to the definition — a
   quick self-running transition driven by .is-active, deliberately NOT a
   scroll-scrubbed fade, which Abdulla rejected — and the next releases the
   page into native scroll. There is NO scroll-snap anywhere: both the
   mandatory (bounce-back) and proximity (pull-up near Destinations' top)
   variants produced 1 Aug bugs. The container runs one --edge-h taller than
   the stage's viewport so dest-rail's wave overhang sits just below the fold
   during the story instead of riding the film's bottom edge; the first 48px
   of real scrolling bring it in. Base styles are the FALLBACK: no JS or
   reduced motion stacks the chapters as plain full-height sections. */
.chapters { position: relative; background-color: var(--charcoal); color: #fff; }
.chapters-stage { position: relative; overflow: hidden; }
.chapters .chapter {
  position: relative; z-index: 2;
  min-height: 100vh; min-height: 100svh;
  display: flex; align-items: center;
}
.chapters .chapter > .wrap { width: 100%; }
.chapters-dim {
  position: absolute; inset: 0; z-index: 1;
  background: rgb(43 33 24); opacity: 0; pointer-events: none;
  transition: opacity 0.45s var(--ease);
}
/* The define block centres itself; its big section-tail margin is not wanted
   mid-stage. */
.chapters .define { margin-bottom: 0; }

.js-chapters .chapters { height: calc(100vh + var(--edge-h)); height: calc(100svh + var(--edge-h)); }
.js-chapters .chapters-stage { height: 100%; }
.js-chapters .chapters .chapter {
  position: absolute; inset: 0 0 auto;
  height: 100vh; height: 100svh; min-height: 0;
  opacity: 0; visibility: hidden; transform: translateY(16px);
  pointer-events: none;
  transition: opacity 0.42s var(--ease), transform 0.42s var(--ease), visibility 0s 0.42s;
}
.js-chapters .chapters .chapter.is-active {
  opacity: 1; visibility: visible; transform: none; pointer-events: auto;
  transition: opacity 0.42s var(--ease), transform 0.42s var(--ease), visibility 0s;
}
.js-chapters .chapters-dim.dimmed { opacity: 0.22; }

/* ---------- Intro ---------- */

/* The pattern band that opened this section was cut on 28 July, so the top
   padding is back. */
.intro {
  /* position: relative is load-bearing for .intro-media: without it the
     absolute media div anchors to the document and paints the photograph
     over the hero instead of this section (shipped broken on 31 July —
     .intro is not in the wave position list, it has no ::before). */
  position: relative;
  background-color: var(--brown); color: var(--cream);
  /* Tightened 31 July (Abdulla: the section ran too tall; Destinations should
     peek in as it arrives). */
  padding: clamp(46px, 5vw, 68px) 0;
  /* "The Jaba Way" nav links land here since 29 July, when the How-it-works
     section and its #how anchor were cut. */
  scroll-margin-top: 90px;
}
/* The photograph went full bleed behind the whole section on 31 July,
   Abdulla's ask: hero treatment rather than a framed image in the grid. The
   scrim opens solid brown at the top so the hero's fade still lands on flat
   colour, and deepens toward the foot where the review cards sit. The brown
   background-color beneath stays as the no-image fallback, and the texture
   tile the section still registers for is simply hidden behind the photo. */
.intro-media { position: absolute; inset: 0; overflow: hidden; }
/* Biased slightly low so the cover crop keeps the sun and the dune line in
   the open right-hand side rather than empty sky. */
.intro-media img { width: 100%; height: 100%; object-fit: cover; object-position: 50% 60%; }
/* Two layers: a solid brown lid at the top so the hero's fade still lands on
   flat colour, then a diagonal wash, near-opaque over the text column and
   easing to the right. With the review cards back in that column the right
   side no longer opens to 0.22: the reviews heading sits on the photograph,
   so the wash holds ~0.6 there and the photo reads around the paper cards. */
.intro-media::after {
  content: ""; position: absolute; inset: 0;
  background:
    linear-gradient(180deg, rgb(112, 81, 55) 0, rgba(112, 81, 55, 0.55) clamp(220px, 24vw, 340px), rgba(112, 81, 55, 0) clamp(330px, 36vw, 500px)),
    linear-gradient(102deg,
      rgba(99, 71, 47, 0.95) 0%,
      rgba(99, 71, 47, 0.88) 48%,
      rgba(99, 71, 47, 0.55) 74%,
      rgba(99, 71, 47, 0.28) 100%);
}
/* The content rides above the media. */
.intro > .wrap { position: relative; z-index: 1; }
/* The name as a dictionary entry, centred above the Jaba Way pair (1 Aug,
   Abdulla, dictionary-poster style; copy from the brand book's Naming page).
   It sits on the scrim's solid brown lid, which was deepened to hold it. */
.define { max-width: 640px; margin: 0 auto clamp(52px, 6.5vw, 88px); text-align: center; }
.define-word { font-family: var(--display); font-size: clamp(40px, 4.4vw, 60px); line-height: 1; }
/* The Arabic original beside the Latin (1 Aug, Abdulla). El Messiri is an
   Arabic-first face, so it sets natively; slightly smaller because the
   Arabic ascends taller than the Latin lowercase. */
.define-ar { font-size: 0.8em; margin-inline-start: 14px; }
.define-say em { font-style: italic; }
.define-say {
  margin-top: 10px;
  font-size: 13px; font-weight: 500;
  letter-spacing: 0.04em;
  color: rgba(255, 255, 255, 0.88);
}
.define-text { max-width: 560px; margin: clamp(14px, 1.6vw, 20px) auto 0; font-family: var(--display); font-size: clamp(16.5px, 1.35vw, 19px); line-height: 1.6; }
.intro .label-light { margin-bottom: 14px; }
/* About Jaba is a single left column since 1 Aug (the reviews moved to the
   foot of the page); the photograph owns the open right-hand side. */
.intro-col { max-width: 660px; }
/* The red .wip-star review marker left with its last host on 29 July: the
   How-it-works section and the Yanbu map both got cut, so no annotations
   remain. Reintroduce the rule from git history if one is ever needed. */

/* Both columns are headed, and the two heads have to read as siblings, so they
   share one size. Well under the global h2: at 62px "Don't just take our word
   for it." runs to three lines in a half-width column. */
.intro-head {
  font-size: clamp(24px, 2.4vw, 33px);
  margin-bottom: clamp(16px, 1.8vw, 24px);
  text-wrap: balance;
}
/* No label above this, the heading leads. One even gap between paragraphs,
   one larger one before the link. */
.intro-statement {
  font-family: var(--display);
  font-size: clamp(17px, 1.5vw, 21px);
  /* Light type on a dark ground wants the extra leading. */
  line-height: 1.6;
  /* The bottom margin left with the "Read the Jaba Way" link; .ways carries
     the gap below now. */
  margin: 0;
}
.intro-statement p + p { margin-top: 0.9em; }
/* Not balance: that was for the old centred block, and left-aligned it just
   makes short ragged last lines. */
.intro-statement p { text-wrap: pretty; }
/* The four brand emblems from the retired How-it-works section, bare and
   riding the heading row since 31 July (Abdulla cut the "We dive" labels the
   same day they were added): "The Jaba Way" left, icons right. Sand on the
   brown ground, decorative. */
/* Fixed gap, not space-between: the icons sit with the heading as one
   cluster instead of floating detached at the column's far edge. */
.intro-head-row {
  display: flex; align-items: center;
  gap: 14px clamp(20px, 2.4vw, 34px); flex-wrap: wrap;
  margin-bottom: clamp(16px, 1.8vw, 24px);
}
.intro-head-row .intro-head { margin-bottom: 0; }
.ways {
  list-style: none; padding: 0; margin: 0;
  display: flex;
  gap: clamp(12px, 1.4vw, 18px);
  color: var(--sand);
}
.ways li { display: flex; }
.ways svg { width: clamp(23px, 1.8vw, 26px); height: clamp(23px, 1.8vw, 26px); opacity: 0.8; flex: none; }

/* ---------- Finder (parked) ---------- */

/* The .finder / .finder-grid / .finder-bar / .finder-go rules below, and the
   destinations-map block further down, are kept deliberately: the section they
   dressed was cut from index.html on 26 July and parked whole in
   archive/finder-map.html, and it is meant to be rollable back. The .field,
   .menu and stepper components under them are NOT dead: enquire.html reuses
   all three. Delete the finder rules only once the map is decided against for
   good. */
.finder {
  background-color: var(--sand); color: var(--charcoal);
  /* Padding tracks viewport height, not width: arriving via the nav anchor
     should land the form and the whole outline on one screen, and on a short
     laptop screen it is the form column, not the map, that runs out of room. */
  padding: clamp(52px, 7vh, 96px) 0;
  scroll-margin-top: 90px;
}
/* Form on the left, map on the right. The map is the wider half: it is the
   thing people look at, and the form reads fine as a narrow rail. */
.finder-grid {
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(0, 1.15fr);
  gap: clamp(36px, 5vw, 80px);
  align-items: center;
}
.finder-title { margin-bottom: 30px; }
.finder-bar {
  display: grid;
  grid-template-columns: 1fr;
  background: var(--paper);
  border: 1.5px solid rgba(112, 81, 55, 0.3);
  border-radius: 16px;
  padding: 10px;
  gap: 2px;
  box-shadow: 0 14px 34px rgba(45, 42, 38, 0.09);
}
.field { position: relative; display: flex; flex-direction: column; justify-content: center; padding: 13px 20px; border-bottom: 1px solid rgba(112, 81, 55, 0.16); min-width: 0; }
.field:last-of-type { border-bottom: 0; }
.field-label { font-size: 11px; font-weight: 600; letter-spacing: 0.03em; opacity: 0.6; margin-bottom: 3px; }
.field-value { text-align: start; font-size: 15px; font-family: var(--display); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.field-value::after { content: " ▾"; font-size: 10px; opacity: 0.5; }
.menu {
  position: absolute; top: calc(100% + 14px); inset-inline-start: 0; min-width: 210px; z-index: 40;
  background: var(--paper); color: var(--charcoal);
  border-radius: 14px; padding: 8px;
  box-shadow: 0 24px 60px rgba(32, 29, 25, 0.35);
  opacity: 0; visibility: hidden; transform: translateY(-6px);
  transition: opacity 0.25s, transform 0.25s var(--ease), visibility 0.25s;
}
.field.open .menu { opacity: 1; visibility: visible; transform: translateY(0); }
.menu li { padding: 9px 14px; border-radius: 9px; font-size: 14.5px; cursor: pointer; transition: background 0.15s; }
.menu li:hover { background: var(--cream); }
.finder-go { justify-content: center; margin: 10px 4px 4px; }

/* Travellers stepper */
.stepper-panel { min-width: 264px; padding: 10px; cursor: default; }
.stepper-row { display: flex; align-items: center; justify-content: space-between; gap: 18px; padding: 10px 8px; }
.stepper-row + .stepper-row { border-top: 1px solid var(--line); }
.stepper-name { font-size: 14.5px; font-weight: 600; }
.stepper-hint { font-size: 12px; opacity: 0.55; margin-top: 1px; }
.stepper-ctl { display: flex; align-items: center; gap: 10px; }
.step-btn {
  width: 32px; height: 32px;
  border: 1px solid var(--line); border-radius: 6px;
  display: grid; place-items: center;
  font-size: 17px; line-height: 1;
  transition: background 0.15s, opacity 0.15s;
}
.step-btn:hover:not(:disabled) { background: var(--cream); }
.step-btn:disabled { opacity: 0.3; cursor: default; }
.step-count { min-width: 22px; text-align: center; font-family: var(--display); font-size: 16px; }

/* ---------- Sections shared ---------- */

.section-head {
  display: flex; align-items: flex-end; justify-content: space-between; gap: 24px;
  margin-bottom: clamp(30px, 4.5vw, 48px);
}
/* The eyebrow hugs its heading inside a section head (2 Aug typography
   pass); the generic 18px .label margin stays for forms and footers. */
.section-head .label { margin-bottom: 10px; }
/* Standfirst under a section heading. Measured so it breaks before the
   "All stories" link on the far side of the row. Opacity lifted 0.75 → 0.88
   on 2 Aug (Abdulla: the caption greys read too faint). */
.section-sub {
  max-width: min(100%, 620px);
  margin-top: 12px;
  font-size: clamp(15px, 1.15vw, 16.5px);
  line-height: 1.65;
  opacity: 0.88;
}

/* ---------- Destinations split ---------- */

/* Sand, so it breaks the brown Welcome above it from the ink Experiences below.
   That is the rhythm the section waves were tuned around before the trip finder
   came out of this slot. */
/* Same 4 Aug easing as .trips: no eyebrow, so the section arrives sooner. */
.dest-rail { background-color: var(--sand); color: var(--charcoal); padding: clamp(44px, 5.5vw, 72px) 0 var(--sec-pad); }
/* Split layout since 3 Aug, from Abdulla's reference: square photograph
   left, the ten names as display type right. The carousel of tiles this
   replaced is in git history (2 Aug version). Hover and focus swap the
   photograph via .is-on; main.js drives it. */
.dest-split {
  /* Margins on both sides since 3 Aug (fourth pass, Abdulla: match the
     reference's sizing exactly — the full-bleed left edge is gone). The
     stage track is the reference's own measured proportion, 46% of the
     viewport, and the gutter its ~13%, so the whole row lands where the
     reference puts it at any width. */
  padding-left: var(--pad);
  padding-right: var(--pad);
  display: grid;
  grid-template-columns: 46vw minmax(0, 1fr);
  gap: clamp(32px, 13vw, 260px);
  align-items: center;
}
/* Squared off completely, Abdulla's ask: the one photograph on the site
   allowed to ignore --r-card. The hairline stays, per the client's borders
   note. */
.dsp-stage {
  position: relative;
  aspect-ratio: 1 / 1;
  overflow: hidden;
  margin: 0;
  border: 1px solid rgba(112, 81, 55, 0.3);
  background-color: var(--sand);
}
.dsp-layer {
  position: absolute; inset: 0;
  opacity: 0;
  transition: opacity 0.55s var(--ease);
}
.dsp-layer.is-on { opacity: 1; }
/* Each arriving photograph settles from a slight zoom, so a swap moves like
   the site's other imagery rather than switching like a slideshow. */
.dsp-layer img { transform: scale(1.06); transition: transform 1.4s var(--ease); }
.dsp-layer.is-on img { transform: scale(1); }
/* A low scrim keeps the white pin caption legible on pale photographs. */
.dsp-layer::after {
  content: ""; position: absolute; inset: auto 0 0 0; height: 90px;
  background: linear-gradient(180deg, rgba(32, 29, 25, 0) 0%, rgba(32, 29, 25, 0.48) 100%);
}
.dsp-pin {
  position: absolute; inset-inline-start: 16px; bottom: 14px; z-index: 1;
  display: inline-flex; align-items: center; gap: 7px;
  color: #fff;
  font-size: 13px; font-weight: 600; letter-spacing: 0.04em;
  text-shadow: 0 1px 10px rgba(20, 18, 15, 0.45);
}
/* The pin is the category pages' filled glyph, not a stroke icon. */
.dsp-pin .mi { fill: currentColor; stroke: none; }
/* The header matches the Experiences section head below it (3 Aug, second
   pass, Abdulla: the two rails must read as siblings): eyebrow over the
   display-size title, then the reference's full-column hairline. The title
   itself links to destinations.html — standing underline so it reads as
   clickable, hover brown, the region-nav treatment at h2 size. */
/* A flex row like .section-head since 4 Aug (Abdulla): plain title left,
   "All Destinations" text-link right, eyebrow and title-underline gone.
   The full-column hairline stays. */
.dsp-head {
  display: flex; align-items: flex-end; justify-content: space-between; gap: 24px;
  padding-bottom: clamp(14px, 1.6vw, 20px);
  margin-bottom: clamp(20px, 2.4vw, 34px);
  border-bottom: 1px solid rgba(45, 42, 38, 0.16);
}
/* Even rhythm from the item gap, not from padded line boxes. Sized so the
   header plus the ten names stay just inside the photograph's height: the
   image must read as the tallest element, like the reference. Eased again
   on 3 Aug when the stage gave up 5% (1.35fr → 1.2fr): at the old
   clamp(26–40) the list ran level with the shorter photograph. */
.dsp-names li + li { margin-top: clamp(6px, 0.8vw, 10px); }
/* 500, not 600: at this size the semibold read heavy against the
   reference's light list, and the active state is carried by colour alone
   so no line shifts width on hover. */
.dsp-names a {
  display: inline-block;
  font-family: var(--display);
  font-size: clamp(25px, 2.4vw, 38px);
  font-weight: 500;
  line-height: 1.15;
  color: rgba(45, 42, 38, 0.42);
  transition: color 0.3s;
}
.dsp-names a.is-on,
.dsp-names a:hover,
.dsp-names a:focus-visible { color: var(--charcoal); }

/* ---------- Experiences tail (parked) ---------- */

/* Parked 28 July: both "None of these suits your fancy?" tails were cut on
   the client's ask, the experiences speak for themselves and the CTA band
   already offers the talk-it-through route. Rules kept for rollback; the
   markup is in git history. */
.trip-tail {
  display: flex; align-items: center; justify-content: center;
  flex-wrap: wrap; gap: clamp(16px, 2vw, 26px);
  margin-top: clamp(44px, 5vw, 68px);
  text-align: center;
}
.trip-tail p { font-family: var(--display); font-size: clamp(18px, 1.7vw, 23px); }
.exp-index .trip-tail { margin-top: clamp(48px, 5.5vw, 72px); }

/* ---------- Destinations map ---------- */

/* Capped by height as well as width so the form and the whole outline share one
   screen when you arrive via the nav anchor. The map is 1.218x wider than tall. */
/* width:100% is load-bearing: auto margins on a grid item cancel the default
   stretch, and the SVG would fall back to its 300px intrinsic width. */
.dest-map { position: relative; width: 100%; max-width: min(100%, max(400px, calc((100svh - 240px) * 1.21))); margin-inline: auto; }
.dest-map svg { display: block; width: 100%; height: auto; overflow: visible; }

/* One pin per region. The halo is a separate circle so hover and the selected
   state can grow it without touching the dot's own geometry. */
.dest-pin { cursor: pointer; }
.dest-pin .pin-halo {
  fill: var(--brown); opacity: 0;
  transform-box: fill-box; transform-origin: center;
  transform: scale(0.5);
  transition: opacity 0.3s, transform 0.35s var(--ease);
}
.dest-pin .pin-dot {
  fill: var(--brown);
  transform-box: fill-box; transform-origin: center;
  transition: transform 0.3s var(--ease), fill 0.25s;
}
.dest-pin text {
  font-family: var(--body); font-size: 40px; font-weight: 600; letter-spacing: 0.03em;
  fill: var(--charcoal); transition: fill 0.25s;
  paint-order: stroke; stroke: var(--sand); stroke-width: 7px; stroke-linejoin: round;
}
.dest-pin:hover .pin-halo, .dest-pin:focus-within .pin-halo { opacity: 0.2; transform: scale(1); }
.dest-pin:hover .pin-dot, .dest-pin:focus-within .pin-dot { transform: scale(1.25); }
.dest-pin:hover text, .dest-pin:focus-within text { fill: var(--brown); }

/* Set from the Destination selector, so picking a region pops it on the map. */
.dest-pin.is-on .pin-halo { opacity: 0.28; transform: scale(1.25); }
.dest-pin.is-on .pin-dot { fill: var(--charcoal); transform: scale(1.45); }
.dest-pin.is-on text { fill: var(--charcoal); font-weight: 700; }
.dest-country {
  fill: rgba(112, 81, 55, 0.13);
  stroke: rgba(112, 81, 55, 0.75);
  stroke-width: 3;
  stroke-linejoin: round;
}

/* ---------- Trips carousel ---------- */

/* Top padding eased under --sec-pad on 4 Aug: the eyebrow went, and the
   heading alone left a hole between the wave and the text. */
.trips { background-color: var(--ink); color: var(--cream); padding: clamp(44px, 5.5vw, 72px) 0 var(--sec-pad); }
.carousel-shell { position: relative; }
.c-prev, .c-next {
  position: absolute;
  top: 50%;
  z-index: 5;
  width: 54px; height: 54px; border-radius: 50%;
  /* Solid brown, not a ring: the outlined version was too easy to lose against
     the trip photography. Same brown on both rails, per Abdulla on 26 July. */
  background: var(--brown);
  border: 0;
  color: var(--cream);
  display: grid; place-items: center;
  box-shadow: 0 2px 10px rgba(32, 29, 25, 0.4);
  opacity: 0; visibility: hidden;
  transition: opacity 0.3s, visibility 0.3s, transform 0.3s var(--ease), background 0.3s;
}
.c-prev:hover, .c-next:hover { background: #8a6544; }
.c-arrow { width: 24px; height: 24px; }
.c-prev { left: var(--pad); transform: translate(-50%, -50%); }
.c-next { right: var(--pad); transform: translate(50%, -50%); }
.c-prev.show, .c-next.show { opacity: 1; visibility: visible; }
.c-prev.show:hover { transform: translate(-50%, -50%) scale(1.07); }
.c-next.show:hover { transform: translate(50%, -50%) scale(1.07); }
.carousel {
  display: flex; gap: 18px;
  overflow-x: auto;
  scroll-snap-type: x mandatory;
  padding-bottom: 10px;
  scrollbar-width: none;
}
.carousel::-webkit-scrollbar { display: none; }
.trip-card {
  flex: 0 0 clamp(260px, 24vw, 330px);
  scroll-snap-align: start;
}
.trip-card a { display: block; }
/* Lightly rounded since 29 July, reversing the 26 July squares on Abdulla's
   ask: the square was not doing it for him. The hairline frame was lifted
   from 0.18 on 28 July, client note: borders on the cards, clean and
   minimal. Sand here for the ink grounds. */
.trip-card figure {
  position: relative; aspect-ratio: 3 / 3.8; overflow: hidden;
  border: 1px solid rgba(241, 218, 178, 0.32);
  border-radius: var(--r-card);
}
.trip-card img { transition: transform 1s var(--ease); }
.trip-card:hover img { transform: scale(1.05); }
/* Overlaid call to action, 28 July: the client wants every experience card to
   say it can be booked. Solid paper rather than the first clear-bordered pass,
   which vanished into the photography; fills brown when the card is hovered. */
.card-cta {
  position: absolute; inset-inline-start: 14px; bottom: 14px; z-index: 2;
  padding: 10px 16px;
  background: var(--paper); color: var(--charcoal);
  border-radius: 4px;
  font-size: 11px; font-weight: 600;
  letter-spacing: 0.16em; text-transform: uppercase;
  box-shadow: 0 6px 16px rgba(32, 29, 25, 0.3);
  transition: background-color 0.3s, color 0.3s;
}
.trip-card a:hover .card-cta { background: var(--brown); color: var(--cream); }
/* Same idea on the linked destination tiles and the category cards: a squared
   arrow chip saying the card goes somewhere. Only tiles that actually link
   carry one. Drawn chevron, not a text glyph, matching the carousel arrows. */
.tile-cta {
  position: absolute; inset-inline-end: 12px; bottom: 12px; z-index: 2;
  width: 36px; height: 36px;
  display: grid; place-items: center;
  background: var(--paper); color: var(--brown);
  border-radius: 4px;
  box-shadow: 0 6px 16px rgba(32, 29, 25, 0.3);
  transition: background-color 0.3s, color 0.3s;
}
.tile-cta svg { width: 17px; height: 17px; }
.dest-tile:hover .tile-cta, .cat-card:hover .tile-cta {
  background: var(--brown); color: var(--cream);
}
.trip-meta { padding: 16px 4px 0; }
.tags { display: flex; gap: 7px; margin-bottom: 8px; }
/* Squared too, same instruction. This reverses the earlier "chips and tags stay
   rounded because they are labels, not buttons" rule.
   Title case since 11 Aug (Abdulla: every word in a chip starts capitalised —
   "Old Town", not "Old town"), the one exception to the sentence-case
   mini-header family. The transform keeps the copy itself sentence case. */
.tags span {
  font-size: 11px; letter-spacing: 0.04em; text-transform: capitalize;
  border: 1px solid var(--line-light); border-radius: 0; padding: 4px 11px; opacity: 0.85;
}
.trip-meta h3 { font-size: 22px; }
/* Where it goes and roughly how long. The price moved off the card entirely:
   it belongs on the experience's own page, behind the click. */
.trip-where { font-size: 13.5px; opacity: 0.85; margin-top: 5px; letter-spacing: 0.02em; }

/* ---------- Overlaid card text (index pages) ---------- */

/* 29 July, Abdulla: on destinations.html and experiences.html the text lives
   on the photograph, not under it. At rest only the name shows, over the
   figure's own scrim. Hover or focus turns the panel to blurred glass and
   unfolds the rest (tags, where-line, blurb, "Guide coming"). The rails on
   index.html and yanbu.html keep their under-photo text and never carry a
   .tile-info, so none of this reaches them. */
.tile-info {
  position: absolute; left: 0; right: 0; bottom: 0; z-index: 2;
  padding: 14px 18px 16px;
  color: #fff;
  background: rgba(32, 29, 25, 0);
  -webkit-backdrop-filter: blur(0);
  backdrop-filter: blur(0);
  transition: background-color 0.45s var(--ease),
              -webkit-backdrop-filter 0.45s var(--ease),
              backdrop-filter 0.45s var(--ease);
}
.tile-info h2, .tile-info h3 {
  font-size: clamp(20px, 1.9vw, 26px);
  line-height: 1.2;
  text-shadow: 0 1px 16px rgba(20, 18, 15, 0.5);
}
/* 0fr -> 1fr is what animates the unfold; the inner div owns the overflow. */
.tile-more {
  display: grid; grid-template-rows: 0fr;
  transition: grid-template-rows 0.5s var(--ease);
}
.tile-more > div {
  overflow: hidden; min-height: 0;
  opacity: 0; transform: translateY(8px);
  transition: opacity 0.4s ease 0.06s, transform 0.55s var(--ease) 0.06s;
}
.tile-info p { font-size: 13.5px; line-height: 1.55; color: rgba(255, 255, 255, 0.88); }
.tile-info .tags { margin: 10px 0 0; }
.tile-info .trip-where { margin-top: 9px; opacity: 0.9; }
.tile-info .trip-blurb { opacity: 0.85; }
.tile-info .dest-soon { margin-top: 9px; }
/* Each destination card carries a complete one-sentence description (2 Aug,
   second pass — Abdulla killed the "…" teaser: fully readable, no clamp).
   The long-form versions live on destinations.html; the rail carries the
   opening sentence whole. */
.dest-tile > .tile-blurb {
  font-size: 14px; line-height: 1.6; margin-top: 8px; opacity: 0.82;
}

/* 0.34 since 2 Aug (Abdulla: the glass read too dark; at least 20% clearer). */
.dest-tile:hover .tile-info, .dest-tile:focus-visible .tile-info,
.trip-card a:hover .tile-info, .trip-card a:focus-visible .tile-info {
  background: rgba(32, 29, 25, 0.34);
  -webkit-backdrop-filter: blur(12px);
  backdrop-filter: blur(12px);
}
.dest-tile:hover .tile-more, .dest-tile:focus-visible .tile-more,
.trip-card a:hover .tile-more, .trip-card a:focus-visible .tile-more {
  grid-template-rows: 1fr;
}
.dest-tile:hover .tile-more > div, .dest-tile:focus-visible .tile-more > div,
.trip-card a:hover .tile-more > div, .trip-card a:focus-visible .tile-more > div {
  opacity: 1; transform: none;
}
/* No hover on touch: the panel stays open, tinted but unblurred, so nothing
   is unreachable and the GPU is spared the backdrop filter. */
@media (hover: none) {
  .tile-info { background: rgba(32, 29, 25, 0.3); }
  .tile-more { grid-template-rows: 1fr; }
  .tile-more > div { opacity: 1; transform: none; }
}

/* The How-it-works section was deleted outright on 29 July on the client's
   instruction; its four emblems moved into the Welcome column as .way-icons.
   Its rules and list registrations went with it. */

/* ---------- Journal (parked) ---------- */

/* The journal rail and journal.html were hidden from the site on 28 July on
   the client's instruction: no stories at launch, back when they say so. The
   page is parked in archive/journal.html and these rules, plus the .journal
   entries in the texture, position, ::before and --edge lists above, are kept
   so the rollback is a move and a relink. */
.journal { background-color: var(--sand); padding: clamp(80px, 10vw, 140px) 0; }

/* Landing-page journal rail. Same family as the trips carousel, but wider
   landscape cards with a rule under the image instead of tall poster cards. */
.story-rail { padding-top: 4px; }
.story-card { flex: 0 0 clamp(260px, 27vw, 340px); scroll-snap-align: start; }
.story-card a { display: block; }
.story-card figure { aspect-ratio: 5 / 4; border-radius: 14px; overflow: hidden; }
.story-card img { transition: transform 1s var(--ease); }
.story-card:hover img { transform: scale(1.05); }
.story-meta { border-top: 1px solid var(--line); margin-top: 16px; padding-top: 14px; }
.story-meta h3 { font-size: 22px; transition: color 0.3s; }
.story-card:hover .story-meta h3 { color: var(--brown); }
.story-desc { font-size: 14px; opacity: 0.72; margin-top: 6px; line-height: 1.55; }
.story-go {
  display: inline-block; margin-top: 12px;
  font-size: 11.5px; font-weight: 600; letter-spacing: 0.16em; text-transform: uppercase;
  color: var(--brown);
}
.story-go span { display: inline-block; transition: transform 0.3s var(--ease); }
.story-card:hover .story-go span { transform: translateX(5px); }

/* Journal page: the full set of regions */
.journal-page { background-color: var(--paper); padding: clamp(70px, 9vw, 120px) 0; }
.region-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(240px, 1fr)); gap: clamp(26px, 3vw, 40px) 22px; }
.region-grid .journal-card figure { margin-bottom: 14px; }
.region-grid .journal-card h3 { font-size: 20px; }
.region-desc { font-size: 13.5px; opacity: 0.7; margin-top: 5px; line-height: 1.5; }
.journal-card figure { aspect-ratio: 16 / 11; border-radius: 16px; overflow: hidden; margin-bottom: 18px; }
.journal-card img { transition: transform 1s var(--ease); }
.journal-card:hover img { transform: scale(1.05); }
.journal-card .label { margin-bottom: 8px; }
.journal-card h3 { font-size: 21px; line-height: 1.25; transition: color 0.3s; }
.journal-card:hover h3 { color: var(--brown); }
.journal-card .meta { font-size: 13px; opacity: 0.6; margin-top: 7px; }

/* ---------- Reviews ---------- */

/* The page's closing section since 1 Aug; a centred quote carousel since
   3 Aug (Abdulla's reference): one review at a time in charcoal straight on
   the cream — the contrast fix; the brown cards buried their own quotes —
   advancing on its own every few seconds. Star gold sits deliberately off
   the brand palette, like the season ribbon: stars have to read as stars.
   The .reviews-grid / .review-card / .rc-* rules below are parked for
   rollback; the card markup is in git history (1 Aug version). */
.guest-reviews { background-color: var(--cream); padding: var(--sec-pad) 0; }
.reviews-grid {
  display: grid; grid-template-columns: 1fr 1fr;
  gap: clamp(20px, 2.6vw, 34px);
  margin-top: clamp(30px, 3.6vw, 48px);
}
/* Brown, not paper, since 31 July (Abdulla), wearing the logo-strand
   hairlines like the section fields so the cards carry the site's own curved
   lines rather than reading as generic white panels. The sand hairline and
   the shadow are what separate them from the scrimmed photograph behind. */
.review-card {
  display: flex; flex-direction: column;
  background-color: var(--brown); color: var(--cream);
  background-image: url("../assets/brand/pattern-logo-dark.svg");
  background-size: 960px 960px;
  border: 1px solid rgba(241, 218, 178, 0.32);
  border-radius: var(--r-card);
  padding: clamp(18px, 1.8vw, 24px);
  box-shadow: 0 18px 40px rgba(32, 29, 25, 0.28);
  transition: transform 0.45s var(--ease), box-shadow 0.45s var(--ease);
}
/* A slight lift on hover (31 July, Abdulla). Reduced motion kills the
   transition globally, so the lift is instant-but-harmless there. */
.review-card:hover { transform: translateY(-4px); box-shadow: 0 24px 48px rgba(32, 29, 25, 0.36); }
/* Identity leads (31 July, Abdulla's reference): avatar and name up top with
   the stars under the name, quote in the middle, the Google attribution at
   the foot. */
.rc-head { display: flex; align-items: center; gap: 11px; }
.rc-who { display: flex; flex-direction: column; gap: 3px; min-width: 0; }
.rc-name { font-family: var(--display); font-size: 16.5px; font-weight: 600; line-height: 1.15; }
.rc-stars { color: var(--star); font-size: 12px; letter-spacing: 0.16em; line-height: 1; }
.review-card blockquote { margin: 12px 0 0; }
.review-card blockquote p { font-family: var(--display); font-size: clamp(15px, 1.15vw, 17px); line-height: 1.55; }
/* The real Google profile image, bare: no chip behind it (Abdulla, 31 July —
   Google's own avatars arrive circular with transparent corners, and the sand
   backing showed through them). The initial sits beneath as the fallback; the
   :has rule restores the sand chip only when onerror has removed the img, so
   a dead link degrades to the lettered chip rather than a floating letter. */
.rc-avatar {
  position: relative;
  width: 38px; height: 38px; flex: none;
  display: grid; place-items: center;
  color: transparent;
  font-family: var(--display); font-size: 18px;
}
.rc-avatar:not(:has(img)) { background: var(--sand); color: var(--brown); border-radius: 4px; }
.rc-avatar img { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; }
/* The card footers (Google review · year, G mark) were cut on 31 July,
   Abdulla: no need for them. The #icon-google symbol stays inlined in
   index.html, unreferenced, like the crest: real work, may return. */

/* The carousel itself (3 Aug). Base styles are the no-JS fallback: the
   slides stack as plain centred blocks. main.js adds .rv-live, which turns
   on the one-at-a-time fade, the arrows and the dots. */
.reviews-carousel {
  position: relative;
  margin-top: clamp(10px, 1.6vw, 20px);
  padding: 0 clamp(56px, 7vw, 80px);
  text-align: center;
}
.rv-track { display: grid; }
.rv-slide { display: flex; flex-direction: column; align-items: center; }
.rv-slide + .rv-slide { margin-top: clamp(40px, 5vw, 64px); }
.rv-live .rv-slide + .rv-slide { margin-top: 0; }
/* All slides share one grid cell; the fade is the same class-driven move as
   the destinations stage. visibility keeps hidden slides' links out of the
   tab order. */
.rv-live .rv-slide {
  grid-area: 1 / 1;
  opacity: 0; visibility: hidden;
  transition: opacity 0.6s var(--ease), visibility 0s 0.6s;
}
.rv-live .rv-slide.is-on {
  opacity: 1; visibility: visible;
  transition: opacity 0.6s var(--ease), visibility 0s;
}
/* The pull-line: one sentence lifted whole from the review, display size.
   Well under the section h2 scale — it is a quote, not a heading. */
.rv-quote {
  font-family: var(--display); font-weight: 600;
  font-size: clamp(28px, 3.2vw, 46px);
  line-height: 1.2;
  max-width: 24ch;
  text-wrap: balance;
}
.rv-body { margin: clamp(18px, 2.2vw, 28px) auto 0; max-width: 62ch; }
.rv-body p { font-size: clamp(15px, 1.15vw, 16.5px); line-height: 1.65; opacity: 0.88; }
/* padding-left mirrors the letter-spacing so the trailing track on the last
   star does not pull the row off centre. */
.rv-stars {
  margin-top: clamp(22px, 2.6vw, 32px);
  color: var(--star);
  font-size: 19px; letter-spacing: 0.22em; padding-left: 0.22em;
  line-height: 1;
}
.rv-name { margin-top: clamp(14px, 1.8vw, 22px); font-size: 13px; font-weight: 600; letter-spacing: 0.04em; }
/* Provenance is the Google "G" itself since 4 Aug (Abdulla: no words), a
   plain icon link back to the listing. */
.rv-source { margin-top: 10px; }
.rv-source a { display: inline-block; transition: transform 0.25s var(--ease), opacity 0.2s; opacity: 0.9; }
.rv-source a:hover { transform: translateY(-2px); opacity: 1; }
.rv-google { width: 20px; height: 20px; display: block; }
/* The site's carousel arrows, the one allowed circle, always shown here:
   there is no scroll edge to hide them against. Hidden without JS (nothing
   to drive) and on phones, where the dots carry the switching. */
.rv-btn {
  position: absolute; top: 34%;
  display: none;
  width: 54px; height: 54px; border-radius: 50%;
  background: var(--brown); color: var(--cream);
  place-items: center;
  box-shadow: 0 2px 10px rgba(32, 29, 25, 0.25);
  transform: translateY(-50%);
  transition: background 0.3s, transform 0.3s var(--ease);
}
.rv-live .rv-btn { display: grid; }
.rv-btn:hover { background: #8a6544; transform: translateY(-50%) scale(1.07); }
/* Pulled toward the quote column since 4 Aug (Abdulla: closer to the
   centre): anchored off the carousel's midline so they hug the text at
   every width instead of drifting to the wrap edges. */
.rv-prev { left: max(8px, calc(50% - 520px)); }
.rv-next { right: max(8px, calc(50% - 520px)); }
/* Squared dashes, deliberately not circles (the circle stays the arrows'
   own); the bar and its radius are the month ribbon's family. The buttons
   are padded well past the bar for the tap. */
.rv-dots { display: flex; justify-content: center; gap: 7px; margin-top: clamp(26px, 3.2vw, 38px); }
.rv-dots button { display: grid; padding: 8px 5px; }
.rv-dots button::before {
  content: "";
  width: 20px; height: 3px; border-radius: 1.5px;
  background: var(--brown); opacity: 0.25;
  transition: opacity 0.3s;
}
.rv-dots button:hover::before { opacity: 0.5; }
.rv-dots button[aria-current="true"]::before { opacity: 1; }

/* ---------- CTA ---------- */

.cta-band {
  background-color: var(--brown); color: #fff;
  padding: clamp(64px, 8vw, 104px) 0;
  text-align: center;
  scroll-margin-top: 90px;
}
/* clamp(38–68) since the 3 Aug audit (was 44–84): at 84px the band's coda
   out-shouted every section heading on the page. Still a display moment,
   just under the h2s' ceiling rather than over it. */
.cta-band h2 { font-size: clamp(38px, 5vw, 68px); }
.cta-band p { margin: 16px auto 36px; font-size: clamp(15px, 1.3vw, 18px); opacity: 0.92; }
.cta-actions { display: flex; gap: 14px; justify-content: center; flex-wrap: wrap; }

/* ---------- Contact page ---------- */

.page-hero {
  background-color: var(--charcoal); color: var(--cream);
  padding: clamp(150px, 18vw, 210px) 0 clamp(60px, 8vw, 90px);
}
/* 0.95 since 2 Aug (Abdulla: the hero standfirst white sat too dim). */
.page-hero p:not(.label) { max-width: 600px; margin-top: 18px; opacity: 0.95; line-height: 1.65; }

/* Cinematic photo variant, on Experiences and Destinations only. Placeholder
   imagery until JABA's own arrives. The charcoal ground and grain tile stay
   underneath as the failure fallback. */
.page-hero-photo { position: relative; overflow: hidden; }
.page-hero-photo .page-hero-media {
  position: absolute; inset: 0;
  width: 100%; height: 100%;
  object-fit: cover; object-position: 50% 42%;
}
.page-hero-photo .page-hero-scrim {
  position: absolute; inset: 0;
  background: linear-gradient(180deg, rgba(32, 29, 25, 0.74) 0%, rgba(32, 29, 25, 0.44) 55%, rgba(32, 29, 25, 0.66) 100%);
}
/* The photograph ends on a clean straight edge against the charcoal below,
   like the region heroes: Abdulla asked for the hard cut on 31 July, saying
   the 29 July dissolve never sat well against the strand-line ground. That
   boundary has now been a wave (28 July), a fade (29 July) and a hard cut —
   do not reintroduce either predecessor unprompted. */
.page-hero-photo .wrap { position: relative; z-index: 2; }
.contact-section { background-color: var(--cream); padding: clamp(70px, 9vw, 110px) 0; }
.contact-grid { display: grid; grid-template-columns: repeat(3, 1fr); gap: var(--gap-card); }
.contact-card {
  background: var(--paper);
  border: 1px solid var(--line);
  border-radius: 14px;
  padding: clamp(26px, 3vw, 38px);
}
.contact-card svg { width: 26px; height: 26px; color: var(--brown); margin-bottom: 16px; }
.contact-card h3 { margin-bottom: 8px; }
.contact-card p { font-size: 15px; line-height: 1.65; opacity: 0.85; }
.contact-card a { border-bottom: 1px solid rgba(45, 42, 38, 0.3); transition: border-color 0.2s, color 0.2s; }
.contact-card a:hover { color: var(--brown); border-color: currentColor; }
/* Parked 12 Aug (Abdulla): the map went with the Al Khobar office. The
   Riyadh office has no street address yet, so there is nothing to point a
   map at; unpark when the client confirms one. Markup in git history.
.contact-map {
  margin-top: clamp(30px, 4vw, 46px);
  border-radius: 14px; overflow: hidden;
  border: 1px solid var(--line);
  aspect-ratio: 21 / 9;
  background: var(--paper);
}
.contact-map iframe { width: 100%; height: 100%; border: 0; display: block; }
*/

/* The Jaba & Opal storefront in Yanbu, from the client's 6 Aug delivery.
   Same dress the map wore; the caption uses the mini-header style. */
.contact-photo {
  margin-top: clamp(30px, 4vw, 46px);
  border-radius: 14px; overflow: hidden;
  border: 1px solid var(--line);
  background: var(--paper);
}
.contact-photo img { width: 100%; max-height: 520px; object-fit: cover; object-position: 50% 60%; display: block; }
.contact-photo figcaption {
  padding: 12px 18px; font-size: 13px; font-weight: 500;
  letter-spacing: 0.04em; color: var(--charcoal);
}

/* ---------- About page (The Jaba Way) ---------- */

/* about.html, 1 Aug: The Jaba Way left the landing chapters for its own page.
   Brown opener with the four emblems ABOVE the heading (Abdulla: not beside
   it), then the team grid on cream. The .ways and .intro-statement rules are
   shared with the old Welcome column and stay generic. */
.way-hero {
  background-color: var(--brown); color: var(--cream);
  padding: clamp(130px, 15vw, 170px) 0 clamp(60px, 7.5vw, 96px);
}
/* The emblem row is as wide as the headline and fans its four marks edge to
   edge (Abdulla, 11 Aug): .way-head shrink-wraps to the h1, .ways stretches to
   match and space-between spreads the marks from "The" to the end of "Way". */
.way-head { width: fit-content; max-width: 100%; }
.way-hero .ways {
  width: 100%; justify-content: space-between;
  margin-bottom: clamp(16px, 1.8vw, 26px);
}
.way-hero .ways svg { width: clamp(38px, 4.4vw, 62px); height: clamp(38px, 4.4vw, 62px); opacity: 0.85; }
.way-hero h1 { font-size: clamp(44px, 6vw, 84px); margin-bottom: clamp(20px, 2.4vw, 32px); text-wrap: balance; }
.way-hero .intro-statement { max-width: 680px; }

/* The team grid: four across, IDEO-style portrait cards. Placeholder
   monogram blocks in the brand tiles until JABA's photography lands; the
   grounds cycle sand / paper / brown on a 3-step so the alternation runs
   diagonally through the 4-column grid rather than striping. */
.team { background-color: var(--cream); padding: var(--sec-pad) 0 clamp(60px, 7.5vw, 100px); }
.team-grid {
  margin-top: clamp(36px, 4.5vw, 58px);
  display: grid; grid-template-columns: repeat(4, 1fr);
  gap: clamp(40px, 5vw, 64px) var(--gap-card);
}
.person-photo {
  aspect-ratio: 1 / 1.06;
  margin: 0 0 16px;
  border-radius: var(--r-card);
  border: 1px solid rgba(112, 81, 55, 0.3);
  display: flex; align-items: center; justify-content: center;
  background-color: var(--sand);
  background-image: url("../assets/brand/pattern-topo.svg");
  background-size: 1920px 2880px;
}
.person-photo span {
  font-family: var(--display);
  font-size: clamp(38px, 3.4vw, 52px); font-weight: 600;
  color: var(--brown); opacity: 0.7;
}
.person:nth-child(3n+2) .person-photo { background-color: var(--paper); }
.person:nth-child(3n) .person-photo {
  background-color: var(--brown);
  background-image: url("../assets/brand/pattern-topo-dark.svg");
  border-color: rgba(241, 218, 178, 0.32);
}
.person:nth-child(3n) .person-photo span { color: var(--sand); opacity: 0.85; }
/* Vary the tile crop per ground so neighbouring placeholders do not repeat
   the same strand. */
.person:nth-child(3n+2) .person-photo { background-position: -320px -180px; }
.person:nth-child(3n) .person-photo { background-position: -640px -420px; }
.person h3 { font-size: 20px; margin-bottom: 3px; }
.person-role { font-size: 13.5px; color: var(--brown); margin-bottom: 10px; }
.person-bio { font-size: 15px; line-height: 1.65; opacity: 0.8; }

/* ---------- About: story, mission, backing (11 Aug) ---------- */

/* The team roster left the page 11 Aug (no real names or photography coming
   for v1); these three sections carry the Company Profile 3.0 content in
   its place: who Jaba is, the vision and mission, and the group behind it.
   Colour run brown way-hero, cream story, charcoal mission, sand backing,
   charcoal footer. */
.about-story { background-color: var(--cream); padding: var(--sec-pad) 0; }
.ab-grid {
  display: grid; grid-template-columns: repeat(4, 1fr);
  gap: var(--gap-card);
  margin-top: clamp(28px, 3.2vw, 44px);
}
.ab-card {
  background: var(--paper);
  border: 1px solid rgba(112, 81, 55, 0.18);
  border-radius: var(--r-card);
  padding: clamp(18px, 2.2vw, 28px);
}
.ab-card svg { width: 30px; height: 30px; color: var(--brown); }
.ab-card h3 { font-size: 20px; margin: 14px 0 8px; }
.ab-card p { font-size: 14.5px; line-height: 1.6; color: rgba(45, 42, 38, 0.8); }

/* About-page mini-headers take the destinations page's display-face treatment
   (El Messiri, a touch larger than the body .label). "Our vision" and "Our
   mission" go further and read as full headlines (Abdulla, 11 Aug), so that
   section carries a heading it otherwise lacked. */
.about-story .section-head .label,
.about-backing .section-head .label {
  font-family: var(--display);
  font-size: 17px; font-weight: 600; letter-spacing: 0.02em;
}

.about-mission {
  background-color: var(--charcoal); color: var(--cream);
  padding: var(--sec-pad) 0;
}
.about-mission .label { color: #fff; opacity: 0.85; }
.about-mission .mv .label {
  font-family: var(--display);
  font-size: clamp(30px, 3.6vw, 46px); font-weight: 600;
  letter-spacing: 0; opacity: 1;
  margin-bottom: clamp(12px, 1.4vw, 18px);
}
.mission-grid {
  display: grid; grid-template-columns: 1fr 1fr;
  gap: clamp(28px, 4vw, 64px);
}
/* The Company Profile's own treatment: a sand hairline on the left of each
   statement, the statement in display size. */
.mv {
  border-inline-start: 1px solid rgba(241, 218, 178, 0.45);
  padding-inline-start: clamp(18px, 2.2vw, 30px);
}
.mv-text {
  font-family: var(--display);
  font-size: clamp(21px, 2.3vw, 30px); line-height: 1.45; font-weight: 500;
}

.about-backing { background-color: var(--sand); padding: var(--sec-pad) 0; }
.backing-grid {
  display: grid; grid-template-columns: repeat(3, 1fr);
  gap: var(--gap-card);
  margin-top: clamp(28px, 3.2vw, 44px);
}
.backing-card {
  background: var(--paper);
  border: 1px solid rgba(112, 81, 55, 0.22);
  border-radius: var(--r-card);
  padding: clamp(18px, 2.2vw, 28px);
}
.backing-card svg { width: 28px; height: 28px; color: var(--brown); }
.backing-card h3 { font-size: 20px; margin: 12px 0 8px; }
.backing-card p { font-size: 14.5px; line-height: 1.6; color: rgba(45, 42, 38, 0.8); }

/* ---------- Enquiry page ---------- */

/* Cream ground so the form can be a paper card, which is the treatment Abdulla
   settled on for the old finder: a hairline on a transparent box disappeared,
   a lifted card reads like every other block on the site. */
.enquiry { background-color: var(--cream); padding: clamp(70px, 9vw, 110px) 0; }
.enquiry-grid {
  display: grid;
  grid-template-columns: minmax(0, 1.75fr) minmax(0, 1fr);
  gap: clamp(28px, 3.4vw, 48px);
  align-items: start;
}
.enquiry-form {
  background: var(--paper);
  border: 1.5px solid rgba(112, 81, 55, 0.3);
  border-radius: 16px;
  padding: clamp(26px, 3.2vw, 46px);
  box-shadow: 0 14px 34px rgba(45, 42, 38, 0.09);
}
.form-block { border: 0; padding: 0; margin: 0; }
.form-block + .form-block { margin-top: clamp(34px, 4vw, 52px); }
.form-block legend {
  padding: 0;
  margin-bottom: clamp(18px, 2vw, 26px);
  padding-bottom: 12px;
  border-bottom: 1px solid var(--line);
  width: 100%;
}
.form-row { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: clamp(14px, 1.6vw, 22px); }
.form-row + .form-row { margin-top: clamp(14px, 1.6vw, 22px); }
.form-row-3 { grid-template-columns: repeat(3, minmax(0, 1fr)); }
.form-full { grid-column: 1 / -1; }
.form-field { display: flex; flex-direction: column; min-width: 0; }
.form-label {
  font-size: 13px; font-weight: 600; letter-spacing: 0.04em;
  opacity: 0.65; margin-bottom: 8px;
}
.req { color: var(--brown); opacity: 0.9; }
.form-input {
  width: 100%;
  font: inherit; font-size: 15px; color: inherit;
  background: var(--cream);
  border: 1px solid rgba(112, 81, 55, 0.26);
  border-radius: 4px;
  padding: 12px 14px;
  transition: border-color 0.2s, background 0.2s;
}
.form-input::placeholder { color: inherit; opacity: 0.42; }
.form-input:focus { outline: 0; border-color: var(--brown); background: #fff; }
textarea.form-input { resize: vertical; min-height: 96px; line-height: 1.6; }

/* The finder's dropdown dressed as a form input. .field was built to sit inside
   the finder bar, where the rows were divided by rules and had no box of their
   own, so the box and the padding both have to be put back here.

   The doubled class is load-bearing. Each of these is the only .field in its
   .form-field, so it also matches .field:last-of-type { border-bottom: 0 },
   which outranks a single class and rubbed the bottom edge off every dropdown
   while the other three sides stayed. */
.field.field-form {
  padding: 0;
  background: var(--cream);
  border: 1px solid rgba(112, 81, 55, 0.26);
  border-radius: 4px;
  transition: border-color 0.2s;
}
.field.field-form:hover, .field.field-form.open { border-color: var(--brown); }
.field-form .field-value {
  width: 100%;
  /* Right padding clears the caret, which is parked in the corner rather than
     trailing the text so it sits in the same place in every field. */
  padding: 12px 42px 12px 14px;
  font-family: var(--body); font-size: 15px;
  white-space: normal;
}
.field-form .field-value.is-empty { opacity: 0.42; }
.field-form .field-value::after {
  content: "";
  position: absolute; inset-inline-end: 15px; top: 50%;
  width: 9px; height: 9px; margin-top: -6px;
  border: solid currentColor; border-width: 0 1.8px 1.8px 0;
  transform: rotate(45deg);
  opacity: 0.55;
  transition: transform 0.25s var(--ease), opacity 0.2s;
}
.field-form:hover .field-value::after { opacity: 0.9; }
.field-form.open .field-value::after { transform: rotate(225deg); margin-top: -2px; opacity: 0.9; }
.field-form .menu { top: calc(100% + 8px); inset-inline-end: 0; max-height: 290px; overflow-y: auto; }
/* Multi-select. The tick is drawn rather than typed so it lines up at any size. */
.field-form[data-multi] .menu li { padding-inline-start: 38px; position: relative; }
.field-form[data-multi] .menu li::before {
  content: "";
  position: absolute; inset-inline-start: 13px; top: 50%; margin-top: -6.5px;
  width: 13px; height: 13px;
  border: 1px solid rgba(112, 81, 55, 0.5); border-radius: 3px;
}
.field-form[data-multi] .menu li[aria-selected="true"] { background: var(--cream); font-weight: 600; }
.field-form[data-multi] .menu li[aria-selected="true"]::before {
  background: var(--brown); border-color: var(--brown);
}
.field-form[data-multi] .menu li[aria-selected="true"]::after {
  content: "";
  position: absolute; inset-inline-start: 17px; top: 50%; margin-top: -3.5px;
  width: 5px; height: 8px;
  border: solid var(--paper); border-width: 0 1.6px 1.6px 0;
  transform: rotate(43deg);
}

.form-foot {
  display: flex; align-items: center; flex-wrap: wrap;
  gap: clamp(14px, 2vw, 24px);
  margin-top: clamp(28px, 3.4vw, 42px);
}
.form-note { font-size: 13px; line-height: 1.6; opacity: 0.6; flex: 1 1 240px; }
.form-input.is-bad, .field-form.is-bad { border-color: #a4442f; background: rgba(164, 68, 47, 0.05); }
.form-status {
  margin-top: 20px; padding: 14px 18px;
  border-radius: 4px; font-size: 14.5px; line-height: 1.6;
  background: rgba(112, 81, 55, 0.09);
  border-inline-start: 3px solid var(--brown);
}
.form-status.is-bad { background: rgba(164, 68, 47, 0.08); border-inline-start-color: #a4442f; }

.enquiry-aside { display: flex; flex-direction: column; gap: clamp(18px, 2.2vw, 26px); }
.enquiry-card {
  background: var(--paper);
  border: 1px solid var(--line);
  border-radius: 14px;
  padding: clamp(24px, 2.8vw, 34px);
}
.enquiry-card h2 { font-size: clamp(21px, 1.9vw, 25px); margin-bottom: 12px; }
.enquiry-card > p { font-size: 15px; line-height: 1.65; opacity: 0.8; }
.enquiry-contact { margin-top: 20px; display: flex; flex-direction: column; gap: 15px; }
.enquiry-contact li { display: flex; align-items: flex-start; gap: 13px; font-size: 15px; line-height: 1.6; }
.enquiry-contact svg { width: 19px; height: 19px; color: var(--brown); flex: none; margin-top: 2px; }
.enquiry-contact a { border-bottom: 1px solid rgba(45, 42, 38, 0.3); transition: border-color 0.2s, color 0.2s; }
.enquiry-contact a:hover { color: var(--brown); border-color: currentColor; }
/* Day on the left, hours on the right, hairline between rows. Tabular figures
   so the times stack in a straight column. */
.enquiry-hours { margin-top: 18px; }
.enquiry-hours > div {
  display: flex; align-items: baseline; justify-content: space-between; gap: 18px;
  padding: 9px 0;
  font-size: 15px;
}
.enquiry-hours > div + div { border-top: 1px solid var(--line); }
.enquiry-hours dt { opacity: 0.8; }
.enquiry-hours dd { font-variant-numeric: tabular-nums; white-space: nowrap; opacity: 0.7; }

/* ---------- Footer ---------- */

.site-footer { background-color: var(--charcoal); color: var(--cream); padding: clamp(60px, 8vw, 90px) 0 0; }
.footer-grid {
  display: grid;
  grid-template-columns: 1.6fr repeat(4, 1fr);
  gap: clamp(28px, 4vw, 60px);
  padding-bottom: clamp(50px, 6vw, 70px);
}
/* Gold stacked lockup, the brand book's dark-ground treatment: gradient mark,
   cream logotype and payoff. Only the footer is charcoal at full width, so the
   gold version lives here; the header keeps the one-colour horizontal lockup. */
/* Horizontal lockup since 28 July (evening), Abdulla prefers the type beside
   the mark. The wide artwork wants less height than the old stacked one. */
.brand-footer .brand-logo-gold { height: 108px; width: auto; display: block; }
.footer-brand p { margin-top: 18px; opacity: 0.75; font-size: 14.5px; line-height: 1.65; }
.site-footer h4 {
  font-size: 13px; letter-spacing: 0.04em;
  color: #fff; margin-bottom: 16px; font-weight: 600;
}
.site-footer nav { display: flex; flex-direction: column; gap: 10px; }
.site-footer nav a { font-size: 14.5px; opacity: 0.8; transition: opacity 0.2s, transform 0.3s var(--ease); }
.site-footer nav a:hover { opacity: 1; transform: translateX(3px); }
/* Social marks sit in a row inside the Follow column, so they lift rather than
   slide on hover. Needs .site-footer in the selector to outrank the nav rule. */
.social { display: flex; align-items: center; gap: 16px; }
.site-footer .social a { display: inline-flex; padding: 4px; }
.site-footer .social a:hover { transform: translateY(-2px); }
.social svg {
  width: 21px; height: 21px; display: block;
  fill: none; stroke: currentColor; stroke-width: 1.7;
  stroke-linecap: round; stroke-linejoin: round;
}
.footer-legal {
  display: flex; justify-content: space-between; gap: 16px; flex-wrap: wrap;
  border-top: 1px dashed rgba(241, 218, 178, 0.25);
  padding-top: 22px; padding-bottom: 26px;
  font-size: 13px; opacity: 0.6;
}

/* ---------- Reveal animations ---------- */

.reveal { opacity: 0; transform: translateY(26px); transition: opacity 0.9s var(--ease), transform 0.9s var(--ease); }
.reveal.in { opacity: 1; transform: none; }
.reveal.d1 { transition-delay: 0.12s; }
.reveal.d2 { transition-delay: 0.24s; }
.reveal.d3 { transition-delay: 0.36s; }

/* ---------- Destinations index ---------- */

/* A mosaic since 29 July, two tall pairs before: Abdulla wanted more of the
   page showing the moment it loads, and the grid livelier than equal squares.
   Three across on uniform rows, with two feature tiles (Yanbu, Hail) spanning
   a double cell. dense lets the singles backfill around the features when the
   grid drops to two columns. Names ride on the photographs via .tile-info;
   the hover unfold is with the trip-card rules. */
/* Charcoal since 28 July (evening): Abdulla wants both index pages on the
   footer's dark ground. Dark-ground overrides for the shared .dest-tile are
   scoped here so the landing rail's sand-ground tiles stay untouched. */
/* Brown since 31 July (Abdulla: the charcoal read black-grey against the warm
   hero photograph; the brown suits it). A side-effect worth keeping: the
   footer boundary's wave is visible again instead of charcoal-on-charcoal. */
.dest-index { background-color: var(--brown); color: var(--cream); padding: clamp(44px, 5.5vw, 72px) 0 var(--sec-pad); }
.dest-tiles {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  grid-auto-rows: clamp(240px, 26vw, 380px);
  grid-auto-flow: dense;
  gap: clamp(14px, 1.7vw, 22px);
}
.dest-tile--wide { grid-column: span 2; }
/* The rail's square ratio gives way to the grid's row height here. */
.dest-index .dest-tile figure { height: 100%; aspect-ratio: auto; }
/* Lightly rounded since 29 July, same reversal as the trip cards. The
   hairline is 28 July, client note. Positioned for the corner chips. */
.dest-tile figure {
  aspect-ratio: 1 / 1; overflow: hidden; background: var(--cream);
  border: 1px solid rgba(112, 81, 55, 0.3);
  border-radius: var(--r-card);
  position: relative;
}
.dest-tile img { transition: transform 1s var(--ease); }
.dest-tile:hover img { transform: scale(1.06); }
.dest-tile > p { font-size: 13px; opacity: 0.62; margin-top: 3px; }
.dest-soon {
  display: inline-block; margin-top: 8px;
  font-size: 11px; font-weight: 600; letter-spacing: 0.04em;
  color: var(--charcoal); opacity: 0.75;
}
/* The standing scrim that keeps the overlaid name legible before hover. */
.dest-index .dest-tile figure::after, .exp-index .trip-card figure::after {
  content: ""; position: absolute; inset: 0; z-index: 1;
  background: linear-gradient(180deg, rgba(32, 29, 25, 0) 52%, rgba(32, 29, 25, 0.58) 100%);
  pointer-events: none;
}
.dest-tile--wide .tile-info h2 { font-size: clamp(24px, 2.4vw, 34px); }
/* The chips ride the top corner on the index pages so the unfolding text
   never runs under them; the rails keep them at the bottom. */
.dest-index .tile-cta { top: 14px; bottom: auto; z-index: 3; }
.exp-index .card-cta { inset-inline-start: auto; inset-inline-end: 14px; top: 14px; bottom: auto; z-index: 3; }
/* The charcoal backing matters as much as the border colour: the figure's
   base cream background bleeds through the antialiased edge inside the 1px
   hairline on a dark ground, which read as a brighter, thicker frame than the
   landing rails carry (Abdulla, 31 July). Backed with the section colour, the
   hairline renders at the same weight as the landing page's. */
.dest-index .dest-tile figure { border-color: rgba(241, 218, 178, 0.32); background-color: var(--brown); }
.dest-index .dest-soon { color: #fff; }

/* ---------- Experiences index ---------- */

/* Reuses .trip-card from the home rail. Its flex basis is ignored on a grid
   item, so the cards just fill their cells. */
/* Charcoal since 28 July (evening), matching the destinations index: Abdulla
   wants both index pages on the footer's dark ground. The trip cards' base
   sand hairline is right here, so no border override is needed any more. */
/* Cream since 2 Aug (Abdulla: the charcoal ground was not vibing with the
   cards), reversing the 28 July "both index pages on the footer's dark
   ground" call for the experiences side only; .dest-index stays brown. */
.exp-index { background-color: var(--cream); color: var(--charcoal); padding: clamp(44px, 5.5vw, 72px) 0 var(--sec-pad); scroll-margin-top: 90px; }
/* Region pages: the Tours grid follows the experiences grid inside the same
   section since 11 Aug (Abdulla) — two categories, one card language. The
   head takes a clear gap and its own anchor. */
.exp-tours-head { margin-top: clamp(48px, 6vw, 84px); scroll-margin-top: 90px; }
/* experiences.html only (4 Aug, Abdulla): the category grid and the CTA
   band below it swap grounds — the five cards sit on brown with the dark
   tile, the band goes cream with the light one. Doubled classes outrank
   the base rules and both texture groups; the category pages and
   tours.html keep the plain cream .exp-index. */
.exp-index.exp-index-dark {
  background-color: var(--brown); color: var(--cream);
  background-image: url("../assets/brand/pattern-topo-dark.svg");
}
.exp-index-dark .cat-card {
  border-color: rgba(241, 218, 178, 0.32);
  background-color: var(--brown);
}
.cta-band.cta-band-light {
  background-color: var(--cream); color: var(--charcoal);
  background-image: url("../assets/brand/pattern-topo.svg");
}
.exp-catalogue-tools { position: relative; margin-bottom: clamp(24px, 3.4vw, 42px); z-index: 8; }
.exp-toolbar {
  position: relative;
  display: flex; align-items: center; justify-content: space-between; gap: 18px;
  padding-bottom: 16px;
  border-bottom: 1px solid rgba(112, 81, 55, 0.28);
}
.exp-result-count {
  font-family: var(--display); font-size: 20px; font-weight: 600;
  line-height: 1.2;
}
.exp-toolbar-actions { display: flex; align-items: center; gap: 10px; }
.exp-tool-btn, .exp-sort {
  position: relative; min-height: 44px;
  border: 1px solid rgba(112, 81, 55, 0.38); border-radius: 4px;
  background: var(--paper);
  /* The paper fill is light on every ground, so the label must stay charcoal
     rather than inherit the section's colour — on .dest-index (brown) the
     inherited cream left the text near-invisible. */
  color: var(--charcoal);
  transition: border-color 0.2s, background 0.2s;
}
.exp-tool-btn {
  display: inline-flex; align-items: center; gap: 9px;
  padding: 10px 40px 10px 15px;
  font-size: 14px; font-weight: 600;
}
.exp-tool-btn:hover, .exp-tool-btn[aria-expanded="true"], .exp-sort:hover {
  border-color: var(--brown); background: #fff;
}
.exp-filter-badge {
  min-width: 20px; padding: 1px 6px;
  background: var(--brown); color: var(--paper);
  border-radius: 3px; text-align: center;
  font-size: 11px; line-height: 18px;
}
.exp-tool-caret {
  position: absolute; inset-inline-end: 16px; top: 50%;
  width: 8px; height: 8px; margin-top: -6px;
  border: solid currentColor; border-width: 0 1.6px 1.6px 0;
  transform: rotate(45deg);
  opacity: 0.58;
  transition: transform 0.25s var(--ease), opacity 0.2s;
}
.exp-tool-btn[aria-expanded="true"] .exp-tool-caret {
  transform: rotate(225deg); margin-top: -2px; opacity: 0.9;
}
.exp-sort { display: block; }
.exp-sort select {
  min-width: 190px; min-height: 42px;
  appearance: none; border: 0; outline: 0; background: transparent; color: var(--charcoal);
  padding: 9px 40px 9px 14px;
  font: 600 14px/1.4 var(--body); cursor: pointer;
}
.exp-sort:focus-within { border-color: var(--brown); background: #fff; }
.exp-filter-panel {
  position: absolute; top: 58px; inset-inline-end: 0; width: min(900px, calc(100vw - (2 * var(--pad))));
  max-height: min(72vh, 650px); overflow-y: auto;
  z-index: 30;
  padding: clamp(20px, 2.6vw, 32px);
  background: var(--paper); color: var(--charcoal);
  border: 1px solid rgba(112, 81, 55, 0.34); border-radius: 10px;
  box-shadow: 0 24px 60px rgba(32, 29, 25, 0.22);
}
.exp-filter-groups {
  display: grid; grid-template-columns: repeat(auto-fit, minmax(210px, 1fr));
  gap: clamp(24px, 3vw, 38px);
}
.exp-filter-group { min-width: 0; border: 0; }
.exp-filter-group legend {
  width: 100%; margin-bottom: 12px;
  font-family: var(--display); font-size: 18px; font-weight: 600;
  line-height: 1.25;
}
.exp-filter-options { display: grid; gap: 3px; max-height: 250px; overflow-y: auto; }
/* Scroll affordance for the long lists (Destination, Activity type): main.js
   sets data-scroll only while the list overflows, and updates it on scroll, so
   the soft edge fade signals "more below/above" without lying on a short list.
   The mask fades the scroller's visible top/bottom edge, not the content. */
.exp-filter-options[data-scroll] { scrollbar-width: thin; }
.exp-filter-options[data-scroll="bottom"] {
  -webkit-mask-image: linear-gradient(to bottom, #000 calc(100% - 26px), transparent);
          mask-image: linear-gradient(to bottom, #000 calc(100% - 26px), transparent);
}
.exp-filter-options[data-scroll="top"] {
  -webkit-mask-image: linear-gradient(to bottom, transparent, #000 26px);
          mask-image: linear-gradient(to bottom, transparent, #000 26px);
}
.exp-filter-options[data-scroll="both"] {
  -webkit-mask-image: linear-gradient(to bottom, transparent, #000 26px, #000 calc(100% - 26px), transparent);
          mask-image: linear-gradient(to bottom, transparent, #000 26px, #000 calc(100% - 26px), transparent);
}
.exp-filter-option {
  position: relative;
  display: grid; grid-template-columns: 1fr auto; align-items: center; gap: 12px;
  min-height: 37px; padding: 6px 5px;
  font-size: 14px; line-height: 1.45; cursor: pointer;
}
.exp-filter-option:hover { background: var(--cream); }
.exp-filter-option input { position: absolute; opacity: 0; pointer-events: none; }
.exp-filter-option span { position: relative; padding-inline-start: 27px; }
.exp-filter-option span::before {
  content: ""; position: absolute; inset-inline-start: 1px; top: 50%;
  width: 14px; height: 14px; margin-top: -8px;
  border: 1px solid rgba(112, 81, 55, 0.52); border-radius: 3px;
  background: #fff;
}
.exp-filter-option input:checked + span { font-weight: 600; }
.exp-filter-option input:checked + span::before { background: var(--brown); border-color: var(--brown); }
.exp-filter-option input:checked + span::after {
  content: ""; position: absolute; inset-inline-start: 6px; top: 50%;
  width: 4px; height: 7px; margin-top: -5px;
  border: solid var(--paper); border-width: 0 1.5px 1.5px 0;
  transform: rotate(43deg);
}
.exp-filter-option input:focus-visible + span::before { outline: 2px solid var(--charcoal); outline-offset: 2px; }
.exp-filter-option small { font-size: 12px; opacity: 0.52; }
.exp-guest-stepper {
  display: grid; grid-template-columns: 38px minmax(116px, 1fr) 38px;
  align-items: center; max-width: 240px;
  border: 1px solid rgba(112, 81, 55, 0.3); border-radius: 4px;
  background: #fff;
}
.exp-guest-stepper button {
  height: 38px; display: grid; place-items: center;
  font-size: 18px; line-height: 1;
  transition: background 0.15s, opacity 0.15s;
}
.exp-guest-stepper button:first-child { border-inline-end: 1px solid rgba(112, 81, 55, 0.2); }
.exp-guest-stepper button:last-child { border-inline-start: 1px solid rgba(112, 81, 55, 0.2); }
.exp-guest-stepper button:hover:not(:disabled) { background: var(--cream); }
.exp-guest-stepper button:disabled { opacity: 0.28; cursor: default; }
.exp-guest-stepper span { text-align: center; font-size: 14px; font-weight: 600; }
.exp-guest-reset, .exp-clear, .exp-active-clear {
  margin-top: 10px; padding-bottom: 2px;
  border-bottom: 1px solid currentColor;
  font-size: 13px; font-weight: 600;
}
.exp-filter-foot {
  display: flex; align-items: center; justify-content: space-between; gap: 18px;
  margin-top: 28px; padding-top: 18px;
  border-top: 1px solid rgba(112, 81, 55, 0.2);
}
.exp-filter-foot .exp-clear { margin-top: 0; }
.exp-filter-done {
  padding: 11px 17px; border: 1px solid var(--brown); border-radius: 4px;
  background: var(--brown); color: var(--paper);
  font-size: 13px; font-weight: 600;
  transition: background 0.2s, color 0.2s;
}
.exp-filter-done:hover { background: var(--paper); color: var(--brown); }
.exp-active-filters {
  display: flex; align-items: center; flex-wrap: wrap; gap: 8px;
  padding-top: 14px;
}
.exp-filter-chip {
  padding: 6px 10px; border: 1px solid rgba(112, 81, 55, 0.34); border-radius: 4px;
  background: var(--paper); font-size: 12px; font-weight: 600;
}
.exp-filter-chip:hover { border-color: var(--brown); background: #fff; }
.exp-active-clear { margin: 0 0 0 4px; }
.exp-empty {
  margin: clamp(34px, 5vw, 64px) 0; padding: clamp(28px, 4vw, 46px);
  border: 1px solid rgba(112, 81, 55, 0.28); border-radius: var(--r-card);
  background: var(--paper); text-align: center;
}
.exp-empty h2 { font-size: clamp(27px, 2.8vw, 38px); }
.exp-empty p { margin-top: 8px; font-size: 14.5px; opacity: 0.7; }
.exp-grid { display: grid; grid-template-columns: repeat(3, 1fr); gap: var(--gap-card-lg); }
/* Sky / Water / Land as three photographic cards, 28 July: the client wants
   the categories clickable side by side. Overlaid name, scrim for legibility,
   the shared arrow chip; each jumps to its group below. */
.cat-cards {
  display: grid; grid-template-columns: repeat(3, 1fr);
  gap: var(--gap-card);
  margin-bottom: clamp(56px, 7vw, 92px);
}
.cat-card {
  position: relative; display: block;
  aspect-ratio: 4 / 3; overflow: hidden;
  border: 1px solid rgba(241, 218, 178, 0.32);
  border-radius: var(--r-card);
}
/* On the home rail the six category cards (five categories + Tours) ride
   the carousel as flex items, 2 Aug, replacing the invented trip cards. */
.carousel .cat-card { flex: 0 0 clamp(280px, 27vw, 390px); scroll-snap-align: start; }
/* Exactly three per view on wide screens (3 Aug, Abdulla): the width is a
   third of the track minus its two 18px gaps, and main.js pages the arrows
   by a full view — three cards at a time. Phones keep the fixed basis and
   swipe. */
@media (min-width: 1081px) {
  .carousel .cat-card { flex-basis: calc((100% - 36px) / 3); }
}
/* Brown hairlines and backing on the cream experience grounds (the base sand
   hairline still serves the ink rail on yanbu.html). */
.trips-light .cat-card, .exp-index .cat-card, .exp-index .trip-card figure {
  border-color: rgba(112, 81, 55, 0.3);
}
.cat-card img { width: 100%; height: 100%; object-fit: cover; transition: transform 1s var(--ease); }
.cat-card:hover img { transform: scale(1.05); }
.cat-card::after {
  content: ""; position: absolute; inset: 0;
  background: linear-gradient(180deg, rgba(32, 29, 25, 0) 45%, rgba(32, 29, 25, 0.6) 100%);
}
.cat-card-name {
  position: absolute; inset-inline-start: 18px; bottom: 12px; z-index: 2;
  /* The right bound keeps long names ("Adventure & Nature") off the chip. */
  inset-inline-end: 64px;
  color: #fff; font-family: var(--display);
  font-size: clamp(24px, 2.6vw, 36px); font-weight: 600;
  line-height: 1.15;
}

/* Sky / Water / Land group heads on the experiences index, 28 July. */
.cat-head { margin-bottom: clamp(20px, 2.6vw, 32px); scroll-margin-top: 90px; }
.exp-grid + .cat-head { margin-top: clamp(56px, 7vw, 96px); }
.cat-head h2 { font-size: clamp(27px, 2.9vw, 40px); }
.cat-head .section-sub { margin-top: 6px; }
.trip-blurb { font-size: 14px; line-height: 1.55; opacity: 0.75; margin-top: 8px; }

/* ---------- Real experience cards (category pages), 2 Aug ---------- */

/* The client's photography has not arrived, so the card figure wears the
   review cards' dress as a stand-in: brown, the dark strand tile, a quiet
   label. Swapped for a real <img> per card when the shoot lands. */
.ph-img {
  position: absolute; inset: 0;
  display: grid; place-items: center;
  background-color: var(--brown);
  background-image: url("../assets/brand/pattern-topo-dark.svg");
  background-size: 1920px 2880px;
  color: rgba(255, 255, 255, 0.5);
  font-size: 12px; font-weight: 500; letter-spacing: 0.02em;
}
/* The card is the whole product page — the click goes straight to Bokun —
   so price and season join the unfold panel. A card can carry several
   category chips, hence the wrap. Since the 2 Aug icon pass the place rides
   at rest under the name with a pin, and the unfold rows lead with stroke
   icons (clock, tag, sun) drawn to match the social set. */
.tile-info .tags { flex-wrap: wrap; row-gap: 7px; }
.mi {
  width: 14px; height: 14px; flex: 0 0 auto;
  fill: none; stroke: currentColor; stroke-width: 2;
  stroke-linecap: round; stroke-linejoin: round;
  opacity: 0.9;
}
.tile-info .trip-where, .tile-info .trip-fact { display: flex; align-items: center; gap: 8px; }
.tile-info .trip-fact { font-size: 13.5px; margin-top: 8px; letter-spacing: 0.02em; color: rgba(255, 255, 255, 0.92); }
.tile-info .trip-fact.trip-price { color: #fff; font-weight: 600; }
/* Not bookable yet ("Coming soon" / "Resuming soon"): the chip states it
   and declines the brown hover fill. */
.trip-card a:hover .card-cta-soon { background: var(--paper); color: var(--charcoal); }

/* ---------- Destinations index (4 Aug redesign) ---------- */

/* Each region is a compact header over a film-strip carousel since 4 Aug
   (Abdulla: the feature rows read sparse, too few photographs per region and
   every row floating alone). The section keeps its class and every
   registration; only its contents changed. The feature-row page is archived
   at archive/destinations-features.html and its rules are parked below. */

/* The jump index, third form (9 Aug, Abdulla: the boxed emblem cards read as
   squared-off tabs). Bare emblems now — no boxes, no fills: the emblem sits
   over the name, sand, centred in a wrapping row, and an underline grows in
   on hover. Scrolling lights the region in view (.is-here, main.js). The
   4 Aug emblem-card rules were replaced outright, not parked: the emblems
   and hrefs are the same markup either way. */
/* Parked 12 Aug (Abdulla): the "Scroll to explore" heading is gone from
   destinations.html — the section opens straight on the router toolbar,
   matching the category pages. Markup in git history; rollback is a paste.
.dest-nav-hint {
  font-size: clamp(32px, 3.8vw, 52px);
  line-height: 1.08;
  color: #fff;
  margin-bottom: clamp(24px, 2.8vw, 38px);
}
*/
/* Parked 11 Aug, third pass (Abdulla: the emblem row read messy and tacky
   once the router toolbar did its job) — the whole .dest-nav block below,
   its media overrides and main.js's scroll-spy are inert with the markup
   gone; markup in git history, emblems still inlined in the page sprite.
   Rollback is a paste. */
.dest-nav {
  display: flex; flex-wrap: wrap; justify-content: flex-start;
  column-gap: clamp(26px, 3.2vw, 52px); row-gap: 24px;
  margin-bottom: clamp(56px, 6.5vw, 92px);
  padding-top: 6px;
  padding-bottom: clamp(28px, 3.4vw, 44px);
  border-bottom: 1px solid rgba(241, 218, 178, 0.25);
}
.dest-nav a {
  display: flex; flex-direction: column; align-items: center; gap: 10px;
  position: relative;
  padding: 4px 2px;
  font-family: var(--display); font-size: 18px; font-weight: 600; line-height: 1.2;
  transition: color 0.3s;
}
.dest-nav svg { width: 30px; height: 30px; flex: none; opacity: 0.85; transition: opacity 0.3s; }
.dest-nav .em-fill { fill: var(--sand); }
/* The stroke-drawn placeholder emblems (dhow, rose, juniper, palm): 9 units
   on their 164 canvas matches the brand pack's line weight. */
.dest-nav .em-line {
  fill: none; stroke: var(--sand); stroke-width: 9;
  stroke-linecap: round; stroke-linejoin: round;
}
.dest-nav a::after {
  content: ""; position: absolute; left: 50%; bottom: -6px;
  width: 0; height: 1px; background: var(--sand);
  transition: width 0.3s var(--ease), left 0.3s var(--ease);
}
.dest-nav a:hover, .dest-nav a:focus-visible, .dest-nav a.is-here { color: var(--sand); }
.dest-nav a:hover svg, .dest-nav a:focus-visible svg, .dest-nav a.is-here svg { opacity: 1; }
.dest-nav a:hover::after, .dest-nav a:focus-visible::after, .dest-nav a.is-here::after {
  width: 100%; left: 0;
}

/* The strips. Gap and heights sized so part of the next region is in view
   at any scroll position (Abdulla: at least two on screen at a time). */
.dest-strips { display: flex; flex-direction: column; gap: clamp(46px, 5.5vw, 72px); }
.dest-strip { scroll-margin-top: 90px; }
/* Each region opens on a cover since 9 Aug (Abdulla: bigger, more visually
   striking; the two-column ds-head read small). Since 11 Aug, second pass,
   the cover is a SPLIT: the photograph on one side and the text — count,
   name, chips, description, View destination — beside it at readable size,
   filling the space the old swing left empty. The photograph track is
   always the 7fr side; odd strips run it right, even strips left. The
   overlay/glass-panel rules the split replaced are parked below.
   Generated by data/destinations.gen.py. */
.ds-cover-wrap {
  display: grid;
  grid-template-columns: minmax(0, 7fr) minmax(0, 5fr);
  gap: clamp(30px, 4.5vw, 84px);
  align-items: center;
  margin-bottom: clamp(16px, 1.8vw, 24px);
}
/* 11 Aug (Abdulla): the covers alternate sides, so their row runs the full
   viewport width (the page padding stays); inside the 1280px wrap the
   split left the text column too narrow to read. */
.ds-cover-wrap.wrap { max-width: none; }
.ds-cover { grid-column: 1; grid-row: 1; }
.ds-side { grid-column: 2; grid-row: 1; }
.dest-strips > .dest-strip:nth-child(odd) .ds-cover-wrap {
  grid-template-columns: minmax(0, 5fr) minmax(0, 7fr);
}
.dest-strips > .dest-strip:nth-child(odd) .ds-cover { grid-column: 2; }
.dest-strips > .dest-strip:nth-child(odd) .ds-side { grid-column: 1; justify-self: end; }
.ds-cover {
  display: block; position: relative; overflow: hidden;
  border-radius: var(--r-card); border: 1px solid rgba(241, 218, 178, 0.32);
  height: clamp(330px, 38.5vw, 530px);
}
/* A region with one product carries no strip (11 Aug, Abdulla: Jazan) —
   the cover stands alone, so it grows to hold the row on its own. */
.dest-strip.is-solo .ds-cover { height: clamp(380px, 45vw, 620px); }
.ds-cover img {
  width: 100%; height: 100%; object-fit: cover;
  transition: transform 1.2s var(--ease);
}
/* The KAEC cover is a square frame (the client's umbrella-on-the-sandbank
   shot); the default centre crop shows sky, the sea sits low. */
#kaec .ds-cover img { object-position: 50% 74%; }
.ds-cover:hover img, .ds-cover:focus-visible img { transform: scale(1.04); }
/* The text column. The three text pieces run larger than the overlay ever
   did (11 Aug, Abdulla: fill the page, more information-dense): 15px
   eyebrow, 13px chips, 16–18px description. */
.ds-side { max-width: 620px; }
/* The count eyebrow in the display face like the Why and section-head
   eyebrows (11 Aug, Abdulla). */
.ds-side .label {
  font-family: var(--display);
  font-size: 17px; letter-spacing: 0.02em;
  color: #fff; opacity: 0.9; margin-bottom: 12px;
}
/* Line height 1.12, not the heading default: El Messiri's Arabic ascenders
   and descender loops overlap wrapped lines at anything tighter. */
.ds-side h2 { font-size: clamp(38px, 4.2vw, 62px); line-height: 1.12; color: #fff; }
/* The Arabic name to the right of the English one (11 Aug, Abdulla). El
   Messiri is an Arabic face first, so the same family sets both scripts;
   the span isolates its bidi run so the Latin line never reorders.
   Full size since 11 Aug, second pass (Abdulla: the 0.55em Arabic matched
   neither the size nor the line of the English) — same em, same baseline,
   same weight and colour as the Latin beside it. */
.ds-side h2 .ds-ar {
  margin-inline-start: 14px;
  unicode-bidi: isolate;
}
/* "Al Qassim القصيم" is the longest one-word pair: at the full 62px it
   cannot hold one line in the text column, so on wide screens this one
   name sizes down a step and stays inline like the rest. Below 1081px the
   stacked layout has the full width. */
@media (min-width: 1081px) {
  #al-qassim .ds-side h2 { font-size: clamp(36px, 3.4vw, 49px); }
}
/* KAEC's and Eastern Province's Arabic names run to several words; at full
   size they bury the English beside them and wrap mid-name. Those two set
   as their own sub-line under the English instead — the only covers where
   the scripts differ in size. */
#kaec .ds-side h2 .ds-ar,
#eastern-province .ds-side h2 .ds-ar {
  display: block;
  font-size: 0.34em;
  line-height: 1.35;
  margin-inline-start: 2px;
  margin-top: 4px;
}
.ds-side .tags { margin-top: 18px; flex-wrap: wrap; row-gap: 8px; }
.ds-side .tags span {
  font-size: 13px; padding: 6px 14px;
  border-color: rgba(255, 255, 255, 0.5); color: #fff;
}
.ds-side .ds-desc {
  margin-top: 18px;
  font-size: clamp(16px, 1.25vw, 18px); line-height: 1.65;
  color: rgba(255, 255, 255, 0.92); opacity: 1; max-width: 58ch;
}
.ds-side .ds-side-cta { margin-top: 28px; }
/* The router toolbar under the heading shares the category pages' parts;
   only the pieces that sit straight on the brown ground re-colour. The
   panel itself stays the same paper card. With the emblem nav gone the
   toolbar's own hairline is the break before the covers, so it takes the
   grown margin the nav used to carry. */
.dest-index .exp-toolbar { border-bottom-color: rgba(241, 218, 178, 0.28); }
.dest-index .exp-result-count { color: #fff; }
.dest-index .exp-catalogue-tools { margin-bottom: clamp(48px, 6vw, 84px); }

/* Parked 11 Aug, second pass: the on-photograph overlay and its hover
   glass panel, replaced by the .ds-side split above. Rollback is a paste. */
.ds-cover-scrim-parked::after {
  content: ""; position: absolute; inset: 0;
  background: linear-gradient(11deg, rgba(24, 20, 16, 0.72) 0%,
              rgba(24, 20, 16, 0.28) 38%, rgba(24, 20, 16, 0) 62%);
}
.ds-cover-text {
  position: absolute; left: clamp(16px, 1.8vw, 28px);
  bottom: clamp(14px, 1.8vw, 26px); z-index: 1; color: #fff;
  max-width: min(64%, 545px);
  padding: clamp(12px, 1.4vw, 20px);
  border-radius: 8px;
  transition: background-color 0.4s;
}
a.ds-cover:hover .ds-cover-text, a.ds-cover:focus-visible .ds-cover-text {
  background: rgba(32, 29, 25, 0.34);
  -webkit-backdrop-filter: blur(12px);
  backdrop-filter: blur(12px);
}
.ds-cover-more {
  display: grid; grid-template-rows: 0fr;
  transition: grid-template-rows 0.45s var(--ease);
}
.ds-cover-more > div { overflow: hidden; }
.ds-cover-more .ds-desc {
  padding-top: 12px; color: rgba(255, 255, 255, 0.92); max-width: none;
}
a.ds-cover:hover .ds-cover-more, a.ds-cover:focus-visible .ds-cover-more {
  grid-template-rows: 1fr;
}
.ds-cover-text .label { color: #fff; opacity: 0.85; margin-bottom: 6px; }
.ds-cover-text h2 { font-size: clamp(40px, 4.6vw, 68px); line-height: 1.12; color: #fff; }
.ds-cover-text .tags { margin-top: 14px; flex-wrap: wrap; row-gap: 6px; }
.ds-cover-text .tags span { border-color: rgba(255, 255, 255, 0.5); color: #fff; }
.ds-cover-cta {
  position: absolute; right: clamp(20px, 2.2vw, 34px);
  bottom: clamp(18px, 2.4vw, 34px); z-index: 1;
}
/* Parked 9 Aug, second pass: description moved into the cover. */
.ds-sub {
  display: grid; grid-template-columns: minmax(0, 7fr) minmax(0, 5fr);
  gap: clamp(24px, 3.5vw, 56px);
  margin-top: clamp(16px, 1.8vw, 22px);
}
.ds-desc { font-size: 15px; line-height: 1.65; opacity: 0.92; max-width: 60ch; }

/* Parked 9 Aug: the two-column strip header the covers replaced. */
.ds-head {
  display: grid; grid-template-columns: minmax(0, 5fr) minmax(0, 7fr);
  gap: clamp(24px, 3.5vw, 56px);
  align-items: end;
  margin-bottom: clamp(16px, 2vw, 24px);
}
.ds-head h2 { font-size: clamp(30px, 3vw, 44px); }
.ds-head .tags { margin-top: 12px; flex-wrap: wrap; row-gap: 6px; }
.ds-actions { margin-top: 16px; }
.ds-actions .dest-soon { margin-top: 0; }
.ds-actions .btn { padding: 12px 24px; }

/* The strip itself: full-bleed, frames of four aspect ratios hung on one
   centre line, heights stepped so the mix reads editorial rather than
   ragged. main.js clones the frames for a seamless loop and drifts
   scrollLeft; without JS, or under reduced motion, this is a plain
   scrollable row and the arrows step it. */
.ds-shell { position: relative; }
.ds-track {
  display: flex; align-items: center; gap: clamp(12px, 1.4vw, 18px);
  height: clamp(300px, 33vw, 430px);
  /* 9 Aug: taller with the covers — the strip is the product shelf and was
     reading small. Room for the hover scale: a frame at 100% track height
     would otherwise be clipped by the track's own overflow when it grows. */
  padding: 14px 0;
  overflow-x: auto;
  scrollbar-width: none;
}
.ds-track::-webkit-scrollbar { display: none; }
.ds-frame {
  position: relative; flex: none; height: 100%;
  border: 1px solid rgba(241, 218, 178, 0.32); border-radius: var(--r-card);
  overflow: hidden;
  background-color: var(--brown);
  transition: transform 0.5s var(--ease), box-shadow 0.5s var(--ease);
}
.ds-frame.is-port { aspect-ratio: 3 / 4; }
.ds-frame.is-sq { aspect-ratio: 1 / 1; height: 88%; }
.ds-frame.is-land { aspect-ratio: 3 / 2; height: 82%; }
.ds-frame.is-wide { aspect-ratio: 2 / 1; height: 72%; }
/* 9 Aug: every frame is a product now, so it has to read as one. The
   caption is gone (the photograph carries it), and pointing at a frame
   lifts it and shows the same arrow chip the destination tiles use. */
.ds-frame img { transition: transform 0.9s var(--ease); }
a.ds-frame:hover, a.ds-frame:focus-visible {
  transform: scale(1.035);
  box-shadow: 0 14px 34px rgba(20, 18, 15, 0.38);
  z-index: 2;
}
a.ds-frame:hover img, a.ds-frame:focus-visible img { transform: scale(1.06); }
/* 11 Aug (Abdulla): the bare arrow chip became the hero pair's ghost box,
   reading "View experience", so the frame says what the click does. The
   span carries .btn .btn-ghost for the box itself; this rule only places
   it and compacts the padding — the hero's 15px/30px outsizes a portrait
   frame. Fill on frame hover unchanged. */
.ds-go {
  position: absolute; inset-inline-end: 12px; bottom: 12px; z-index: 2;
  max-width: calc(100% - 24px);
  padding: 11px 16px;
  font-size: 12px;
  white-space: nowrap;
}
a.ds-frame:hover .ds-go, a.ds-frame:focus-visible .ds-go {
  background: var(--brown); border-color: var(--brown); color: var(--cream);
}
/* Regions short of real photography: the category cards' .ph-img treatment
   recut as a strip frame, the caption naming the shot to come. */
.ds-frame.ds-ph {
  display: grid; place-items: center;
  background-image: url("../assets/brand/pattern-topo-dark.svg");
  background-size: 1920px 2880px;
}
.ds-ph-note { color: rgba(255, 255, 255, 0.5); font-size: 12px; font-weight: 500; letter-spacing: 0.02em; }

/* ---------- Destinations features (parked 4 Aug) ---------- */

/* The 2 Aug editorial feature rows and their 3 Aug display-type jump index,
   superseded by the strips above. Markup archived at
   archive/destinations-features.html; rollback is a paste. */
.dest-jump {
  display: flex; flex-wrap: wrap;
  gap: 12px clamp(26px, 3.2vw, 44px);
  margin-bottom: clamp(44px, 5.5vw, 72px);
  padding-bottom: clamp(26px, 3.2vw, 38px);
  border-bottom: 1px solid rgba(241, 218, 178, 0.25);
}
.dest-jump a {
  font-family: var(--display); font-size: clamp(20px, 2.1vw, 25px); font-weight: 600;
  line-height: 1.25;
  padding-bottom: 5px; border-bottom: 2px solid rgba(241, 218, 178, 0.35);
  transition: border-color 0.25s, color 0.25s;
}
.dest-jump a:hover { border-color: var(--sand); color: var(--sand); }
.dest-features { display: flex; flex-direction: column; gap: clamp(52px, 6.5vw, 88px); }
.dest-feature {
  display: grid; grid-template-columns: minmax(0, 7fr) minmax(0, 5fr);
  gap: clamp(28px, 4vw, 60px); align-items: center;
  /* Arriving via the jump index or a landing-page name, the row's top clears
     the fixed header. */
  scroll-margin-top: 90px;
}
.dest-feature:nth-child(even) .df-media { order: 2; }
.df-media { display: grid; grid-template-columns: 1fr 1fr; gap: clamp(10px, 1.2vw, 16px); }
.df-media img {
  width: 100%; height: 100%; object-fit: cover; display: block;
  border: 1px solid rgba(241, 218, 178, 0.32); border-radius: var(--r-card);
  background-color: var(--brown);
}
.df-main { grid-column: 1 / -1; aspect-ratio: 16 / 10; }
.df-side { aspect-ratio: 4 / 3; }
/* One-photo regions: a single frame, no empty tracks. */
.df-media--single .df-main { aspect-ratio: 4 / 3; }
/* Two-photo regions: the second frame runs full width under the first. */
.df-media--pair .df-side { grid-column: 1 / -1; aspect-ratio: 21 / 9; }
.df-text h2 { font-size: clamp(28px, 2.8vw, 40px); }
.df-text .tags { margin-top: 12px; flex-wrap: wrap; row-gap: 6px; }
.df-desc { margin-top: 16px; font-size: 15px; line-height: 1.65; opacity: 0.92; max-width: 52ch; }
.df-actions { margin-top: 22px; }
.df-actions .dest-soon { margin-top: 0; }

/* ---------- Region page ---------- */

.crumbs { font-size: 13px; letter-spacing: 0.04em; margin-bottom: clamp(20px, 3vw, 34px); opacity: 0.85; }
.crumbs span { margin: 0 9px; opacity: 0.5; }
.crumbs a { border-bottom: 1px solid transparent; transition: border-color 0.25s; }
.crumbs a:hover { border-color: currentColor; }

/* Wireframe calls for 16:9 on desktop and 4:5 on mobile. */
.region-hero {
  position: relative;
  display: flex; align-items: flex-end;
  min-height: clamp(430px, 54vw, 660px);
  overflow: hidden;
  color: #fff;
}
.region-hero-media { position: absolute; inset: 0; }
.region-hero-scrim {
  position: absolute; inset: 0;
  background: linear-gradient(180deg, rgba(32, 29, 25, 0.6) 0%, rgba(32, 29, 25, 0.15) 42%, rgba(32, 29, 25, 0.62) 100%);
}
.region-hero-content {
  position: relative; z-index: 2; width: 100%;
  padding-top: clamp(130px, 15vw, 180px);
  /* Deep enough that the fact card, which reaches up into the photograph,
     stays clear of the standfirst. */
  padding-bottom: clamp(96px, 11vw, 140px);
}
/* The region h1's own clamp(50–104) override was cut in the 3 Aug audit:
   it was a near-duplicate of the global h1 scale doing nothing but adding a
   second system. */
/* Parked 3 Aug: the weather left the photograph for a fact-card cell
   (.fact-weather below), Abdulla's ask — the badge floated with no anchor
   while the card carried the same paper dress a scroll away. Rules kept for
   rollback; the badge markup is in git history (31 July version). */
.weather-badge {
  position: absolute; z-index: 3;
  top: clamp(100px, 12vw, 148px);
  right: max(var(--pad), calc((100vw - 1280px) / 2 + var(--pad)));
  display: flex; align-items: center; gap: 13px;
  padding: 13px 20px 13px 16px;
  background: var(--paper); color: var(--charcoal);
  border: 1.5px solid rgba(112, 81, 55, 0.3);
  border-radius: 14px;
  box-shadow: 0 14px 34px rgba(45, 42, 38, 0.28);
}
.wb-icon { width: 38px; height: 38px; color: var(--brown); flex: none; }
.wb-text { display: flex; flex-direction: column; gap: 3px; }
.wb-temp { font-family: var(--display); font-size: 26px; line-height: 1; }
.wb-cond { font-size: 12px; font-weight: 600; letter-spacing: 0.03em; color: var(--charcoal); opacity: 0.8; }
.standfirst { max-width: 580px; margin-top: 16px; font-size: clamp(15px, 1.3vw, 18px); line-height: 1.65; opacity: 0.93; }

/* The fact card straddles the hero's bottom edge: the photograph runs down
   behind its top half and the cream below picks up the rest. The brown band it
   sat on was cut on 27 July, per Abdulla. flow-root stops the wrap's negative
   margin collapsing through the section, which would drag the cream up over
   the photograph; the wrap is positioned so the card paints above the hero.
   The why wave below now lands cream on cream and disappears, which keeps the
   photograph's bottom edge straight, same rule as the landing hero. */
.factbar {
  display: flow-root;
  background-color: var(--cream);
  padding-bottom: clamp(10px, 1.2vw, 16px);
}
.factbar .wrap {
  position: relative; z-index: 2;
  margin-top: calc(-1 * clamp(44px, 4.5vw, 64px));
}
/* Facts sit on a paper card matching the enquiry form's treatment. */
.fact-card {
  background: var(--paper); color: var(--charcoal);
  border: 1.5px solid rgba(112, 81, 55, 0.3);
  border-radius: 16px;
  box-shadow: 0 14px 34px rgba(45, 42, 38, 0.09);
  padding: clamp(22px, 2.8vw, 32px) clamp(24px, 3.4vw, 46px);
  /* Four cells since 3 Aug (Best months cut — the ribbon below already says
     it) plus the live weather cell, which ships hidden: main.js adds
     .has-weather when Open-Meteo answers, so the grid only makes room once
     there is data. Breakpoints run 4/5, then 3+2, then 2+2(+1). */
  display: grid; grid-template-columns: repeat(4, auto);
  justify-content: space-between; gap: 22px 28px;
}
.fact-card.has-weather { grid-template-columns: repeat(5, auto); }
.factbar dt {
  font-size: 13px; font-weight: 600;
  letter-spacing: 0.04em; color: var(--charcoal); margin-bottom: 7px;
}
.factbar dd { font-family: var(--display); font-size: clamp(19px, 1.9vw, 24px); }
/* Live weather as the card's fifth cell (3 Aug, off the photograph): the
   brown line icon leads the value, temperature and condition in the same
   display size as the other cells so the weather reads as one more fact.
   The .wb-temp / .wb-cond overrides put back what the parked badge rules
   below would otherwise impose. */
.fact-weather dd { display: flex; align-items: center; gap: 10px; }
.fact-weather .wb-icon { width: 23px; height: 23px; color: var(--brown); flex: none; }
.fact-weather .wb-temp { font-size: inherit; line-height: inherit; }
.fact-weather .wb-sep { opacity: 0.45; }
.fact-weather .wb-cond {
  font-size: inherit; font-weight: inherit; letter-spacing: 0;
  color: inherit; opacity: 1;
}

.why { background-color: var(--cream); padding: var(--sec-pad) 0; }
.why-grid {
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(0, 1.05fr);
  gap: clamp(28px, 5vw, 76px);
  align-items: start;
}
.why h2 { font-size: clamp(30px, 3.7vw, 54px); }
.why-body p + p { margin-top: 1em; }
/* display: table so each link shrink-wraps on its own line — regions with
   packages carry a second "See our {name} tours" link under the first
   (11 Aug, Abdulla). */
.why-body .text-link { display: table; margin-top: 26px; }
.why-body .text-link + .text-link { margin-top: 14px; }
/* Display-face eyebrows since 11 Aug (Abdulla: the 13px body-face labels
   read lost and misaligned beside the display headings they sit over).
   Scoped to the Why sections, the region-page section heads and the
   destination covers; the rest of the mini-header family stays 13px body. */
.why .label,
.exp-index .section-head .label {
  font-family: var(--display);
  font-size: 16.5px;
  font-weight: 600;
  letter-spacing: 0.02em;
  margin-bottom: 12px;
  opacity: 0.85;
}

/* In-page index. Display-size links with a standing underline so they read as
   links at rest; the "Jump to:" eyebrow reuses .label. */
.region-nav {
  margin-top: clamp(46px, 6vw, 76px);
  padding-top: clamp(26px, 3.2vw, 38px);
  border-top: 1px solid var(--line);
}
.region-nav-links { display: flex; flex-wrap: wrap; gap: 16px clamp(28px, 3.6vw, 52px); }
.region-nav a {
  font-family: var(--display); font-size: clamp(20px, 2.1vw, 25px); font-weight: 600;
  line-height: 1.25;
  padding-bottom: 5px; border-bottom: 2px solid rgba(112, 81, 55, 0.35);
  transition: border-color 0.25s, color 0.25s;
}
.region-nav a:hover { border-color: var(--brown); color: var(--brown); }
/* Anchor targets clear the fixed header; 90px matches the finder's allowance. */
.todo, .season, .stays, .trips, .stories { scroll-margin-top: 90px; }

.todo { background-color: var(--sand); padding: var(--sec-pad) 0; }
.todo-grid { display: grid; grid-template-columns: repeat(3, 1fr); gap: var(--gap-card-lg); }
.todo-card figure { aspect-ratio: 4 / 3; border-radius: 16px; overflow: hidden; margin-bottom: 16px; }
.todo-card h3 { font-size: 20px; }
.todo-card p { font-size: 14.5px; opacity: 0.76; margin-top: 6px; }

/* Reworked 28 July on the client's ask: the bordered boxes did not match the
   site, and a brown-only scale never said "do not come in August". Now one
   segmented ribbon, months beneath it, in dusty pastels: green go, yellow
   shoulder, red too-hot. This is the one place the site steps outside the
   brand colours, deliberately, because the meaning needs the traffic light;
   the tones are muted so they sit quietly on the cream. The visually-hidden
   season names carry the meaning for screen readers and anyone who cannot
   split red from green. */
/* Parked 28 July: the When-to-go section is gone from yanbu.html, its ribbon
   now rides in the factbar directly under the fact card. Rule kept with the
   .season entries in the lists above in case a region ever wants the full
   section back. */
.season { background-color: var(--cream); padding: clamp(70px, 9vw, 110px) 0; }
/* Ribbon spacing when it sits under the fact card. */
.factbar .months { margin-top: clamp(30px, 3.6vw, 46px); }
.factbar .months-key { margin-bottom: clamp(8px, 1vw, 14px); }
.months { display: grid; grid-template-columns: repeat(12, 1fr); gap: 3px; }
.mo { text-align: center; }
.mo-name {
  display: block; font-size: 11px;
  letter-spacing: 0.04em; opacity: 0.72; margin-top: 10px;
}
.mo-bar {
  display: block; height: 14px; border-radius: 4px;
  background: #a5bd95;
  box-shadow: inset 0 0 0 1px rgba(45, 42, 38, 0.08);
}
.mo-shoulder .mo-bar, .mo-bar.mo-shoulder { background: #e5cf9a; }
.mo-hot .mo-bar, .mo-bar.mo-hot { background: #d8a49a; }
.months-key { display: flex; flex-wrap: wrap; gap: 8px 24px; margin-top: 22px; font-size: 13px; }
.months-key li { display: flex; align-items: center; gap: 9px; opacity: 0.8; }
.months-key .mo-bar { width: 26px; height: 10px; }

/* Cream since 28 July: the cream When-to-go section that used to separate this
   from the sand Things-to-do left the page, so stays takes over the light slot
   to keep the alternation. */
.stays { background-color: var(--cream); padding: var(--sec-pad) 0; }
.stay-grid { display: grid; grid-template-columns: repeat(4, 1fr); gap: var(--gap-card); }
.stay-card figure { aspect-ratio: 3 / 2.6; border-radius: 16px; overflow: hidden; margin-bottom: 14px; }
.stay-card h3 { font-size: 20px; }
.stay-card p { font-size: 13.5px; opacity: 0.7; margin-top: 3px; }


/* The "Yanbu, pinned" region map was deleted outright on 29 July, Abdulla:
   it will not be used again. Its rules and list registrations went with it. */

.story-grid { display: grid; grid-template-columns: repeat(3, 1fr); gap: clamp(22px, 3vw, 42px); }

/* ---------- Responsive ---------- */

@media (max-width: 1080px) {
  .main-nav { display: none; }
  .lang-switch { top: 0; }
  .burger { display: flex; margin-inline-start: 4px; }
  .header-utils { margin-inline-start: auto; }
  /* The destination features stack; media always leads the text again. */
  .dest-feature { grid-template-columns: 1fr; gap: 22px; }
  .dest-feature:nth-child(even) .df-media { order: 0; }
  /* The strip headers stack, name over description. */
  .ds-head { grid-template-columns: 1fr; gap: 14px; align-items: start; }
  /* Parked 9 Aug, second pass: description moved into the cover. */
.ds-sub { grid-template-columns: 1fr; }
  /* The cover split stacks: photograph, then the text below it. Both
     variants spelled out so the odd strips' column swap loses too. */
  .ds-cover-wrap, .dest-strips > .dest-strip:nth-child(odd) .ds-cover-wrap {
    grid-template-columns: 1fr; gap: 20px;
  }
  .ds-cover, .dest-strips > .dest-strip:nth-child(odd) .ds-cover {
    grid-column: 1; grid-row: 1; height: clamp(330px, 62vw, 530px);
  }
  .ds-side, .dest-strips > .dest-strip:nth-child(odd) .ds-side {
    grid-column: 1; grid-row: 2; justify-self: start; max-width: none;
  }
  .ds-side h2 { font-size: clamp(34px, 6vw, 52px); }
  .dest-nav { column-gap: clamp(18px, 3vw, 34px); }
  /* A portrait frame at the shorter track is ~225px wide; the View-experience
     box compacts a step further so it never outruns the frame. */
  .ds-go { font-size: 11px; padding: 9px 12px; }
  /* The split stacks too; a full-width square would run a screen tall, so
     the stage relaxes to 4:3 once it owns the whole row, and the container
     takes its left padding back — the flush edge is a wide-screen move. */
  .dest-split { grid-template-columns: 1fr; gap: 26px; }
  .dsp-stage { aspect-ratio: 4 / 3; }
  /* Stacked, the text runs the full width, so the diagonal wash that opens
     the photograph up on the right would put type on barely-tinted sun.
     Flatten it to a near-even brown instead. */
  .intro-media::after {
    background:
      linear-gradient(180deg, rgb(112, 81, 55) 0, rgba(112, 81, 55, 0) clamp(110px, 13vw, 170px)),
      linear-gradient(102deg, rgba(99, 71, 47, 0.94) 0%, rgba(99, 71, 47, 0.88) 70%, rgba(99, 71, 47, 0.78) 100%);
  }
  .reviews-grid { grid-template-columns: 1fr; }
  .finder-grid { grid-template-columns: 1fr; gap: clamp(40px, 6vw, 60px); }
  .finder-col { max-width: 620px; }
  .dest-map { max-width: min(100%, 560px); margin-inline: 0; }
  /* The map shrinks with the column, so the labels need to grow in SVG
     units to stay legible once it stacks. */
  .dest-pin text { font-size: 50px; }
  .dest-pin .pin-dot { r: 17; }
  /* The mosaic drops to two columns; the wide tiles become full rows and
     dense flow backfills the gaps the reflow opens. */
  .dest-tiles { grid-template-columns: repeat(2, 1fr); grid-auto-rows: clamp(220px, 30vw, 320px); }
  .cat-cards { gap: 14px; }
  .exp-grid { grid-template-columns: repeat(2, 1fr); }
  .why-grid { grid-template-columns: 1fr; }
  .todo-grid, .story-grid { grid-template-columns: repeat(2, 1fr); }
  .stay-grid { grid-template-columns: repeat(2, 1fr); }
  .fact-card, .fact-card.has-weather { grid-template-columns: repeat(3, auto); }
  .months { grid-template-columns: repeat(6, 1fr); }
  .contact-grid { grid-template-columns: 1fr; }
  /* .contact-map { aspect-ratio: 16 / 10; }  parked 12 Aug with the map */
  /* The aside is supporting detail, so it goes under the form rather than
     squeezing it. Its two cards run side by side while there is room. */
  .enquiry-grid { grid-template-columns: 1fr; }
  .enquiry-aside { flex-direction: row; }
  .enquiry-aside > * { flex: 1 1 0; }
  .footer-grid { grid-template-columns: 1fr 1fr; }
  .footer-brand { grid-column: 1 / -1; }
  .team-grid { grid-template-columns: repeat(2, 1fr); }
  .ab-grid { grid-template-columns: repeat(2, 1fr); }
  .mission-grid { grid-template-columns: 1fr; }
  .backing-grid { grid-template-columns: 1fr; }
}

@media (max-width: 640px) {
  .btn-nav { display: none; }
  .region-grid { grid-template-columns: 1fr 1fr; }
  .fact-card, .fact-card.has-weather { grid-template-columns: repeat(2, 1fr); }
  .dest-tiles { grid-template-columns: 1fr; grid-auto-rows: clamp(220px, 62vw, 320px); }
  .dest-tile--wide { grid-column: auto; }
  .cat-cards { grid-template-columns: 1fr; }
  .exp-grid { grid-template-columns: 1fr; }
  .exp-toolbar { align-items: flex-start; flex-direction: column; }
  .exp-toolbar-actions { width: 100%; }
  .exp-tool-btn, .exp-sort { flex: 1 1 0; }
  .exp-tool-btn { justify-content: flex-start; }
  .exp-sort select { width: 100%; min-width: 0; }
  .exp-filter-panel { top: calc(100% + 8px); left: 0; right: 0; width: 100%; max-height: 72vh; }
  .exp-filter-groups { grid-template-columns: 1fr; }
  .exp-filter-foot { align-items: stretch; flex-direction: column-reverse; }
  .exp-filter-done { width: 100%; }
  .exp-active-filters { padding-top: 12px; }
  .todo-grid, .story-grid, .stay-grid { grid-template-columns: 1fr; }
  /* The old .factbar dl 2-column rule and its hairline dividers (a leftover
     from the on-band card) were cut on 3 Aug: .fact-card above already runs
     the two columns, and the sand rule beside "Pairs well with" was the last
     of the band's dress. */
  .months { grid-template-columns: repeat(4, 1fr); }
  .dest-pin text { font-size: 58px; }
  .dest-pin .pin-dot { r: 20; }
  .region-hero { min-height: 118vw; }
  /* Below the breadcrumb line and clear of the short h1 beside it. */
  .weather-badge { top: 150px; padding: 10px 15px 10px 12px; gap: 10px; }
  .wb-icon { width: 32px; height: 32px; }
  .wb-temp { font-size: 21px; }
  /* Every field full width: two 15px inputs side by side stop being fillable
     well before the viewport runs out. */
  .form-row, .form-row-3 { grid-template-columns: 1fr; }
  .enquiry-aside { flex-direction: column; }
  .trip-tail { flex-direction: column; gap: 18px; }
  .section-head { flex-direction: column; align-items: flex-start; gap: 14px; }
  /* The review arrows come off on phones: the dots carry the switching and
     the quote column takes the width back. */
  .reviews-carousel { padding: 0; }
  .rv-live .rv-btn { display: none; }
  .hero-content { padding-top: 140px; padding-bottom: 120px; }
  .team-grid { grid-template-columns: 1fr; }
  .ab-grid { grid-template-columns: 1fr; }
  .person-photo { aspect-ratio: 1 / 0.92; }
  /* The destination strips on phones: a tighter emblem row, a shorter track,
     smaller arrows. Swiping does most of the moving; the arrows stay as the
     visible affordance. */
  .dest-nav { column-gap: 20px; row-gap: 18px; }
  .dest-nav a { font-size: 16px; gap: 9px; }
  .dest-nav svg { width: 25px; height: 25px; }
  .ds-track { height: clamp(230px, 62vw, 280px); }
  .ds-cover, .dest-strips > .dest-strip:nth-child(odd) .ds-cover { height: clamp(260px, 72vw, 360px); }
  /* The frame box comes off on phones: the frame is one whole tap target,
     and the narrowest portrait frame cannot hold it. The cover's own
     button lives in the text flow now, so it stays. */
  .ds-go { display: none; }
  .ds-shell .c-prev, .ds-shell .c-next { width: 44px; height: 44px; }
  .ds-shell .c-arrow { width: 20px; height: 20px; }
}

/* ---------- Reduced motion ---------- */

@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
  .hero-poster { animation: none; }
  .scroll-cue i::after { animation: none; }
  .reveal { opacity: 1; transform: none; transition: none; }
  * { transition-duration: 0.01ms !important; }
}

/* ---------- Arabic (RTL) ---------- */

/* The ar/ tree (written by data/translate.gen.py) sets lang="ar" dir="rtl"
   on <html>. The rules above use logical properties wherever direction is
   semantic, so the layout mirrors itself; this block holds only what cannot
   self-mirror. El Messiri is an Arabic-first face and covers display type as
   it already does on the covers; Helvetica has no Arabic, so the body stack
   swaps to Noto Sans Arabic (loaded only by ar/ pages). The brand book names
   Somar for Arabic headings - a licensed face we do not hold - so display
   stays El Messiri until the client supplies files; the swap is this block. */
html[lang="ar"] { --body: "Noto Sans Arabic", "Helvetica Neue", Helvetica, Arial, sans-serif; }
/* Arabic joins: any letter-spacing tears the script apart, so the whole
   tracked mini-header family flattens at once. Latin fragments (the "AR"
   code) lose 0.04em of air, which is the cheaper wrong. */
html[lang="ar"] * { letter-spacing: 0; }
html[lang="ar"] em { font-style: normal; } /* synthetic oblique mangles Arabic */
html[lang="ar"] .rv-stars { letter-spacing: 0.22em; padding-inline-start: 0.22em; } /* stars are glyphs, keep the track */

/* Carousel arrows: sides swap, chevrons mirror. Flipping only the drawn
   arrow keeps the circle and its shadow untouched. */
[dir="rtl"] .c-prev { left: auto; right: var(--pad); transform: translate(50%, -50%); }
[dir="rtl"] .c-next { right: auto; left: var(--pad); transform: translate(-50%, -50%); }
[dir="rtl"] .c-prev.show:hover { transform: translate(50%, -50%) scale(1.07); }
[dir="rtl"] .c-next.show:hover { transform: translate(-50%, -50%) scale(1.07); }
[dir="rtl"] .rv-prev { left: auto; right: max(8px, calc(50% - 520px)); }
[dir="rtl"] .rv-next { right: auto; left: max(8px, calc(50% - 520px)); }
[dir="rtl"] .c-arrow { transform: scaleX(-1); }
[dir="rtl"] .tile-cta svg { transform: scaleX(-1); }
