/*
Shahraah design system — tokens, layout, motion.
Ported and extended from the approved reference build
(shahraah-homepage-reference/styles.css) for real, variable-count,
CMS-driven content. See CLAUDE.md for the design-system decisions
this encodes.
*/

:root{
  --surface-page:#F7F5EF; --surface-card:#FFFFFF; --surface-inverse:#1E1D19;
  --ink-primary:#1E1D19; --ink-secondary:#5C5A52; --ink-inverse:#F2F0E8; --ink-inverse-secondary:#B0AEA3;
  --border:#E3E0D5; --border-strong:#C9C5B4;
  --brand-green:#054E39; --brand-green-hover:#043D2D;
  --accent-gold:#BB9E61; --accent-gold-strong:#A38650;
  --state-focus:#054E39;
  /* Footer's own near-black photographic-backdrop anchor — reused as a
     raw hex 4x across the footer/scrim rules before this token existed.
     Deliberately NOT shared with Technology's gradient (which ends at
     --surface-inverse, a different and lighter warm-charcoal tone by
     design): the two dark sections stay visually distinct on purpose
     (site-cleanup Phase 3A/3B). */
  --surface-deepest:#04140F;
  --font-serif:'Newsreader', Georgia, serif;
  --font-sans:'IBM Plex Sans', system-ui, -apple-system, sans-serif;
  --font-mono:'IBM Plex Mono', 'SFMono-Regular', monospace;
  --space-4:16px; --space-5:24px; --space-6:32px; --space-7:48px; --space-8:64px; --space-9:96px;
  --ease: cubic-bezier(.22,.8,.28,1);
  /* Names the pill CTA language already used identically in 3 places
     (nav's transparent-hero CTA, Opening's CTA, Discover's scene-nav
     control) — same value, no new visual language introduced. */
  --radius-pill:999px;
  /* Documents the two-tier motion durations already in use sitewide;
     Opening's 1.4s cinematic crossfade stays its own explicit one-off
     signature value, intentionally not folded into this scale. */
  --duration-micro:.2s;
  --duration-transition:.35s;
  --duration-reveal:.6s;
}
*{box-sizing:border-box;}
html{scroll-behavior:smooth;}
body{margin:0; background:var(--surface-page); color:var(--ink-primary); font-family:var(--font-sans);}
a{color:inherit;}
img,video{max-width:100%; display:block;}
.skip-link{position:absolute; left:-999px; top:0; background:var(--brand-green); color:var(--ink-inverse); padding:10px 16px; z-index:100;}
.skip-link:focus{left:16px; top:16px;}
:focus-visible{outline:2px solid var(--state-focus); outline-offset:2px;}

/* ---------- type ---------- */
/* .06em is the one tracking for every 11px uppercase label sitewide
   (eyebrows, footer/trust labels, Services footer labels). */
.eyebrow{font:600 11px/14px var(--font-sans); letter-spacing:.06em; text-transform:uppercase; color:var(--ink-secondary); margin:0;}
.h1{font:400 56px/1.08 var(--font-serif); margin:0; color:var(--ink-primary);}
.h2{font:400 34px/1.15 var(--font-serif); margin:0; color:var(--ink-primary);}
.h3{font:400 24px/1.25 var(--font-serif); margin:0; color:var(--ink-primary);}
.body-l{font:400 18px/1.6 var(--font-sans); color:var(--ink-secondary); margin:0;}
.body{font:400 15px/1.6 var(--font-sans); color:var(--ink-secondary); margin:0;}
.label{font:600 13px/1.3 var(--font-sans); color:var(--ink-primary); margin:0;}
.mono{font:400 14px/1.4 var(--font-mono); color:var(--ink-primary);}
.gold-rule{width:28px; height:2px; background:var(--accent-gold);}

/* ---------- buttons ---------- */
.btn{display:inline-flex; align-items:center; justify-content:center; height:48px; padding:0 26px; border-radius:3px; font:600 14px var(--font-sans); text-decoration:none; cursor:pointer; transition:background var(--duration-micro) ease, border-color var(--duration-micro) ease, color var(--duration-micro) ease;}
.btn-primary{background:var(--brand-green); color:var(--ink-inverse); border:1px solid var(--brand-green);}
.btn-primary:hover{background:var(--brand-green-hover);}
.btn-secondary{background:transparent; color:var(--ink-primary); border:1px solid var(--border-strong);}
.btn-secondary:hover{border-color:var(--ink-primary);}
.cta-row, .human-cta{display:flex; align-items:center; gap:20px; flex-wrap:wrap;}

/* ---------- nav ---------- */
/* Navigation visual rebuild: the
   scrolled/default nav used to be a plain edge-to-edge white bar — it
   didn't read as part of the same designed system as the rest of the
   homepage. `.nav` itself now never paints anything and never changes
   height (a fixed 76px, matching `.opening--stage`'s own hardcoded
   `margin-top:-76px` compensation exactly — see that rule's own
   comment — so nothing here can ever introduce a layout jump: `.nav`'s
   footprint in the document flow is identical in every state). All the
   actual visual chrome — background, blur, radius, shadow, width, and
   height — lives on `.nav-inner`, a floating surface centered and
   vertically inset *inside* that constant 76px band (`position:
   absolute; top:50%; transform:translateY(-50%)`, taken out of flow so
   `.mobile-menu` below can stack under the *whole* 76px band rather
   than becoming a flex sibling of it), exactly like the approved
   reference's "quiet floating editorial navigation." This floating
   look is now the nav's ordinary,
   default appearance (used as-is on any page without the transparent-
   hero treatment below); the front page's transparent-over-hero state
   is the one deliberate override, not the other way around. */
/* display:flow-root (not overflow:hidden) establishes a block
   formatting context so `.mobile-menu`'s own top margin (below) can't
   collapse through `.nav` and land in the wrong place — a real bug
   caught in testing: without this, the margin escaped upward through
   `.nav` (which has no padding/border of its own) and the menu
   rendered flush with the nav's own top instead of below it.
   flow-root still lets both `.nav-inner` and `.mobile-menu` overflow
   this fixed 76px box visually, unlike overflow:hidden, which would
   have clipped the floating card's shadow and the open menu itself. */
.nav{position:sticky; top:0; z-index:50; height:76px; display:flow-root;}
.nav-inner{
  position:absolute; top:50%; left:0; right:0; transform:translateY(-50%);
  width:min(1240px, calc(100% - clamp(32px, 6vw, 96px))); margin:0 auto; height:60px;
  padding:0 clamp(16px, 3vw, 28px);
  padding-left:max(clamp(16px, 3vw, 28px), env(safe-area-inset-left));
  padding-right:max(clamp(16px, 3vw, 28px), env(safe-area-inset-right));
  display:flex; align-items:center; justify-content:space-between; gap:clamp(12px, 3vw, 32px);
  background:rgba(247,245,239,.88);
  -webkit-backdrop-filter:blur(14px); backdrop-filter:blur(14px);
  border:1px solid var(--border); border-radius:20px;
  box-shadow:0 10px 30px rgba(30,29,25,.10), 0 1px 2px rgba(30,29,25,.05);
  transition:background .3s var(--ease), border-color .3s var(--ease), box-shadow .3s var(--ease),
    width .3s var(--ease), height .3s var(--ease), border-radius .3s var(--ease), padding .3s var(--ease);
}
/* The real horizontal lockup (icon + "SHAHRAAH" + tagline,
   assets/img/branding/SHAHRAAH_Horizontal_Logo.svg) replaces the old
   dynamic site-title text — that title ("Shahraah Local") was always a
   dev-only placeholder, never the brand. `.wordmark-logo`'s own fixed
   `height` + `width:auto` below is what governs the rendered size;
   width/height attributes in the markup only reserve the correct
   aspect ratio (1775x479) before the SVG loads. The mark keeps its own fixed
   green/gold fills in every nav state (the asset is used exactly as
   supplied, not recolored) — legible on the floating ivory surface by
   construction, and over the transparent hero state specifically
   because it sits against the Opening stage's own top scrim
   (`.stage-scrim-top`, already lightened there for this exact reason;
   unchanged by this pass). */
.wordmark{display:flex; align-items:center; min-width:0; flex:0 0 auto;}
.wordmark-logo{height:30px; width:auto; display:block; transition:height .3s var(--ease);}
.nav-links{display:flex; gap:28px; list-style:none; margin:0; padding:0; min-width:0;}
.nav-links li{list-style:none;}
.nav-links a{font:400 14px var(--font-sans); text-decoration:none; color:var(--ink-primary); border-bottom:2px solid transparent; padding-bottom:2px; transition:border-color .2s ease, color .2s ease;}
.nav-links a:hover{border-bottom-color:var(--accent-gold);}
/* flex-shrink:0 on both the CTA and the toggle: whatever else in the
   row concedes space first, these two controls — the ones the brief
   specifically flags as clipping — never deform or shrink themselves. */
.nav-cta{height:38px; padding:0 18px; gap:8px; flex-shrink:0;}
/* The two WhatsApp CTAs (nav + mobile drawer) — a light surface with a
   deep-green mark, so the one always-present contact action stays
   distinct from the deep green used throughout the page instead of
   adding another green block. Built on .btn-secondary; white card
   surface reads against both the ivory nav and the ivory drawer. */
.nav-cta, .mobile-menu-whatsapp{background:var(--surface-card); color:var(--brand-green); border-color:var(--border-strong);}
.nav-cta:hover, .mobile-menu-whatsapp:hover{border-color:var(--brand-green);}
.mobile-menu-whatsapp:active{background:rgba(5,78,57,.06);}
/* Real supplied WhatsApp marks (see header.php), drawn as masks of the
   unmodified asset files and filled with the button's currentColor —
   the glyph shapes are exact; only the colour follows the button.
   The wordmark shows at full width; the icon-only mark swaps in
   wherever the row is tight (tablet, below). */
.wa-mark{display:block; flex-shrink:0; background:currentColor;
  -webkit-mask:var(--wa-src) center / contain no-repeat; mask:var(--wa-src) center / contain no-repeat;}
.wa-mark--wordmark{--wa-src:url("../img/third-party/whatsapp-wordmark.svg"); aspect-ratio:1486 / 345;}
.wa-mark--icon{--wa-src:url("../img/third-party/whatsapp-icon.svg"); aspect-ratio:360 / 362;}
.nav-cta-mark{height:18px;}
.nav-cta-mark--compact{display:none;}
/* Divider between the WhatsApp CTA and the menu toggle — only ever
   shown where both controls appear together (mobile; see the
   max-width:860px block below). At every wider range the toggle is
   hidden entirely (see Responsive priority notes there), so the
   divider has nothing to separate and stays hidden too. */
.nav-divider{display:none;}
.menu-toggle{position:relative; display:none; background:none; border:1px solid var(--border-strong); border-radius:3px; width:38px; height:38px; align-items:center; justify-content:center; color:var(--ink-primary); cursor:pointer; flex-shrink:0;}
/* Invisible hit-area expansion only — visible chrome stays 38x38 (the
   already-tuned nav geometry is locked); the pseudo-element enlarges
   only the clickable/tappable region to the ~44x44px convention used
   elsewhere (site-cleanup Phase 3B, nav ergonomics fix). */
.menu-toggle::before{content:''; position:absolute; inset:-3px;}
/* Mobile menu visual polish — same floating ivory/blur/radius/shadow
   surface, same width as the nav above it (so it reads as the nav
   itself expanding, not a second unrelated rectangle), same open/close
   JS untouched. Only what's inside changed: a small eyebrow, a
   2-column editorial icon grid instead of a plain hairline-separated
   list, and the real WhatsApp wordmark as the closing action. */
.mobile-menu{
  display:none; flex-direction:column; gap:16px;
  width:min(1240px, calc(100% - clamp(32px, 6vw, 96px))); margin:0 auto;
  margin-top:82px; /* nav's own constant 76px band + 6px breathing room —
    `.nav-inner` is position:absolute (see above) so this can't rely on
    normal-flow stacking after it; matches the same 76px constant used
    for `.opening--stage`'s own nav-height compensation. */
  padding:20px 18px;
  background:rgba(247,245,239,.96);
  -webkit-backdrop-filter:blur(14px); backdrop-filter:blur(14px);
  border:1px solid var(--border); border-radius:20px; /* equal to the floating nav's own 20px */
  box-shadow:0 14px 34px rgba(30,29,25,.14);
}
.mobile-menu.open{display:flex;}

/* Opening motion — pure CSS, no JS change: `transition-behavior:
   allow-discrete` + `@starting-style` let `display:none -> flex`
   itself animate alongside opacity/transform, so the existing
   classList.toggle('open')/hidden dance in main.js needs no changes at
   all. Unsupported browsers (pre-2024 engines) simply ignore
   `@starting-style`/`allow-discrete` and get today's exact instant
   open/close — a safe, invisible fallback, not a regression. Scoped
   entirely inside prefers-reduced-motion:no-preference so reduced-
   motion visitors always get the instant version. */
@media (prefers-reduced-motion: no-preference){
  .mobile-menu{
    opacity:0; transform:translateY(-6px);
    transition:opacity .22s var(--ease), transform .22s var(--ease), display .22s allow-discrete;
  }
  .mobile-menu.open{opacity:1; transform:translateY(0);}
  @starting-style{
    .mobile-menu.open{opacity:0; transform:translateY(-6px);}
  }
}

.mobile-menu-eyebrow{display:flex; align-items:center; gap:8px;}
.mobile-menu-eyebrow .eyebrow{margin:0;}

/* 2-column editorial grid, row-major (Flights/Hotels, Visas/Tours,
   Umrah/Assistance) purely from source order — see header.php. Below
   340px (roughly 320px devices) a two-column row genuinely can't fit
   icon+number+label without cramping, so it falls back to one column
   there rather than shrinking type/icons past legibility. */
.mobile-menu-grid{display:grid; grid-template-columns:1fr 1fr; gap:4px 10px;}
@media (max-width:339px){
  .mobile-menu-grid{grid-template-columns:1fr;}
}
.mobile-menu-link{
  display:flex; align-items:center; gap:10px; min-height:44px; min-width:0;
  padding:8px 10px; border-radius:12px;
  text-decoration:none; color:var(--ink-primary);
  transition:background-color .15s ease, color .15s ease;
}
.mobile-menu-link-num{font:600 11px var(--font-mono); color:var(--accent-gold-strong); flex-shrink:0; min-width:15px;}
.mobile-menu-link-icon{display:flex; color:var(--accent-gold-strong); flex-shrink:0; transition:color .15s ease;}
.mobile-menu-link-label{font:500 14.5px var(--font-sans); line-height:1.2; color:var(--ink-primary); transition:color .15s ease;}
/* Restrained hover/press response, pointer-capable devices only (this
   is a touch menu first) — a warm tint, not a card lift or scale. */
@media (hover:hover) and (pointer:fine){
  .mobile-menu-link:hover{background:rgba(187,158,97,.14);}
  .mobile-menu-link:hover .mobile-menu-link-icon,
  .mobile-menu-link:hover .mobile-menu-link-label{color:var(--brand-green);}
}
.mobile-menu-link:active{background:rgba(5,78,57,.10);}
.mobile-menu-link:focus-visible{background:rgba(187,158,97,.14);}

.mobile-menu-cta{
  display:flex; flex-direction:column; align-items:center; gap:8px;
  padding-top:14px; border-top:1px solid var(--border);
}
.mobile-menu-cta-note{font:400 12.5px var(--font-sans); color:var(--ink-secondary);}
.mobile-menu-whatsapp{width:100%;}
.mobile-menu-whatsapp-mark{height:20px;}

/* Overlay nav: on the front page (desktop only — mobile keeps
   the normal solid nav, see the mobile block below), the nav starts
   transparent so the Opening stage's photograph shows through it, and
   switches to the normal floating-ivory surface once the visitor
   scrolls into the calmer content below (assets/js/main.js toggles
   .is-scrolled on a small scroll threshold). The nav's own position/
   stacking/height is completely unchanged — still the same sticky bar,
   first in the document, fixed 76px, z-index 50; only `.nav-inner`'s
   own background/border/radius/shadow/size become conditional. Every
   other page/template never gets this class, and gets the floating
   surface immediately, with no scroll dependency at all. */
@media (min-width:861px){
  .nav--overlay:not(.is-scrolled) .nav-inner{
    width:min(1240px, 100%); height:76px; padding:0 clamp(16px, 4vw, 32px);
    background:transparent; border-color:transparent; border-radius:0; box-shadow:none;
    -webkit-backdrop-filter:none; backdrop-filter:none;
  }
  /* Polish pass:
     stronger brand presence at first paint — the previous 34px hero
     logo read too weak against the full stage. Scrolled/default stays
     at its own restrained 30px (untouched) so the transition clearly
     reads TOP: stronger brand presence -> SCROLLED: compact utility
     nav, per the brief. 52px sits inside the requested 50-56px band;
     kept off the top of that range on purpose since this same rule
     also applies down to 861px (tablet), where the row is already
     tighter (see the 861-1099px gap-tightening rule below it). */
  .nav--overlay:not(.is-scrolled) .wordmark-logo{height:52px;}
  /* Polish pass: nav-links now use the same dark ink as every other
     nav state instead of the old light/inverse color. That inverse
     color assumed a dark photo directly behind the text; it stopped
     being reliable once the top scrim below (`.stage-scrim-top`) was
     strengthened into a dependable light backdrop for the logo's own
     legibility — light text on that same light backdrop is exactly
     the low-contrast failure the brief's real-video review caught on
     bright/pale hero frames. Dark ink on a guaranteed-light scrim is
     the same strategy the horizontal logo mark already relies on
     (see the `.wordmark-logo` note above), applied consistently
     rather than per-image. */
  .nav--overlay:not(.is-scrolled) .nav-links a{color:var(--ink-primary);}
  .nav--overlay:not(.is-scrolled) .menu-toggle{color:var(--ink-primary); border-color:rgba(30,29,25,.3);}
  /* The sitewide :focus-visible ring is deep green — near-invisible
     against a dark/photo hero, same low-contrast case the footer
     already has its own override for (see .site-footer :focus-visible
     below). Gold reads reliably here regardless of the photo behind. */
  .nav--overlay:not(.is-scrolled) :focus-visible{outline-color:var(--accent-gold);}
  /* While the nav floats over the hero, its CTA borrows the Opening's
     own pill language (see .opening .btn) instead of the sitewide
     rectangle — reverts to the normal light .nav-cta the instant the
     nav goes solid on scroll (.is-scrolled). A translucent white fill
     (enough surface behind the deep-green mark over any hero photo,
     sitting on the light top scrim) — a utility control, not
     competing with the hero's own CTA hierarchy. */
  .nav--overlay:not(.is-scrolled) .nav-cta{
    background:rgba(255,255,255,.55); color:var(--brand-green); border-color:rgba(30,29,25,.3); border-radius:var(--radius-pill);
  }
  .nav--overlay:not(.is-scrolled) .nav-cta:hover,
  .nav--overlay:not(.is-scrolled) .nav-cta:focus-visible{
    background:rgba(255,255,255,.8); border-color:var(--brand-green);
  }
}

/* Pass 4 (kept): root-cause fix for "header approaches/clips the
   viewport edge at intermediate widths". At 861px (the moment the
   six-item desktop nav-links + logo + nav-cta all appear in one row)
   the row's own min-content width sits within single-digit pixels of
   the viewport itself. This band (the tight end of the tablet range
   only) tightens the two gaps that cost the most width before that
   margin ever gets that thin; from ~1100px up nothing here applies and
   the original spacing returns via the base min-width:861px rule. */
@media (min-width:861px) and (max-width:1099px){
  .nav-links{gap:18px;}
  .nav-links a{font-size:13px;}
}

/* Responsive priority:
   logo, then WhatsApp access, then
   the menu/hamburger, then direct links — links are the first thing to
   concede space, never the CTA or the toggle.

   Tablet (861–1179px): all six real business links still fit at the
   tightened gap above, so they stay — dropping a real destination
   arbitrarily just to match the reference's illustrative 5-link tablet
   row isn't warranted when the actual content fits. WhatsApp becomes
   icon-only here (still a real 44px-ish tap target, just without the
   label) to keep the row from ever feeling crowded, per the brief's
   own mobile/tablet WhatsApp treatment. No hamburger in this range:
   every real link is already directly visible, so a hamburger here
   would open a menu that only repeats them — a decorative control with
   no genuine function, which the brief explicitly says to omit. */
@media (min-width:861px) and (max-width:1179px){
  .nav-cta-mark--full{display:none;}
  .nav-cta-mark--compact{display:block;}
  .nav-cta{padding:0; width:38px; justify-content:center; border-radius:12px; position:relative;}
  /* Same invisible hit-area expansion as .menu-toggle above, scoped to
     this icon-only compact range only — the full-label desktop CTA is
     untouched (site-cleanup Phase 3B, nav ergonomics fix). */
  .nav-cta::before{content:''; position:absolute; inset:-3px;}
}

/* ---------- section rhythm ---------- */
/* scroll-margin-top: purely a scroll-target offset (zero visual
   effect) so an in-page anchor (e.g. Services' Visas/Umrah CTAs -> #human
   / #discover) doesn't land a section's top edge under the sticky
   76px nav. Applied sitewide since every section is a legitimate
   anchor target, not just the two Services currently jumps to. */
.section{max-width:1240px; margin:0 auto; padding:var(--space-9) var(--space-6); scroll-margin-top:92px;}

/* reveal-on-scroll: content is fully present/legible without JS or motion;
   this only adds a restrained entrance when motion is available */
[data-reveal]{opacity:1; transform:none;}
@media (prefers-reduced-motion: no-preference){
  [data-reveal]{opacity:0; transform:translateY(14px); transition:opacity var(--duration-reveal) var(--ease), transform var(--duration-reveal) var(--ease);}
  [data-reveal].in{opacity:1; transform:translateY(0);}
}
@media (prefers-reduced-motion: reduce){
  .service-nav-item, .service-nav-icon, .service-nav-label, .service-nav-item::after, .service-nav-item::before,
  .service-stage-panel, .service-stage-media, .service-panel-item,
  .service-progress-seg::after, .service-footer-label, .service-nav-wrap::before, .service-nav-wrap::after,
  .discover-response, .dest-card, .destination-rail,
  .nav-inner, .nav-links a, .wordmark-logo{transition:none !important;}
  /* The mobile nav-scroll rail and any programmatic scrollIntoView
     into it (see main.js) become instant, matching every other
     motion in this section under reduced motion. */
  .service-nav{scroll-behavior:auto;}
}

/* ---------- 01 opening — cinematic editorial hero (Creative Experience Pass) ----------
   Kept from the earlier synthesis: a single full-bleed stage across a
   bounded, viewport-aware height (never rigid 100vh); no separate
   media column, so the copy's height can never fall out of sync with
   the media's; a genuine, engineered guarantee that body copy stays
   readable — see .stage-copy-veil below — regardless of whatever
   photo is underneath.
   New this pass: the stage now holds a rotating collection of
   .stage-scene layers (1..n, count entirely template/data-driven —
   see opening.php and main.js) instead of a single flat image, so the
   Opening can cycle through real destination photography (Departure
   -> Stay -> Meaning/Purpose) as one stable content layer stays put
   on top — translating docs/design-references/hero-direction-v1.png's
   editorial/cinematic language, not copying it. */
.opening--stage{
  max-width:none; padding:0; position:relative; overflow:hidden;
  /* Desktop: the stage owns the entire initial viewport (see Pass 2
     brief, "Hero must fully own the first desktop viewport") so
     Services never peeks in above the fold on load. 100vh first as a
     safe fallback, then 100svh ("small viewport height" — excludes
     any transient browser UI) overrides it in browsers that support
     it; unsupported browsers simply ignore the second declaration and
     keep the first. Not a fixed px height — still fully fluid per
     actual viewport size, and this is min-height, not height, so
     unusually long copy can still push the box taller rather than
     clipping. Pass 4: this min-height is now shared by mobile too —
     see the max-width:860px block, which only resets margin-top back
     to 0 there (mobile's nav is the static solid bar, never the
     overlay, so the stage never needs to pull up underneath it). */
  min-height:100vh;
  min-height:100svh;
  /* Pulls the stage up by exactly the nav's own height so the
     photograph starts at the true top of the page, sliding underneath
     the (transparent, higher z-index) nav rather than starting only
     after it — this is what lets the nav "float over" the image
     without changing the nav's position/stacking model at all. */
  margin-top:-76px;
}
.stage-media{position:absolute; inset:0; z-index:0;}
.stage-media-el{width:100%; height:100%; object-fit:cover; display:block;}
.stage-media--placeholder .stage-media-el{object-fit:cover; background:#EFEAE0;}

/* Each scene is stacked exactly on top of the others (position:absolute,
   inset:0) and crossfades via opacity alone — no sliding, no wipes.
   Only .is-active is visible/interactive; the rest sit at opacity:0
   underneath, ready for the next crossfade. Gated behind
   prefers-reduced-motion:no-preference so a reduced-motion visitor
   gets an instant swap instead — see main.js, which skips autoplay
   and the animated class dance entirely in that case. */
.stage-scene{position:absolute; inset:0; opacity:0;}
.stage-scene.is-active{opacity:1;}
@media (prefers-reduced-motion: no-preference){
  .stage-scene{transition:opacity 1.4s ease;}
}
/* Per-scene crop, tuned for the actual bundled photography (see
   inc/hero.php) so each keeps its meaningful subject in frame at
   typical desktop widths: 01 keeps the tarmac/aircraft and skyline
   in view, 02 keeps the pool/coastline horizon, 03 keeps the Kaaba
   itself centered rather than cropped toward the room's interior. */
.stage-scene[data-scene-index="0"] .stage-scene-media{object-position:62% 46%;}
.stage-scene[data-scene-index="1"] .stage-scene-media{object-position:68% 42%;}
.stage-scene[data-scene-index="2"] .stage-scene-media{object-position:66% 40%;}

/* Localized treatments, not a heavy overlay across the whole photo:
   a soft warm-cream wash at the very top (guarantees the transparent
   nav stays legible regardless of what's in that part of the image)
   and a fade at the bottom into the page's own background color, so
   the stage dissolves into the calmer Services content rather than
   ending on a hard rectangle.
   Polish pass: strengthened from a
   flat 2-stop .55->0 ramp (too weak — by the nav's own 76px band it
   had already faded to roughly a third of its top value, which real-
   video review caught as unreliable over bright/pale hero frames like
   a sunset sky or a pale hotel interior). The added mid-stop keeps the
   wash close to its peak opacity through the whole 76px nav band
   before continuing the same smooth fade to fully transparent by the
   bottom of the box — still one continuous gradient, no hard edge, no
   visible strip; it reads as the hero calming behind the nav, not as
   a header background. This is also what makes the nav-links'
   dark-ink color (below) reliable regardless of the photo underneath:
   a consistently light backdrop, not per-image tuning. */
.stage-scrim-top{position:absolute; top:0; left:0; right:0; height:260px; background:linear-gradient(to bottom, rgba(247,245,239,.82) 0%, rgba(247,245,239,.7) 30%, rgba(247,245,239,.32) 62%, rgba(247,245,239,0) 100%); pointer-events:none; z-index:1;}
.stage-fade-bottom{position:absolute; left:0; right:0; bottom:0; height:160px; background:linear-gradient(to bottom, rgba(247,245,239,0), var(--surface-page)); pointer-events:none; z-index:1;}

/* The text-protection mechanism: a soft, asymmetric pool of the
   page's own cream surface color, sized and positioned to comfortably
   contain the whole copy block with a wide margin, fading smoothly to
   fully transparent well before it reaches the photo's more active
   areas (the subject, the skyline). This is not a card, panel, or
   glass box — there is no edge, border, or shadow anywhere; it simply
   reads as light. Because its center is ~95% opaque, the effective
   color behind the text is always close to pure cream regardless of
   what's in the photo underneath — this is what makes it robust for
   ANY of the rotating scenes (or any future CMS-uploaded one), not
   tuned to a single candidate. The ellipse's size is percentage-based
   (relative to the stage box), so it scales with the stage rather
   than needing per-image or per-copy-length tuning. */
/* Pass 2: lightened and tightened from the original candidate — the
   center opacity, the mid-stop opacity and the falloff distance were
   all pulled back so more of the photography reads through beyond
   the copy column itself, while the center still sits close enough
   to opaque cream that text contrast is unaffected. Re-checked
   against all three bundled scenes, not tuned to Scene 01 alone. */
.stage-copy-veil{
  position:absolute; inset:0; z-index:1; pointer-events:none;
  background:radial-gradient(ellipse 56% 70% at 23% 45%,
    rgba(247,245,239,.86) 0%,
    rgba(247,245,239,.72) 32%,
    rgba(247,245,239,.34) 54%,
    rgba(247,245,239,0) 74%);
}

.stage-copy-wrap{position:relative; z-index:2; max-width:1240px; margin:0 auto; padding:calc(76px + var(--space-7)) var(--space-6) 0;}
.opening-copy{max-width:600px; display:flex; flex-direction:column; gap:18px;}
/* Larger, more editorial than the sitewide default .h1 (56px) — the
   Opening is the one place on the homepage this scale is earned; .h1
   is otherwise unused elsewhere on the page. clamp() keeps it from
   overrunning narrower desktop widths before the mobile block below
   takes over entirely. */
/* Pass 4: one fluid clamp spanning the full phone -> desktop range
   (previously a fixed 38px below 861px and a separate clamp(42px,
   5vw, 72px) above it — a real, if modest, jump right at the
   breakpoint). 8vw naturally bottoms out at the 34px floor below
   ~425px, scales smoothly from there through every intermediate width
   in the brief's test matrix, and reaches the 72px ceiling around
   ~900px — no breakpoint-authored jump anywhere in between. */
.opening .h1{max-width:600px; font-size:clamp(34px, 8vw, 72px); line-height:1.08;}
.opening-lede{max-width:460px;}
.opening .cta-row{margin-top:10px;}
/* Restrained outlined/pill CTA language, scoped to the Opening only
   (sitewide .btn-primary/.btn-secondary elsewhere — e.g. Human — stay
   exactly as they are). Both become full pills instead of the
   sitewide 3px corner radius. Pass 2: brought closer to
   docs/design-references/hero-direction-v1.png's editorial weight without
   copying it literally (no uppercase, no tracked type, no arrow
   glyph, no icon) — a touch shorter, a lighter font-weight and a
   fraction more letter-spacing than the sitewide .btn so the pill
   reads as typography-led rather than a stock website button; still
   flat, no gradient, no shadow, no glass. */
.opening .btn{
  border-radius:var(--radius-pill); padding:0 32px; height:46px;
  font:500 13.5px var(--font-sans); letter-spacing:.02em;
  transition:background .2s ease, border-color .2s ease, color .2s ease, transform .2s ease;
}
.opening .btn-primary:hover{transform:translateY(-1px);}
/* Pass 3: the hero's secondary WhatsApp CTA moved from a hollow
   outline (too dependent on whatever photo sat behind it — read as
   too weak over brighter scenes, especially at mobile widths) to a
   soft cream fill, so its own surface — not the photograph — carries
   readability. Still clearly lower-hierarchy than the emerald primary
   (no fill saturation, no brand color), still a fine border rather
   than a heavy one, still flat — no shadow, no glow, no glass. */
.opening .btn-secondary{
  background:rgba(247,245,239,.62);
  border-color:rgba(30,29,25,.24);
  color:var(--ink-primary);
}
.opening .btn-secondary:hover,
.opening .btn-secondary:focus-visible{
  background:rgba(247,245,239,.8);
  border-color:var(--ink-primary);
  transform:translateY(-1px);
}

/* Quiet invitation to continue, not a second CTA — a short vertical
   track with a dot that eases down it on loop under motion;
   reduced-motion gets the label/line with no animated dot. Fades out
   once the visitor actually starts scrolling (see main.js, the same
   scroll listener that already toggles the overlay nav).
   Pass 2: moved from bottom-left (felt detached from the other
   bottom-level hero controls) to bottom-center on desktop, kept as
   its own affordance rather than folded into the scene-control unit
   on the right — see the min-width:861px override below. Base rule
   stays left-aligned for mobile, which keeps its own bottom-left
   placement unchanged (see the max-width:860px block). */
/* Pass 4: fluid inset (clamp, not a fixed 24px/32px that jumped at
   the breakpoint) so the cue's distance from the edge scales smoothly
   across the full width range instead of two flat values. */
.stage-scroll-cue{
  position:absolute; left:clamp(16px, 4vw, 32px); bottom:clamp(16px, 4vw, 32px); z-index:2;
  display:flex; align-items:center; gap:10px; text-decoration:none; color:var(--ink-primary);
  opacity:1; transition:opacity .4s ease;
}
@media (min-width:861px){
  .stage-scroll-cue{left:50%; transform:translateX(-50%);}
}
.stage-scroll-cue.is-hidden{opacity:0; pointer-events:none;}
.stage-scroll-cue-track{position:relative; width:1px; height:34px; background:var(--border-strong); overflow:hidden; transition:height .25s ease;}
.stage-scroll-cue-dot{position:absolute; left:-1.5px; top:0; width:4px; height:4px; border-radius:50%; background:var(--accent-gold);}
@media (prefers-reduced-motion: no-preference){
  .stage-scroll-cue-dot{animation:shahraah-scroll-cue 2.2s ease-in-out infinite;}
}
@keyframes shahraah-scroll-cue{
  0%{transform:translateY(0); opacity:0;}
  15%{opacity:1;}
  85%{opacity:1;}
  100%{transform:translateY(30px); opacity:0;}
}
.stage-scroll-cue-label{font:600 11px/1 var(--font-sans); letter-spacing:.06em; text-transform:uppercase; color:var(--ink-secondary); transition:color .2s ease;}
/* Restrained hover/focus — a quiet browsing affordance, not a button:
   the line lengthens a touch and the label darkens, nothing bouncing
   or dramatic. Keyboard focus still gets the sitewide :focus-visible
   outline (see top of file) — untouched here. */
.stage-scroll-cue:hover .stage-scroll-cue-track,
.stage-scroll-cue:focus-visible .stage-scroll-cue-track{height:42px;}
.stage-scroll-cue:hover .stage-scroll-cue-label,
.stage-scroll-cue:focus-visible .stage-scroll-cue-label{color:var(--ink-primary);}

/* Bottom-right scene counter + manual controls — functional, not
   decorative pagination dots: shows `current — total`, both derived
   from the real rendered scene collection (see opening.php/main.js),
   only rendered at all when there are 2+ scenes.
   Pass 2: previously three separately-chromed elements (two bordered
   circle buttons either side of bare text) — now one hairline pill
   container holds prev / counter / next as a single compact editorial
   control system. The buttons themselves drop their own border/fill
   (the container now carries that) so nothing gets louder or larger;
   cohesion, not prominence, per the brief. No slider dots, no large
   arrows added. */
/* Pass 4: fluid inset here too, same reasoning as the scroll cue
   above — scales smoothly instead of jumping at the breakpoint. */
.stage-scene-nav{
  position:absolute; right:clamp(16px, 4vw, 32px); bottom:clamp(16px, 4vw, 32px); z-index:2;
  display:flex; align-items:center; gap:8px;
  padding:5px 12px 5px 5px;
  border:1px solid var(--border-strong); border-radius:var(--radius-pill);
  background:rgba(247,245,239,.55);
}
.stage-scene-btn{
  display:flex; align-items:center; justify-content:center; width:30px; height:30px;
  border-radius:50%; border:none; background:transparent;
  color:var(--ink-primary); cursor:pointer; padding:0; opacity:.72;
  transition:opacity .2s ease, transform .2s ease;
}
.stage-scene-btn:hover,
.stage-scene-btn:focus-visible{opacity:1; transform:translateY(-1px);}
.stage-scene-counter{font-size:12.5px; letter-spacing:.02em; color:var(--ink-secondary); display:flex; align-items:center; gap:6px;}
.stage-scene-counter [data-hero-current]{color:var(--ink-primary);}
.stage-scene-counter-sep{color:var(--border-strong);}

/* ---------- 02 services — reference v2 rebuild ----------
   Visual rebuild against the approved
   docs/design-references/services-reference-v2.png. Replaces the
   prior selector-list + sticky stage/context composition with the
   reference's own shape: a centered head, a six-item icon nav, one
   large cinematic image stage with real prev/next controls, an
   overlapping ivory content panel, and a counter/progress footer row.
   One active-service index still drives everything (nav, image,
   panel, counter, progress, footer labels — see capabilities.php and
   main.js); there is no separate hover-preview state this pass — the
   reference has no such concept, and manual nav/arrow interaction is
   the only way to change the active service (no autoplay, per brief).
   - .service-nav — always fully legible, exactly one active.
   - .service-stage — the single visual layer (photo only, never any
     baked-in text); its own real prev/next controls live on it.
   - .service-panel — the informational/action layer, overlapping the
     stage's lower-right (mobile: emerging from beneath it). All six
     .service-panel-item share one CSS Grid cell so the panel's height
     is always the tallest of the six, regardless of which is active —
     no layout jump switching between different-length copy or
     with/without a CTA (Travel Assistance has none; see
     .service-panel-item's vertical centering below). */
/* Reference-fidelity correction pass: vertical rhythm is now fluid/svh-aware
   throughout (section padding, head/nav/footer margins) so the whole
   chapter is perceivable at short desktop viewports (1366x768)
   without either forcing 100vh or shrinking everything down — same
   svh-clamp technique Discover's `.discover-grid` already uses. */
.services{max-width:none; padding-top:clamp(28px, 5svh, var(--space-7)); padding-bottom:clamp(40px, 7svh, var(--space-8));}
.services-inner{max-width:1128px; margin:0 auto;}

.services-head{max-width:640px; margin:0 auto clamp(24px, 3.5svh, var(--space-6)); text-align:center;}
.services-eyebrow-row{display:flex; align-items:center; justify-content:center; gap:12px; margin-bottom:var(--space-4);}
.services-eyebrow-row .eyebrow{margin:0;}
.services-subline{font:500 12.5px/1.4 var(--font-sans); letter-spacing:.06em; text-transform:uppercase; color:var(--ink-secondary); margin:var(--space-4) 0 0;}

/* Nav: every service fully legible at rest, exactly one active — the
   focus/respond index for this section (see CLAUDE.md). Wraps on
   narrow desktop widths rather than shrinking illegibly; the mobile
   block below switches this to a compact single-row horizontal-scroll
   rail instead. The header-row prev/next arrows from the first pass
   were removed per direct review — the on-image stage arrows
   (.service-stage-arrow below) are the reference's only arrow pair. */
.service-nav-wrap{position:relative; margin:0 auto clamp(20px, 3svh, var(--space-6));}
.service-nav{display:flex; align-items:flex-start; justify-content:center; gap:clamp(18px, 3vw, 40px); flex-wrap:wrap;}
.service-nav-item{
  position:relative; display:flex; flex-direction:column; align-items:center; gap:8px;
  background:none; border:none; padding:4px 2px 10px; font-family:inherit; cursor:pointer;
}
/* Desktop-only separators between the six selector items — a
   reference detail this pass adds (see the correction-pass brief,
   "Desktop Selector Separator Lines"). A real (but aria-hidden)
   element between nav buttons, not a pseudo-element on .service-nav-
   item — both of that element's own pseudo slots are already spoken
   for (::before is the halo below, ::after is the gold underline).
   Hidden entirely on mobile (see the max-width:860px query) — a
   desktop composition detail only, per the brief. */
.service-nav-sep{display:block; width:1px; align-self:center; height:34px; background:var(--border-strong); opacity:.6;}

/* Soft selection halo behind the active icon — a pale radial wash,
   not a card/pill/glow (see CLAUDE.md's anti-glassmorphism rule).
   Painted first (before the icon/label spans in source order), so it
   sits behind them without needing an explicit z-index. Enlarged and
   strengthened this pass — direct review found the original halo
   "barely perceptible"; still restrained (no hard edge, no card). */
.service-nav-item::before{
  content:""; position:absolute; top:-22px; left:50%; width:108px; height:108px;
  transform:translateX(-50%); border-radius:50%;
  background:radial-gradient(circle, rgba(5,78,57,.16) 0%, rgba(5,78,57,.07) 52%, rgba(5,78,57,0) 74%);
  opacity:0; transition:opacity .3s var(--ease); pointer-events:none;
}
.service-nav-item.is-active::before{opacity:1;}
.service-nav-icon{
  display:flex; align-items:center; justify-content:center; width:56px; height:56px;
  border-radius:50%; border:1px solid var(--border-strong); background:var(--surface-card);
  color:var(--accent-gold-strong);
  transition:background-color .25s var(--ease), border-color .25s var(--ease), color .25s var(--ease);
}
.service-nav-label{font:500 13px var(--font-sans); color:var(--ink-primary); transition:color .2s var(--ease), font-weight .2s var(--ease);}
.service-nav-item::after{content:""; position:absolute; left:50%; bottom:0; width:0; height:2px; background:var(--accent-gold); transform:translateX(-50%); transition:width .25s var(--ease);}
.service-nav-item:hover .service-nav-icon{border-color:var(--accent-gold); color:var(--accent-gold-strong);}
.service-nav-item.is-active .service-nav-icon{background:var(--brand-green); border-color:var(--brand-green); color:var(--ink-inverse); box-shadow:0 0 0 4px rgba(5,78,57,.06);}
.service-nav-item.is-active .service-nav-label{color:var(--brand-green); font-weight:600;}
.service-nav-item.is-active::after{width:24px;}

/* Mobile-only horizontal-scroll affordance chrome — hidden entirely
   on desktop (see the min-width:861px query) where the nav never
   overflows. See main.js for the scroll/edge-detection logic that
   toggles [hidden] and the has-scroll-* classes below. */
.service-nav-scroll-btn{display:none;}

/* Stage: one cinematic photo field. Purely visual — real service
   imagery only, no text baked in, no gradient/flat fallback needed
   (all six services always carry approved photography, see
   capabilities.php). Height is svh-clamped on desktop (see the
   min-width:861px query) rather than a fixed aspect-ratio, so the
   whole composition can genuinely fit shorter viewports instead of
   just getting cropped by the fold. */
.service-stage-wrap{position:relative;}
.service-stage{position:relative; overflow:hidden; border-radius:24px; background:var(--brand-green); aspect-ratio:3/2;}
.service-stage-panel{position:absolute; inset:0; opacity:0; transition:opacity .4s var(--ease);}
.service-stage-panel.is-active{opacity:1; z-index:1;}
.service-stage-media{position:absolute; inset:0; width:100%; height:100%; object-fit:cover; object-position:center;}
/* Essential subject (ihram travel kit) sits left-of-centre in the
   source photo — bias the crop so a wide stage never trims it off the
   left edge. See capabilities.php. */
.service-stage-media--umrah{object-position:32% 55%;}

.service-stage-arrow{
  position:absolute; top:50%; transform:translateY(-50%); z-index:3;
  display:flex; align-items:center; justify-content:center; width:42px; height:42px;
  border-radius:50%; border:none; background:rgba(247,245,239,.92); color:var(--ink-primary);
  cursor:pointer; box-shadow:0 6px 18px rgba(30,29,25,.18);
  transition:background-color .2s ease, color .2s ease, transform .2s ease;
}
.service-stage-arrow--prev{left:clamp(10px, 2vw, 18px);}
/* Right arrow sits further from the panel than the left one from its
   own edge — this pass's fix for the arrow/panel collision the user
   found (see .service-panel's right inset below, which was widened
   to match): the two values together guarantee a real gap at every
   viewport in the 861-1920px range, verified live. */
.service-stage-arrow--next{right:clamp(10px, 1.6vw, 16px);}
.service-stage-arrow:hover, .service-stage-arrow:focus-visible{background:var(--surface-card); color:var(--accent-gold-strong); transform:translateY(-52%);}

/* Panel: mobile-first default overlaps the stage's lower area via a
   negative top margin (unchanged from the original pass — the mobile
   composition was reviewed and kept). Desktop (861px query) replaces
   this entirely: the panel becomes CONTAINED within the image stage's
   own box (top+bottom insets, not a bottom overflow). See the
   file-level comment above for the shared-grid-cell height mechanism
   (unchanged).

   Correction pass: padding split top/bottom (was one shorthand value)
   — direct review found real wasted space above the `01 / 06` counter,
   traced to .service-panel-item's own justify-content:center (below)
   vertically centering a SHORT content block inside a TALL fixed-
   height panel, not the padding itself. Top padding is now
   deliberately smaller than bottom; the content-vertical-centering fix
   is the real correction (see .service-panel-item below). */
.service-panel{
  position:relative; z-index:2; margin:-15% 16px 0; display:grid;
  background:var(--surface-page); border-radius:18px; padding:22px 24px 28px;
  box-shadow:0 20px 44px rgba(30,29,25,.14);
}
/* Every service now shares one fixed internal skeleton — counter,
   title, a fixed-height description slot, perks, then (when present)
   a CTA pinned to the bottom via margin-top:auto — instead of
   centering the whole content block per service. This is what
   actually guarantees the counter/title/perks begin at an identical
   position across all six services (see the correction-pass brief,
   points 1-3, 9-11): a top-aligned column with a reserved description
   height is a structural guarantee, not a per-service coincidence.
   Travel Assistance (no CTA) shares the exact same top anchors; its
   column is simply shorter, with the panel's own fixed height (see
   the 861px query) absorbing the difference as quiet lower breathing
   room rather than an empty button-shaped hole. */
.service-panel-item{
  grid-column:1; grid-row:1; display:flex; flex-direction:column; gap:12px;
  opacity:0; visibility:hidden; pointer-events:none; transition:opacity var(--duration-transition) var(--ease), visibility var(--duration-transition) var(--ease);
}
.service-panel-item.is-active{opacity:1; visibility:visible; pointer-events:auto;}
.service-panel-counter{display:flex; align-items:center; gap:10px; color:var(--accent-gold-strong); font-size:13px;}
.service-panel-counter-rule{width:22px; height:1px; background:var(--border-strong);}
.service-panel-title{font:400 28px/1.15 var(--font-serif); color:var(--brand-green); margin:0;}
/* Fixed 2-line slot: a real CSS line-clamp (the visual GUARANTEE — a
   third line can never render, regardless of what ACF's maxlength
   editorial guard lets through), plus a min-height reserved for two
   lines even when the real copy is short enough to wrap to one — so
   perks/CTA never "jump upward" for a one-line description (see the
   brief, points 7-9). min-height math: 15px font x 1.55 line-height x
   2 lines = 46.5px, expressed in em so it tracks the font-size above. */
.service-panel-desc{
  font:400 15px/1.55 var(--font-sans); color:var(--ink-secondary); margin:0;
  min-height:3.1em;
  display:-webkit-box; -webkit-line-clamp:2; line-clamp:2; -webkit-box-orient:vertical; overflow:hidden;
}
.service-panel-perks{list-style:none; margin:0; padding:0; display:flex; flex-wrap:wrap; align-items:center; gap:8px 0;}
.service-panel-perk{display:flex; align-items:center; gap:6px; font:400 12.5px var(--font-sans); color:var(--ink-secondary);}
.service-panel-perk-icon{display:flex; color:var(--accent-gold-strong);}
.service-panel-perk-sep{width:1px; height:12px; background:var(--border-strong); margin:0 10px;}
/* margin-top:auto pins the CTA to the same bottom band regardless of
   which service is active (see the brief, point 11: "all CTA buttons
   should feel anchored to a shared grid") — it's the last flex child,
   so it absorbs whatever space the (now fixed-height) content above
   it doesn't use, rather than sitting immediately after the perks. */
.service-panel-cta{align-self:flex-start; margin-top:auto; padding-top:4px;}

/* Footer row: counter + six-segment progress (left) and the service
   labels (right) — BOTH are now a real, fully synchronized second
   navigation surface (see the correction-pass brief: "Footer Nav +
   Progress = Second Navigation Surface"), not decorative echoes.
   Every control targets the same shared activeIndex via main.js. */
.service-footer-row{display:flex; flex-wrap:wrap; align-items:center; justify-content:space-between; gap:12px; margin-top:clamp(20px, 3svh, var(--space-6));}
.service-progress{display:flex; align-items:center; gap:14px;}
.service-progress-counter{font-size:13px; color:var(--accent-gold-strong);}
/* The visible bar stays a quiet, narrow 22x2px device; the button's
   own box is a larger 22x22 tap target (see the brief: "visual bar
   can remain narrow, hit area can be larger"). */
.service-progress-track{display:flex; gap:6px;}
.service-progress-seg{
  width:22px; height:22px; padding:0; border:none; background:none; cursor:pointer;
  display:flex; align-items:center; justify-content:center;
}
.service-progress-seg::after{content:""; display:block; width:100%; height:2px; background:var(--border); transition:background-color .2s ease;}
.service-progress-seg.is-active::after{background:var(--accent-gold);}
.service-progress-seg:hover::after{background:var(--accent-gold-strong);}

.service-footer-labels{display:flex; align-items:center; flex-wrap:wrap; gap:6px; font:400 11px var(--font-sans); letter-spacing:.06em; text-transform:uppercase;}
.service-footer-label{
  background:none; border:none; padding:2px; font:inherit; letter-spacing:inherit; text-transform:inherit;
  color:var(--ink-secondary); cursor:pointer; transition:color .2s ease, font-weight .2s ease;
}
.service-footer-label:hover{color:var(--ink-primary);}
.service-footer-label.is-active{color:var(--accent-gold-strong); font-weight:600;}
.service-footer-sep{color:var(--border-strong);}

@media (min-width:861px){
  .service-panel{
    position:absolute; margin:0; z-index:2;
    top:9%; right:clamp(68px, 6vw, 92px); bottom:9%;
    width:clamp(300px, 32%, 380px); padding:clamp(22px, 2.6vw, 32px) clamp(24px, 2.6vw, 34px) clamp(26px, 3vw, 34px);
  }
  .service-stage{aspect-ratio:auto; height:clamp(320px, 46svh, 480px);}
}

/* ---------- 03 discover (V2.1 reference-match correction) ----------
   Rebuilt again against the same approved reference
   (docs/design-references/discover-reference-v2.png) for literal
   visual fidelity, per direct review — the prior pass (v2) had the
   right components (ivory column, static photo, floating rail,
   response panel) but read as a contained text column beside a
   rectangular photo card, not the reference's one integrated
   cinematic composition. This pass changes ONLY the visual
   architecture; the interaction/data layer is untouched (see
   inc/discover.php, assets/js/main.js — same functions, same ACF
   repeater, same rail-window/selection logic).

   Root architectural change: `.discover-visual` is no longer a grid
   sibling sized by row content — at desktop it becomes a full-bleed
   `position:absolute` layer behind everything, and `.discover-grid`
   gets an explicit fluid height (clamp/svh) instead of a
   content-derived one. This is also the fix for a real bug found in
   review: the response panel's height used to vary by destination
   (an empty state vs. a real linked flight offer are different
   heights), and because the old grid-template-areas approach sized
   `.discover-visual` by MERGING it across those same rows, a taller
   response row made the row track taller, which made the visual
   layer — including the "static" background photo — taller too,
   producing a visible resize/repaint on selection (most noticeable
   selecting Istanbul, the one seeded destination with real linked
   content, since its response is a different height than the three
   empty-state defaults). An absolutely-positioned visual layer can't
   be resized by its siblings' content at all, which is what makes the
   background genuinely inert now, not just visually unchanged. */
.discover{padding-top:var(--space-6);}
/* Viewport-fit pass: the grid's own
   uniform `gap` is gone in favor of explicit per-child margins below —
   needed once `.discover-response` became a grid-level sibling rather
   than nested inside `.discover-intro` (see the template), so each
   join (intro→visual, visual→response, response→rule) can be tuned on
   its own terms instead of sharing one clamp() value. */
.discover-grid{display:flex; flex-direction:column;}
.discover-intro{display:flex; flex-direction:column; gap:14px; max-width:520px;}
.discover-eyebrow-row{display:flex; align-items:center; gap:12px;}
.discover-eyebrow-rule{width:36px;}
.discover-heading{max-width:9ch;}
.discover-body{max-width:440px;}
.discover-rule{margin:22px 0 20px;}

/* ---- visual: one static photographic stage + floating destination rail ----
   Reference-fidelity pass (v3): mobile is now genuinely ONE
   visual unit rather than photo-then-list. `.discover-visual` is the
   whole stage (a fixed, viewport-relative height); the photo and the
   rail are both its children, the rail absolutely positioned to overlap
   the photo's own lower portion instead of sitting in normal flow below
   it. `--dcard-h`/`--dcard-gap` size both the cards and the viewport
   window that clips them to exactly 3 — redefined once, at the 861px
   breakpoint, back to the original 88px/12px desktop figures, so the
   "exactly 3 cards" window stays correct at both sizes without
   duplicating the arithmetic in two places. The decorative flight-arc
   SVG this build used to carry here has been removed outright (not
   replaced) — CLAUDE.md already rejects route-line/waypoint motifs
   sitewide; this was the one section-specific exception, now retired
   per direct review. */
.discover-visual{
  position:relative; height:clamp(400px, 104vw, 460px);
  margin-top:clamp(24px, 6vw, 36px);
  --dcard-h:clamp(60px, 16vw, 72px); --dcard-gap:8px;
}
/* Mobile cropping correction: a real
   <img> (object-fit:cover) instead of a CSS background-image, so mobile
   can get its own object-position without a second positioned element.
   `--default-crop` (added in the template only when no dedicated mobile
   photo is set) shifts the shared photo's crop rightward on mobile —
   the default centered crop showed mostly the traveler's head/shoulders
   at mobile's narrow portrait ratio, cropping out most of the skyline;
   shifting right keeps him present at the frame's left while revealing
   the mosque/skyline that was previously cropped off. Forced back to
   dead-center at 861px+ (desktop, unchanged) and for a real dedicated
   mobile upload (already composed for the crop, not this photo's own
   tuned offset). */
.discover-visual-media{
  position:absolute; inset:0; border-radius:14px; overflow:hidden;
  background:var(--brand-green);
}
.discover-visual-media img{
  display:block; width:100%; height:100%; object-fit:cover; object-position:center;
}
.discover-visual-media--default-crop img{object-position:64% center;}
/* Photographic readability gradient — mostly natural near the top (the
   traveler and skyline stay legible), a deliberate darkening toward the
   bottom where the rail sits so white card labels stay readable. Warm
   dark-green, never flat black, so the photo keeps its own cast. */
.discover-visual-fade{
  position:absolute; inset:0; pointer-events:none; border-radius:14px;
  background:linear-gradient(180deg,
    rgba(6,14,11,0) 0%,
    rgba(6,14,11,0) 40%,
    rgba(6,14,11,.38) 66%,
    rgba(6,14,11,.8) 100%);
}
/* Selector module polish pass: the rail wrap is now the grouped panel
   itself — a "smoked glass / dark emerald film" that the 3-card rail
   and its browse arrows visually live inside, instead of floating
   loose over the busy photo. Padding reserves room for the browse
   arrows (repositioned to sit just inside the panel's own edge — see
   `.rail-arrow-*` below) without them ever touching the viewport;
   `.destination-rail-viewport`'s own width stays relative (`100%` of
   this panel's now-smaller content box), so the cards themselves don't
   need their own dimensions touched. Mobile gets a touch darker than
   desktop (smaller module, denser photography behind it, less native
   contrast) — see the 861px override below. */
.destination-rail-wrap{
  position:absolute; left:10px; right:10px; bottom:10px; z-index:1;
  display:flex; flex-direction:column; align-items:center; gap:8px;
  padding:30px 10px;
  border-radius:24px;
  background:rgba(5,20,16,.40);
  border:1px solid rgba(255,255,255,.12);
  box-shadow:0 14px 34px rgba(0,0,0,.28);
  -webkit-backdrop-filter:blur(10px);
  backdrop-filter:blur(10px);
}
.destination-rail-viewport{
  width:100%; height:calc(var(--dcard-h) * 3 + var(--dcard-gap) * 2); overflow:hidden;
}
.destination-rail{
  display:flex; flex-direction:column; gap:var(--dcard-gap);
  transition:transform .38s var(--ease);
}
.dest-card{
  flex:0 0 auto; cursor:pointer; padding:0; text-align:left;
  width:100%; height:var(--dcard-h); border-radius:12px; border:none;
  position:relative; overflow:hidden; background:var(--surface-card);
  box-shadow:0 8px 20px rgba(0,0,0,.18);
  transition:box-shadow .25s ease, transform .25s var(--ease);
}
.dest-card-media{position:absolute; inset:0; background:var(--brand-green) center/cover no-repeat; transition:filter .25s ease;}
.dest-card-media--fallback{background:var(--brand-green);}
/* Readability polish pass: mobile-first, slightly stronger than desktop
   — smaller cards, denser photography, less room for contrast at that
   scale (desktop restores a lighter version inside the 861px query, same
   three-stop shape). Warm dark-green-black (not flat black) so the photo
   keeps its own cast underneath; still real photography, not a flattened
   dark card. */
.dest-card-scrim{
  position:absolute; inset:0;
  background:linear-gradient(180deg, rgba(5,20,16,.14) 0%, rgba(5,20,16,.30) 45%, rgba(5,20,16,.70) 100%);
}
.dest-card-name{
  position:absolute; left:14px; bottom:10px; font:600 15px var(--font-serif); color:var(--ink-inverse);
  text-shadow:0 1px 4px rgba(0,0,0,.35);
}
.dest-card-arrow{
  position:absolute; right:10px; bottom:10px; width:26px; height:26px; border-radius:50%;
  display:flex; align-items:center; justify-content:center; color:var(--ink-inverse);
  border:1px solid rgba(187,158,97,.7); background:rgba(6,14,11,.4);
  transition:border-color .2s ease, background-color .2s ease;
}
/* Selected state: a fine gold line INSIDE the card's own geometry via
   ::after (inset:0, border-radius:inherit) — never an exterior
   box-shadow ring, which visibly changed the card's apparent size and
   read as unbalanced in review. The card's footprint/layout position
   never changes between selected and unselected. Readability pass adds
   a slightly stronger arrow border/backdrop on the selected card only —
   geometry and the gold hairline itself are untouched. */
.dest-card::after{
  content:""; position:absolute; inset:0; border-radius:inherit;
  pointer-events:none; box-sizing:border-box; border:1px solid transparent;
  transition:border-color .2s ease;
}
.dest-card[aria-selected="true"]::after{border-color:var(--accent-gold);}
.dest-card[aria-selected="true"] .dest-card-media{filter:brightness(1.04) saturate(1.05);}
.dest-card[aria-selected="true"] .dest-card-arrow{border-color:rgba(187,158,97,.95); background:rgba(6,14,11,.52);}
.dest-card:hover{transform:translateY(-2px); box-shadow:0 10px 24px rgba(0,0,0,.22);}
.dest-card:hover .dest-card-media{filter:brightness(1.06);}
.dest-card[aria-selected="true"]:hover .dest-card-media{filter:brightness(1.08) saturate(1.05);}

/* Browse arrows: `[hidden]` removes them from layout, the tab order
   and the accessibility tree entirely at each boundary — but each is
   ALSO always `position:absolute` (below) relative to the rail wrap,
   so even that removal can never shift the rail viewport itself,
   which sits in normal flow between them regardless of which arrows
   exist at a given moment. */
.rail-arrow{
  position:absolute; left:50%; margin-left:-14px; z-index:2;
  display:flex; align-items:center; justify-content:center; width:28px; height:28px;
  border-radius:50%; border:1px solid rgba(247,245,239,.55); background:rgba(30,29,25,.32);
  backdrop-filter:blur(2px); color:var(--ink-inverse); cursor:pointer; padding:0;
}
/* margin-left, not transform:translateX, for the centering above —
   the float animation below already animates `transform`
   (translateY), and a static transform on the same property would be
   clobbered the instant the keyframe starts. Reserved/absolute at every
   breakpoint (mobile figures here, desktop restores the original 32px
   figures) so `[hidden]` toggling can never shift the rail viewport
   itself or grow the visual stage. Selector-module pass: repositioned
   from floating outside the rail-wrap (`-16px`/`-44px`, before the wrap
   became a visible panel) to sitting just inside its own padded edge,
   so the browse control now visually belongs to the same glass module
   as the cards rather than reading as a separate floating control. */
.rail-arrow-up{top:8px;}
.rail-arrow-down{bottom:8px;}
.rail-arrow:hover{border-color:var(--accent-gold); color:var(--accent-gold);}
.rail-arrow[hidden]{display:none;}
@media (prefers-reduced-motion: no-preference){
  /* A very subtle affordance float — never a bounce, never looping
     large distances, gated off under reduced-motion. */
  .rail-arrow:not([hidden]){animation:discover-arrow-float 2.4s ease-in-out infinite;}
}
@keyframes discover-arrow-float{
  0%, 100%{transform:translateY(0);}
  50%{transform:translateY(3px);}
}

/* ---- response: real, structured, per-destination facts ----
   Destination-response content rebuild: the response is no longer a
   fixed-height single fact line — it's a compact editorial row list,
   one row per category (Flight, Visa, Hotel, Transport, More, or
   whatever staff configure), height varying naturally with however
   many rows a destination actually has. This is safe now (unlike the
   v2.1 fixed-height workaround this replaces): `.discover-visual` is
   independently positioned — absolute on desktop, its own fixed
   clamp() height on mobile (see above) — so it can no longer be
   resized by the response's content, regardless of row count. Opacity
   is the only thing that ever transitions here — a plain, restrained
   fade driven from main.js, skipped entirely under
   prefers-reduced-motion. */
.discover-response{
  display:flex; flex-direction:column; gap:4px; max-width:520px;
  margin-top:24px; padding-top:14px; border-top:1px solid var(--border);
  transition:opacity .2s ease;
}
.discover-response.is-transitioning{opacity:0;}
.discover-response-eyebrow{color:var(--brand-green); display:block; white-space:nowrap; overflow:hidden; text-overflow:ellipsis;}
.discover-response-desc{
  margin:0 0 6px; font:400 14px/1.4 var(--font-sans); color:var(--ink-secondary);
  white-space:nowrap; overflow:hidden; text-overflow:ellipsis;
}
.discover-response-fact{
  margin:0; font:400 13.5px/1.4 var(--font-sans); color:var(--ink-primary);
  white-space:nowrap; overflow:hidden; text-overflow:ellipsis;
}
.discover-response-fact--empty{font-style:italic; color:var(--ink-secondary);}
.discover-response-fact--empty a{color:var(--brand-green); text-decoration:underline; font-style:normal;}

/* ---- response rows: structured editorial facts, never feature cards ----
   [icon] [GOLD LABEL] [content], label column width fixed so rows
   visually align regardless of icon presence (an icon-less row still
   reserves the column via the empty `.discover-response-row-icon`
   span). Ordinary rows are plain, non-interactive containers — only
   the inline "See more" control (a real button, revealed by
   assets/js/main.js's applyRowClamps() when a row's text genuinely
   overflows one line) is ever clickable inside them. The "More" row
   is the one exception: the whole row is a real <button> (never a
   clickable div), styled distinctly (gold, plus-circle icon) so it
   reads as intentionally actionable without becoming a filled CTA. */
.discover-response-rows{display:flex; flex-direction:column;}
.discover-response-row{
  display:flex; align-items:flex-start; gap:10px;
  padding:6px 0; border-top:1px solid var(--border);
  text-align:left;
}
.discover-response-row:first-child{border-top:0; padding-top:0;}
.discover-response-row-icon{
  flex:0 0 16px; width:16px; height:16px; margin-top:2px;
  color:var(--accent-gold-strong);
}
.discover-response-row-icon svg{display:block; width:100%; height:100%;}
.discover-response-row-body{flex:1 1 auto; min-width:0; display:flex; flex-direction:column; gap:1px;}
.discover-response-row-label{
  font:600 10.5px var(--font-sans); letter-spacing:.06em; text-transform:uppercase;
  color:var(--accent-gold-strong);
}
/* One-line clamp: a flex row rather than
   `-webkit-line-clamp`, so "See more" sits inline at the end of the
   same truncated line instead of wrapping onto a detached line below
   it — exactly the "belongs to that row" requirement the brief calls
   out, and it also keeps each row exactly one line tall (line-clamp's
   block-level box forced an extra line for the trailing button
   underneath it, which was the real cause of taller-than-intended
   rows). `min-width:0` on the shrinkable text span is required for
   flex-child `text-overflow:ellipsis` to work at all — without it a
   flex item won't shrink below its content's natural width. Measured
   the same way as before (assets/js/main.js's applyRowClamps()), just
   comparing `scrollWidth`/`clientWidth` now instead of height. */
.discover-response-row-text{
  display:flex; align-items:baseline; gap:4px; min-width:0;
  margin:0; font:400 13px/1.4 var(--font-sans); color:var(--ink-primary);
}
.discover-response-row-text-content{
  flex:1 1 auto; min-width:0;
  white-space:nowrap; overflow:hidden; text-overflow:ellipsis;
}
.discover-response-row-more{
  flex:0 0 auto; padding:0; border:0; background:none;
  font:600 13px var(--font-sans); color:var(--brand-green); text-decoration:underline;
  cursor:pointer;
}
.discover-response-row-more[hidden]{display:none;}
.discover-response-row-more:focus-visible{outline:2px solid var(--accent-gold); outline-offset:2px; border-radius:2px;}

/* "More" row affordance polish: a real button wrapping the same
   icon/label/text shape as ordinary rows, now with a quiet "highlighted
   editorial action row" treatment — a faint warm-gold tint and hairline
   border, small radius, a trailing chevron — so it reads as clickable
   at a glance without becoming a filled CTA or a generic card. Visual
   only: `data-discover-more` stays inert, no popup/modal wired up.
   Padding grows only slightly over ordinary rows (7px/10px vs 6px/0)
   so section height isn't materially affected. */
.discover-response-row--more{
  width:100%; cursor:pointer; font-family:inherit;
  background:rgba(187, 158, 97, .06); border:1px solid rgba(187, 158, 97, .32);
  border-radius:8px; padding:7px 10px; margin-top:6px;
  transition:background-color .2s ease, border-color .2s ease;
}
.discover-response-row--more:first-child{margin-top:0;}
.discover-response-row--more .discover-response-row-icon,
.discover-response-row--more .discover-response-row-label{color:var(--accent-gold);}
.discover-response-row--more .discover-response-row-label{font-weight:700;}
.discover-response-row--more:hover{background:rgba(187, 158, 97, .11); border-color:rgba(187, 158, 97, .5);}
.discover-response-row--more:hover .discover-response-row-icon,
.discover-response-row--more:hover .discover-response-row-label{color:var(--brand-green);}
.discover-response-row--more:focus-visible{outline:2px solid var(--accent-gold); outline-offset:2px; border-radius:8px;}
/* Trailing chevron — "opens something," restrained: small, gold,
   vertically centered, a 2px nudge on hover/focus rather than a
   competing animation. */
.discover-response-row-chevron{
  flex:0 0 auto; align-self:center; width:14px; height:14px; color:var(--accent-gold);
  transition:transform .2s ease, color .2s ease;
}
.discover-response-row-chevron svg{display:block; width:100%; height:100%;}
.discover-response-row--more:hover .discover-response-row-chevron,
.discover-response-row--more:focus-visible .discover-response-row-chevron{
  transform:translateX(2px); color:var(--brand-green);
}

@media (min-width:861px){
  .discover-response-row{padding:7px 0;}
  .discover-response-row-text{font-size:13.5px;}
  /* Desktop compaction pass: response sits directly under the intro
     copy again (its grid-level sibling position collapses back next to
     intro once `.discover-visual` is taken out of flow below), so this
     stays a tight, concise-itinerary-summary gap rather than the
     mobile figure above (which has the in-flow visual/selector between
     intro and response instead). */
  .discover-response{margin-top:18px; max-width:420px;}
}

/* ---- service row: icon-over-label, three columns, never cards ----
   Mobile-first: always 3-across (`flex:1 1 0`, no wrap) at a compact
   scale, matching the reference's mobile ending rather than wrapping to
   2+1 once labels/subs no longer fit one row. Desktop restores the
   original larger scale and free-wrap flex row below. */
.discover-services{display:flex; gap:14px;}
.discover-service{display:flex; flex-direction:column; align-items:flex-start; gap:6px; flex:1 1 0; min-width:0;}
.discover-service-icon{color:var(--accent-gold-strong);}
.discover-service-icon svg{width:18px; height:18px;}
.discover-service-label{font:600 12.5px var(--font-sans); color:var(--ink-primary);}
.discover-service-sub{font:400 11px var(--font-sans); color:var(--ink-secondary); margin-top:-2px;}

@media (min-width:861px){
  /* Cinematic full-bleed: substantially wider than the sitewide
     .section cap, so the photo can occupy real width instead of being
     squeezed into a narrow centered card. */
  /* Viewport-fit pass: tightened
     from clamp(48,6vw,80) — the prior figure, combined with the old
     78svh grid min-height below, routinely pushed the section past one
     viewport at common laptop heights (1366×768/1440×900), forcing
     scroll to see eyebrow-through-service-row as one chapter. Section
     padding and grid height are both reduced here; the one-line
     response-row clamp (see `.discover-response-row-text-content`)
     does the rest without touching typography size or the photo. */
  .discover{
    max-width:min(1760px, 96vw); margin:0 auto;
    padding-left:clamp(24px, 4vw, 72px); padding-right:clamp(24px, 4vw, 72px);
    padding-top:clamp(32px, 4vw, 56px); padding-bottom:clamp(32px, 4vw, 56px);
  }
  /* The positioning context for the absolute visual layer below, and
     a real, explicit, fluid height (never content-derived) so the
     section reads as one complete cinematic chapter at normal desktop
     sizes without the response panel — or anything else — able to
     resize it. `.discover-grid` is a column flexbox rather than a
     grid now: `.discover-visual` becomes `position:absolute` and is
     automatically excluded from flex layout, leaving intro/response/
     rule/services to stack and center naturally as the only real flex
     items. */
  .discover-grid{
    position:relative; display:flex; flex-direction:column;
    align-items:flex-start; justify-content:center; gap:0;
    min-height:clamp(480px, 64svh, 660px);
  }
  /* Explicit position+z-index — without it these static-flow siblings
     would paint underneath the absolutely-positioned visual layer
     below, per normal stacking-context rules. `.discover-response`
     needs the same treatment now that it's a `.discover-grid` sibling
     in its own right rather than a descendant of `.discover-intro`
     (see the template) — without it, it would render invisibly behind
     the photo. */
  .discover-intro{position:relative; z-index:1; max-width:420px; gap:16px;}
  .discover-response{position:relative; z-index:1;}
  .discover-rule{position:relative; z-index:1; margin:20px 0 18px;}
  .discover-services{position:relative; z-index:1; gap:32px;}
  .discover-service{flex:0 0 auto;}
  .discover-service-icon svg{width:22px; height:22px;}
  .discover-service-label{font-size:14px;}
  .discover-service-sub{font-size:13px; margin-top:-4px;}

  /* The photographic "mass": a near-square, right-anchored block that
     dominates the center-right of the composition, per direct review —
     not the previous full-bleed panoramic strip spanning the whole
     grid width (which, against `.discover-grid`'s own fixed but
     comparatively short height, always read as a wide, shallow band
     rather than the reference's taller, weightier photo). Height comes
     from `top/bottom:0` against the grid's own fixed height (set
     above); width is a viewport-relative share tuned to land close to
     1:1 — a hair wider at typical desktop widths, per the approved
     reference — across the range, rather than a literal `aspect-ratio`
     forcing a crop the source photo doesn't actually have.
     `--dcard-h`/`--dcard-gap` restore the original 88px/12px card
     figures the mobile rewrite above now parameterizes. */
  .discover-visual{
    position:absolute; top:0; right:0; bottom:0; left:auto; z-index:0;
    width:clamp(660px, 58vw, 900px); height:auto;
    --dcard-h:88px; --dcard-gap:12px;
  }
  .discover-visual-media{
    position:absolute; inset:0; border-radius:0;
  }
  /* Desktop composition is unchanged by this pass — always the
     centered crop, regardless of the mobile `--default-crop`
     modifier. */
  .discover-visual-media img{object-position:center;}
  /* The reference's ivory-into-photograph integration: a soft left-edge
     blend confined to this narrower box's own left portion — percentage
     stops (not fixed px) so the blend tracks the box's own clamp()d
     width instead of a figure calibrated to the old full-section-width
     photo.
     Eased, not a plain 2-stop linear ramp (a straight 8%→42% linear fade
     has an abrupt slope change at each end — flat, then a constant slope,
     then flat again — and that kink in the opacity curve reads as a
     visible Mach-band edge against high-contrast photography, independent
     of how much clear space sits on either side of it; this is "the
     line." Explicit rgba(247,245,239,…) stops (the same --surface-page
     value, spelled out because a gradient needs the alpha channel to
     vary and var() can't be partially overridden) approximate an
     ease-out curve instead — each segment's slope changes only a little
     from the last — so the same ivory-to-photo distance now reads as one
     continuous dissolve with no perceptible edge anywhere along it.
     This is the genuinely-wide-desktop figure (≳1300px, see the
     tablet-range override below) — at those widths there's already a
     large natural gap between the editorial column and the photo, so a
     narrower fade is plenty; widening it to the tablet figure here too
     would fade the traveler/skyline more than was ever approved for
     desktop. */
  /* inset:-2px — the fade overhangs the photo box slightly so the two
     layers' coincident (sub-pixel) edges can't anti-alias into a
     visible seam on the left and stray hairlines along the top/bottom
     where they cross the ivory dissolve. The overhang paints only
     ivory-on-ivory or transparent. The tablet-range rule below sets
     its own larger overhang. */
  .discover-visual-fade{
    inset:-2px; border-radius:0;
    background:linear-gradient(90deg,
      rgba(247,245,239,1) 0%,
      rgba(247,245,239,1) 8%,
      rgba(247,245,239,.85) 16%,
      rgba(247,245,239,.62) 24%,
      rgba(247,245,239,.38) 32%,
      rgba(247,245,239,.18) 40%,
      rgba(247,245,239,.06) 48%,
      rgba(247,245,239,0) 56%);
  }

  /* Panel grows by its own padding (16px each side, 44px top/bottom for
     arrow clearance) beyond the 300px card/viewport width the pass
     above already established — `width` bumped to 332px so the content
     box (332 − 2×16) still lands on exactly 300px, and `right` reduced
     by the same 16px so the cards themselves keep roughly the same
     on-screen position as before the panel gained its own frame. Card
     width/height are untouched. Desktop's panel is a shade lighter than
     mobile's (larger module, more native contrast against the photo). */
  .destination-rail-wrap{
    position:absolute; right:clamp(24px, 6vw, 74px); top:50%; left:auto; bottom:auto;
    transform:translateY(-50%); width:332px; z-index:2;
    padding:44px 16px;
    border-radius:26px;
    background:rgba(5,20,16,.34);
  }
  .destination-rail-viewport{max-width:300px;}
  .dest-card-name{left:16px; bottom:12px; font-size:17px;}
  .dest-card-arrow{right:12px; bottom:12px; width:30px; height:30px;}
  /* Wider desktop cards can carry a lighter scrim than mobile's smaller,
     denser tiles and still stay readable — same three-stop shape, just
     less of it. */
  .dest-card-scrim{
    background:linear-gradient(180deg, rgba(5,20,16,.09) 0%, rgba(5,20,16,.22) 45%, rgba(5,20,16,.62) 100%);
  }
  .rail-arrow{width:32px; height:32px; margin-left:-16px;}
}

/* Tablet-only override: `.discover-visual`'s width is
   floor-bound at a constant 660px for the whole 861px–~1140px range (the
   `58vw` term in its clamp() doesn't overtake the 660px floor until
   roughly there), which is also where the editorial column sits closest
   to the photo. The general 56%-span fade above reads fine once there's
   real breathing room (≳1300px, where the natural gap between the two
   is already large), but at this tighter range it finishes well short of
   the photo's actual high-contrast content — the traveler's silhouette,
   the skyline — so the eye could still trace a consistent vertical
   alignment where the box's raw, un-faded pixels began, independent of
   how gradual the curve's own shape was. Only this range gets the wider
   treatment; genuinely wide desktop is untouched (still the 56%-span
   curve above, unchanged from the prior pass), and mobile (<861px, a
   different, vertical fade for card-label readability) is untouched.
   1280px, not a device-named breakpoint, is simply where `.discover-visual`
   has grown enough beyond the 660px floor that the narrower fade already
   reads fine again. */
@media (min-width:861px) and (max-width:1280px){
  /* Root-caused via document.elementsFromPoint() + getComputedStyle()
     at the exact seam: no border/outline/box-shadow/
     pseudo-element anywhere — the seam is `.discover-visual`'s own
     literal left edge, the point where the absolutely-positioned photo
     box's boundary sits, independent of how soft the fade drawn inside
     it is. A fade confined to `inset:0` (the box's own bounds, the
     prior pass's approach) still has its 0% stop sitting exactly ON
     that boundary — any pixel-level seam at the box edge itself (sub-
     pixel compositing of an absolutely-positioned layer against its
     parent) sits outside what an internal gradient can ever soften,
     because the box boundary and the gradient's start coincide exactly.
     The fix extends `.discover-visual-fade` 160px further left than
     `.discover-visual` itself (a purely visual overlay — the `<img>`
     and its crop are untouched, per the brief), so the fade's own 0%
     start sits well inside pure, already-ivory territory, and the
     image box's real edge falls in the *middle* of an already-continuous
     gradient instead of at a boundary. There is no longer any single
     coordinate where "box present" meets "box absent" at anything other
     than full, matching ivory on both sides. The gradient stops below
     are the prior pass's same smoothstep curve and the same on-screen
     dissolve position — recalculated for the new, wider (820px vs
     660px) box so the visible result is unchanged, only where the box's
     own edge falls relative to it. */
  /* Top/bottom/right hard-edge fix: the photo's top, bottom, and right edges were
     always flat, untreated cuts — by design, only the left edge (where
     the photo meets the editorial column) was ever meant to dissolve.
     Once the left edge became genuinely soft, the still-hard remaining
     edges read as an unfinished continuation of the same seam rather
     than an intentional frame, at this same tablet range. Same
     technique as the left fix, applied to the other three sides:
     `.discover-visual-fade` extends 36px past `.discover-visual`'s own
     top/bottom (comfortably inside `.discover`'s own top/bottom section
     padding at this range — ~34px at the narrow, 861px end of this
     query — so it never reaches into the Services section above or How
     Shahraah Works below) and 28px past its right edge (kept under that
     same ~34px worst-case padding-right at 861px, so it can never cause
     horizontal overflow anywhere in this range — left/top/bottom
     extensions don't carry that risk since they only ever add vertical
     space or move further left into already-generous editorial-column
     clearance). A third, `270deg` gradient layer handles the right
     dissolve, independent of the horizontal (`90deg`) and vertical
     (`180deg`) layers already here, for the same reason those two are
     already separate: each needs its own opaque-near-one-end,
     transparent-elsewhere shape, which one shared gradient can't
     express. The horizontal (`90deg`) stops are recalculated below
     (same absolute on-screen dissolve position as before — only the
     box's own total width changed, from 820px to 848px, by adding the
     28px right extension) so the approved left-edge dissolve is
     pixel-identical to before this pass; the vertical (`180deg`) layer
     is untouched (box height unaffected by a right-only change). */
  .discover-visual-fade{
    left:-160px; right:-28px; top:-36px; bottom:-36px;
    background:
      linear-gradient(90deg,
        rgba(247,245,239,1) 0%,
        rgba(247,245,239,1) 22%,
        rgba(247,245,239,.96) 29%,
        rgba(247,245,239,.84) 37%,
        rgba(247,245,239,.68) 44%,
        rgba(247,245,239,.50) 51%,
        rgba(247,245,239,.32) 58%,
        rgba(247,245,239,.16) 66%,
        rgba(247,245,239,.04) 73%,
        rgba(247,245,239,0) 79%),
      linear-gradient(180deg,
        rgba(247,245,239,1) 0%,
        rgba(247,245,239,1) 8%,
        rgba(247,245,239,.7) 12%,
        rgba(247,245,239,.35) 16%,
        rgba(247,245,239,0) 20%,
        rgba(247,245,239,0) 80%,
        rgba(247,245,239,.35) 84%,
        rgba(247,245,239,.7) 88%,
        rgba(247,245,239,1) 92%,
        rgba(247,245,239,1) 100%),
      linear-gradient(270deg,
        rgba(247,245,239,1) 0%,
        rgba(247,245,239,1) 6%,
        rgba(247,245,239,.7) 9%,
        rgba(247,245,239,.35) 13%,
        rgba(247,245,239,0) 16%);
  }
}

/* ---------- Discover: destination-details popup ----------
   Approved reference: docs/design-references/discover-popup-
   reference-v1.png. Fixed overlay, independent of Discover's own
   layout/stacking — opened by an inline "See more" or the MORE row
   (assets/js/main.js), always for the currently selected destination.
   Nothing else in Discover (rail, response, service row, background)
   is touched by any rule below. */
.discover-popup{position:fixed; inset:0; z-index:200; display:flex; align-items:center; justify-content:center; padding:24px;}
.discover-popup[hidden]{display:none;}
.discover-popup-backdrop{
  position:absolute; inset:0; background:rgba(30,29,25,.55);
  -webkit-backdrop-filter:blur(3px); backdrop-filter:blur(3px);
  opacity:0; transition:opacity .22s var(--ease);
}
.discover-popup.is-open .discover-popup-backdrop{opacity:1;}
.discover-popup-dialog{
  position:relative; z-index:1; width:100%; max-width:860px;
  max-height:calc(100svh - 48px);
  background:var(--surface-page); border-radius:22px;
  box-shadow:0 30px 80px rgba(0,0,0,.35);
  display:flex; flex-direction:column; overflow:hidden;
  opacity:0; transform:translateY(14px) scale(.98);
  transition:opacity .24s var(--ease), transform .24s var(--ease);
}
.discover-popup.is-open .discover-popup-dialog{opacity:1; transform:translateY(0) scale(1);}
@media (prefers-reduced-motion: reduce){
  .discover-popup-backdrop, .discover-popup-dialog{transition:none;}
}
.discover-popup-close{
  position:absolute; top:16px; right:16px; z-index:2; width:36px; height:36px; border-radius:50%; padding:0;
  display:flex; align-items:center; justify-content:center;
  background:rgba(255,255,255,.85); border:1px solid rgba(30,29,25,.14); color:var(--ink-primary);
  cursor:pointer; transition:background-color .2s ease, color .2s ease;
}
.discover-popup-close:hover{background:#fff; color:var(--brand-green);}
.discover-popup-scroll{flex:1 1 auto; min-height:0; overflow-y:auto; -webkit-overflow-scrolling:touch;}

.discover-popup-header{padding:30px 32px 4px;}
.discover-popup-eyebrow{color:var(--accent-gold-strong);}
.discover-popup-title{font:400 32px/1.15 var(--font-serif); margin:8px 0 6px; color:var(--ink-primary);}
.discover-popup-desc{margin:0; font:400 15px/1.55 var(--font-sans); color:var(--ink-secondary); max-width:640px;}

.discover-popup-media{margin:18px 32px 0; border-radius:14px; overflow:hidden;}
.discover-popup-media img{width:100%; height:240px; object-fit:cover; display:block;}

.discover-popup-sections{padding:20px 32px 4px; display:flex; flex-direction:column; gap:12px;}
.discover-popup-section{border:1px solid var(--border); border-radius:14px; background:var(--surface-card); overflow:hidden;}
.discover-popup-section-header{
  width:100%; display:flex; align-items:center; gap:14px; padding:16px 18px;
  background:none; border:0; text-align:left; cursor:pointer; font-family:inherit;
}
.discover-popup-section-icon{
  flex:0 0 auto; width:38px; height:38px; border-radius:50%;
  display:flex; align-items:center; justify-content:center;
  background:rgba(187,158,97,.14); color:var(--accent-gold-strong);
}
.discover-popup-section-icon svg{width:17px; height:17px;}
.discover-popup-section-title{flex:1 1 auto; min-width:0; display:flex; flex-direction:column; gap:2px;}
.discover-popup-section-label{font:600 17px var(--font-serif); color:var(--ink-primary);}
.discover-popup-section-subtitle{font:400 13px var(--font-sans); color:var(--ink-secondary);}
.discover-popup-section-chevron{flex:0 0 auto; width:16px; height:16px; color:var(--accent-gold-strong); transition:transform .2s ease;}
.discover-popup-section-header[aria-expanded="true"] .discover-popup-section-chevron{transform:rotate(180deg);}
.discover-popup-section-panel[hidden]{display:none;}
.discover-popup-section-panel-inner{padding:0 18px 18px; margin-top:2px; border-top:1px solid var(--border); padding-top:14px;}
.discover-popup-section-text{margin:0; font:400 14.5px/1.65 var(--font-sans); color:var(--ink-primary); white-space:pre-line;}
@media (prefers-reduced-motion: no-preference){
  .discover-popup-section-panel:not([hidden]) .discover-popup-section-panel-inner{animation:discover-popup-panel-in .22s var(--ease);}
}
@keyframes discover-popup-panel-in{
  from{opacity:0; transform:translateY(-4px);}
  to{opacity:1; transform:translateY(0);}
}

.discover-popup-flight-extra{display:grid; grid-template-columns:1fr 1fr; gap:18px; margin-top:16px; background:rgba(187,158,97,.08); border:1px solid rgba(187,158,97,.22); border-radius:10px; padding:14px 16px;}
.discover-popup-block-label{margin:0 0 8px; font:600 10.5px var(--font-sans); letter-spacing:.06em; text-transform:uppercase; color:var(--accent-gold-strong);}
.discover-popup-route{display:flex; align-items:baseline; justify-content:space-between; gap:10px; padding:5px 0; font:400 13.5px var(--font-sans); border-top:1px solid rgba(187,158,97,.18);}
.discover-popup-route:first-child{border-top:0; padding-top:0;}
.discover-popup-route-text{color:var(--ink-primary);}
.discover-popup-route-meta{color:var(--ink-secondary); font-size:12.5px; white-space:nowrap;}
.discover-popup-know-list{margin:0; padding-left:16px; display:flex; flex-direction:column; gap:6px; font:400 13.5px/1.5 var(--font-sans); color:var(--ink-primary);}

.discover-popup-cta{
  display:inline-flex; align-items:center; gap:5px; margin-top:14px;
  font:600 13.5px var(--font-sans); color:var(--brand-green); text-decoration:underline; text-underline-offset:2px;
}

.discover-popup-footer{padding:20px 32px 28px; display:flex; justify-content:flex-end;}
.discover-popup-bottom-cta{gap:8px;}
.discover-popup-bottom-cta svg{width:16px; height:16px;}

body.discover-popup-lock{overflow:hidden;}

@media (max-width:860px){
  .discover-popup{padding:10px;}
  .discover-popup-dialog{max-width:none; width:100%; max-height:calc(100svh - 20px); border-radius:20px;}
  .discover-popup-close{top:12px; right:12px;}
  .discover-popup-header{padding:22px 20px 4px;}
  .discover-popup-title{font-size:24px;}
  .discover-popup-desc{font-size:14px;}
  .discover-popup-media{margin:14px 20px 0;}
  .discover-popup-media img{height:170px;}
  .discover-popup-sections{padding:16px 20px 4px; gap:10px;}
  .discover-popup-section-header{padding:14px 14px;}
  .discover-popup-section-label{font-size:16px;}
  .discover-popup-flight-extra{grid-template-columns:1fr; padding:12px 14px;}
  .discover-popup-route{flex-direction:column; align-items:flex-start; gap:1px;}
  .discover-popup-route-meta{white-space:normal;}
  .discover-popup-footer{padding:16px 20px 22px;}
  .discover-popup-bottom-cta{width:100%;}
}

/* ---------- 04 how it works ---------- */
/* Static creative rebuild. Replaces the prior text-only "two-voice
   exchange" with an editorial intro column plus three photographic
   step cards, built against the approved reference
   `docs/design-references/how-shahraah-works-reference-v1.png`.

   `.hiw-inner` is mobile-first a single stacked column (intro, then
   the three step cards in source order); the min-width:861px override
   below turns it into the reference's asymmetric two-column
   composition — a fixed intro column beside a wider steps column —
   reusing the same fixed-left-column-plus-wide-right-column shape the
   prior pass already established works well for this section's
   available width, without reusing any of its now-removed markup or
   classes. */
.hiw-inner{display:flex; flex-direction:column; gap:var(--space-7);}

/* ---- intro column ---- */
.hiw-intro{display:flex; flex-direction:column; gap:14px; max-width:480px;}
.hiw-eyebrow-row{display:flex; align-items:center; gap:12px;}
.hiw-eyebrow-rule{width:36px;}
.hiw-lead{font:500 21px/1.45 var(--font-serif); color:var(--ink-primary); margin:6px 0 0;}
.hiw-support{margin:0; max-width:420px;}

/* Icon-over-label feature row (Flexible options / Clear information /
   Real human support) — the reference's own left-column device,
   reusing the site's existing restrained line-icon vocabulary (same
   paths as `.human-reassurance-icon` in human.php) rather than
   designing new iconography. Deliberately its own `.hiw-*` classes,
   not shared with `.human-reassurance*`, so the two sections stay
   independently stylable — the same per-section-prefix convention
   already used throughout this file (tech-*, human-*, dest-*, etc). */
/* Full padding shorthand: setting only padding-top left the <ul>'s UA
   40px inline-start indent in place, pushing the row off the intro
   column's left edge. */
.hiw-features{list-style:none; margin:0; padding:var(--space-5) 0 0; border-top:1px solid var(--border); display:flex; gap:28px;}
.hiw-feature{display:flex; flex-direction:column; align-items:flex-start; gap:9px; flex:1 1 0; min-width:0;}
.hiw-feature-icon{color:var(--accent-gold-strong);}
/* Same label type as Human's reassurance row — the two rows are the
   same device in two sections. */
.hiw-feature-label{font:500 13.5px/1.4 var(--font-sans); color:var(--ink-secondary);}

/* ---- steps column ---- */
/* Each step is a two-column grid — a fixed-width "rail" column (the
   number badge) beside a content column (media + body) — rather than
   badges positioned absolutely, so the rail's width lives in exactly
   one place (`grid-template-columns`) and the connecting hairline
   below can anchor to it with simple math, no magic offsets repeated
   between mobile and desktop. */
.hiw-steps{list-style:none; margin:0; padding:0; position:relative; display:flex; flex-direction:column; gap:var(--space-5);}
.hiw-step{
  display:grid; grid-template-columns:34px 1fr; column-gap:16px;
  padding-bottom:var(--space-5); border-bottom:1px solid var(--border);
}
.hiw-step:last-child{padding-bottom:0; border-bottom:none;}
.hiw-step-index{
  grid-column:1; grid-row:1; align-self:start;
  display:flex; align-items:center; justify-content:center;
  width:34px; height:34px; border-radius:50%; border:1px solid var(--accent-gold);
  font:600 12px/1 var(--font-sans); color:var(--accent-gold-strong); background:var(--surface-page);
}
.hiw-step-content{grid-column:2; grid-row:1; display:flex; align-items:flex-start; gap:16px;}
.hiw-step-media{flex-shrink:0; width:96px; aspect-ratio:1/1; border-radius:6px; overflow:hidden; border:1px solid var(--border); order:2;}
.hiw-step-media img{width:100%; height:100%; object-fit:cover; display:block;}
.hiw-step-body{flex:1 1 auto; min-width:0; order:1;}
.hiw-step-title{font:400 19px/1.3 var(--font-serif); color:var(--ink-primary); margin:0 0 5px;}
.hiw-step-text{font:400 13.5px/1.55 var(--font-sans); color:var(--ink-secondary); margin:0;}

/* Connecting hairline through the three number badges — the one
   genuinely new structural device this pass introduces, deliberately
   a plain vertical rule (not a dashed/dotted "route line": CLAUDE.md
   explicitly rejects the route-line/waypoint motif as a travel-
   branding cliché) marking sequence, the way the reference's own
   composition does. Anchored to the 34px rail column's own center
   (17px) so it never needs re-tuning if the badge size changes. */
.hiw-steps::before{
  content:""; position:absolute; top:17px; bottom:17px; left:17px;
  width:1px; background:var(--border-strong);
}

@media (min-width:861px){
  .hiw-inner{
    display:grid;
    grid-template-columns:340px 1fr;
    column-gap:var(--space-8);
    align-items:start;
  }
  .hiw-intro{max-width:none;}

  .hiw-steps{gap:var(--space-6);}
  .hiw-step{grid-template-columns:38px 1fr; column-gap:28px; padding-bottom:var(--space-6); align-items:center;}
  .hiw-step-index{width:38px; height:38px; font-size:13px; align-self:start;}
  .hiw-step-content{align-items:center;}
  .hiw-steps::before{left:19px;}
  .hiw-step-media{width:240px; aspect-ratio:16/10;}
  /* Brought to 24px to match .h3's scale step — this is a numbered
     step title, the same semantic/visual role as other sub-headings
     sitewide (site-cleanup Phase 3B; corrects a real, if small, desktop
     type-scale gap). Mobile's 19px base rule (above) is unchanged. */
  .hiw-step-title{font-size:24px;}
  .hiw-step-text{font-size:14.5px; max-width:420px;}
}

/* ---------- 05 tech ---------- */
/* Technology — static two-column composition with decorative portal
   artwork.

   `.tech` keeps the established "section breaks out of the normal
   container to carry its own full-bleed background" technique — same
   static deep-emerald/charcoal gradient, this project's own tokens
   only. Padding is intentionally tighter than the section's first pass
   (compaction refinement: this used to run 144px top/bottom; a
   near-full-screen "one chapter" composition needs the padding to stop
   competing with the content for vertical space).

   `.tech-inner` is CSS Grid with three named areas — `intro`, `visual`,
   `details` — matching the same three DOM blocks in tech.php
   (`.tech-intro`, `.tech-visual`, `.tech-details`). Mobile-first: a
   single column, `intro visual details` top to bottom, so the visual is
   introduced right after the heading/body hook instead of stranded
   below the supporting points. Desktop reflows the identical three
   blocks into two columns via `grid-template-areas` alone — `visual`
   spans both left-column rows, so its height is set by the combined
   height of `intro` + `details` (grid's default stretch alignment),
   not a guessed aspect-ratio — no markup duplication, no `order`
   hacks. */
.tech{
  position:relative;
  overflow:hidden;
  max-width:none;
  background:linear-gradient(160deg, #0a3f2d 0%, var(--brand-green) 38%, var(--surface-inverse) 100%);
  /* --space-8 (not the editorial --space-9) is intentional here — a
     denser rhythm for this full-bleed, photo/graphic-composition
     section; see the compaction note above. Don't "fix" this to
     --space-9 (site-cleanup Phase 3B documentation pass). */
  padding-top:var(--space-8);
  padding-bottom:var(--space-8);
}

.tech-inner{
  position:relative;
  max-width:1240px; margin:0 auto; width:100%;
  display:grid;
  grid-template-columns:1fr;
  grid-template-areas:"intro" "visual" "details";
  row-gap:var(--space-6);
}
.tech-intro{grid-area:intro; display:flex; flex-direction:column; min-width:0;}
.tech-details{grid-area:details; display:flex; flex-direction:column; gap:18px; min-width:0;}

/* "SEARCH → PLAN → COMPARE" is the section's one real heading (an
   `<h2>` in markup) — template-controlled, no ACF field, same
   fixed-content status the outer brand lockup had before it was
   removed. A separate `.tech-heading` line
   underneath it ("Where the technology is headed") was tried and
   removed — redundant with this line, and its own reveal styling was
   what caused the section to render an empty gap on desktop. One line on desktop by design: flex
   row + nowrap, with font-size/letter-spacing scaled down via clamp()
   before anything is allowed to wrap on narrow viewports. COMPARE
   carries the section's one gold accent, everything else stays warm
   ivory. `margin-bottom` is the only gap between this heading and
   `.tech-body` — `.tech-intro` sets no flex `gap` of its own, and
   `.tech-body{margin:0}` zeroes the paragraph's own margin, so this is
   the single, explicit source of that space (kept small and editorial,
   not the section's old uniform gap). */
.tech-kicker{
  display:flex; align-items:center; flex-wrap:nowrap;
  gap:clamp(8px, 1.6vw, 12px);
  font:600 clamp(17px, 1vw + 11px, 24px)/1.15 var(--font-sans);
  letter-spacing:.07em; text-transform:uppercase;
  white-space:nowrap;
  color:var(--ink-inverse-secondary);
  margin:0 0 14px;
}
.tech-kicker-arrow{color:var(--ink-inverse-secondary); opacity:.5; font-weight:400;}
.tech-kicker-gold{color:var(--accent-gold);}

.tech-body{max-width:520px; margin:0; color:var(--ink-inverse-secondary);}
/* Bright ink-inverse (not the brand-green used on the light surface
   elsewhere) so the sentence's real "search"/"compare"/"plan" emphasis
   stays legible against this section's dark field. */
.tech-body .tech-verb{color:var(--ink-inverse); font-weight:600;}

.tech-points{list-style:none; margin:0; padding:0; display:flex; flex-direction:column; gap:12px;}
.tech-point{display:flex; align-items:flex-start; gap:12px;}
.tech-point-mark{width:6px; height:6px; margin-top:7px; border-radius:50%; background:var(--accent-gold); flex-shrink:0;}
.tech-point-body{display:flex; flex-direction:column; gap:1px;}
.tech-point-title{display:block; font:600 14px/1.3 var(--font-sans); color:var(--ink-inverse);}
.tech-point-text{display:block; font:400 13px/1.5 var(--font-sans); color:var(--ink-inverse-secondary);}

.tech-cta{align-self:flex-start; border-color:rgba(242,240,232,.35); color:var(--ink-inverse);}
.tech-cta:hover{border-color:var(--ink-inverse);}

/* ---- portal visual (Reference Fidelity Correction) ----
   Uses the approved reference photo itself
   (assets/img/technology/technology-portal.png — the live production
   asset, not a discardable mockup) as
   decorative artwork, not a recreated HTML mockup. The reference is a single wide
   image: real left-side text/logo baked into its left ~35%, the
   laptop/phone/gold-arc portal filling its right ~60-65%.

   `.tech-portal-image` uses `object-fit:cover` with a right-anchored
   `object-position` so the browser always crops away from the LEFT
   (where the baked-in text lives) and keeps the RIGHT edge (phone,
   gold arc) intact, however the container is sized — no separate
   cropped image asset needed. Mobile sizes the container with a fixed
   aspect-ratio (its own grid row, not spanning anything); desktop
   instead lets the grid area itself set both dimensions (see the
   `.tech-inner` comment above) and cancels the aspect-ratio so it
   doesn't fight that. `.tech-portal`'s mask-image fades the crop's cut
   (left) edge to transparent so it blends into `.tech`'s own dark
   gradient instead of reading as a pasted rectangular screenshot. */
.tech-visual{
  grid-area:visual;
  position:relative;
  width:100%; max-width:420px;
  aspect-ratio:1.15/1;
  margin:0 auto;
  overflow:hidden;
  border-radius:2px;
}
.tech-portal{
  position:absolute; inset:0;
  -webkit-mask-image:linear-gradient(to right, transparent 0%, #000 26%);
  mask-image:linear-gradient(to right, transparent 0%, #000 26%);
}
.tech-portal-image{
  position:absolute; inset:0;
  width:100%; height:100%;
  object-fit:cover;
  object-position:100% 42%;
  display:block;
}
/* Soft bottom settle so the photo's own ground/rock edge eases into
   the section rather than stopping on a hard line. */
.tech-visual::after{
  content:"";
  position:absolute; inset:0;
  background:linear-gradient(180deg, rgba(10,63,45,0) 78%, rgba(10,63,45,.5) 100%);
  pointer-events:none;
}
/* Motion Pass 1's one gold "energy" moment — a real element rather than a
   pseudo-element so GSAP can animate it directly. Static/inert without
   JS (opacity:0, no motion); `initTechnologySequence()` in motion.js is
   what fades it in and sweeps it across once. A soft diagonal band, not
   a hard-edged beam, clipped by `.tech-visual`'s own `overflow:hidden`
   so it can never bleed outside the portal artwork; direction echoes
   the reference image's own gold arc rather than competing with it. */
.tech-visual-sweep{
  position:absolute; inset:0;
  background:linear-gradient(115deg, rgba(187,158,97,0) 38%, rgba(187,158,97,.62) 50%, rgba(187,158,97,0) 62%);
  opacity:0;
  pointer-events:none;
}

@media (min-width:861px){
  .tech{padding-top:clamp(48px, 7vh, 88px); padding-bottom:clamp(48px, 7vh, 88px);}

  .tech-inner{
    grid-template-columns:460px 1fr;
    grid-template-areas:"intro visual" "details visual";
    column-gap:var(--space-8); row-gap:var(--space-6);
  }
  /* Natural editorial spacing, not a forced stretch: an earlier pass
     gave `.tech-details` a `min-height` and made `.tech-points` consume
     it with `flex:1`+`justify-content:space-between` to visually match
     the portal's height — that pushed the third point below the
     portal's own lower edge and read as artificial. Removed. The three
     points now just take a comfortable, equal `gap` and the column
     (and the portal, which derives its height from this column — see
     the sizing note below) sizes to that natural content height. */
  .tech-points{gap:clamp(24px, 2vw, 32px);}
  /* Deliberately NOT full `align-items:stretch` on both axes: letting
     width stretch to the whole `visual` track while height comes from
     the (now much shorter, post-compaction) spanned rows pushed this
     box's own aspect ratio past the source photo's own (~1.77:1),
     which flips `object-fit:cover` from height-driven to width-driven
     scaling — i.e. it stops cropping horizontally at all, and the
     reference's baked-in left-side text bleeds back into view. Height
     still stretches to fill the spanned `intro`+`details` rows
     (default `align-self:stretch`); `justify-self:end` takes width out
     of stretch so it's computed from that definite height via
     `aspect-ratio` instead — cheap, reliable crop-safety that doesn't
     depend on guessing the row height. Flush to the column's own right
     edge, which also matches the reference's own right-anchored
     laptop/phone/arc composition. */
  .tech-visual{
    width:auto; max-width:100%; margin:0;
    aspect-ratio:1.3/1;
    justify-self:end;
  }
}

/* Technology's entrance is now owned entirely by GSAP/ScrollTrigger
   (`initTechnologySequence()` in assets/js/motion.js, Motion Pass 1) —
   none of its elements carry `data-reveal` any more, so the site's
   default IntersectionObserver reveal (and its old `.tech`-scoped
   `.9s`-transition and `.tech-heading` `clip-path` overrides, both
   removed here) no longer applies to this section at all. motion.js's
   own `prefers-reduced-motion: reduce` guard is what keeps this
   section's approved static composition showing immediately when
   motion is skipped. */

/* ---------- 06 human ---------- */
/* Warm editorial conversion moment, not another Trust section.
   The hard deep-green (Technology) -> white transition already reads
   as a strong chapter change; Human lowers the energy, gets warmer
   and direct, and — for the first time since Opening/Discover —
   brings photography back in, on the right, to make "a real person"
   felt rather than only stated. Mobile (max-width:860px block below)
   opens with a shallow photo band the message rises out of; the
   min-width:861px block turns it into the full-bleed scene with the
   copy column on top.
   --space-8 (not --space-9) is intentional here too, for the same
   denser/photo-oriented reason as Technology above — do not normalize
   to --space-9 (site-cleanup Phase 3B documentation pass). */
.human{background:var(--surface-page); max-width:none; border-top:1px solid var(--border); border-bottom:1px solid var(--border); padding-top:var(--space-8); padding-bottom:var(--space-8);}
.human-inner{max-width:1240px; margin:0 auto; width:100%; display:flex; flex-direction:column; gap:var(--space-7);}
/* Mobile: no separate wrapping box — .human-content participates
   directly in .human-inner's column flow. Desktop reclaims this as
   the centered copy column (see min-width:861px block). */
.human-copy-wrap{display:contents;}
.human-content{display:flex; flex-direction:column; gap:var(--space-6);}
.human-message{display:flex; flex-direction:column; gap:16px; max-width:520px;}
.human-message .eyebrow{margin-bottom:2px;}

.human-cta{display:flex; align-items:center; gap:16px; flex-wrap:wrap;}
/* Fallback state, styled deliberately rather than left as a bare
   sentence: a quiet bordered note on the page's own warm surface, so
   "no contact info yet" still reads as an intentional design state
   instead of an unstyled afterthought. */
.human-fallback{
  margin:0; max-width:420px; padding:14px 18px; border:1px solid var(--border); border-radius:4px;
  background:var(--surface-page); font:400 14.5px/1.55 var(--font-sans); color:var(--ink-secondary);
}

/* Reassurance row: quiet icon+label items, hairline-divided like the
   rest of the page's editorial devices (never a card grid). Icons use
   --accent-gold-strong — the same restrained "small mark" role gold
   already plays elsewhere (service-row-num, context-num, step
   markers), not a new decorative device. */
.human-reassurance{display:flex; flex-direction:column; gap:16px; padding-top:var(--space-5); border-top:1px solid var(--border);}
.human-reassurance-item{display:flex; align-items:flex-start; gap:12px;}
.human-reassurance-icon{flex-shrink:0; margin-top:1px; color:var(--accent-gold-strong);}
.human-reassurance-label{font:500 13.5px/1.4 var(--font-sans); color:var(--ink-secondary);}

/* The visual: two separately-composed approved production photos,
   swapped via <picture> at the 861px breakpoint —
   assets/img/human/human-consultation-mobile.png (portrait 1122x1402)
   below it, -desktop.png (landscape 4:3, 1448x1086) at 861px and up,
   recomposed as a full-bleed desktop scene (see the min-width:861px
   block below). */
.human-visual{overflow:hidden;}
.human-visual img{width:100%; height:100%; object-fit:cover; object-position:50% 50%;}

/* Mobile composition (second mobile pass): the phone version of the
   desktop scene rather than "copy, then a 4:5 photo slab". The photo
   opens the section as a shallow full-bleed editorial band (order:-1;
   it stays aria-hidden, so reading order is unaffected) and the
   message rises out of its lower edge — the desktop's
   ivory-wash-into-photograph device, turned vertical. The band height
   is fluid with a ceiling so it never becomes the dominant block; the
   crop holds the hands/map exchange. The photo itself fades out (a
   mask, not an ivory overlay), so it dissolves into the section's own
   surface with no layer edge to anti-alias into a seam. */
@media (max-width:860px){
  /* .section.human: must outrank the later global mobile .section padding. */
  .section.human{padding-top:0;}
  .human-inner{gap:0;}
  .human-visual{
    order:-1; margin:0 calc(var(--space-5) * -1);
    height:clamp(230px, 66vw, 380px);
    -webkit-mask-image:linear-gradient(180deg, #000 40%, rgba(0,0,0,.38) 68%, rgba(0,0,0,.06) 86%, transparent 96%);
    mask-image:linear-gradient(180deg, #000 40%, rgba(0,0,0,.38) 68%, rgba(0,0,0,.06) 86%, transparent 96%);
  }
  .human-visual img{object-position:50% 62%;}
  .human-content{position:relative; z-index:1; margin-top:calc(var(--space-7) * -1); gap:var(--space-5);}
  .human-message{gap:12px;}
  /* WhatsApp carries the row; Call Us stays its natural width beside it. */
  .human-cta{gap:12px; flex-wrap:nowrap;}
  .human-cta .btn-primary{flex:1 1 auto; text-align:center;}
  .human-cta .btn-secondary{flex:0 0 auto;}
  .human-reassurance{gap:12px; padding-top:var(--space-4);}
}

/* Desktop composition match to reference: the earlier build read as a text column beside a
   boxed photo card. This composes the section the way
   docs/design-references/human-reference-v1.png does — one full-bleed
   editorial scene, image edge-to-edge to the right of the viewport,
   with the message/CTA/reassurance content sitting directly on top of
   it on the left, on a soft warm-ivory wash that fades into the
   photograph rather than stopping at a hard card boundary. Layout
   model: `.human-inner` is the full-bleed positioning context (no
   max-width here at this breakpoint); `.human-visual` fills it
   entirely as the base layer (z-index:0); `.human-copy-wrap` reclaims
   the sitewide 1240px reading column on top (z-index:2) so the copy
   still lines up with every other section's content edge, even though
   the photograph behind it does not. */
@media (min-width:861px){
  .human{padding-top:0; padding-bottom:0; padding-left:0; padding-right:0; position:relative; overflow:hidden;}
  .human-inner{
    display:block; max-width:none; margin:0; position:relative;
    /* Fluid, not viewport-fragile: scales with width between the two
       bounds instead of a fixed px height, so the section stays one
       balanced "chapter" (image and content vertically in sync)
       across normal desktop/laptop widths without a hardcoded vh. */
    min-height:clamp(480px, 44vw, 620px);
  }
  .human-copy-wrap{
    display:flex; align-items:center;
    position:relative; z-index:2;
    max-width:1240px; margin:0 auto; width:100%; min-height:inherit;
    padding:var(--space-9) var(--space-6);
    box-sizing:border-box;
  }
  .human-content{max-width:480px; display:flex; flex-direction:column; gap:var(--space-6);}
  /* The photograph: absolutely filled behind the copy, no card
     boundary, no radius, no shadow — it IS the section surface on the
     right rather than a tile placed on it. object-position is tuned
     to the bundled desktop photo so the hands/laptop exchange (the
     actual "real person" moment) stays in frame once the wide,
     comparatively short crop trims the sky above and the desk clutter
     below; re-tune only if that source photo changes. */
  .human-visual{
    position:absolute; inset:0; width:100%; height:100%;
    max-width:none; border-radius:0; aspect-ratio:auto; overflow:hidden; z-index:0;
  }
  .human-visual img{object-position:58% 42%;}
  /* Subtle left-edge fade merging the photo into the warm ivory
     content surface — a soft gradient, not a hard card edge, glass
     panel, or dramatic shadow. Sized to comfortably clear the copy
     column (480px content + gutter) at common desktop widths while
     leaving most of the photograph's right two-thirds untouched. */
  .human-visual::before{
    content:""; position:absolute; inset:0; z-index:1; pointer-events:none;
    /* Stops hold opacity well past the ~480px copy column (including
       its own left gutter) across normal desktop widths, so the
       reassurance row's icons/labels and the CTA fallback note stay
       legible against a busy photograph, then fall fully transparent
       comfortably before the frame's right two-thirds. */
    background:linear-gradient(90deg,
      var(--surface-page) 0%,
      var(--surface-page) 12%,
      rgba(247,245,239,.95) 38%,
      rgba(247,245,239,.72) 54%,
      rgba(247,245,239,.34) 68%,
      rgba(247,245,239,0) 82%);
  }
  /* Vertical hairline dividers between the three reassurance items,
     matching the reference direction, once there's a row's worth of
     width to divide. */
  .human-reassurance{flex-direction:row; gap:0;}
  .human-reassurance-item{flex:1 1 0; padding-right:20px;}
  .human-reassurance-item + .human-reassurance-item{padding-left:20px; border-left:1px solid var(--border);}
}

/* ---------- 07 trust ---------- */
/* Quiet proof after Human, not a second conversion moment — factual,
   calm, editorial (following
   docs/design-references/trust-reference-v1.png). Mobile-first base is
   the plain stacked sequence (identity, then the factual ledger, then
   the reassurance row); the min-width:861px block below turns it into
   an asymmetric row — a narrow identity column (eyebrow, the real
   master logo, a short company line) beside a wider record column
   that itself stacks a 3x2 factual grid above a 3-up reassurance row.
   `.trust` itself stays a normal contained `.section` (no
   `max-width:none`) so the quiet white -> off-white transition after
   Human is untouched. */
.trust-inner{display:flex; flex-direction:column; gap:var(--space-7);}

/* Mobile compaction pass: the approved desktop composition (3x2 ledger,
   3-up reassurance row) is untouched — every rule in this pass lives
   inside the max-width:860px block below (one pixel under the existing
   min-width:861px desktop breakpoint, so there is zero overlap) rather
   than in the shared/mobile-first base rules above, specifically so
   nothing here can leak into or alter the desktop result. */
.trust-identity{display:flex; flex-direction:column; gap:14px; max-width:420px;}
.trust-logo-wrap{display:block;}
/* The real brand lockup (icon mark + "Shahraah" wordmark + tagline,
   wp-content/themes/shahraah/assets/img/branding/shahraah-logo-master.svg)
   replaces what the reference renders as a large serif heading — the
   logo already carries that identity, so no separate <h2> repeats it.
   width/height attributes match the SVG's own intrinsic ratio
   (1128x713) purely to reserve layout space; `height:auto` below is
   what actually governs the rendered size. */
.trust-logo{width:auto; max-width:170px; height:auto;}
.trust-lede{max-width:400px;}

.trust-record{display:flex; flex-direction:column; gap:var(--space-7);}

/* The factual ledger: icon + label/value, six real Site Settings
   facts (plus the one constant non-ACF fact, Registered Name). A
   blank fact never disappears from the layout — it renders an
   intentionally-styled pending state so the ledger's shape stays
   constant and the section never looks half-finished. Desktop (see
   min-width:861px block) is the reference's bounded 3x2 grid,
   unchanged by this pass. Mobile base here is the simple stacked
   list; the max-width:860px block below shapes it into full-width
   rows plus one shared row for the short facts. */
.trust-facts{display:flex; flex-direction:column;}
.trust-fact{display:flex; gap:14px; align-items:flex-start; padding:16px 0; border-top:1px solid var(--border);}
.trust-facts .trust-fact:last-child{border-bottom:1px solid var(--border);}
.trust-fact-icon{flex-shrink:0; margin-top:2px; color:var(--accent-gold-strong);}
.trust-fact-text{display:flex; flex-direction:column; gap:4px;}
.trust-fact-label{font:600 11px/1.3 var(--font-sans); letter-spacing:.06em; text-transform:uppercase; color:var(--ink-secondary); margin:0;}
/* tel:/mailto: links (Phone/Email) are deliberately styled identically
   to the plain-text facts around them — real function, zero added
   visual weight, so they never compete with Human/Contact's actual
   WhatsApp/phone CTAs. */
.trust-fact-value{display:block; font:500 15.5px/1.4 var(--font-sans); color:var(--ink-primary); text-decoration:none;}
.trust-fact-value--pending{color:var(--ink-secondary); font-style:italic; font-weight:400;}
/* Per-cell explanatory note: shown on desktop (reset to display:block
   in the min-width:861px block), hidden below it — the mobile
   compaction pass replaces six repeated copies of this with one
   shared `.trust-facts-note` line under the whole grid instead (see
   the max-width:860px block). */
.trust-fact-note{display:none; font:400 12.5px/1.4 var(--font-sans); color:var(--ink-secondary); opacity:.85;}
/* The one shared explanatory line the mobile compaction pass adds
   below the whole facts grid — desktop hides it (min-width:861px
   block) since desktop keeps its original per-fact notes instead. */
.trust-facts-note{display:none; margin:0; font:400 12.5px/1.4 var(--font-sans); color:var(--ink-secondary); opacity:.85; font-style:italic;}

/* Reassurance row: three quiet icon+label+detail items, same
   hairline-divided grammar as Human's reassurance row — never a card
   grid, never a marketing badge. Desktop (min-width:861px block)
   keeps the original icon-left row with subtitles, unchanged by this
   pass; the max-width:860px block below is what compacts this into a
   subtitle-free 3-up row on mobile. */
.trust-reassurance{display:flex; flex-direction:column; gap:18px; padding-top:var(--space-5); border-top:1px solid var(--border);}
.trust-reassurance-item{display:flex; gap:14px; align-items:flex-start;}
.trust-reassurance-icon{flex-shrink:0; margin-top:2px; color:var(--accent-gold-strong);}
.trust-reassurance-text{display:flex; flex-direction:column; gap:2px;}
.trust-reassurance-label{font:600 14px/1.3 var(--font-sans); color:var(--ink-primary);}
.trust-reassurance-detail{font:400 13.5px/1.4 var(--font-sans); color:var(--ink-secondary);}

/* ---- mobile composition (max-width:860px only — never reaches desktop) ---- */
/* Second mobile pass. Same facts, same Site Settings reads, same
   pending-state logic and links; only the mobile presentation changes.
   Scoped to max-width:860px so desktop's 3x2 ledger is untouched.

   Identity: the horizontal lockup (swapped in via <picture> in
   trust.php) sits left-aligned under the eyebrow rule as the
   company's name line, at a cap height close to the section H2s, and
   the lede follows it directly — one introduction, not a centred logo
   object between two blocks of text. The closing gold rule is
   dropped on mobile because the ledger's own top hairline now ends
   the introduction.

   Ledger: an information layout built for the real content, not the
   desktop grid narrowed. Long values (registered name, office address,
   email, phone) each get a full-width row, so an address wraps as
   prose instead of a word tower; the two naturally short facts
   (.trust-fact--short: year, registration) share the last row. DOM
   order is unchanged, so reading/tab order matches what's shown. */
@media (max-width:860px){
  .trust{padding-top:var(--space-7); padding-bottom:var(--space-7);}
  .trust-inner{gap:var(--space-6);}
  .trust-identity{gap:12px; max-width:none;}
  .trust-identity .gold-rule:last-child{display:none;}
  .trust-logo{height:42px; width:auto; max-width:100%; margin:6px 0 2px;}
  .trust-lede{max-width:36ch;}

  .trust-facts{display:grid; grid-template-columns:minmax(0,1fr) minmax(0,1fr); border-bottom:1px solid var(--border);}
  .trust-fact{grid-column:1 / -1; min-width:0; gap:14px; padding:14px 0;}
  .trust-facts .trust-fact:last-child{border-bottom:none;}
  .trust-fact--short{grid-column:auto;}
  .trust-fact--short + .trust-fact--short{padding-left:16px; border-left:1px solid var(--border);}
  .trust-fact-text{min-width:0;}
  /* Only ever breaks inside a token when it genuinely can't fit (e.g. a
     very long email at 320px) — normal values wrap at spaces. */
  .trust-fact-value{overflow-wrap:anywhere;}
  /* The shared note belongs to the ledger, not between two record
     blocks: spacing is set per child so it sits directly under the
     facts instead of inheriting the record's gap on both sides. */
  .trust-record{gap:0;}
  .trust-reassurance{margin-top:var(--space-6);}

  /* One shared note under the grid replaces six repeated per-cell
     ones — see trust.php ($has_pending_fact), which only prints it
     when at least one fact is actually pending. */
  .trust-facts-note{display:block; margin-top:12px;}

  /* Compact 3-across reassurance row: icon above a short title only
     (subtitles hidden on mobile per instruction, kept on desktop).
     Titles wrap to two lines naturally rather than truncating or
     forcing a wider column. */
  .trust-reassurance{flex-direction:row; gap:0; padding-top:var(--space-4);}
  .trust-reassurance-item{flex:1 1 0; flex-direction:column; align-items:flex-start; gap:6px; padding-right:10px;}
  .trust-reassurance-item + .trust-reassurance-item{padding-left:10px; border-left:1px solid var(--border);}
  .trust-reassurance-label{font-size:12.5px; line-height:1.25;}
  .trust-reassurance-detail{display:none;}
}

@media (min-width:861px){
  .trust-inner{flex-direction:row; align-items:flex-start; gap:var(--space-8);}
  .trust-identity{flex:0 0 280px; max-width:280px; padding-top:4px;}
  .trust-logo{max-width:190px;}
  .trust-record{flex:1 1 auto;}

  /* The bounded 3x2 ledger: a full-width top/bottom hairline, an
     internal hairline between the two rows, and vertical hairlines
     between the three columns — one continuous grid, not six loose
     cards. Unchanged by the mobile compaction pass. */
  .trust-facts{display:grid; grid-template-columns:repeat(3, 1fr); border-top:1px solid var(--border); border-bottom:1px solid var(--border);}
  .trust-fact{border-top:none; padding:20px var(--space-5) 20px 0;}
  .trust-facts .trust-fact:last-child{border-bottom:none;}
  .trust-fact:nth-child(3n){padding-right:0;}
  .trust-fact:not(:nth-child(3n+1)){padding-left:var(--space-5); border-left:1px solid var(--border);}
  .trust-fact:nth-child(n+4){border-top:1px solid var(--border);}
  .trust-fact-note{display:block;}
  .trust-facts-note{display:none;}

  /* Vertical hairline dividers between the three reassurance items,
     same device as Human's own reassurance row once there's a row's
     worth of width to divide. Unchanged by the mobile compaction
     pass. */
  .trust-reassurance{flex-direction:row; gap:0; border-top:1px solid var(--border);}
  .trust-reassurance-item{flex:1 1 0; flex-direction:row; padding-right:20px;}
  .trust-reassurance-item + .trust-reassurance-item{padding-left:20px; border-left:1px solid var(--border);}
  .trust-reassurance-detail{display:block;}
}

/* ---------- footer — visual rebuild against footer-reference-v1.png ----------
   (docs/design-references/; "OPTION 1 — PRIMARY (DESKTOP)" / "MOBILE —
   ALTERNATIVE" only).
   Correction pass: the reference's cream mockup-board margin around
   the panel was never meant to be real page background — `.site-footer`
   itself is now the full-bleed dark surface (no outer radius, no
   shadow, no cream gutters left/right), terminating the page the way a
   real footer should. `.footer-inner` is the only thing that keeps a
   max-width, exactly like `.footer-top`/`.footer-legal` did in the
   original pre-rebuild footer — content stays readable/aligned, the
   surface itself doesn't. */
.site-footer{
  position:relative; overflow:hidden; isolation:isolate;
  background:linear-gradient(135deg, #0B4636 0%, #063325 45%, var(--surface-deepest) 100%);
}
/* Scenic photo integrated on the right only — a dark-to-transparent
   veil (left solid emerald-black, fading to reveal the photo by the
   right edge) keeps the brand/nav/app content fully readable while the
   image still reads as part of one composition, not a separate pasted-
   in rectangle. Correction pass: extended further left (was 34%/50%,
   now 44%/60%) since the surface is wider now that it's full-bleed —
   without this the photo would start appearing noticeably sooner
   relative to the (now wider) content, instead of staying an emphasis
   on the right the way the reference intends. */
.footer-media{position:absolute; inset:0; z-index:0;}
.footer-media-img{width:100%; height:100%; object-fit:cover; object-position:78% 38%; display:block;}
.footer-media-veil{
  position:absolute; inset:0;
  background:linear-gradient(90deg, var(--surface-deepest) 0%, var(--surface-deepest) 44%, rgba(4,20,15,.88) 60%, rgba(4,20,15,.45) 76%, rgba(4,20,15,.14) 100%);
}

/* Same 1240px column and --space-6 gutter as every .section above, so
   the footer's brand/columns/legal row share the page's left and right
   content edges instead of sitting ~60px further out. The surface
   itself stays full-bleed. */
.footer-inner{position:relative; z-index:1; max-width:1240px; margin:0 auto;}

.footer-main{
  display:flex; flex-wrap:wrap; align-items:flex-start;
  gap:var(--space-6); padding:56px var(--space-6) 40px;
}
.footer-brand{display:flex; flex-direction:column; gap:16px; flex:0 0 240px; max-width:260px;}
.footer-logo-link{display:inline-block; align-self:flex-start;}
.footer-logo-link picture{display:block;}
.footer-logo{display:block; width:140px; height:auto;}
.footer-brand-copy{margin:0; color:var(--ink-inverse-secondary); font:400 14px/1.6 var(--font-sans);}
.footer-social{display:flex; gap:10px;}
.footer-social-link{
  width:32px; height:32px; border-radius:50%; display:flex; align-items:center; justify-content:center;
  border:1px solid rgba(187,158,97,.5); color:var(--accent-gold);
  transition:background-color .15s ease, border-color .15s ease, color .15s ease;
}
.footer-social-link:hover, .footer-social-link:focus-visible{background:var(--accent-gold); border-color:var(--accent-gold); color:var(--surface-deepest);}

.footer-divider{width:1px; align-self:stretch; background:rgba(242,240,232,.16); flex:0 0 1px;}

.footer-columns{display:flex; flex-wrap:wrap; gap:var(--space-6); flex:1 1 320px;}
.footer-column{display:flex; flex-direction:column; gap:10px; min-width:150px;}
.footer-column-toggle{
  display:flex; align-items:center; gap:8px; background:none; border:none; padding:0;
  font:inherit; text-align:left; cursor:default; color:inherit;
}
.footer-column-label{font:600 11px var(--font-sans); letter-spacing:.06em; text-transform:uppercase; color:var(--accent-gold);}
.footer-column-toggle-icon{display:none;} /* shown only in the mobile accordion — see the max-width:860px block */
.footer-column-links{display:flex; flex-direction:column; gap:8px;}
.footer-link{
  display:block; text-decoration:none; font:400 14px var(--font-sans);
  color:var(--ink-inverse-secondary); transition:color .15s ease;
}
a.footer-link:hover, a.footer-link:focus-visible{color:var(--accent-gold);}
/* Genuinely non-interactive — see footer.php's comment on the Help
   column: these six items have no real page yet, so they render as
   plain text (not `<a>`, no `#` href) rather than a dead/fake link. */
.footer-link--soon{color:rgba(242,240,232,.34); cursor:default;}

/* Replaces the reference's WhatsApp callout — the app isn't live yet,
   so this is an honest "coming soon" block, not a disguised WhatsApp
   substitute. Same visual prominence/placement (upper-right, over the
   photo) via an outlined card, matching the reference's WhatsApp box
   treatment. */
.footer-app{
  flex:0 0 260px; max-width:280px; display:flex; flex-direction:column; gap:8px;
  border:1px solid rgba(242,240,232,.22); border-radius:16px; padding:20px 22px;
  background:rgba(4,20,15,.38); -webkit-backdrop-filter:blur(6px); backdrop-filter:blur(6px);
}
.footer-app-eyebrow{font:600 11px var(--font-sans); letter-spacing:.06em; text-transform:uppercase; color:var(--accent-gold);}
/* Correction pass: the "Shahraah App" heading is gone — eyebrow +
   this one line is the whole hierarchy now, per the brief ("do not
   replace the removed heading with another heading"). */
.footer-app-copy{margin:0; font:400 13.5px/1.5 var(--font-sans); color:var(--ink-inverse-secondary);}
/* The two badges are one matched pair: equal-width grid tracks (so
   "App Store" and "Google Play" never size to their own text), side by
   side wherever two 150px tracks fit, otherwise stacked — still equal
   width. Equal height comes from the shared row / identical content. */
.footer-app-badges{display:grid; grid-template-columns:repeat(auto-fit, minmax(130px, 1fr)); gap:10px; margin-top:4px;}
/* Deliberately not a link/button — the apps aren't live. aria-disabled
   plus no interactive semantics at all (no href, no role, no tabindex)
   so assistive tech never announces this as an actionable control. */
.footer-store-badge{display:flex; align-items:center; gap:8px; min-width:0; border:1px solid rgba(242,240,232,.2); border-radius:10px; padding:7px 10px; cursor:default;}
/* Correction pass: appstore.svg (800x800, a heavier/denser icon) and
   googleplay.svg (466x511.98, a lighter triangular mark) read as
   mismatched in size when both were just given the same raw `height`
   attribute — same numeric height, visually unequal weight. A fixed
   equal-size slot with `object-fit:contain` normalizes both to the
   same actual bounding box regardless of their own intrinsic canvas,
   so the pair now reads as a matched set — the real fix the brief
   asks for, not another guessed height number. */
.footer-store-badge-mark{width:24px; height:24px; flex-shrink:0; display:flex; align-items:center; justify-content:center;}
.footer-store-badge-mark img{width:100%; height:100%; object-fit:contain; display:block;}
/* Optical match: the App Store mark is a solid full-bleed tile, the
   Play mark an open triangle with built-in negative space — at the
   same box the tile reads heavier. Uniform scale only, never distorted. */
.footer-store-badge-mark--tile img{width:88%; height:88%;}
.footer-app-badge-text{display:flex; flex-direction:column; line-height:1.2;}
.footer-app-badge-text small{font:400 9px var(--font-sans); letter-spacing:.02em; text-transform:uppercase; color:var(--ink-inverse-secondary);}
.footer-app-badge-text span{font:600 13px var(--font-sans); color:var(--ink-inverse);}

.footer-bottom{
  display:flex; flex-wrap:wrap; justify-content:space-between; align-items:center;
  gap:12px; padding:18px var(--space-6); border-top:1px solid rgba(242,240,232,.14);
}
.footer-copyright{margin:0; font-size:12.5px; color:var(--ink-inverse-secondary);}
.footer-tagline{margin:0; display:flex; align-items:center; gap:8px; font:600 11px var(--font-sans); letter-spacing:.06em; text-transform:uppercase; color:var(--accent-gold);}

/* The sitewide :focus-visible ring (--state-focus, deep green) reads
   at ~1.7:1 against this section's near-black background — under the
   3:1 WCAG non-text contrast minimum. Scoped fix, not a change to
   the global token: everywhere else --state-focus sits on a light
   surface and is fine. Gold measures ~6.6:1 here and is already the
   site's device for "this is active/selected". */
.site-footer :focus-visible{outline-color:var(--accent-gold);}

@media (prefers-reduced-motion: reduce){
  .footer-link, .footer-social-link, .footer-column-links, .footer-column-toggle-icon::after{transition:none !important;}
}

/* =========================================================
   MOBILE — intentional, not shrunk
   ========================================================= */
@media (max-width: 860px){
  /* Phone top-bar declutter: the compact WhatsApp icon that used
     to sit here (see the 861-1179px tablet rule below, which still
     uses it) is removed at true phone widths — the top bar was
     starting to feel cluttered on the smallest screens, and WhatsApp
     is never actually lost: it's the strong, full-width branded action
     at the bottom of the opened mobile menu (see the
     `.mobile-menu-cta` rules above). The divider existed only to
     separate the WhatsApp icon from the toggle, so it goes too —
     logo left, hamburger right, via the existing `.nav-inner`
     `justify-content:space-between`, no new layout rule needed. */
  .nav-links{display:none;}
  .nav-cta{display:none;}
  .nav-divider{display:none;}
  .menu-toggle{display:flex;}
  .wordmark-logo{height:26px;}
  .nav-inner{gap:8px;}
  .section{padding:var(--space-8) var(--space-5);}
  .h1{font-size:34px;}
  .h2{font-size:26px;}

  /* Opening — a separate composition, not the desktop stage scaled
     down: the nav stays the normal solid/sticky bar (no overlay — see
     the min-width:861px scoping above), so the stage doesn't need to
     slide underneath it; the stage itself uses a shorter, portrait-
     friendlier viewport-relative height, and the copy sits with normal
     top padding rather than nav-overlay compensation. */
  /* Pass 4: previously clamp(480px, 72vh, 640px) — on many real phone
     viewports (e.g. 390×844, where 72vh is only 608px) that left a
     visible strip of Services on initial load, contradicting the same
     "hero owns the first screen" rule already applied on desktop. Now
     uses the identical 100vh/100svh pair the base rule already
     declares above (this override only needs to reset margin-top back
     to 0, since mobile's nav is the normal static/solid bar, never the
     overlay — that distinction, not viewport height, is the real
     reason this breakpoint exists). Still min-height, so an
     exceptionally short landscape viewport where the copy genuinely
     can't fit within 100svh grows the box taller instead of clipping
     anything — see Pass 4 notes. */
  .opening--stage{margin-top:0;}
  /* Mobile crops far more aggressively on the sides than desktop does
     top/bottom — landscape 3:2-ish sources in a narrow portrait box
     lose most of their horizontal extent. Each scene's position is
     biased to keep its meaningful subject (aircraft/skyline, pool/
     coastline, the Kaaba itself) in frame; foreground-left elements
     are sacrificed the way desktop's own object-position already
     sacrifices some edges. Dedicated mobile-cropped hero media should
     be generated later — these are best-fit crops of the same
     landscape-first source files, not purpose-built mobile art. */
  .stage-scene[data-scene-index="0"] .stage-scene-media{object-position:68% 38%;}
  .stage-scene[data-scene-index="1"] .stage-scene-media{object-position:74% 34%;}
  .stage-scene[data-scene-index="2"] .stage-scene-media{object-position:70% 32%;}
  .stage-scrim-top{height:120px;}
  .stage-fade-bottom{height:110px;}
  .stage-copy-wrap{padding:var(--space-6) var(--space-5) 0;}
  /* Pass 4: font-size no longer overridden here — the base rule's
     fluid clamp(34px, 8vw, 72px) already covers this entire range
     smoothly. max-width still needs overriding (mobile's copy spans
     full width, not the desktop 600px column). */
  .opening .h1{max-width:100%;}
  .opening-copy{max-width:100%;}
  /* Mobile copy spans nearly the full width rather than sitting in a
     left-aligned column, so the veil widens to match (90% vs desktop's
     60%) and centers higher, where the copy actually starts. Not
     visually verified — see report. */
  .stage-copy-veil{
    background:radial-gradient(ellipse 92% 60% at 50% 30%,
      rgba(247,245,239,.95) 0%,
      rgba(247,245,239,.88) 35%,
      rgba(247,245,239,.5) 58%,
      rgba(247,245,239,0) 82%);
  }
  /* Secondary editorial details simplified rather than removed: the
     scroll cue drops its label (dot + line alone still reads as
     "scroll"), and the scene nav shrinks/tightens so neither competes
     with the CTA row for thumb space on a small screen. Pass 4: the
     left/right/bottom insets themselves are no longer overridden here
     — the base rules' fluid clamp(16px, 4vw, 32px) already covers
     this whole width range smoothly (see above); only the
     mobile-specific simplifications (gap, button size, hiding the
     label) still belong in this block. */
  .stage-scroll-cue{gap:0;}
  .stage-scroll-cue-label{display:none;}
  .stage-scene-nav{gap:10px;}
  .stage-scene-btn{width:28px; height:28px;}
  /* Pass 3: mobile can't rely on a faint border over the photograph
     the way desktop's larger, calmer crop can — the hero secondary
     WhatsApp CTA gets a more opaque cream fill here than its desktop
     value so it stays reliably readable over all three scenes on a
     small screen, not just the calmer ones. Size unchanged — this is
     a readability fix, not a touch-target change.
     Pass 4: bumped .86 -> .90. This session's tooling can't actually
     render narrow viewports to judge the result directly (see Pass 4
     notes), so rather than trust the previous value untested, the
     margin was widened — at .90 the button's own surface, not the
     photo, is what's carrying contrast in every realistic case. */
  .opening .btn-secondary{background:rgba(247,245,239,.9);}
  /* Pass 4: the two hero CTAs previously matched in height/padding but
     could still end up looking unrelated on mobile — flex-wrap let
     the shorter "WhatsApp Us" drop to its own line beneath the
     full-width-looking primary, reading as an afterthought rather
     than a deliberate pair. Stacked, both full-width, hierarchy still
     carried entirely by fill color (emerald vs. cream) per the brief —
     not by size. */
  .opening .cta-row{flex-direction:column; align-items:stretch; gap:12px;}
  .opening .btn{width:100%;}

  /* Services, visual rebuild against services-reference-v2.png — an
     independent mobile composition (not desktop shrunk), matching the
     reference's own mobile mockup: a compact single-row horizontal-
     scroll icon+label nav, the stage directly below, its own on-image
     prev/next controls, and the ivory panel emerging from/overlapping
     the image's lower edge via a negative top margin. */
  .services-head{text-align:left; max-width:none;}
  /* Left-aligned on mobile, so the leading rule would indent the
     eyebrow off the content edge — drop it; the trailing rule matches
     Discover/How-it-works' "EYEBROW ——" pattern. */
  .services-eyebrow-row{justify-content:flex-start;}
  .services-eyebrow-row .gold-rule:first-child{display:none;}

  /* Correction pass: the full-bleed negative margin moved from
     .service-nav onto .service-nav-wrap, so the edge-fade masks and
     scroll chevrons below (new this pass — see the brief, "Mobile
     Swipe / Scroll Affordance") sit flush with the section's real
     visual edges instead of the narrower unbled wrap box. */
  /* The bleed must equal the mobile .section gutter (--space-5): a
     --space-6 bleed overhung the viewport by 8px at every phone width,
     making the whole document pannable sideways (the right-edge strip). */
  .service-nav-wrap{margin:0 calc(var(--space-5) * -1) var(--space-5);}
  .service-nav{
    flex-wrap:nowrap; justify-content:flex-start; overflow-x:auto; gap:22px;
    padding:2px var(--space-5) 6px; scroll-behavior:smooth;
    -webkit-overflow-scrolling:touch; scroll-padding-inline:var(--space-5);
    /* The edge fades + chevrons are the scroll affordance; a classic
       (non-overlay) scrollbar under the rail is visual noise. */
    scrollbar-width:none;
  }
  .service-nav::-webkit-scrollbar{display:none;}
  .service-nav-item{flex:0 0 auto;}
  .service-nav-icon{width:46px; height:46px;}
  .service-nav-label{font-size:11.5px; white-space:nowrap;}

  /* Edge fades — quiet gradient masks hinting more services sit off-
     screen, shown/hidden per scroll position (has-scroll-left/-right
     classes toggled in main.js, mirroring the chevrons' own
     [hidden] state). aria-hidden via CSS-only decoration (no DOM
     node), so nothing needs an explicit aria-hidden attribute. */
  .service-nav-wrap::before, .service-nav-wrap::after{
    content:""; position:absolute; top:0; bottom:6px; width:28px; z-index:2;
    opacity:0; transition:opacity .2s ease; pointer-events:none;
  }
  .service-nav-wrap::before{left:0; background:linear-gradient(to right, var(--surface-page), rgba(247,245,239,0));}
  .service-nav-wrap::after{right:0; background:linear-gradient(to left, var(--surface-page), rgba(247,245,239,0));}
  .service-nav-wrap.has-scroll-left::before{opacity:1;}
  .service-nav-wrap.has-scroll-right::after{opacity:1;}

  /* Chevrons: small quiet circular buttons at each bled edge, only
     ever visible when that direction actually has more to reveal
     (main.js sets [hidden] from real scrollWidth/scrollLeft math). */
  .service-nav-scroll-btn{
    display:flex; align-items:center; justify-content:center;
    position:absolute; top:20px; z-index:3; width:26px; height:26px;
    border-radius:50%; border:1px solid var(--border-strong); background:var(--surface-card);
    color:var(--ink-primary); padding:0; cursor:pointer;
  }
  .service-nav-scroll-btn--prev{left:6px;}
  .service-nav-scroll-btn--next{right:6px;}
  .service-nav-scroll-btn[hidden]{display:none;}

  /* Desktop-only separator lines between nav items (see the base rule
     above) — never shown on mobile, per the brief. */
  .service-nav-sep{display:none;}

  /* Halo re-tuned smaller than desktop's (108px) so it doesn't touch
     neighbouring items at mobile's tighter 46px icon size / 22px gap
     — still clearly stronger than the pre-correction-pass halo. */
  .service-nav-item::before{top:-14px; width:78px; height:78px;}

  .service-stage{aspect-ratio:4/3; border-radius:18px;}
  .service-panel{margin:-56px 6px 0; padding:20px 18px 24px; border-radius:16px;}
  .service-panel-title{font-size:24px;}
  /* Mobile CTA is centered, not left-aligned, per direct reference
     review — a deliberate, substantial centered action inside the
     panel rather than the desktop's left-anchored button. */
  .service-panel-cta{align-self:center; width:min(100%, 280px); text-align:center;}

  /* The triptych's three panels span the full source image's width —
     a narrow mobile cover-crop would trim the outer two, so this one
     image letterboxes (brand-green fill, the same honest flat-fallback
     color used sitewide) instead of cropping away part of the concept
     it illustrates. See capabilities.php. */
  .service-stage-media--assistance{object-fit:contain; background:var(--brand-green);}

  .service-footer-labels{display:none;}

  /* Footer — "MOBILE — ALTERNATIVE" from footer-reference-v1.png: the
     scenic photo reads as a full background layer (more present than
     desktop), darkening toward the bottom so the accordion/app/legal
     content stays readable — a vertical veil instead of desktop's
     horizontal one, tuned independently per the brief rather than
     assuming the desktop crop/positioning works here.
     Correction pass: full-bleed (no outer radius/cream gutter, same as
     desktop — see the un-prefixed `.site-footer` rule above) and
     deliberately compact — every gap/padding below was tightened from
     the first pass, which measured noticeably taller than it needed to
     at 390px. */
  .footer-media-img{object-position:64% 30%;}
  .footer-media-veil{
    background:linear-gradient(180deg, rgba(4,20,15,.22) 0%, rgba(4,20,15,.5) 32%, rgba(4,20,15,.86) 62%, var(--surface-deepest) 88%);
  }
  .footer-main{flex-direction:column; align-items:stretch; gap:var(--space-5); padding:var(--space-7) var(--space-5) 0;}
  /* Real bug caught while measuring the height reduction below: the
     desktop `.footer-brand{flex:0 0 240px}` / `.footer-app{flex:0 0
     260px}` set a flex-basis meant to control WIDTH in the desktop row
     layout — but `.footer-main` is flex-direction:column here, so that
     same flex-basis pins their HEIGHT to 240px/260px instead, padding
     both blocks with dead space regardless of how short their actual
     content is. Resetting to `flex:0 0 auto` was most of the real
     "mobile footer too long" fix, well before any padding/gap tuning. */
  /* Second mobile pass — brand block as one signature: the light
     horizontal lockup (see footer.php's <picture>) as a single line,
     the brand sentence set directly beneath it at a comfortable
     measure, socials (when configured) closing the group. Gutters
     match the --space-5 content edge of every section above, so the
     page's left alignment carries straight into the footer. */
  .footer-brand{flex:0 0 auto; max-width:none; gap:14px;}
  .footer-logo{width:auto; height:40px; max-width:100%;}
  .footer-brand-copy{max-width:32ch; font-size:14.5px; color:var(--ink-inverse);}
  .footer-social-link{width:36px; height:36px;}
  .footer-social{gap:10px; margin-top:2px;}
  .footer-divider{display:none;}
  /* Same column-direction flex-basis bug as .footer-brand/.footer-app
     (see above): desktop's `flex:1 1 320px` pinned the accordion block
     to 320px tall here, leaving an empty band of photo under Help. */
  .footer-columns{flex:0 0 auto; flex-direction:column; gap:0; border-top:1px solid rgba(242,240,232,.16);}
  .footer-column{gap:0; border-bottom:1px solid rgba(242,240,232,.16);}
  .footer-column-toggle{
    width:100%; justify-content:space-between; padding:10px 2px; min-height:44px; cursor:pointer;
  }
  .footer-column-toggle-icon{
    display:block; position:relative; width:14px; height:14px; flex-shrink:0; color:var(--accent-gold);
  }
  .footer-column-toggle-icon::before, .footer-column-toggle-icon::after{
    content:''; position:absolute; top:50%; left:50%; background:currentColor; transform:translate(-50%,-50%);
  }
  .footer-column-toggle-icon::before{width:14px; height:1.5px;}
  .footer-column-toggle-icon::after{width:1.5px; height:14px; transition:transform .2s var(--ease);}
  .footer-column-toggle[aria-expanded="true"] .footer-column-toggle-icon::after{transform:translate(-50%,-50%) scaleY(0);}
  /* Collapsed/expanded state itself is JS-applied (see main.js, "footer:
     mobile accordion") only once it has detected this is the mobile
     breakpoint — the links are fully present and visible without JS
     (this class is simply never added), same "content first, motion is
     enhancement only" approach used everywhere else on the page. */
  .footer-column-links.is-collapsible{max-height:0; overflow:hidden; opacity:0;}
  @media (prefers-reduced-motion: no-preference){
    .footer-column-links.is-collapsible{transition:max-height .25s var(--ease), opacity .2s ease;}
  }
  .footer-column-links.is-collapsible.is-open{max-height:400px; opacity:1; padding-bottom:12px;}

  /* App block — the single biggest height-saving target the brief
     flags: `flex:0 0 auto` (see the `.footer-brand` comment above —
     same bug, same fix), tighter padding/gaps, and a smaller
     equal-size badge slot (still object-fit:contain, still
     undistorted) so both badges comfortably share one row at
     360-430px instead of wrapping to two — verified live, see the
     production report. */
  .footer-app{flex:0 0 auto; max-width:none; padding:12px 14px; gap:5px;}
  .footer-app-badges{gap:8px; margin-top:2px;}
  .footer-store-badge{gap:6px; padding:6px 8px;}
  .footer-store-badge-mark{width:19px; height:19px;}
  .footer-app-badge-text small{font-size:8px;}
  .footer-app-badge-text span{font-size:11.5px;}

  .footer-bottom{gap:6px; margin-top:var(--space-5); padding:16px var(--space-5) 20px;}
}

/* =========================================================
   TABLET INTERMEDIATE TIER — Pass 4
   ========================================================= */
/* The phone-tuned crops/veil above (max-width:860px) and the
   desktop-tuned ones (min-width:861px) were each calibrated for a
   genuinely different aspect ratio — a narrow phone portrait
   (~0.45-0.5 width/height) versus a wide desktop frame. A tablet in
   portrait (e.g. 768x1024, 820x1180) is neither: squarer than a
   phone, still narrower than desktop, and it was inheriting the raw
   phone values wholesale (they share the same max-width:860px bucket)
   with no adaptation in between — exactly the "one generic mobile
   crop for all scenes" the brief flags in point 8, and the reason
   goal 4 asks the veil to adapt as the crop changes rather than
   staying fixed. This band sits inside the existing mobile bucket
   (600-860px covers large phones in landscape and tablets in
   portrait) and only overrides the two things that are actually
   aspect-ratio-sensitive — object-position and the veil's own
   ellipse — nothing else in the mobile composition (copy width,
   scroll cue, scene controls, CTA layout) changes here. Values are
   reasoned from the same crop logic as the existing phone/desktop
   tiers (interpolated between them, keeping each scene's real subject
   — the tarmac/aircraft, the pool/coastline, the Kaaba — in frame) and
   from the box math of a squarer aspect ratio; not visually confirmed
   live this pass — see Pass 4 notes on this session's tooling
   limitation. */
@media (min-width:600px) and (max-width:860px){
  .stage-scene[data-scene-index="0"] .stage-scene-media{object-position:65% 42%;}
  .stage-scene[data-scene-index="1"] .stage-scene-media{object-position:71% 38%;}
  .stage-scene[data-scene-index="2"] .stage-scene-media{object-position:68% 36%;}
  /* Narrower and shorter than the phone ellipse (which was sized for
     a much taller, narrower box) — still centered over the full-width
     mobile-style copy layout, still fully opaque at its core, still
     nothing but opacity/position/size tuning (no new overlay system,
     per the brief). */
  .stage-copy-veil{
    background:radial-gradient(ellipse 74% 56% at 50% 28%,
      rgba(247,245,239,.92) 0%,
      rgba(247,245,239,.8) 32%,
      rgba(247,245,239,.4) 56%,
      rgba(247,245,239,0) 78%);
  }
}
